You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ag...@apache.org on 2014/01/15 17:37:01 UTC

[1/5] CB-5799 Update version of OkHTTP to 1.3

Updated Branches:
  refs/heads/master a643c3dba -> e16cab6b9


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/tls/DistinguishedNameParser.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/tls/DistinguishedNameParser.java b/framework/src/com/squareup/okhttp/internal/tls/DistinguishedNameParser.java
new file mode 100755
index 0000000..e0aef14
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/internal/tls/DistinguishedNameParser.java
@@ -0,0 +1,407 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.internal.tls;
+
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * A distinguished name (DN) parser. This parser only supports extracting a
+ * string value from a DN. It doesn't support values in the hex-string style.
+ */
+final class DistinguishedNameParser {
+  private final String dn;
+  private final int length;
+  private int pos;
+  private int beg;
+  private int end;
+
+  /** Temporary variable to store positions of the currently parsed item. */
+  private int cur;
+
+  /** Distinguished name characters. */
+  private char[] chars;
+
+  public DistinguishedNameParser(X500Principal principal) {
+    // RFC2253 is used to ensure we get attributes in the reverse
+    // order of the underlying ASN.1 encoding, so that the most
+    // significant values of repeated attributes occur first.
+    this.dn = principal.getName(X500Principal.RFC2253);
+    this.length = this.dn.length();
+  }
+
+  // gets next attribute type: (ALPHA 1*keychar) / oid
+  private String nextAT() {
+    // skip preceding space chars, they can present after
+    // comma or semicolon (compatibility with RFC 1779)
+    for (; pos < length && chars[pos] == ' '; pos++) {
+    }
+    if (pos == length) {
+      return null; // reached the end of DN
+    }
+
+    // mark the beginning of attribute type
+    beg = pos;
+
+    // attribute type chars
+    pos++;
+    for (; pos < length && chars[pos] != '=' && chars[pos] != ' '; pos++) {
+      // we don't follow exact BNF syntax here:
+      // accept any char except space and '='
+    }
+    if (pos >= length) {
+      throw new IllegalStateException("Unexpected end of DN: " + dn);
+    }
+
+    // mark the end of attribute type
+    end = pos;
+
+    // skip trailing space chars between attribute type and '='
+    // (compatibility with RFC 1779)
+    if (chars[pos] == ' ') {
+      for (; pos < length && chars[pos] != '=' && chars[pos] == ' '; pos++) {
+      }
+
+      if (chars[pos] != '=' || pos == length) {
+        throw new IllegalStateException("Unexpected end of DN: " + dn);
+      }
+    }
+
+    pos++; //skip '=' char
+
+    // skip space chars between '=' and attribute value
+    // (compatibility with RFC 1779)
+    for (; pos < length && chars[pos] == ' '; pos++) {
+    }
+
+    // in case of oid attribute type skip its prefix: "oid." or "OID."
+    // (compatibility with RFC 1779)
+    if ((end - beg > 4) && (chars[beg + 3] == '.')
+        && (chars[beg] == 'O' || chars[beg] == 'o')
+        && (chars[beg + 1] == 'I' || chars[beg + 1] == 'i')
+        && (chars[beg + 2] == 'D' || chars[beg + 2] == 'd')) {
+      beg += 4;
+    }
+
+    return new String(chars, beg, end - beg);
+  }
+
+  // gets quoted attribute value: QUOTATION *( quotechar / pair ) QUOTATION
+  private String quotedAV() {
+    pos++;
+    beg = pos;
+    end = beg;
+    while (true) {
+
+      if (pos == length) {
+        throw new IllegalStateException("Unexpected end of DN: " + dn);
+      }
+
+      if (chars[pos] == '"') {
+        // enclosing quotation was found
+        pos++;
+        break;
+      } else if (chars[pos] == '\\') {
+        chars[end] = getEscaped();
+      } else {
+        // shift char: required for string with escaped chars
+        chars[end] = chars[pos];
+      }
+      pos++;
+      end++;
+    }
+
+    // skip trailing space chars before comma or semicolon.
+    // (compatibility with RFC 1779)
+    for (; pos < length && chars[pos] == ' '; pos++) {
+    }
+
+    return new String(chars, beg, end - beg);
+  }
+
+  // gets hex string attribute value: "#" hexstring
+  private String hexAV() {
+    if (pos + 4 >= length) {
+      // encoded byte array  must be not less then 4 c
+      throw new IllegalStateException("Unexpected end of DN: " + dn);
+    }
+
+    beg = pos; // store '#' position
+    pos++;
+    while (true) {
+
+      // check for end of attribute value
+      // looks for space and component separators
+      if (pos == length || chars[pos] == '+' || chars[pos] == ','
+          || chars[pos] == ';') {
+        end = pos;
+        break;
+      }
+
+      if (chars[pos] == ' ') {
+        end = pos;
+        pos++;
+        // skip trailing space chars before comma or semicolon.
+        // (compatibility with RFC 1779)
+        for (; pos < length && chars[pos] == ' '; pos++) {
+        }
+        break;
+      } else if (chars[pos] >= 'A' && chars[pos] <= 'F') {
+        chars[pos] += 32; //to low case
+      }
+
+      pos++;
+    }
+
+    // verify length of hex string
+    // encoded byte array  must be not less then 4 and must be even number
+    int hexLen = end - beg; // skip first '#' char
+    if (hexLen < 5 || (hexLen & 1) == 0) {
+      throw new IllegalStateException("Unexpected end of DN: " + dn);
+    }
+
+    // get byte encoding from string representation
+    byte[] encoded = new byte[hexLen / 2];
+    for (int i = 0, p = beg + 1; i < encoded.length; p += 2, i++) {
+      encoded[i] = (byte) getByte(p);
+    }
+
+    return new String(chars, beg, hexLen);
+  }
+
+  // gets string attribute value: *( stringchar / pair )
+  private String escapedAV() {
+    beg = pos;
+    end = pos;
+    while (true) {
+      if (pos >= length) {
+        // the end of DN has been found
+        return new String(chars, beg, end - beg);
+      }
+
+      switch (chars[pos]) {
+        case '+':
+        case ',':
+        case ';':
+          // separator char has been found
+          return new String(chars, beg, end - beg);
+        case '\\':
+          // escaped char
+          chars[end++] = getEscaped();
+          pos++;
+          break;
+        case ' ':
+          // need to figure out whether space defines
+          // the end of attribute value or not
+          cur = end;
+
+          pos++;
+          chars[end++] = ' ';
+
+          for (; pos < length && chars[pos] == ' '; pos++) {
+            chars[end++] = ' ';
+          }
+          if (pos == length || chars[pos] == ',' || chars[pos] == '+'
+              || chars[pos] == ';') {
+            // separator char or the end of DN has been found
+            return new String(chars, beg, cur - beg);
+          }
+          break;
+        default:
+          chars[end++] = chars[pos];
+          pos++;
+      }
+    }
+  }
+
+  // returns escaped char
+  private char getEscaped() {
+    pos++;
+    if (pos == length) {
+      throw new IllegalStateException("Unexpected end of DN: " + dn);
+    }
+
+    switch (chars[pos]) {
+      case '"':
+      case '\\':
+      case ',':
+      case '=':
+      case '+':
+      case '<':
+      case '>':
+      case '#':
+      case ';':
+      case ' ':
+      case '*':
+      case '%':
+      case '_':
+        //FIXME: escaping is allowed only for leading or trailing space char
+        return chars[pos];
+      default:
+        // RFC doesn't explicitly say that escaped hex pair is
+        // interpreted as UTF-8 char. It only contains an example of such DN.
+        return getUTF8();
+    }
+  }
+
+  // decodes UTF-8 char
+  // see http://www.unicode.org for UTF-8 bit distribution table
+  private char getUTF8() {
+    int res = getByte(pos);
+    pos++; //FIXME tmp
+
+    if (res < 128) { // one byte: 0-7F
+      return (char) res;
+    } else if (res >= 192 && res <= 247) {
+
+      int count;
+      if (res <= 223) { // two bytes: C0-DF
+        count = 1;
+        res = res & 0x1F;
+      } else if (res <= 239) { // three bytes: E0-EF
+        count = 2;
+        res = res & 0x0F;
+      } else { // four bytes: F0-F7
+        count = 3;
+        res = res & 0x07;
+      }
+
+      int b;
+      for (int i = 0; i < count; i++) {
+        pos++;
+        if (pos == length || chars[pos] != '\\') {
+          return 0x3F; //FIXME failed to decode UTF-8 char - return '?'
+        }
+        pos++;
+
+        b = getByte(pos);
+        pos++; //FIXME tmp
+        if ((b & 0xC0) != 0x80) {
+          return 0x3F; //FIXME failed to decode UTF-8 char - return '?'
+        }
+
+        res = (res << 6) + (b & 0x3F);
+      }
+      return (char) res;
+    } else {
+      return 0x3F; //FIXME failed to decode UTF-8 char - return '?'
+    }
+  }
+
+  // Returns byte representation of a char pair
+  // The char pair is composed of DN char in
+  // specified 'position' and the next char
+  // According to BNF syntax:
+  // hexchar    = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
+  //                    / "a" / "b" / "c" / "d" / "e" / "f"
+  private int getByte(int position) {
+    if (position + 1 >= length) {
+      throw new IllegalStateException("Malformed DN: " + dn);
+    }
+
+    int b1, b2;
+
+    b1 = chars[position];
+    if (b1 >= '0' && b1 <= '9') {
+      b1 = b1 - '0';
+    } else if (b1 >= 'a' && b1 <= 'f') {
+      b1 = b1 - 87; // 87 = 'a' - 10
+    } else if (b1 >= 'A' && b1 <= 'F') {
+      b1 = b1 - 55; // 55 = 'A' - 10
+    } else {
+      throw new IllegalStateException("Malformed DN: " + dn);
+    }
+
+    b2 = chars[position + 1];
+    if (b2 >= '0' && b2 <= '9') {
+      b2 = b2 - '0';
+    } else if (b2 >= 'a' && b2 <= 'f') {
+      b2 = b2 - 87; // 87 = 'a' - 10
+    } else if (b2 >= 'A' && b2 <= 'F') {
+      b2 = b2 - 55; // 55 = 'A' - 10
+    } else {
+      throw new IllegalStateException("Malformed DN: " + dn);
+    }
+
+    return (b1 << 4) + b2;
+  }
+
+  /**
+   * Parses the DN and returns the most significant attribute value
+   * for an attribute type, or null if none found.
+   *
+   * @param attributeType attribute type to look for (e.g. "ca")
+   */
+  public String findMostSpecific(String attributeType) {
+    // Initialize internal state.
+    pos = 0;
+    beg = 0;
+    end = 0;
+    cur = 0;
+    chars = dn.toCharArray();
+
+    String attType = nextAT();
+    if (attType == null) {
+      return null;
+    }
+    while (true) {
+      String attValue = "";
+
+      if (pos == length) {
+        return null;
+      }
+
+      switch (chars[pos]) {
+        case '"':
+          attValue = quotedAV();
+          break;
+        case '#':
+          attValue = hexAV();
+          break;
+        case '+':
+        case ',':
+        case ';': // compatibility with RFC 1779: semicolon can separate RDNs
+          //empty attribute value
+          break;
+        default:
+          attValue = escapedAV();
+      }
+
+      // Values are ordered from most specific to least specific
+      // due to the RFC2253 formatting. So take the first match
+      // we see.
+      if (attributeType.equalsIgnoreCase(attType)) {
+        return attValue;
+      }
+
+      if (pos >= length) {
+        return null;
+      }
+
+      if (chars[pos] == ',' || chars[pos] == ';') {
+      } else if (chars[pos] != '+') {
+        throw new IllegalStateException("Malformed DN: " + dn);
+      }
+
+      pos++;
+      attType = nextAT();
+      if (attType == null) {
+        throw new IllegalStateException("Malformed DN: " + dn);
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/tls/OkHostnameVerifier.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/tls/OkHostnameVerifier.java b/framework/src/com/squareup/okhttp/internal/tls/OkHostnameVerifier.java
new file mode 100755
index 0000000..a08773f
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/internal/tls/OkHostnameVerifier.java
@@ -0,0 +1,194 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.internal.tls;
+
+import java.security.cert.Certificate;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Pattern;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * A HostnameVerifier consistent with <a
+ * href="http://www.ietf.org/rfc/rfc2818.txt">RFC 2818</a>.
+ */
+public final class OkHostnameVerifier implements HostnameVerifier {
+  public static final OkHostnameVerifier INSTANCE = new OkHostnameVerifier();
+
+  /**
+   * Quick and dirty pattern to differentiate IP addresses from hostnames. This
+   * is an approximation of Android's private InetAddress#isNumeric API.
+   *
+   * <p>This matches IPv6 addresses as a hex string containing at least one
+   * colon, and possibly including dots after the first colon. It matches IPv4
+   * addresses as strings containing only decimal digits and dots. This pattern
+   * matches strings like "a:.23" and "54" that are neither IP addresses nor
+   * hostnames; they will be verified as IP addresses (which is a more strict
+   * verification).
+   */
+  private static final Pattern VERIFY_AS_IP_ADDRESS = Pattern.compile(
+      "([0-9a-fA-F]*:[0-9a-fA-F:.]*)|([\\d.]+)");
+
+  private static final int ALT_DNS_NAME = 2;
+  private static final int ALT_IPA_NAME = 7;
+
+  private OkHostnameVerifier() {
+  }
+
+  public boolean verify(String host, SSLSession session) {
+    try {
+      Certificate[] certificates = session.getPeerCertificates();
+      return verify(host, (X509Certificate) certificates[0]);
+    } catch (SSLException e) {
+      return false;
+    }
+  }
+
+  public boolean verify(String host, X509Certificate certificate) {
+    return verifyAsIpAddress(host)
+        ? verifyIpAddress(host, certificate)
+        : verifyHostName(host, certificate);
+  }
+
+  static boolean verifyAsIpAddress(String host) {
+    return VERIFY_AS_IP_ADDRESS.matcher(host).matches();
+  }
+
+  /**
+   * Returns true if {@code certificate} matches {@code ipAddress}.
+   */
+  private boolean verifyIpAddress(String ipAddress, X509Certificate certificate) {
+    for (String altName : getSubjectAltNames(certificate, ALT_IPA_NAME)) {
+      if (ipAddress.equalsIgnoreCase(altName)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Returns true if {@code certificate} matches {@code hostName}.
+   */
+  private boolean verifyHostName(String hostName, X509Certificate certificate) {
+    hostName = hostName.toLowerCase(Locale.US);
+    boolean hasDns = false;
+    for (String altName : getSubjectAltNames(certificate, ALT_DNS_NAME)) {
+      hasDns = true;
+      if (verifyHostName(hostName, altName)) {
+        return true;
+      }
+    }
+
+    if (!hasDns) {
+      X500Principal principal = certificate.getSubjectX500Principal();
+      // RFC 2818 advises using the most specific name for matching.
+      String cn = new DistinguishedNameParser(principal).findMostSpecific("cn");
+      if (cn != null) {
+        return verifyHostName(hostName, cn);
+      }
+    }
+
+    return false;
+  }
+
+  private List<String> getSubjectAltNames(X509Certificate certificate, int type) {
+    List<String> result = new ArrayList<String>();
+    try {
+      Collection<?> subjectAltNames = certificate.getSubjectAlternativeNames();
+      if (subjectAltNames == null) {
+        return Collections.emptyList();
+      }
+      for (Object subjectAltName : subjectAltNames) {
+        List<?> entry = (List<?>) subjectAltName;
+        if (entry == null || entry.size() < 2) {
+          continue;
+        }
+        Integer altNameType = (Integer) entry.get(0);
+        if (altNameType == null) {
+          continue;
+        }
+        if (altNameType == type) {
+          String altName = (String) entry.get(1);
+          if (altName != null) {
+            result.add(altName);
+          }
+        }
+      }
+      return result;
+    } catch (CertificateParsingException e) {
+      return Collections.emptyList();
+    }
+  }
+
+  /**
+   * Returns true if {@code hostName} matches the name or pattern {@code cn}.
+   *
+   * @param hostName lowercase host name.
+   * @param cn certificate host name. May include wildcards like
+   *     {@code *.android.com}.
+   */
+  public boolean verifyHostName(String hostName, String cn) {
+    // Check length == 0 instead of .isEmpty() to support Java 5.
+    if (hostName == null || hostName.length() == 0 || cn == null || cn.length() == 0) {
+      return false;
+    }
+
+    cn = cn.toLowerCase(Locale.US);
+
+    if (!cn.contains("*")) {
+      return hostName.equals(cn);
+    }
+
+    if (cn.startsWith("*.") && hostName.regionMatches(0, cn, 2, cn.length() - 2)) {
+      return true; // "*.foo.com" matches "foo.com"
+    }
+
+    int asterisk = cn.indexOf('*');
+    int dot = cn.indexOf('.');
+    if (asterisk > dot) {
+      return false; // malformed; wildcard must be in the first part of the cn
+    }
+
+    if (!hostName.regionMatches(0, cn, 0, asterisk)) {
+      return false; // prefix before '*' doesn't match
+    }
+
+    int suffixLength = cn.length() - (asterisk + 1);
+    int suffixStart = hostName.length() - suffixLength;
+    if (hostName.indexOf('.', asterisk) < suffixStart) {
+      // TODO: remove workaround for *.clients.google.com http://b/5426333
+      if (!hostName.endsWith(".clients.google.com")) {
+        return false; // wildcard '*' can't match a '.'
+      }
+    }
+
+    if (!hostName.regionMatches(suffixStart, cn, asterisk + 1, suffixLength)) {
+      return false; // suffix after '*' doesn't match
+    }
+
+    return true;
+  }
+}


[5/5] android commit: CB-5799 Update version of OkHTTP to 1.3

Posted by ag...@apache.org.
CB-5799 Update version of OkHTTP to 1.3


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/e16cab6b
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/e16cab6b
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/e16cab6b

Branch: refs/heads/master
Commit: e16cab6b9c685590fe58357ab66d7f6af55eb7be
Parents: a643c3d
Author: Andrew Grieve <ag...@chromium.org>
Authored: Wed Jan 15 11:36:18 2014 -0500
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Wed Jan 15 11:36:43 2014 -0500

----------------------------------------------------------------------
 framework/src/com/squareup/okhttp/Address.java  |  33 +-
 .../src/com/squareup/okhttp/Connection.java     |  78 ++-
 .../src/com/squareup/okhttp/ConnectionPool.java |   9 +-
 .../src/com/squareup/okhttp/Dispatcher.java     |  86 +++
 framework/src/com/squareup/okhttp/Failure.java  |  59 ++
 .../com/squareup/okhttp/HttpResponseCache.java  | 119 ++--
 framework/src/com/squareup/okhttp/Job.java      | 232 +++++++
 .../src/com/squareup/okhttp/MediaType.java      | 120 ++++
 .../com/squareup/okhttp/OkAuthenticator.java    | 123 ++++
 .../src/com/squareup/okhttp/OkHttpClient.java   | 228 ++++++-
 .../com/squareup/okhttp/OkResponseCache.java    |  36 +-
 framework/src/com/squareup/okhttp/Request.java  | 284 +++++++++
 framework/src/com/squareup/okhttp/Response.java | 290 +++++++++
 .../src/com/squareup/okhttp/ResponseSource.java |   0
 framework/src/com/squareup/okhttp/Route.java    |   6 +-
 .../src/com/squareup/okhttp/RouteDatabase.java  |  57 ++
 .../src/com/squareup/okhttp/TunnelRequest.java  |   0
 .../okhttp/internal/AbstractOutputStream.java   |   0
 .../com/squareup/okhttp/internal/Base64.java    |   0
 .../squareup/okhttp/internal/DiskLruCache.java  |   0
 .../src/com/squareup/okhttp/internal/Dns.java   |   0
 .../internal/FaultRecoveringOutputStream.java   |   0
 .../squareup/okhttp/internal/NamedRunnable.java |   6 +-
 .../com/squareup/okhttp/internal/Platform.java  | 117 ++--
 .../okhttp/internal/StrictLineReader.java       |   3 +-
 .../src/com/squareup/okhttp/internal/Util.java  |  67 ++
 .../internal/http/AbstractHttpInputStream.java  |   4 +-
 .../internal/http/AbstractHttpOutputStream.java |  40 --
 .../okhttp/internal/http/HeaderParser.java      |   8 +-
 .../okhttp/internal/http/HttpAuthenticator.java | 153 +++--
 .../squareup/okhttp/internal/http/HttpDate.java |  30 +-
 .../okhttp/internal/http/HttpEngine.java        | 108 ++--
 .../okhttp/internal/http/HttpResponseCache.java | 608 -------------------
 .../okhttp/internal/http/HttpTransport.java     |  48 +-
 .../internal/http/HttpURLConnectionImpl.java    | 226 ++++---
 .../okhttp/internal/http/HttpsEngine.java       |  72 +++
 .../internal/http/HttpsURLConnectionImpl.java   | 231 +++----
 .../okhttp/internal/http/OkResponseCache.java   |  55 --
 .../internal/http/OkResponseCacheAdapter.java   |   4 +
 .../squareup/okhttp/internal/http/Policy.java   |  49 ++
 .../okhttp/internal/http/RawHeaders.java        |  73 ++-
 .../okhttp/internal/http/RequestHeaders.java    |  39 +-
 .../okhttp/internal/http/ResponseHeaders.java   |  27 +-
 .../internal/http/RetryableOutputStream.java    |   0
 .../okhttp/internal/http/RouteSelector.java     |  28 +-
 .../okhttp/internal/http/SpdyTransport.java     |  21 +-
 .../okhttp/internal/http/Transport.java         |   2 +-
 .../http/UnknownLengthHttpInputStream.java      |   6 +-
 .../okhttp/internal/spdy/ErrorCode.java         |  67 ++
 .../okhttp/internal/spdy/FrameReader.java       |  55 ++
 .../okhttp/internal/spdy/FrameWriter.java       |  43 ++
 .../okhttp/internal/spdy/HeadersMode.java       |  49 ++
 .../squareup/okhttp/internal/spdy/Hpack.java    | 371 +++++++++++
 .../okhttp/internal/spdy/Http20Draft06.java     | 385 ++++++++++++
 .../internal/spdy/IncomingStreamHandler.java    |   2 +-
 .../internal/spdy/NameValueBlockReader.java     | 123 ++++
 .../com/squareup/okhttp/internal/spdy/Ping.java |   0
 .../squareup/okhttp/internal/spdy/Settings.java |  31 +-
 .../squareup/okhttp/internal/spdy/Spdy3.java    | 463 ++++++++++++++
 .../okhttp/internal/spdy/SpdyConnection.java    | 348 ++++++-----
 .../okhttp/internal/spdy/SpdyReader.java        | 326 ----------
 .../okhttp/internal/spdy/SpdyStream.java        | 217 +++----
 .../okhttp/internal/spdy/SpdyWriter.java        | 176 ------
 .../squareup/okhttp/internal/spdy/Variant.java  |  37 ++
 .../internal/tls/DistinguishedNameParser.java   | 407 +++++++++++++
 .../okhttp/internal/tls/OkHostnameVerifier.java | 194 ++++++
 66 files changed, 4912 insertions(+), 2167 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/Address.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/Address.java b/framework/src/com/squareup/okhttp/Address.java
old mode 100644
new mode 100755
index cd41ac9..b34bd91
--- a/framework/src/com/squareup/okhttp/Address.java
+++ b/framework/src/com/squareup/okhttp/Address.java
@@ -15,8 +15,10 @@
  */
 package com.squareup.okhttp;
 
+import com.squareup.okhttp.internal.Util;
 import java.net.Proxy;
 import java.net.UnknownHostException;
+import java.util.List;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.SSLSocketFactory;
 
@@ -38,16 +40,23 @@ public final class Address {
   final int uriPort;
   final SSLSocketFactory sslSocketFactory;
   final HostnameVerifier hostnameVerifier;
+  final OkAuthenticator authenticator;
+  final List<String> transports;
 
   public Address(String uriHost, int uriPort, SSLSocketFactory sslSocketFactory,
-      HostnameVerifier hostnameVerifier, Proxy proxy) throws UnknownHostException {
+      HostnameVerifier hostnameVerifier, OkAuthenticator authenticator, Proxy proxy,
+      List<String> transports) throws UnknownHostException {
     if (uriHost == null) throw new NullPointerException("uriHost == null");
     if (uriPort <= 0) throw new IllegalArgumentException("uriPort <= 0: " + uriPort);
+    if (authenticator == null) throw new IllegalArgumentException("authenticator == null");
+    if (transports == null) throw new IllegalArgumentException("transports == null");
     this.proxy = proxy;
     this.uriHost = uriHost;
     this.uriPort = uriPort;
     this.sslSocketFactory = sslSocketFactory;
     this.hostnameVerifier = hostnameVerifier;
+    this.authenticator = authenticator;
+    this.transports = Util.immutableList(transports);
   }
 
   /** Returns the hostname of the origin server. */
@@ -79,6 +88,22 @@ public final class Address {
     return hostnameVerifier;
   }
 
+
+  /**
+   * Returns the client's authenticator. This method never returns null.
+   */
+  public OkAuthenticator getAuthenticator() {
+    return authenticator;
+  }
+
+  /**
+   * Returns the client's transports. This method always returns a non-null list
+   * that contains "http/1.1", possibly among other transports.
+   */
+  public List<String> getTransports() {
+    return transports;
+  }
+
   /**
    * Returns this address's explicitly-specified HTTP proxy, or null to
    * delegate to the HTTP client's proxy selector.
@@ -94,7 +119,9 @@ public final class Address {
           && this.uriHost.equals(that.uriHost)
           && this.uriPort == that.uriPort
           && equal(this.sslSocketFactory, that.sslSocketFactory)
-          && equal(this.hostnameVerifier, that.hostnameVerifier);
+          && equal(this.hostnameVerifier, that.hostnameVerifier)
+          && equal(this.authenticator, that.authenticator)
+          && equal(this.transports, that.transports);
     }
     return false;
   }
@@ -105,7 +132,9 @@ public final class Address {
     result = 31 * result + uriPort;
     result = 31 * result + (sslSocketFactory != null ? sslSocketFactory.hashCode() : 0);
     result = 31 * result + (hostnameVerifier != null ? hostnameVerifier.hashCode() : 0);
+    result = 31 * result + (authenticator != null ? authenticator.hashCode() : 0);
     result = 31 * result + (proxy != null ? proxy.hashCode() : 0);
+    result = 31 * result + transports.hashCode();
     return result;
   }
 }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/Connection.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/Connection.java b/framework/src/com/squareup/okhttp/Connection.java
old mode 100644
new mode 100755
index 6a6c84d..6bb9cb3
--- a/framework/src/com/squareup/okhttp/Connection.java
+++ b/framework/src/com/squareup/okhttp/Connection.java
@@ -31,6 +31,7 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.Proxy;
 import java.net.Socket;
+import java.net.SocketTimeoutException;
 import java.net.URL;
 import java.util.Arrays;
 import javax.net.ssl.SSLSocket;
@@ -92,24 +93,20 @@ public final class Connection implements Closeable {
 
   public void connect(int connectTimeout, int readTimeout, TunnelRequest tunnelRequest)
       throws IOException {
-    if (connected) {
-      throw new IllegalStateException("already connected");
-    }
-    connected = true;
+    if (connected) throw new IllegalStateException("already connected");
+
     socket = (route.proxy.type() != Proxy.Type.HTTP) ? new Socket(route.proxy) : new Socket();
-    socket.connect(route.inetSocketAddress, connectTimeout);
+    Platform.get().connectSocket(socket, route.inetSocketAddress, connectTimeout);
     socket.setSoTimeout(readTimeout);
     in = socket.getInputStream();
     out = socket.getOutputStream();
 
     if (route.address.sslSocketFactory != null) {
       upgradeToTls(tunnelRequest);
+    } else {
+      streamWrapper();
     }
-
-    // Use MTU-sized buffers to send fewer packets.
-    int mtu = Platform.get().getMtu(socket);
-    in = new BufferedInputStream(in, mtu);
-    out = new BufferedOutputStream(out, mtu);
+    connected = true;
   }
 
   /**
@@ -134,7 +131,8 @@ public final class Connection implements Closeable {
       platform.supportTlsIntolerantServer(sslSocket);
     }
 
-    if (route.modernTls) {
+    boolean useNpn = route.modernTls && route.address.transports.contains("spdy/3");
+    if (useNpn) {
       platform.setNpnProtocols(sslSocket, NPN_PROTOCOLS);
     }
 
@@ -148,14 +146,15 @@ public final class Connection implements Closeable {
 
     out = sslSocket.getOutputStream();
     in = sslSocket.getInputStream();
+    streamWrapper();
 
     byte[] selectedProtocol;
-    if (route.modernTls
-        && (selectedProtocol = platform.getNpnSelectedProtocol(sslSocket)) != null) {
+    if (useNpn && (selectedProtocol = platform.getNpnSelectedProtocol(sslSocket)) != null) {
       if (Arrays.equals(selectedProtocol, SPDY3)) {
         sslSocket.setSoTimeout(0); // SPDY timeouts are set per-stream.
         spdyConnection = new SpdyConnection.Builder(route.address.getUriHost(), true, in, out)
             .build();
+        spdyConnection.sendConnectionHeader();
       } else if (!Arrays.equals(selectedProtocol, HTTP_11)) {
         throw new IOException(
             "Unexpected NPN transport " + new String(selectedProtocol, "ISO-8859-1"));
@@ -190,6 +189,39 @@ public final class Connection implements Closeable {
     return !socket.isClosed() && !socket.isInputShutdown() && !socket.isOutputShutdown();
   }
 
+  /**
+   * Returns true if we are confident that we can read data from this
+   * connection. This is more expensive and more accurate than {@link
+   * #isAlive()}; callers should check {@link #isAlive()} first.
+   */
+  public boolean isReadable() {
+    if (!(in instanceof BufferedInputStream)) {
+      return true; // Optimistic.
+    }
+    if (isSpdy()) {
+      return true; // Optimistic. We can't test SPDY because its streams are in use.
+    }
+    BufferedInputStream bufferedInputStream = (BufferedInputStream) in;
+    try {
+      int readTimeout = socket.getSoTimeout();
+      try {
+        socket.setSoTimeout(1);
+        bufferedInputStream.mark(1);
+        if (bufferedInputStream.read() == -1) {
+          return false; // Stream is exhausted; socket is closed.
+        }
+        bufferedInputStream.reset();
+        return true;
+      } finally {
+        socket.setSoTimeout(readTimeout);
+      }
+    } catch (SocketTimeoutException ignored) {
+      return true; // Read timed out; socket is good.
+    } catch (IOException e) {
+      return false; // Couldn't read; socket is closed.
+    }
+  }
+
   public void resetIdleStartTime() {
     if (spdyConnection != null) {
       throw new IllegalStateException("spdyConnection != null");
@@ -207,7 +239,7 @@ public final class Connection implements Closeable {
    * {@code keepAliveDurationNs}.
    */
   public boolean isExpired(long keepAliveDurationNs) {
-    return isIdle() && System.nanoTime() - getIdleStartTimeNs() > keepAliveDurationNs;
+    return getIdleStartTimeNs() < System.nanoTime() - keepAliveDurationNs;
   }
 
   /**
@@ -220,7 +252,8 @@ public final class Connection implements Closeable {
 
   /** Returns the transport appropriate for this connection. */
   public Object newTransport(HttpEngine httpEngine) throws IOException {
-    return (spdyConnection != null) ? new SpdyTransport(httpEngine, spdyConnection)
+    return (spdyConnection != null)
+        ? new SpdyTransport(httpEngine, spdyConnection)
         : new HttpTransport(httpEngine, out, in);
   }
 
@@ -258,6 +291,11 @@ public final class Connection implements Closeable {
     return route.address.sslSocketFactory != null && route.proxy.type() == Proxy.Type.HTTP;
   }
 
+  public void updateReadTimeout(int newTimeout) throws IOException {
+    if (!connected) throw new IllegalStateException("updateReadTimeout - not connected");
+    socket.setSoTimeout(newTimeout);
+  }
+
   /**
    * To make an HTTPS connection over an HTTP proxy, send an unencrypted
    * CONNECT request to create the proxy connection. This may need to be
@@ -275,8 +313,9 @@ public final class Connection implements Closeable {
         case HTTP_PROXY_AUTH:
           requestHeaders = new RawHeaders(requestHeaders);
           URL url = new URL("https", tunnelRequest.host, tunnelRequest.port, "/");
-          boolean credentialsFound = HttpAuthenticator.processAuthHeader(HTTP_PROXY_AUTH,
-              responseHeaders, requestHeaders, route.proxy, url);
+          boolean credentialsFound = HttpAuthenticator.processAuthHeader(
+              route.address.authenticator, HTTP_PROXY_AUTH, responseHeaders, requestHeaders,
+              route.proxy, url);
           if (credentialsFound) {
             continue;
           } else {
@@ -288,4 +327,9 @@ public final class Connection implements Closeable {
       }
     }
   }
+
+  private void streamWrapper() throws IOException {
+    in = new BufferedInputStream(in, 4096);
+    out = new BufferedOutputStream(out, 256);
+  }
 }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/ConnectionPool.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/ConnectionPool.java b/framework/src/com/squareup/okhttp/ConnectionPool.java
old mode 100644
new mode 100755
index 933bd73..42b70b9
--- a/framework/src/com/squareup/okhttp/ConnectionPool.java
+++ b/framework/src/com/squareup/okhttp/ConnectionPool.java
@@ -80,8 +80,9 @@ public class ConnectionPool {
   private final LinkedList<Connection> connections = new LinkedList<Connection>();
 
   /** We use a single background thread to cleanup expired connections. */
-  private final ExecutorService executorService =
-      new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
+  private final ExecutorService executorService = new ThreadPoolExecutor(0, 1,
+      60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
+      Util.daemonThreadFactory("OkHttp ConnectionPool"));
   private final Callable<Void> connectionsCleanupCallable = new Callable<Void>() {
     @Override public Void call() throws Exception {
       List<Connection> expiredConnections = new ArrayList<Connection>(MAX_CONNECTIONS_TO_CLEANUP);
@@ -215,8 +216,6 @@ public class ConnectionPool {
    * <p>It is an error to use {@code connection} after calling this method.
    */
   public void recycle(Connection connection) {
-    executorService.submit(connectionsCleanupCallable);
-
     if (connection.isSpdy()) {
       return;
     }
@@ -239,6 +238,8 @@ public class ConnectionPool {
       connections.addFirst(connection);
       connection.resetIdleStartTime();
     }
+
+    executorService.submit(connectionsCleanupCallable);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/Dispatcher.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/Dispatcher.java b/framework/src/com/squareup/okhttp/Dispatcher.java
new file mode 100755
index 0000000..1982a8a
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/Dispatcher.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2013 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;
+
+import com.squareup.okhttp.internal.http.ResponseHeaders;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+final class Dispatcher {
+  // TODO: thread pool size should be configurable; possibly configurable per host.
+  private final ThreadPoolExecutor executorService = new ThreadPoolExecutor(
+      8, 8, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
+  private final Map<Object, List<Job>> enqueuedJobs = new LinkedHashMap<Object, List<Job>>();
+
+  public synchronized void enqueue(
+      OkHttpClient client, Request request, Response.Receiver responseReceiver) {
+    Job job = new Job(this, client, request, responseReceiver);
+    List<Job> jobsForTag = enqueuedJobs.get(request.tag());
+    if (jobsForTag == null) {
+      jobsForTag = new ArrayList<Job>(2);
+      enqueuedJobs.put(request.tag(), jobsForTag);
+    }
+    jobsForTag.add(job);
+    executorService.execute(job);
+  }
+
+  public synchronized void cancel(Object tag) {
+    List<Job> jobs = enqueuedJobs.remove(tag);
+    if (jobs == null) return;
+    for (Job job : jobs) {
+      executorService.remove(job);
+    }
+  }
+
+  synchronized void finished(Job job) {
+    List<Job> jobs = enqueuedJobs.get(job.tag());
+    if (jobs != null) jobs.remove(job);
+  }
+
+  static class RealResponseBody extends Response.Body {
+    private final ResponseHeaders responseHeaders;
+    private final InputStream in;
+
+    RealResponseBody(ResponseHeaders responseHeaders, InputStream in) {
+      this.responseHeaders = responseHeaders;
+      this.in = in;
+    }
+
+    @Override public boolean ready() throws IOException {
+      return true;
+    }
+
+    @Override public MediaType contentType() {
+      String contentType = responseHeaders.getContentType();
+      return contentType != null ? MediaType.parse(contentType) : null;
+    }
+
+    @Override public long contentLength() {
+      return responseHeaders.getContentLength();
+    }
+
+    @Override public InputStream byteStream() throws IOException {
+      return in;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/Failure.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/Failure.java b/framework/src/com/squareup/okhttp/Failure.java
new file mode 100755
index 0000000..a354700
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/Failure.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2013 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;
+
+/**
+ * A failure attempting to retrieve an HTTP response.
+ *
+ * <h3>Warning: Experimental OkHttp 2.0 API</h3>
+ * This class is in beta. APIs are subject to change!
+ */
+/* OkHttp 2.0: public */ class Failure {
+  private final Request request;
+  private final Throwable exception;
+
+  private Failure(Builder builder) {
+    this.request = builder.request;
+    this.exception = builder.exception;
+  }
+
+  public Request request() {
+    return request;
+  }
+
+  public Throwable exception() {
+    return exception;
+  }
+
+  public static class Builder {
+    private Request request;
+    private Throwable exception;
+
+    public Builder request(Request request) {
+      this.request = request;
+      return this;
+    }
+
+    public Builder exception(Throwable exception) {
+      this.exception = exception;
+      return this;
+    }
+
+    public Failure build() {
+      return new Failure(this);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/HttpResponseCache.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/HttpResponseCache.java b/framework/src/com/squareup/okhttp/HttpResponseCache.java
old mode 100644
new mode 100755
index a6d380a..8210318
--- a/framework/src/com/squareup/okhttp/HttpResponseCache.java
+++ b/framework/src/com/squareup/okhttp/HttpResponseCache.java
@@ -22,8 +22,8 @@ import com.squareup.okhttp.internal.StrictLineReader;
 import com.squareup.okhttp.internal.Util;
 import com.squareup.okhttp.internal.http.HttpEngine;
 import com.squareup.okhttp.internal.http.HttpURLConnectionImpl;
+import com.squareup.okhttp.internal.http.HttpsEngine;
 import com.squareup.okhttp.internal.http.HttpsURLConnectionImpl;
-import com.squareup.okhttp.internal.http.OkResponseCache;
 import com.squareup.okhttp.internal.http.RawHeaders;
 import com.squareup.okhttp.internal.http.ResponseHeaders;
 import java.io.BufferedWriter;
@@ -35,7 +35,6 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
 import java.io.Writer;
 import java.net.CacheRequest;
 import java.net.CacheResponse;
@@ -44,8 +43,6 @@ import java.net.ResponseCache;
 import java.net.SecureCacheResponse;
 import java.net.URI;
 import java.net.URLConnection;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
 import java.security.Principal;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateEncodingException;
@@ -55,8 +52,8 @@ import java.security.cert.X509Certificate;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
-import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSocket;
 
 import static com.squareup.okhttp.internal.Util.US_ASCII;
 import static com.squareup.okhttp.internal.Util.UTF_8;
@@ -119,9 +116,6 @@ import static com.squareup.okhttp.internal.Util.UTF_8;
  * }</pre>
  */
 public final class HttpResponseCache extends ResponseCache {
-  private static final char[] DIGITS =
-      { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
-
   // TODO: add APIs to iterate the cache?
   private static final int VERSION = 201105;
   private static final int ENTRY_METADATA = 0;
@@ -153,6 +147,10 @@ public final class HttpResponseCache extends ResponseCache {
       return HttpResponseCache.this.put(uri, connection);
     }
 
+    @Override public void maybeRemove(String requestMethod, URI uri) throws IOException {
+      HttpResponseCache.this.maybeRemove(requestMethod, uri);
+    }
+
     @Override public void update(
         CacheResponse conditionalCacheHit, HttpURLConnection connection) throws IOException {
       HttpResponseCache.this.update(conditionalCacheHit, connection);
@@ -172,26 +170,7 @@ public final class HttpResponseCache extends ResponseCache {
   }
 
   private String uriToKey(URI uri) {
-    try {
-      MessageDigest messageDigest = MessageDigest.getInstance("MD5");
-      byte[] md5bytes = messageDigest.digest(uri.toString().getBytes("UTF-8"));
-      return bytesToHexString(md5bytes);
-    } catch (NoSuchAlgorithmException e) {
-      throw new AssertionError(e);
-    } catch (UnsupportedEncodingException e) {
-      throw new AssertionError(e);
-    }
-  }
-
-  private static String bytesToHexString(byte[] bytes) {
-    char[] digits = DIGITS;
-    char[] buf = new char[bytes.length * 2];
-    int c = 0;
-    for (byte b : bytes) {
-      buf[c++] = digits[(b >> 4) & 0xf];
-      buf[c++] = digits[b & 0xf];
-    }
-    return new String(buf);
+    return Util.hash(uri.toString());
   }
 
   @Override public CacheResponse get(URI uri, String requestMethod,
@@ -226,17 +205,11 @@ public final class HttpResponseCache extends ResponseCache {
 
     HttpURLConnection httpConnection = (HttpURLConnection) urlConnection;
     String requestMethod = httpConnection.getRequestMethod();
-    String key = uriToKey(uri);
 
-    if (requestMethod.equals("POST") || requestMethod.equals("PUT") || requestMethod.equals(
-        "DELETE")) {
-      try {
-        cache.remove(key);
-      } catch (IOException ignored) {
-        // The cache cannot be written.
-      }
+    if (maybeRemove(requestMethod, uri)) {
       return null;
-    } else if (!requestMethod.equals("GET")) {
+    }
+    if (!requestMethod.equals("GET")) {
       // Don't cache non-GET responses. We're technically allowed to cache
       // HEAD requests and some POST requests, but the complexity of doing
       // so is high and the benefit is low.
@@ -259,7 +232,7 @@ public final class HttpResponseCache extends ResponseCache {
     Entry entry = new Entry(uri, varyHeaders, httpConnection);
     DiskLruCache.Editor editor = null;
     try {
-      editor = cache.edit(key);
+      editor = cache.edit(uriToKey(uri));
       if (editor == null) {
         return null;
       }
@@ -271,6 +244,23 @@ public final class HttpResponseCache extends ResponseCache {
     }
   }
 
+  /**
+   * Returns true if the supplied {@code requestMethod} potentially invalidates an entry in the
+   * cache.
+   */
+  private boolean maybeRemove(String requestMethod, URI uri) {
+    if (requestMethod.equals("POST") || requestMethod.equals("PUT") || requestMethod.equals(
+        "DELETE")) {
+      try {
+        cache.remove(uriToKey(uri));
+      } catch (IOException ignored) {
+        // The cache cannot be written.
+      }
+      return true;
+    }
+    return false;
+  }
+
   private void update(CacheResponse conditionalCacheHit, HttpURLConnection httpConnection)
       throws IOException {
     HttpEngine httpEngine = getHttpEngine(httpConnection);
@@ -331,6 +321,30 @@ public final class HttpResponseCache extends ResponseCache {
     return writeSuccessCount;
   }
 
+  public long getSize() {
+    return cache.size();
+  }
+
+  public long getMaxSize() {
+    return cache.getMaxSize();
+  }
+
+  public void flush() throws IOException {
+    cache.flush();
+  }
+
+  public void close() throws IOException {
+    cache.close();
+  }
+
+  public File getDirectory() {
+    return cache.getDirectory();
+  }
+
+  public boolean isClosed() {
+    return cache.isClosed();
+  }
+
   private synchronized void trackResponse(ResponseSource source) {
     requestCount++;
 
@@ -383,8 +397,7 @@ public final class HttpResponseCache extends ResponseCache {
           editor.commit();
         }
 
-        @Override
-        public void write(byte[] buffer, int offset, int length) throws IOException {
+        @Override public void write(byte[] buffer, int offset, int length) throws IOException {
           // Since we don't override "write(int oneByte)", we can write directly to "out"
           // and avoid the inefficient implementation from the FilterOutputStream.
           out.write(buffer, offset, length);
@@ -513,16 +526,16 @@ public final class HttpResponseCache extends ResponseCache {
       this.requestMethod = httpConnection.getRequestMethod();
       this.responseHeaders = RawHeaders.fromMultimap(httpConnection.getHeaderFields(), true);
 
-      if (isHttps()) {
-        HttpsURLConnection httpsConnection = (HttpsURLConnection) httpConnection;
-        cipherSuite = httpsConnection.getCipherSuite();
+      SSLSocket sslSocket = getSslSocket(httpConnection);
+      if (sslSocket != null) {
+        cipherSuite = sslSocket.getSession().getCipherSuite();
         Certificate[] peerCertificatesNonFinal = null;
         try {
-          peerCertificatesNonFinal = httpsConnection.getServerCertificates();
+          peerCertificatesNonFinal = sslSocket.getSession().getPeerCertificates();
         } catch (SSLPeerUnverifiedException ignored) {
         }
         peerCertificates = peerCertificatesNonFinal;
-        localCertificates = httpsConnection.getLocalCertificates();
+        localCertificates = sslSocket.getSession().getLocalCertificates();
       } else {
         cipherSuite = null;
         peerCertificates = null;
@@ -530,6 +543,22 @@ public final class HttpResponseCache extends ResponseCache {
       }
     }
 
+    /**
+     * Returns the SSL socket used by {@code httpConnection} for HTTPS, nor null
+     * if the connection isn't using HTTPS. Since we permit redirects across
+     * protocols (HTTP to HTTPS or vice versa), the implementation type of the
+     * connection doesn't necessarily match the implementation type of its HTTP
+     * engine.
+     */
+    private SSLSocket getSslSocket(HttpURLConnection httpConnection) {
+      HttpEngine engine = httpConnection instanceof HttpsURLConnectionImpl
+          ? ((HttpsURLConnectionImpl) httpConnection).getHttpEngine()
+          : ((HttpURLConnectionImpl) httpConnection).getHttpEngine();
+      return engine instanceof HttpsEngine
+          ? ((HttpsEngine) engine).getSslSocket()
+          : null;
+    }
+
     public void writeTo(DiskLruCache.Editor editor) throws IOException {
       OutputStream out = editor.newOutputStream(ENTRY_METADATA);
       Writer writer = new BufferedWriter(new OutputStreamWriter(out, UTF_8));

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/Job.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/Job.java b/framework/src/com/squareup/okhttp/Job.java
new file mode 100755
index 0000000..1bfeb1d
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/Job.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2013 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;
+
+import com.squareup.okhttp.internal.http.HttpAuthenticator;
+import com.squareup.okhttp.internal.http.HttpEngine;
+import com.squareup.okhttp.internal.http.HttpTransport;
+import com.squareup.okhttp.internal.http.HttpsEngine;
+import com.squareup.okhttp.internal.http.Policy;
+import com.squareup.okhttp.internal.http.RawHeaders;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.ProtocolException;
+import java.net.Proxy;
+import java.net.URL;
+
+import static com.squareup.okhttp.internal.Util.getEffectivePort;
+import static com.squareup.okhttp.internal.http.HttpURLConnectionImpl.HTTP_MOVED_PERM;
+import static com.squareup.okhttp.internal.http.HttpURLConnectionImpl.HTTP_MOVED_TEMP;
+import static com.squareup.okhttp.internal.http.HttpURLConnectionImpl.HTTP_MULT_CHOICE;
+import static com.squareup.okhttp.internal.http.HttpURLConnectionImpl.HTTP_PROXY_AUTH;
+import static com.squareup.okhttp.internal.http.HttpURLConnectionImpl.HTTP_SEE_OTHER;
+import static com.squareup.okhttp.internal.http.HttpURLConnectionImpl.HTTP_TEMP_REDIRECT;
+import static com.squareup.okhttp.internal.http.HttpURLConnectionImpl.HTTP_UNAUTHORIZED;
+
+final class Job implements Runnable, Policy {
+  private final Dispatcher dispatcher;
+  private final OkHttpClient client;
+  private final Response.Receiver responseReceiver;
+
+  /** The request; possibly a consequence of redirects or auth headers. */
+  private Request request;
+
+  public Job(Dispatcher dispatcher, OkHttpClient client, Request request,
+      Response.Receiver responseReceiver) {
+    this.dispatcher = dispatcher;
+    this.client = client;
+    this.request = request;
+    this.responseReceiver = responseReceiver;
+  }
+
+  @Override public int getChunkLength() {
+    return request.body().contentLength() == -1 ? HttpTransport.DEFAULT_CHUNK_LENGTH : -1;
+  }
+
+  @Override public long getFixedContentLength() {
+    return request.body().contentLength();
+  }
+
+  @Override public boolean getUseCaches() {
+    return false; // TODO.
+  }
+
+  @Override public HttpURLConnection getHttpConnectionToCache() {
+    return null;
+  }
+
+  @Override public URL getURL() {
+    return request.url();
+  }
+
+  @Override public long getIfModifiedSince() {
+    return 0; // For HttpURLConnection only. We let the cache drive this.
+  }
+
+  @Override public boolean usingProxy() {
+    return false; // We let the connection decide this.
+  }
+
+  @Override public void setSelectedProxy(Proxy proxy) {
+    // Do nothing.
+  }
+
+  Object tag() {
+    return request.tag();
+  }
+
+  @Override public void run() {
+    try {
+      Response response = execute();
+      responseReceiver.onResponse(response);
+    } catch (IOException e) {
+      responseReceiver.onFailure(new Failure.Builder()
+          .request(request)
+          .exception(e)
+          .build());
+    } finally {
+      // TODO: close the response body
+      // TODO: release the HTTP engine (potentially multiple!)
+      dispatcher.finished(this);
+    }
+  }
+
+  private Response execute() throws IOException {
+    Connection connection = null;
+    Response redirectedBy = null;
+
+    while (true) {
+      HttpEngine engine = newEngine(connection);
+
+      Request.Body body = request.body();
+      if (body != null) {
+        MediaType contentType = body.contentType();
+        if (contentType == null) throw new IllegalStateException("contentType == null");
+        if (engine.getRequestHeaders().getContentType() == null) {
+          engine.getRequestHeaders().setContentType(contentType.toString());
+        }
+      }
+
+      engine.sendRequest();
+
+      if (body != null) {
+        body.writeTo(engine.getRequestBody());
+      }
+
+      engine.readResponse();
+
+      int responseCode = engine.getResponseCode();
+      Dispatcher.RealResponseBody responseBody = new Dispatcher.RealResponseBody(
+          engine.getResponseHeaders(), engine.getResponseBody());
+
+      Response response = new Response.Builder(request, responseCode)
+          .rawHeaders(engine.getResponseHeaders().getHeaders())
+          .body(responseBody)
+          .redirectedBy(redirectedBy)
+          .build();
+
+      Request redirect = processResponse(engine, response);
+
+      if (redirect == null) {
+        engine.automaticallyReleaseConnectionToPool();
+        return response;
+      }
+
+      // TODO: fail if too many redirects
+      // TODO: fail if not following redirects
+      // TODO: release engine
+
+      connection = sameConnection(request, redirect) ? engine.getConnection() : null;
+      redirectedBy = response;
+      request = redirect;
+    }
+  }
+
+  HttpEngine newEngine(Connection connection) throws IOException {
+    String protocol = request.url().getProtocol();
+    RawHeaders requestHeaders = request.rawHeaders();
+    if (protocol.equals("http")) {
+      return new HttpEngine(client, this, request.method(), requestHeaders, connection, null);
+    } else if (protocol.equals("https")) {
+      return new HttpsEngine(client, this, request.method(), requestHeaders, connection, null);
+    } else {
+      throw new AssertionError();
+    }
+  }
+
+  /**
+   * Figures out the HTTP request to make in response to receiving {@code
+   * response}. This will either add authentication headers or follow
+   * redirects. If a follow-up is either unnecessary or not applicable, this
+   * returns null.
+   */
+  private Request processResponse(HttpEngine engine, Response response) throws IOException {
+    Request request = response.request();
+    Proxy selectedProxy = engine.getConnection() != null
+        ? engine.getConnection().getRoute().getProxy()
+        : client.getProxy();
+    int responseCode = response.code();
+
+    switch (responseCode) {
+      case HTTP_PROXY_AUTH:
+        if (selectedProxy.type() != Proxy.Type.HTTP) {
+          throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");
+        }
+        // fall-through
+      case HTTP_UNAUTHORIZED:
+        RawHeaders successorRequestHeaders = request.rawHeaders();
+        boolean credentialsFound = HttpAuthenticator.processAuthHeader(client.getAuthenticator(),
+            response.code(), response.rawHeaders(), successorRequestHeaders, selectedProxy,
+            this.request.url());
+        return credentialsFound
+            ? request.newBuilder().rawHeaders(successorRequestHeaders).build()
+            : null;
+
+      case HTTP_MULT_CHOICE:
+      case HTTP_MOVED_PERM:
+      case HTTP_MOVED_TEMP:
+      case HTTP_SEE_OTHER:
+      case HTTP_TEMP_REDIRECT:
+        String method = request.method();
+        if (responseCode == HTTP_TEMP_REDIRECT && !method.equals("GET") && !method.equals("HEAD")) {
+          // "If the 307 status code is received in response to a request other than GET or HEAD,
+          // the user agent MUST NOT automatically redirect the request"
+          return null;
+        }
+
+        String location = response.header("Location");
+        if (location == null) {
+          return null;
+        }
+
+        URL url = new URL(request.url(), location);
+        if (!url.getProtocol().equals("https") && !url.getProtocol().equals("http")) {
+          return null; // Don't follow redirects to unsupported protocols.
+        }
+
+        return this.request.newBuilder().url(url).build();
+
+      default:
+        return null;
+    }
+  }
+
+  private boolean sameConnection(Request a, Request b) {
+    return a.url().getHost().equals(b.url().getHost())
+        && getEffectivePort(a.url()) == getEffectivePort(b.url())
+        && a.url().getProtocol().equals(b.url().getProtocol());
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/MediaType.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/MediaType.java b/framework/src/com/squareup/okhttp/MediaType.java
new file mode 100755
index 0000000..2c09596
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/MediaType.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2013 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;
+
+import java.nio.charset.Charset;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * An <a href="http://tools.ietf.org/html/rfc2045">RFC 2045</a> Media Type,
+ * appropriate to describe the content type of an HTTP request or response body.
+ */
+public final class MediaType {
+  private static final String TOKEN = "([a-zA-Z0-9-!#$%&'*+.^_`{|}~]+)";
+  private static final String QUOTED = "\"([^\"]*)\"";
+  private static final Pattern TYPE_SUBTYPE = Pattern.compile(TOKEN + "/" + TOKEN);
+  private static final Pattern PARAMETER = Pattern.compile(
+      ";\\s*" + TOKEN + "=(?:" + TOKEN + "|" + QUOTED + ")");
+
+  private final String mediaType;
+  private final String type;
+  private final String subtype;
+  private final String charset;
+
+  private MediaType(String mediaType, String type, String subtype, String charset) {
+    this.mediaType = mediaType;
+    this.type = type;
+    this.subtype = subtype;
+    this.charset = charset;
+  }
+
+  /**
+   * Returns a media type for {@code string}, or null if {@code string} is not a
+   * well-formed media type.
+   */
+  public static MediaType parse(String string) {
+    Matcher typeSubtype = TYPE_SUBTYPE.matcher(string);
+    if (!typeSubtype.lookingAt()) return null;
+    String type = typeSubtype.group(1).toLowerCase(Locale.US);
+    String subtype = typeSubtype.group(2).toLowerCase(Locale.US);
+
+    String charset = null;
+    Matcher parameter = PARAMETER.matcher(string);
+    for (int s = typeSubtype.end(); s < string.length(); s = parameter.end()) {
+      parameter.region(s, string.length());
+      if (!parameter.lookingAt()) return null; // This is not a well-formed media type.
+
+      String name = parameter.group(1);
+      if (name == null || !name.equalsIgnoreCase("charset")) continue;
+      if (charset != null) throw new IllegalArgumentException("Multiple charsets: " + string);
+      charset = parameter.group(2) != null
+          ? parameter.group(2)  // Value is a token.
+          : parameter.group(3); // Value is a quoted string.
+    }
+
+    return new MediaType(string, type, subtype, charset);
+  }
+
+  /**
+   * Returns the high-level media type, such as "text", "image", "audio",
+   * "video", or "application".
+   */
+  public String type() {
+    return type;
+  }
+
+  /**
+   * Returns a specific media subtype, such as "plain" or "png", "mpeg",
+   * "mp4" or "xml".
+   */
+  public String subtype() {
+    return subtype;
+  }
+
+  /**
+   * Returns the charset of this media type, or null if this media type doesn't
+   * specify a charset.
+   */
+  public Charset charset() {
+    return charset != null ? Charset.forName(charset) : null;
+  }
+
+  /**
+   * Returns the charset of this media type, or {@code defaultValue} if this
+   * media type doesn't specify a charset.
+   */
+  public Charset charset(Charset defaultValue) {
+    return charset != null ? Charset.forName(charset) : defaultValue;
+  }
+
+  /**
+   * Returns the encoded media type, like "text/plain; charset=utf-8",
+   * appropriate for use in a Content-Type header.
+   */
+  @Override public String toString() {
+    return mediaType;
+  }
+
+  @Override public boolean equals(Object o) {
+    return o instanceof MediaType && ((MediaType) o).mediaType.equals(mediaType);
+  }
+
+  @Override public int hashCode() {
+    return mediaType.hashCode();
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/OkAuthenticator.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/OkAuthenticator.java b/framework/src/com/squareup/okhttp/OkAuthenticator.java
new file mode 100755
index 0000000..a505419
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/OkAuthenticator.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2013 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;
+
+import com.squareup.okhttp.internal.Base64;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.Proxy;
+import java.net.URL;
+import java.util.List;
+
+/**
+ * Responds to authentication challenges from the remote web or proxy server by
+ * returning credentials.
+ */
+public interface OkAuthenticator {
+  /**
+   * Returns a credential that satisfies the authentication challenge made by
+   * {@code url}. Returns null if the challenge cannot be satisfied. This method
+   * is called in response to an HTTP 401 unauthorized status code sent by the
+   * origin server.
+   *
+   * @param challenges parsed "WWW-Authenticate" challenge headers from the HTTP
+   *     response.
+   */
+  Credential authenticate(Proxy proxy, URL url, List<Challenge> challenges) throws IOException;
+
+  /**
+   * Returns a credential that satisfies the authentication challenge made by
+   * {@code proxy}. Returns null if the challenge cannot be satisfied. This
+   * method is called in response to an HTTP 401 unauthorized status code sent
+   * by the proxy server.
+   *
+   * @param challenges parsed "Proxy-Authenticate" challenge headers from the
+   *     HTTP response.
+   */
+  Credential authenticateProxy(Proxy proxy, URL url, List<Challenge> challenges) throws IOException;
+
+  /** An RFC 2617 challenge. */
+  public final class Challenge {
+    private final String scheme;
+    private final String realm;
+
+    public Challenge(String scheme, String realm) {
+      this.scheme = scheme;
+      this.realm = realm;
+    }
+
+    /** Returns the authentication scheme, like {@code Basic}. */
+    public String getScheme() {
+      return scheme;
+    }
+
+    /** Returns the protection space. */
+    public String getRealm() {
+      return realm;
+    }
+
+    @Override public boolean equals(Object o) {
+      return o instanceof Challenge
+          && ((Challenge) o).scheme.equals(scheme)
+          && ((Challenge) o).realm.equals(realm);
+    }
+
+    @Override public int hashCode() {
+      return scheme.hashCode() + 31 * realm.hashCode();
+    }
+
+    @Override public String toString() {
+      return scheme + " realm=\"" + realm + "\"";
+    }
+  }
+
+  /** An RFC 2617 credential. */
+  public final class Credential {
+    private final String headerValue;
+
+    private Credential(String headerValue) {
+      this.headerValue = headerValue;
+    }
+
+    /** Returns an auth credential for the Basic scheme. */
+    public static Credential basic(String userName, String password) {
+      try {
+        String usernameAndPassword = userName + ":" + password;
+        byte[] bytes = usernameAndPassword.getBytes("ISO-8859-1");
+        String encoded = Base64.encode(bytes);
+        return new Credential("Basic " + encoded);
+      } catch (UnsupportedEncodingException e) {
+        throw new AssertionError();
+      }
+    }
+
+    public String getHeaderValue() {
+      return headerValue;
+    }
+
+    @Override public boolean equals(Object o) {
+      return o instanceof Credential && ((Credential) o).headerValue.equals(headerValue);
+    }
+
+    @Override public int hashCode() {
+      return headerValue.hashCode();
+    }
+
+    @Override public String toString() {
+      return headerValue;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/OkHttpClient.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/OkHttpClient.java b/framework/src/com/squareup/okhttp/OkHttpClient.java
old mode 100644
new mode 100755
index 7834bd6..f78592f
--- a/framework/src/com/squareup/okhttp/OkHttpClient.java
+++ b/framework/src/com/squareup/okhttp/OkHttpClient.java
@@ -15,34 +15,105 @@
  */
 package com.squareup.okhttp;
 
+import com.squareup.okhttp.internal.Util;
+import com.squareup.okhttp.internal.http.HttpAuthenticator;
 import com.squareup.okhttp.internal.http.HttpURLConnectionImpl;
 import com.squareup.okhttp.internal.http.HttpsURLConnectionImpl;
-import com.squareup.okhttp.internal.http.OkResponseCache;
 import com.squareup.okhttp.internal.http.OkResponseCacheAdapter;
+import com.squareup.okhttp.internal.tls.OkHostnameVerifier;
 import java.net.CookieHandler;
 import java.net.HttpURLConnection;
 import java.net.Proxy;
 import java.net.ProxySelector;
 import java.net.ResponseCache;
 import java.net.URL;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.Set;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.net.URLStreamHandlerFactory;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLSocketFactory;
 
 /** Configures and creates HTTP connections. */
-public final class OkHttpClient {
+public final class OkHttpClient implements URLStreamHandlerFactory {
+  private static final List<String> DEFAULT_TRANSPORTS
+      = Util.immutableList(Arrays.asList("spdy/3", "http/1.1"));
+
+  private final RouteDatabase routeDatabase;
+  private final Dispatcher dispatcher;
   private Proxy proxy;
-  private Set<Route> failedRoutes = Collections.synchronizedSet(new LinkedHashSet<Route>());
+  private List<String> transports;
   private ProxySelector proxySelector;
   private CookieHandler cookieHandler;
   private ResponseCache responseCache;
   private SSLSocketFactory sslSocketFactory;
   private HostnameVerifier hostnameVerifier;
+  private OkAuthenticator authenticator;
   private ConnectionPool connectionPool;
   private boolean followProtocolRedirects = true;
+  private int connectTimeout;
+  private int readTimeout;
+
+  public OkHttpClient() {
+    routeDatabase = new RouteDatabase();
+    dispatcher = new Dispatcher();
+  }
+
+  private OkHttpClient(OkHttpClient copyFrom) {
+    routeDatabase = copyFrom.routeDatabase;
+    dispatcher = copyFrom.dispatcher;
+  }
+
+  /**
+   * Sets the default connect timeout for new connections. A value of 0 means no timeout.
+   *
+   * @see URLConnection#setConnectTimeout(int)
+   */
+  public void setConnectTimeout(long timeout, TimeUnit unit) {
+    if (timeout < 0) {
+      throw new IllegalArgumentException("timeout < 0");
+    }
+    if (unit == null) {
+      throw new IllegalArgumentException("unit == null");
+    }
+    long millis = unit.toMillis(timeout);
+    if (millis > Integer.MAX_VALUE) {
+      throw new IllegalArgumentException("Timeout too large.");
+    }
+    connectTimeout = (int) millis;
+  }
+
+  /** Default connect timeout (in milliseconds). */
+  public int getConnectTimeout() {
+    return connectTimeout;
+  }
+
+  /**
+   * Sets the default read timeout for new connections. A value of 0 means no timeout.
+   *
+   * @see URLConnection#setReadTimeout(int)
+   */
+  public void setReadTimeout(long timeout, TimeUnit unit) {
+    if (timeout < 0) {
+      throw new IllegalArgumentException("timeout < 0");
+    }
+    if (unit == null) {
+      throw new IllegalArgumentException("unit == null");
+    }
+    long millis = unit.toMillis(timeout);
+    if (millis > Integer.MAX_VALUE) {
+      throw new IllegalArgumentException("Timeout too large.");
+    }
+    readTimeout = (int) millis;
+  }
+
+  /** Default read timeout (in milliseconds). */
+  public int getReadTimeout() {
+    return readTimeout;
+  }
 
   /**
    * Sets the HTTP proxy that will be used by connections created by this
@@ -108,7 +179,7 @@ public final class OkHttpClient {
     return responseCache;
   }
 
-  private OkResponseCache okResponseCache() {
+  public OkResponseCache getOkResponseCache() {
     if (responseCache instanceof HttpResponseCache) {
       return ((HttpResponseCache) responseCache).okResponseCache;
     } else if (responseCache != null) {
@@ -124,7 +195,7 @@ public final class OkHttpClient {
    * <p>If unset, the {@link HttpsURLConnection#getDefaultSSLSocketFactory()
    * system-wide default} SSL socket factory will be used.
    */
-  public OkHttpClient setSSLSocketFactory(SSLSocketFactory sslSocketFactory) {
+  public OkHttpClient setSslSocketFactory(SSLSocketFactory sslSocketFactory) {
     this.sslSocketFactory = sslSocketFactory;
     return this;
   }
@@ -150,6 +221,22 @@ public final class OkHttpClient {
   }
 
   /**
+   * Sets the authenticator used to respond to challenges from the remote web
+   * server or proxy server.
+   *
+   * <p>If unset, the {@link java.net.Authenticator#setDefault system-wide default}
+   * authenticator will be used.
+   */
+  public OkHttpClient setAuthenticator(OkAuthenticator authenticator) {
+    this.authenticator = authenticator;
+    return this;
+  }
+
+  public OkAuthenticator getAuthenticator() {
+    return authenticator;
+  }
+
+  /**
    * Sets the connection pool used to recycle HTTP and HTTPS connections.
    *
    * <p>If unset, the {@link ConnectionPool#getDefault() system-wide
@@ -180,16 +267,86 @@ public final class OkHttpClient {
     return followProtocolRedirects;
   }
 
+  public RouteDatabase getRoutesDatabase() {
+    return routeDatabase;
+  }
+
+  /**
+   * Configure the transports used by this client to communicate with remote
+   * servers. By default this client will prefer the most efficient transport
+   * available, falling back to more ubiquitous transports. Applications should
+   * only call this method to avoid specific compatibility problems, such as web
+   * servers that behave incorrectly when SPDY is enabled.
+   *
+   * <p>The following transports are currently supported:
+   * <ul>
+   *   <li><a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">http/1.1</a>
+   *   <li><a href="http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3">spdy/3</a>
+   * </ul>
+   *
+   * <p><strong>This is an evolving set.</strong> Future releases may drop
+   * support for transitional transports (like spdy/3), in favor of their
+   * successors (spdy/4 or http/2.0). The http/1.1 transport will never be
+   * dropped.
+   *
+   * <p>If multiple protocols are specified, <a
+   * href="https://technotes.googlecode.com/git/nextprotoneg.html">NPN</a> will
+   * be used to negotiate a transport. Future releases may use another mechanism
+   * (such as <a href="http://tools.ietf.org/html/draft-friedl-tls-applayerprotoneg-02">ALPN</a>)
+   * to negotiate a transport.
+   *
+   * @param transports the transports to use, in order of preference. The list
+   *     must contain "http/1.1". It must not contain null.
+   */
+  public OkHttpClient setTransports(List<String> transports) {
+    transports = Util.immutableList(transports);
+    if (!transports.contains("http/1.1")) {
+      throw new IllegalArgumentException("transports doesn't contain http/1.1: " + transports);
+    }
+    if (transports.contains(null)) {
+      throw new IllegalArgumentException("transports must not contain null");
+    }
+    if (transports.contains("")) {
+      throw new IllegalArgumentException("transports contains an empty string");
+    }
+    this.transports = transports;
+    return this;
+  }
+
+  public List<String> getTransports() {
+    return transports;
+  }
+
+  /**
+   * Schedules {@code request} to be executed.
+   */
+  /* OkHttp 2.0: public */ void enqueue(Request request, Response.Receiver responseReceiver) {
+    // Create the HttpURLConnection immediately so the enqueued job gets the current settings of
+    // this client. Otherwise changes to this client (socket factory, redirect policy, etc.) may
+    // incorrectly be reflected in the request when it is dispatched later.
+    dispatcher.enqueue(copyWithDefaults(), request, responseReceiver);
+  }
+
+  /**
+   * Cancels all scheduled tasks tagged with {@code tag}. Requests that are already
+   * in flight might not be canceled.
+   */
+  /* OkHttp 2.0: public */ void cancel(Object tag) {
+    dispatcher.cancel(tag);
+  }
+
   public HttpURLConnection open(URL url) {
+    return open(url, proxy);
+  }
+
+  HttpURLConnection open(URL url, Proxy proxy) {
     String protocol = url.getProtocol();
     OkHttpClient copy = copyWithDefaults();
-    if (protocol.equals("http")) {
-      return new HttpURLConnectionImpl(url, copy, copy.okResponseCache(), copy.failedRoutes);
-    } else if (protocol.equals("https")) {
-      return new HttpsURLConnectionImpl(url, copy, copy.okResponseCache(), copy.failedRoutes);
-    } else {
-      throw new IllegalArgumentException("Unexpected protocol: " + protocol);
-    }
+    copy.proxy = proxy;
+
+    if (protocol.equals("http")) return new HttpURLConnectionImpl(url, copy);
+    if (protocol.equals("https")) return new HttpsURLConnectionImpl(url, copy);
+    throw new IllegalArgumentException("Unexpected protocol: " + protocol);
   }
 
   /**
@@ -197,9 +354,8 @@ public final class OkHttpClient {
    * each field that hasn't been explicitly configured.
    */
   private OkHttpClient copyWithDefaults() {
-    OkHttpClient result = new OkHttpClient();
+    OkHttpClient result = new OkHttpClient(this);
     result.proxy = proxy;
-    result.failedRoutes = failedRoutes;
     result.proxySelector = proxySelector != null ? proxySelector : ProxySelector.getDefault();
     result.cookieHandler = cookieHandler != null ? cookieHandler : CookieHandler.getDefault();
     result.responseCache = responseCache != null ? responseCache : ResponseCache.getDefault();
@@ -208,9 +364,45 @@ public final class OkHttpClient {
         : HttpsURLConnection.getDefaultSSLSocketFactory();
     result.hostnameVerifier = hostnameVerifier != null
         ? hostnameVerifier
-        : HttpsURLConnection.getDefaultHostnameVerifier();
+        : OkHostnameVerifier.INSTANCE;
+    result.authenticator = authenticator != null
+        ? authenticator
+        : HttpAuthenticator.SYSTEM_DEFAULT;
     result.connectionPool = connectionPool != null ? connectionPool : ConnectionPool.getDefault();
     result.followProtocolRedirects = followProtocolRedirects;
+    result.transports = transports != null ? transports : DEFAULT_TRANSPORTS;
+    result.connectTimeout = connectTimeout;
+    result.readTimeout = readTimeout;
     return result;
   }
+
+  /**
+   * Creates a URLStreamHandler as a {@link URL#setURLStreamHandlerFactory}.
+   *
+   * <p>This code configures OkHttp to handle all HTTP and HTTPS connections
+   * created with {@link URL#openConnection()}: <pre>   {@code
+   *
+   *   OkHttpClient okHttpClient = new OkHttpClient();
+   *   URL.setURLStreamHandlerFactory(okHttpClient);
+   * }</pre>
+   */
+  public URLStreamHandler createURLStreamHandler(final String protocol) {
+    if (!protocol.equals("http") && !protocol.equals("https")) return null;
+
+    return new URLStreamHandler() {
+      @Override protected URLConnection openConnection(URL url) {
+        return open(url);
+      }
+
+      @Override protected URLConnection openConnection(URL url, Proxy proxy) {
+        return open(url, proxy);
+      }
+
+      @Override protected int getDefaultPort() {
+        if (protocol.equals("http")) return 80;
+        if (protocol.equals("https")) return 443;
+        throw new AssertionError();
+      }
+    };
+  }
 }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/OkResponseCache.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/OkResponseCache.java b/framework/src/com/squareup/okhttp/OkResponseCache.java
old mode 100644
new mode 100755
index b7e3801..ffe6f54
--- a/framework/src/com/squareup/okhttp/OkResponseCache.java
+++ b/framework/src/com/squareup/okhttp/OkResponseCache.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2013 Square, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,23 +16,41 @@
 package com.squareup.okhttp;
 
 import java.io.IOException;
+import java.net.CacheRequest;
 import java.net.CacheResponse;
 import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URLConnection;
+import java.util.List;
+import java.util.Map;
 
 /**
- * A response cache that supports statistics tracking and updating stored
- * responses. Implementations of {@link java.net.ResponseCache} should implement
- * this interface to receive additional support from the HTTP engine.
+ * An extended response cache API. Unlike {@link java.net.ResponseCache}, this
+ * interface supports conditional caching and statistics.
+ *
+ * <h3>Warning: Experimental OkHttp 2.0 API</h3>
+ * This class is in beta. APIs are subject to change!
  */
 public interface OkResponseCache {
+  CacheResponse get(URI uri, String requestMethod, Map<String, List<String>> requestHeaders)
+      throws IOException;
 
-  /** Track an HTTP response being satisfied by {@code source}. */
-  void trackResponse(ResponseSource source);
+  CacheRequest put(URI uri, URLConnection urlConnection) throws IOException;
+
+  /** Remove any cache entries for the supplied {@code uri} if the request method invalidates. */
+  void maybeRemove(String requestMethod, URI uri) throws IOException;
+
+  /**
+   * Handles a conditional request hit by updating the stored cache response
+   * with the headers from {@code httpConnection}. The cached response body is
+   * not updated. If the stored response has changed since {@code
+   * conditionalCacheHit} was returned, this does nothing.
+   */
+  void update(CacheResponse conditionalCacheHit, HttpURLConnection connection) throws IOException;
 
   /** Track an conditional GET that was satisfied by this cache. */
   void trackConditionalCacheHit();
 
-  /** Updates stored HTTP headers using a hit on a conditional GET. */
-  void update(CacheResponse conditionalCacheHit, HttpURLConnection httpConnection)
-      throws IOException;
+  /** Track an HTTP response being satisfied by {@code source}. */
+  void trackResponse(ResponseSource source);
 }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/Request.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/Request.java b/framework/src/com/squareup/okhttp/Request.java
new file mode 100755
index 0000000..f95303e
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/Request.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2013 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;
+
+import com.squareup.okhttp.internal.Util;
+import com.squareup.okhttp.internal.http.RawHeaders;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * An HTTP request. Instances of this class are immutable if their {@link #body}
+ * is null or itself immutable.
+ *
+ * <h3>Warning: Experimental OkHttp 2.0 API</h3>
+ * This class is in beta. APIs are subject to change!
+ */
+/* OkHttp 2.0: public */ final class Request {
+  private final URL url;
+  private final String method;
+  private final RawHeaders headers;
+  private final Body body;
+  private final Object tag;
+
+  private Request(Builder builder) {
+    this.url = builder.url;
+    this.method = builder.method;
+    this.headers = new RawHeaders(builder.headers);
+    this.body = builder.body;
+    this.tag = builder.tag != null ? builder.tag : this;
+  }
+
+  public URL url() {
+    return url;
+  }
+
+  public String urlString() {
+    return url.toString();
+  }
+
+  public String method() {
+    return method;
+  }
+
+  public String header(String name) {
+    return headers.get(name);
+  }
+
+  public List<String> headers(String name) {
+    return headers.values(name);
+  }
+
+  public Set<String> headerNames() {
+    return headers.names();
+  }
+
+  RawHeaders rawHeaders() {
+    return new RawHeaders(headers);
+  }
+
+  public int headerCount() {
+    return headers.length();
+  }
+
+  public String headerName(int index) {
+    return headers.getFieldName(index);
+  }
+
+  public String headerValue(int index) {
+    return headers.getValue(index);
+  }
+
+  public Body body() {
+    return body;
+  }
+
+  public Object tag() {
+    return tag;
+  }
+
+  Builder newBuilder() {
+    return new Builder(url)
+        .method(method, body)
+        .rawHeaders(headers)
+        .tag(tag);
+  }
+
+  public abstract static class Body {
+    /** Returns the Content-Type header for this body. */
+    public abstract MediaType contentType();
+
+    /**
+     * Returns the number of bytes that will be written to {@code out} in a call
+     * to {@link #writeTo}, or -1 if that count is unknown.
+     */
+    public long contentLength() {
+      return -1;
+    }
+
+    /** Writes the content of this request to {@code out}. */
+    public abstract void writeTo(OutputStream out) throws IOException;
+
+    /**
+     * Returns a new request body that transmits {@code content}. If {@code
+     * contentType} lacks a charset, this will use UTF-8.
+     */
+    public static Body create(MediaType contentType, String content) {
+      contentType = contentType.charset() != null
+          ? contentType
+          : MediaType.parse(contentType + "; charset=utf-8");
+      try {
+        byte[] bytes = content.getBytes(contentType.charset().name());
+        return create(contentType, bytes);
+      } catch (UnsupportedEncodingException e) {
+        throw new AssertionError();
+      }
+    }
+
+    /** Returns a new request body that transmits {@code content}. */
+    public static Body create(final MediaType contentType, final byte[] content) {
+      if (contentType == null) throw new NullPointerException("contentType == null");
+      if (content == null) throw new NullPointerException("content == null");
+
+      return new Body() {
+        @Override public MediaType contentType() {
+          return contentType;
+        }
+
+        @Override public long contentLength() {
+          return content.length;
+        }
+
+        @Override public void writeTo(OutputStream out) throws IOException {
+          out.write(content);
+        }
+      };
+    }
+
+    /** Returns a new request body that transmits the content of {@code file}. */
+    public static Body create(final MediaType contentType, final File file) {
+      if (contentType == null) throw new NullPointerException("contentType == null");
+      if (file == null) throw new NullPointerException("content == null");
+
+      return new Body() {
+        @Override public MediaType contentType() {
+          return contentType;
+        }
+
+        @Override public long contentLength() {
+          return file.length();
+        }
+
+        @Override public void writeTo(OutputStream out) throws IOException {
+          long length = contentLength();
+          if (length == 0) return;
+
+          InputStream in = null;
+          try {
+            in = new FileInputStream(file);
+            byte[] buffer = new byte[(int) Math.min(8192, length)];
+            for (int c; (c = in.read(buffer)) != -1; ) {
+              out.write(buffer, 0, c);
+            }
+          } finally {
+            Util.closeQuietly(in);
+          }
+        }
+      };
+    }
+  }
+
+  public static class Builder {
+    private URL url;
+    private String method = "GET";
+    private RawHeaders headers = new RawHeaders();
+    private Body body;
+    private Object tag;
+
+    public Builder(String url) {
+      url(url);
+    }
+
+    public Builder(URL url) {
+      url(url);
+    }
+
+    public Builder url(String url) {
+      try {
+        this.url = new URL(url);
+        return this;
+      } catch (MalformedURLException e) {
+        throw new IllegalArgumentException("Malformed URL: " + url);
+      }
+    }
+
+    public Builder url(URL url) {
+      if (url == null) throw new IllegalStateException("url == null");
+      this.url = url;
+      return this;
+    }
+
+    /**
+     * Sets the header named {@code name} to {@code value}. If this request
+     * already has any headers with that name, they are all replaced.
+     */
+    public Builder header(String name, String value) {
+      headers.set(name, value);
+      return this;
+    }
+
+    /**
+     * Adds a header with {@code name} and {@code value}. Prefer this method for
+     * multiply-valued headers like "Cookie".
+     */
+    public Builder addHeader(String name, String value) {
+      headers.add(name, value);
+      return this;
+    }
+
+    Builder rawHeaders(RawHeaders rawHeaders) {
+      headers = new RawHeaders(rawHeaders);
+      return this;
+    }
+
+    public Builder get() {
+      return method("GET", null);
+    }
+
+    public Builder head() {
+      return method("HEAD", null);
+    }
+
+    public Builder post(Body body) {
+      return method("POST", body);
+    }
+
+    public Builder put(Body body) {
+      return method("PUT", body);
+    }
+
+    public Builder method(String method, Body body) {
+      if (method == null || method.length() == 0) {
+        throw new IllegalArgumentException("method == null || method.length() == 0");
+      }
+      this.method = method;
+      this.body = body;
+      return this;
+    }
+
+    /**
+     * Attaches {@code tag} to the request. It can be used later to cancel the
+     * request. If the tag is unspecified or null, the request is canceled by
+     * using the request itself as the tag.
+     */
+    public Builder tag(Object tag) {
+      this.tag = tag;
+      return this;
+    }
+
+    public Request build() {
+      return new Request(this);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/Response.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/Response.java b/framework/src/com/squareup/okhttp/Response.java
new file mode 100755
index 0000000..1e67968
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/Response.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2013 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;
+
+import com.squareup.okhttp.internal.Util;
+import com.squareup.okhttp.internal.http.RawHeaders;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.Set;
+
+import static com.squareup.okhttp.internal.Util.UTF_8;
+
+/**
+ * An HTTP response. Instances of this class are not immutable: the response
+ * body is a one-shot value that may be consumed only once. All other properties
+ * are immutable.
+ *
+ * <h3>Warning: Experimental OkHttp 2.0 API</h3>
+ * This class is in beta. APIs are subject to change!
+ */
+/* OkHttp 2.0: public */ final class Response {
+  private final Request request;
+  private final int code;
+  private final RawHeaders headers;
+  private final Body body;
+  private final Response redirectedBy;
+
+  private Response(Builder builder) {
+    this.request = builder.request;
+    this.code = builder.code;
+    this.headers = new RawHeaders(builder.headers);
+    this.body = builder.body;
+    this.redirectedBy = builder.redirectedBy;
+  }
+
+  /**
+   * The wire-level request that initiated this HTTP response. This is usually
+   * <strong>not</strong> the same request instance provided to the HTTP client:
+   * <ul>
+   *     <li>It may be transformed by the HTTP client. For example, the client
+   *         may have added its own {@code Content-Encoding} header to enable
+   *         response compression.
+   *     <li>It may be the request generated in response to an HTTP redirect.
+   *         In this case the request URL may be different than the initial
+   *         request URL.
+   * </ul>
+   */
+  public Request request() {
+    return request;
+  }
+
+  public int code() {
+    return code;
+  }
+
+  public String header(String name) {
+    return header(name, null);
+  }
+
+  public String header(String name, String defaultValue) {
+    String result = headers.get(name);
+    return result != null ? result : defaultValue;
+  }
+
+  public List<String> headers(String name) {
+    return headers.values(name);
+  }
+
+  public Set<String> headerNames() {
+    return headers.names();
+  }
+
+  public int headerCount() {
+    return headers.length();
+  }
+
+  public String headerName(int index) {
+    return headers.getFieldName(index);
+  }
+
+  RawHeaders rawHeaders() {
+    return new RawHeaders(headers);
+  }
+
+  public String headerValue(int index) {
+    return headers.getValue(index);
+  }
+
+  public Body body() {
+    return body;
+  }
+
+  /**
+   * Returns the response for the HTTP redirect that triggered this response, or
+   * null if this response wasn't triggered by an automatic redirect. The body
+   * of the returned response should not be read because it has already been
+   * consumed by the redirecting client.
+   */
+  public Response redirectedBy() {
+    return redirectedBy;
+  }
+
+  public abstract static class Body {
+    /** Multiple calls to {@link #charStream()} must return the same instance. */
+    private Reader reader;
+
+    /**
+     * Returns true if further data from this response body should be read at
+     * this time. For asynchronous transports like SPDY and HTTP/2.0, this will
+     * return false once all locally-available body bytes have been read.
+     *
+     * <p>Clients with many concurrent downloads can use this method to reduce
+     * the number of idle threads blocking on reads. See {@link
+     * Receiver#onResponse} for details.
+     */
+    // <h3>Body.ready() vs. InputStream.available()</h3>
+    // TODO: Can we fix response bodies to implement InputStream.available well?
+    // The deflater implementation is broken by default but we could do better.
+    public abstract boolean ready() throws IOException;
+
+    public abstract MediaType contentType();
+
+    /**
+     * Returns the number of bytes in that will returned by {@link #bytes}, or
+     * {@link #byteStream}, or -1 if unknown.
+     */
+    public abstract long contentLength();
+
+    public abstract InputStream byteStream() throws IOException;
+
+    public final byte[] bytes() throws IOException {
+      long contentLength = contentLength();
+      if (contentLength > Integer.MAX_VALUE) {
+        throw new IOException("Cannot buffer entire body for content length: " + contentLength);
+      }
+
+      if (contentLength != -1) {
+        byte[] content = new byte[(int) contentLength];
+        InputStream in = byteStream();
+        Util.readFully(in, content);
+        if (in.read() != -1) throw new IOException("Content-Length and stream length disagree");
+        return content;
+
+      } else {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        Util.copy(byteStream(), out);
+        return out.toByteArray();
+      }
+    }
+
+    /**
+     * Returns the response as a character stream decoded with the charset
+     * of the Content-Type header. If that header is either absent or lacks a
+     * charset, this will attempt to decode the response body as UTF-8.
+     */
+    public final Reader charStream() throws IOException {
+      if (reader == null) {
+        reader = new InputStreamReader(byteStream(), charset());
+      }
+      return reader;
+    }
+
+    /**
+     * Returns the response as a string decoded with the charset of the
+     * Content-Type header. If that header is either absent or lacks a charset,
+     * this will attempt to decode the response body as UTF-8.
+     */
+    public final String string() throws IOException {
+      return new String(bytes(), charset().name());
+    }
+
+    private Charset charset() {
+      MediaType contentType = contentType();
+      return contentType != null ? contentType.charset(UTF_8) : UTF_8;
+    }
+  }
+
+  public interface Receiver {
+    /**
+     * Called when the request could not be executed due to a connectivity
+     * problem or timeout. Because networks can fail during an exchange, it is
+     * possible that the remote server accepted the request before the failure.
+     */
+    void onFailure(Failure failure);
+
+    /**
+     * Called when the HTTP response was successfully returned by the remote
+     * server. The receiver may proceed to read the response body with the
+     * response's {@link #body} method.
+     *
+     * <p>Note that transport-layer success (receiving a HTTP response code,
+     * headers and body) does not necessarily indicate application-layer
+     * success: {@code response} may still indicate an unhappy HTTP response
+     * code like 404 or 500.
+     *
+     * <h3>Non-blocking responses</h3>
+     *
+     * <p>Receivers do not need to block while waiting for the response body to
+     * download. Instead, they can get called back as data arrives. Use {@link
+     * Body#ready} to check if bytes should be read immediately. While there is
+     * data ready, read it. If there isn't, return false: receivers will be
+     * called back with {@code onResponse()} as additional data is downloaded.
+     *
+     * <p>Return true to indicate that the receiver has finished handling the
+     * response body. If the response body has unread data, it will be
+     * discarded.
+     *
+     * <p>When the response body has been fully consumed the returned value is
+     * undefined.
+     *
+     * <p>The current implementation of {@link Body#ready} always returns true
+     * when the underlying transport is HTTP/1. This results in blocking on that
+     * transport. For effective non-blocking your server must support SPDY or
+     * HTTP/2.
+     */
+    boolean onResponse(Response response) throws IOException;
+  }
+
+  public static class Builder {
+    private final Request request;
+    private final int code;
+    private RawHeaders headers = new RawHeaders();
+    private Body body;
+    private Response redirectedBy;
+
+    public Builder(Request request, int code) {
+      if (request == null) throw new IllegalArgumentException("request == null");
+      if (code <= 0) throw new IllegalArgumentException("code <= 0");
+      this.request = request;
+      this.code = code;
+    }
+
+    /**
+     * Sets the header named {@code name} to {@code value}. If this request
+     * already has any headers with that name, they are all replaced.
+     */
+    public Builder header(String name, String value) {
+      headers.set(name, value);
+      return this;
+    }
+
+    /**
+     * Adds a header with {@code name} and {@code value}. Prefer this method for
+     * multiply-valued headers like "Set-Cookie".
+     */
+    public Builder addHeader(String name, String value) {
+      headers.add(name, value);
+      return this;
+    }
+
+    Builder rawHeaders(RawHeaders rawHeaders) {
+      headers = new RawHeaders(rawHeaders);
+      return this;
+    }
+
+    public Builder body(Body body) {
+      this.body = body;
+      return this;
+    }
+
+    public Builder redirectedBy(Response redirectedBy) {
+      this.redirectedBy = redirectedBy;
+      return this;
+    }
+
+    public Response build() {
+      if (request == null) throw new IllegalStateException("Response has no request.");
+      if (code == -1) throw new IllegalStateException("Response has no code.");
+      return new Response(this);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/ResponseSource.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/ResponseSource.java b/framework/src/com/squareup/okhttp/ResponseSource.java
old mode 100644
new mode 100755

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/Route.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/Route.java b/framework/src/com/squareup/okhttp/Route.java
old mode 100644
new mode 100755
index 6968c60..4b8786d
--- a/framework/src/com/squareup/okhttp/Route.java
+++ b/framework/src/com/squareup/okhttp/Route.java
@@ -59,13 +59,13 @@ public class Route {
     return inetSocketAddress;
   }
 
-  /** Returns true if this route uses modern tls. */
+  /** Returns true if this route uses modern TLS. */
   public boolean isModernTls() {
     return modernTls;
   }
 
-  /** Returns a copy of this route with flipped tls mode. */
-  public Route flipTlsMode() {
+  /** Returns a copy of this route with flipped TLS mode. */
+  Route flipTlsMode() {
     return new Route(address, proxy, inetSocketAddress, !modernTls);
   }
 

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/RouteDatabase.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/RouteDatabase.java b/framework/src/com/squareup/okhttp/RouteDatabase.java
new file mode 100755
index 0000000..9cbeaa7
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/RouteDatabase.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2013 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;
+
+import java.io.IOException;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import javax.net.ssl.SSLHandshakeException;
+
+/**
+ * A blacklist of failed routes to avoid when creating a new connection to a
+ * target address. This is used so that OkHttp can learn from its mistakes: if
+ * there was a failure attempting to connect to a specific IP address, proxy
+ * server or TLS mode, that failure is remembered and alternate routes are
+ * preferred.
+ */
+public final class RouteDatabase {
+  private final Set<Route> failedRoutes = new LinkedHashSet<Route>();
+
+  /** Records a failure connecting to {@code failedRoute}. */
+  public synchronized void failed(Route failedRoute, IOException failure) {
+    failedRoutes.add(failedRoute);
+
+    if (!(failure instanceof SSLHandshakeException)) {
+      // If the problem was not related to SSL then it will also fail with
+      // a different TLS mode therefore we can be proactive about it.
+      failedRoutes.add(failedRoute.flipTlsMode());
+    }
+  }
+
+  /** Records success connecting to {@code failedRoute}. */
+  public synchronized void connected(Route route) {
+    failedRoutes.remove(route);
+  }
+
+  /** Returns true if {@code route} has failed recently and should be avoided. */
+  public synchronized boolean shouldPostpone(Route route) {
+    return failedRoutes.contains(route);
+  }
+
+  public synchronized int failedRoutesCount() {
+    return failedRoutes.size();
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/TunnelRequest.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/TunnelRequest.java b/framework/src/com/squareup/okhttp/TunnelRequest.java
old mode 100644
new mode 100755

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/AbstractOutputStream.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/AbstractOutputStream.java b/framework/src/com/squareup/okhttp/internal/AbstractOutputStream.java
old mode 100644
new mode 100755

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/Base64.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/Base64.java b/framework/src/com/squareup/okhttp/internal/Base64.java
old mode 100644
new mode 100755

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/DiskLruCache.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/DiskLruCache.java b/framework/src/com/squareup/okhttp/internal/DiskLruCache.java
old mode 100644
new mode 100755

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/Dns.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/Dns.java b/framework/src/com/squareup/okhttp/internal/Dns.java
old mode 100644
new mode 100755

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/FaultRecoveringOutputStream.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/FaultRecoveringOutputStream.java b/framework/src/com/squareup/okhttp/internal/FaultRecoveringOutputStream.java
old mode 100644
new mode 100755

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/NamedRunnable.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/NamedRunnable.java b/framework/src/com/squareup/okhttp/internal/NamedRunnable.java
old mode 100644
new mode 100755
index ce430b2..992b2ae
--- a/framework/src/com/squareup/okhttp/internal/NamedRunnable.java
+++ b/framework/src/com/squareup/okhttp/internal/NamedRunnable.java
@@ -20,10 +20,10 @@ package com.squareup.okhttp.internal;
  * Runnable implementation which always sets its thread name.
  */
 public abstract class NamedRunnable implements Runnable {
-  private String name;
+  private final String name;
 
-  public NamedRunnable(String name) {
-    this.name = name;
+  public NamedRunnable(String format, Object... args) {
+    this.name = String.format(format, args);
   }
 
   @Override public final void run() {


[3/5] CB-5799 Update version of OkHTTP to 1.3

Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/HttpsEngine.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/HttpsEngine.java b/framework/src/com/squareup/okhttp/internal/http/HttpsEngine.java
new file mode 100755
index 0000000..2bc1d68
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/internal/http/HttpsEngine.java
@@ -0,0 +1,72 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.internal.http;
+
+import com.squareup.okhttp.Connection;
+import com.squareup.okhttp.OkHttpClient;
+import com.squareup.okhttp.TunnelRequest;
+import java.io.IOException;
+import java.net.CacheResponse;
+import java.net.SecureCacheResponse;
+import java.net.URL;
+import javax.net.ssl.SSLSocket;
+
+import static com.squareup.okhttp.internal.Util.getEffectivePort;
+
+public final class HttpsEngine extends HttpEngine {
+  /**
+   * Stash of HttpsEngine.connection.socket to implement requests like {@code
+   * HttpsURLConnection#getCipherSuite} even after the connection has been
+   * recycled.
+   */
+  private SSLSocket sslSocket;
+
+  public HttpsEngine(OkHttpClient client, Policy policy, String method, RawHeaders requestHeaders,
+      Connection connection, RetryableOutputStream requestBody) throws IOException {
+    super(client, policy, method, requestHeaders, connection, requestBody);
+    this.sslSocket = connection != null ? (SSLSocket) connection.getSocket() : null;
+  }
+
+  @Override protected void connected(Connection connection) {
+    this.sslSocket = (SSLSocket) connection.getSocket();
+    super.connected(connection);
+  }
+
+  @Override protected boolean acceptCacheResponseType(CacheResponse cacheResponse) {
+    return cacheResponse instanceof SecureCacheResponse;
+  }
+
+  @Override protected boolean includeAuthorityInRequestLine() {
+    // Even if there is a proxy, it isn't involved. Always request just the path.
+    return false;
+  }
+
+  public SSLSocket getSslSocket() {
+    return sslSocket;
+  }
+
+  @Override protected TunnelRequest getTunnelConfig() {
+    String userAgent = requestHeaders.getUserAgent();
+    if (userAgent == null) {
+      userAgent = getDefaultUserAgent();
+    }
+
+    URL url = policy.getURL();
+    return new TunnelRequest(url.getHost(), getEffectivePort(url), userAgent,
+        requestHeaders.getProxyAuthorization());
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/HttpsURLConnectionImpl.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/HttpsURLConnectionImpl.java b/framework/src/com/squareup/okhttp/internal/http/HttpsURLConnectionImpl.java
old mode 100644
new mode 100755
index 235f862..e64fb98
--- a/framework/src/com/squareup/okhttp/internal/http/HttpsURLConnectionImpl.java
+++ b/framework/src/com/squareup/okhttp/internal/http/HttpsURLConnectionImpl.java
@@ -16,14 +16,11 @@
  */
 package com.squareup.okhttp.internal.http;
 
-import com.squareup.okhttp.Connection;
+import android.annotation.SuppressLint;
 import com.squareup.okhttp.OkHttpClient;
-import com.squareup.okhttp.Route;
-import com.squareup.okhttp.TunnelRequest;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.net.CacheResponse;
 import java.net.HttpURLConnection;
 import java.net.ProtocolException;
 import java.net.SecureCacheResponse;
@@ -33,24 +30,20 @@ import java.security.Principal;
 import java.security.cert.Certificate;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLPeerUnverifiedException;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
 
-import static com.squareup.okhttp.internal.Util.getEffectivePort;
-
 public final class HttpsURLConnectionImpl extends HttpsURLConnection {
 
   /** HttpUrlConnectionDelegate allows reuse of HttpURLConnectionImpl. */
   private final HttpUrlConnectionDelegate delegate;
 
-  public HttpsURLConnectionImpl(URL url, OkHttpClient client, OkResponseCache responseCache,
-      Set<Route> failedRoutes) {
+  public HttpsURLConnectionImpl(URL url, OkHttpClient client) {
     super(url);
-    delegate = new HttpUrlConnectionDelegate(url, client, responseCache, failedRoutes);
+    delegate = new HttpUrlConnectionDelegate(url, client);
   }
 
   @Override public String getCipherSuite() {
@@ -120,294 +113,247 @@ public final class HttpsURLConnectionImpl extends HttpsURLConnection {
   }
 
   private SSLSocket getSslSocket() {
-    if (delegate.httpEngine == null || delegate.httpEngine.sentRequestMillis == -1) {
+    if (delegate.httpEngine == null || !delegate.httpEngine.connected) {
       throw new IllegalStateException("Connection has not yet been established");
     }
     return delegate.httpEngine instanceof HttpsEngine
-        ? ((HttpsEngine) delegate.httpEngine).sslSocket
+        ? ((HttpsEngine) delegate.httpEngine).getSslSocket()
         : null; // Not HTTPS! Probably an https:// to http:// redirect.
   }
 
-  @Override
-  public void disconnect() {
+  @Override public void disconnect() {
     delegate.disconnect();
   }
 
-  @Override
-  public InputStream getErrorStream() {
+  @Override public InputStream getErrorStream() {
     return delegate.getErrorStream();
   }
 
-  @Override
-  public String getRequestMethod() {
+  @Override public String getRequestMethod() {
     return delegate.getRequestMethod();
   }
 
-  @Override
-  public int getResponseCode() throws IOException {
+  @Override public int getResponseCode() throws IOException {
     return delegate.getResponseCode();
   }
 
-  @Override
-  public String getResponseMessage() throws IOException {
+  @Override public String getResponseMessage() throws IOException {
     return delegate.getResponseMessage();
   }
 
-  @Override
-  public void setRequestMethod(String method) throws ProtocolException {
+  @Override public void setRequestMethod(String method) throws ProtocolException {
     delegate.setRequestMethod(method);
   }
 
-  @Override
-  public boolean usingProxy() {
+  @Override public boolean usingProxy() {
     return delegate.usingProxy();
   }
 
-  @Override
-  public boolean getInstanceFollowRedirects() {
+  @Override public boolean getInstanceFollowRedirects() {
     return delegate.getInstanceFollowRedirects();
   }
 
-  @Override
-  public void setInstanceFollowRedirects(boolean followRedirects) {
+  @Override public void setInstanceFollowRedirects(boolean followRedirects) {
     delegate.setInstanceFollowRedirects(followRedirects);
   }
 
-  @Override
-  public void connect() throws IOException {
+  @Override public void connect() throws IOException {
     connected = true;
     delegate.connect();
   }
 
-  @Override
-  public boolean getAllowUserInteraction() {
+  @Override public boolean getAllowUserInteraction() {
     return delegate.getAllowUserInteraction();
   }
 
-  @Override
-  public Object getContent() throws IOException {
+  @Override public Object getContent() throws IOException {
     return delegate.getContent();
   }
 
   @SuppressWarnings("unchecked") // Spec does not generify
-  @Override
-  public Object getContent(Class[] types) throws IOException {
+  @Override public Object getContent(Class[] types) throws IOException {
     return delegate.getContent(types);
   }
 
-  @Override
-  public String getContentEncoding() {
+  @Override public String getContentEncoding() {
     return delegate.getContentEncoding();
   }
 
-  @Override
-  public int getContentLength() {
+  @Override public int getContentLength() {
     return delegate.getContentLength();
   }
 
-  @Override
-  public String getContentType() {
+  @Override public String getContentType() {
     return delegate.getContentType();
   }
 
-  @Override
-  public long getDate() {
+  @Override public long getDate() {
     return delegate.getDate();
   }
 
-  @Override
-  public boolean getDefaultUseCaches() {
+  @Override public boolean getDefaultUseCaches() {
     return delegate.getDefaultUseCaches();
   }
 
-  @Override
-  public boolean getDoInput() {
+  @Override public boolean getDoInput() {
     return delegate.getDoInput();
   }
 
-  @Override
-  public boolean getDoOutput() {
+  @Override public boolean getDoOutput() {
     return delegate.getDoOutput();
   }
 
-  @Override
-  public long getExpiration() {
+  @Override public long getExpiration() {
     return delegate.getExpiration();
   }
 
-  @Override
-  public String getHeaderField(int pos) {
+  @Override public String getHeaderField(int pos) {
     return delegate.getHeaderField(pos);
   }
 
-  @Override
-  public Map<String, List<String>> getHeaderFields() {
+  @Override public Map<String, List<String>> getHeaderFields() {
     return delegate.getHeaderFields();
   }
 
-  @Override
-  public Map<String, List<String>> getRequestProperties() {
+  @Override public Map<String, List<String>> getRequestProperties() {
     return delegate.getRequestProperties();
   }
 
-  @Override
-  public void addRequestProperty(String field, String newValue) {
+  @Override public void addRequestProperty(String field, String newValue) {
     delegate.addRequestProperty(field, newValue);
   }
 
-  @Override
-  public String getHeaderField(String key) {
+  @Override public String getHeaderField(String key) {
     return delegate.getHeaderField(key);
   }
 
-  @Override
-  public long getHeaderFieldDate(String field, long defaultValue) {
+  @Override public long getHeaderFieldDate(String field, long defaultValue) {
     return delegate.getHeaderFieldDate(field, defaultValue);
   }
 
-  @Override
-  public int getHeaderFieldInt(String field, int defaultValue) {
+  @Override public int getHeaderFieldInt(String field, int defaultValue) {
     return delegate.getHeaderFieldInt(field, defaultValue);
   }
 
-  @Override
-  public String getHeaderFieldKey(int position) {
+  @Override public String getHeaderFieldKey(int position) {
     return delegate.getHeaderFieldKey(position);
   }
 
-  @Override
-  public long getIfModifiedSince() {
+  @Override public long getIfModifiedSince() {
     return delegate.getIfModifiedSince();
   }
 
-  @Override
-  public InputStream getInputStream() throws IOException {
+  @Override public InputStream getInputStream() throws IOException {
     return delegate.getInputStream();
   }
 
-  @Override
-  public long getLastModified() {
+  @Override public long getLastModified() {
     return delegate.getLastModified();
   }
 
-  @Override
-  public OutputStream getOutputStream() throws IOException {
+  @Override public OutputStream getOutputStream() throws IOException {
     return delegate.getOutputStream();
   }
 
-  @Override
-  public Permission getPermission() throws IOException {
+  @Override public Permission getPermission() throws IOException {
     return delegate.getPermission();
   }
 
-  @Override
-  public String getRequestProperty(String field) {
+  @Override public String getRequestProperty(String field) {
     return delegate.getRequestProperty(field);
   }
 
-  @Override
-  public URL getURL() {
+  @Override public URL getURL() {
     return delegate.getURL();
   }
 
-  @Override
-  public boolean getUseCaches() {
+  @Override public boolean getUseCaches() {
     return delegate.getUseCaches();
   }
 
-  @Override
-  public void setAllowUserInteraction(boolean newValue) {
+  @Override public void setAllowUserInteraction(boolean newValue) {
     delegate.setAllowUserInteraction(newValue);
   }
 
-  @Override
-  public void setDefaultUseCaches(boolean newValue) {
+  @Override public void setDefaultUseCaches(boolean newValue) {
     delegate.setDefaultUseCaches(newValue);
   }
 
-  @Override
-  public void setDoInput(boolean newValue) {
+  @Override public void setDoInput(boolean newValue) {
     delegate.setDoInput(newValue);
   }
 
-  @Override
-  public void setDoOutput(boolean newValue) {
+  @Override public void setDoOutput(boolean newValue) {
     delegate.setDoOutput(newValue);
   }
 
-  @Override
-  public void setIfModifiedSince(long newValue) {
+  @Override public void setIfModifiedSince(long newValue) {
     delegate.setIfModifiedSince(newValue);
   }
 
-  @Override
-  public void setRequestProperty(String field, String newValue) {
+  @Override public void setRequestProperty(String field, String newValue) {
     delegate.setRequestProperty(field, newValue);
   }
 
-  @Override
-  public void setUseCaches(boolean newValue) {
+  @Override public void setUseCaches(boolean newValue) {
     delegate.setUseCaches(newValue);
   }
 
-  @Override
-  public void setConnectTimeout(int timeoutMillis) {
+  @Override public void setConnectTimeout(int timeoutMillis) {
     delegate.setConnectTimeout(timeoutMillis);
   }
 
-  @Override
-  public int getConnectTimeout() {
+  @Override public int getConnectTimeout() {
     return delegate.getConnectTimeout();
   }
 
-  @Override
-  public void setReadTimeout(int timeoutMillis) {
+  @Override public void setReadTimeout(int timeoutMillis) {
     delegate.setReadTimeout(timeoutMillis);
   }
 
-  @Override
-  public int getReadTimeout() {
+  @Override public int getReadTimeout() {
     return delegate.getReadTimeout();
   }
 
-  @Override
-  public String toString() {
+  @Override public String toString() {
     return delegate.toString();
   }
 
-  @Override
-  public void setFixedLengthStreamingMode(int contentLength) {
+  @Override public void setFixedLengthStreamingMode(int contentLength) {
     delegate.setFixedLengthStreamingMode(contentLength);
   }
 
-  @Override
-  public void setChunkedStreamingMode(int chunkLength) {
+  @Override public void setChunkedStreamingMode(int chunkLength) {
     delegate.setChunkedStreamingMode(chunkLength);
   }
 
   @Override public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {
-    delegate.hostnameVerifier = hostnameVerifier;
+    delegate.client.setHostnameVerifier(hostnameVerifier);
   }
 
   @Override public HostnameVerifier getHostnameVerifier() {
-    return delegate.hostnameVerifier;
+    return delegate.client.getHostnameVerifier();
   }
 
   @Override public void setSSLSocketFactory(SSLSocketFactory sslSocketFactory) {
-    delegate.sslSocketFactory = sslSocketFactory;
+    delegate.client.setSslSocketFactory(sslSocketFactory);
   }
 
   @Override public SSLSocketFactory getSSLSocketFactory() {
-    return delegate.sslSocketFactory;
+    return delegate.client.getSslSocketFactory();
+  }
+
+  @SuppressLint("NewApi")
+  @Override public void setFixedLengthStreamingMode(long contentLength) {
+    delegate.setFixedLengthStreamingMode(contentLength);
   }
 
   private final class HttpUrlConnectionDelegate extends HttpURLConnectionImpl {
-    private HttpUrlConnectionDelegate(URL url, OkHttpClient client, OkResponseCache responseCache,
-        Set<Route> failedRoutes) {
-      super(url, client, responseCache, failedRoutes);
+    private HttpUrlConnectionDelegate(URL url, OkHttpClient client) {
+      super(url, client);
     }
 
-    @Override protected HttpURLConnection getHttpConnectionToCache() {
+    @Override public HttpURLConnection getHttpConnectionToCache() {
       return HttpsURLConnectionImpl.this;
     }
 
@@ -417,45 +363,4 @@ public final class HttpsURLConnectionImpl extends HttpsURLConnection {
           : null;
     }
   }
-
-  public static final class HttpsEngine extends HttpEngine {
-    /**
-     * Stash of HttpsEngine.connection.socket to implement requests like
-     * {@link #getCipherSuite} even after the connection has been recycled.
-     */
-    private SSLSocket sslSocket;
-
-    /**
-     * @param policy the HttpURLConnectionImpl with connection configuration
-     */
-    public HttpsEngine(HttpURLConnectionImpl policy, String method, RawHeaders requestHeaders,
-        Connection connection, RetryableOutputStream requestBody) throws IOException {
-      super(policy, method, requestHeaders, connection, requestBody);
-      this.sslSocket = connection != null ? (SSLSocket) connection.getSocket() : null;
-    }
-
-    @Override protected void connected(Connection connection) {
-      this.sslSocket = (SSLSocket) connection.getSocket();
-    }
-
-    @Override protected boolean acceptCacheResponseType(CacheResponse cacheResponse) {
-      return cacheResponse instanceof SecureCacheResponse;
-    }
-
-    @Override protected boolean includeAuthorityInRequestLine() {
-      // Even if there is a proxy, it isn't involved. Always request just the file.
-      return false;
-    }
-
-    @Override protected TunnelRequest getTunnelConfig() {
-      String userAgent = requestHeaders.getUserAgent();
-      if (userAgent == null) {
-        userAgent = getDefaultUserAgent();
-      }
-
-      URL url = policy.getURL();
-      return new TunnelRequest(url.getHost(), getEffectivePort(url), userAgent,
-          requestHeaders.getProxyAuthorization());
-    }
-  }
 }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/OkResponseCache.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/OkResponseCache.java b/framework/src/com/squareup/okhttp/internal/http/OkResponseCache.java
deleted file mode 100644
index 5829f02..0000000
--- a/framework/src/com/squareup/okhttp/internal/http/OkResponseCache.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2013 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.internal.http;
-
-import com.squareup.okhttp.ResponseSource;
-import java.io.IOException;
-import java.net.CacheRequest;
-import java.net.CacheResponse;
-import java.net.HttpURLConnection;
-import java.net.URI;
-import java.net.URLConnection;
-import java.util.List;
-import java.util.Map;
-
-/**
- * An extended response cache API. Unlike {@link java.net.ResponseCache}, this
- * interface supports conditional caching and statistics.
- *
- * <p>Along with the rest of the {@code internal} package, this is not a public
- * API. Applications wishing to supply their own caches must use the more
- * limited {@link java.net.ResponseCache} interface.
- */
-public interface OkResponseCache {
-  CacheResponse get(URI uri, String requestMethod, Map<String, List<String>> requestHeaders)
-      throws IOException;
-
-  CacheRequest put(URI uri, URLConnection urlConnection) throws IOException;
-
-  /**
-   * Handles a conditional request hit by updating the stored cache response
-   * with the headers from {@code httpConnection}. The cached response body is
-   * not updated. If the stored response has changed since {@code
-   * conditionalCacheHit} was returned, this does nothing.
-   */
-  void update(CacheResponse conditionalCacheHit, HttpURLConnection connection) throws IOException;
-
-  /** Track an conditional GET that was satisfied by this cache. */
-  void trackConditionalCacheHit();
-
-  /** Track an HTTP response being satisfied by {@code source}. */
-  void trackResponse(ResponseSource source);
-}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/OkResponseCacheAdapter.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/OkResponseCacheAdapter.java b/framework/src/com/squareup/okhttp/internal/http/OkResponseCacheAdapter.java
old mode 100644
new mode 100755
index 2ac915a..5335c2b
--- a/framework/src/com/squareup/okhttp/internal/http/OkResponseCacheAdapter.java
+++ b/framework/src/com/squareup/okhttp/internal/http/OkResponseCacheAdapter.java
@@ -15,6 +15,7 @@
  */
 package com.squareup.okhttp.internal.http;
 
+import com.squareup.okhttp.OkResponseCache;
 import com.squareup.okhttp.ResponseSource;
 import java.io.IOException;
 import java.net.CacheRequest;
@@ -41,6 +42,9 @@ public final class OkResponseCacheAdapter implements OkResponseCache {
     return responseCache.put(uri, urlConnection);
   }
 
+  @Override public void maybeRemove(String requestMethod, URI uri) throws IOException {
+  }
+
   @Override public void update(CacheResponse conditionalCacheHit, HttpURLConnection connection)
       throws IOException {
   }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/Policy.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/Policy.java b/framework/src/com/squareup/okhttp/internal/http/Policy.java
new file mode 100755
index 0000000..0a29d4b
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/internal/http/Policy.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2013 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.internal.http;
+
+import java.net.HttpURLConnection;
+import java.net.Proxy;
+import java.net.URL;
+
+public interface Policy {
+  /** Returns true if HTTP response caches should be used. */
+  boolean getUseCaches();
+
+  /** Returns the HttpURLConnection instance to store in the cache. */
+  HttpURLConnection getHttpConnectionToCache();
+
+  /** Returns the current destination URL, possibly a redirect. */
+  URL getURL();
+
+  /** Returns the If-Modified-Since timestamp, or 0 if none is set. */
+  long getIfModifiedSince();
+
+  /** Returns true if a non-direct proxy is specified. */
+  boolean usingProxy();
+
+  /** @see java.net.HttpURLConnection#setChunkedStreamingMode(int) */
+  int getChunkLength();
+
+  /** @see java.net.HttpURLConnection#setFixedLengthStreamingMode(int) */
+  long getFixedContentLength();
+
+  /**
+   * Sets the current proxy that this connection is using.
+   * @see java.net.HttpURLConnection#usingProxy
+   */
+  void setSelectedProxy(Proxy proxy);
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/RawHeaders.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/RawHeaders.java b/framework/src/com/squareup/okhttp/internal/http/RawHeaders.java
old mode 100644
new mode 100755
index eba887e..8b45320
--- a/framework/src/com/squareup/okhttp/internal/http/RawHeaders.java
+++ b/framework/src/com/squareup/okhttp/internal/http/RawHeaders.java
@@ -32,6 +32,7 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.TreeSet;
 
 /**
  * The HTTP status and unparsed header fields of a single HTTP message. Values
@@ -122,23 +123,6 @@ public final class RawHeaders {
     this.httpMinorVersion = httpMinorVersion;
   }
 
-  public void computeResponseStatusLineFromSpdyHeaders() throws IOException {
-    String status = null;
-    String version = null;
-    for (int i = 0; i < namesAndValues.size(); i += 2) {
-      String name = namesAndValues.get(i);
-      if (":status".equals(name)) {
-        status = namesAndValues.get(i + 1);
-      } else if (":version".equals(name)) {
-        version = namesAndValues.get(i + 1);
-      }
-    }
-    if (status == null || version == null) {
-      throw new ProtocolException("Expected ':status' and ':version' headers not present");
-    }
-    setStatusLine(version + " " + status);
-  }
-
   /**
    * @param method like "GET", "POST", "HEAD", etc.
    * @param path like "/foo/bar.html"
@@ -180,14 +164,17 @@ public final class RawHeaders {
 
   /**
    * Add an HTTP header line containing a field name, a literal colon, and a
-   * value.
+   * value. This works around empty header names and header names that start
+   * with a colon (created by old broken SPDY versions of the response cache).
    */
   public void addLine(String line) {
-    int index = line.indexOf(":");
-    if (index == -1) {
-      addLenient("", line);
-    } else {
+    int index = line.indexOf(":", 1);
+    if (index != -1) {
       addLenient(line.substring(0, index), line.substring(index + 1));
+    } else if (line.startsWith(":")) {
+      addLenient("", line.substring(1)); // Empty header name.
+    } else {
+      addLenient("", line); // No header name.
     }
   }
 
@@ -248,6 +235,15 @@ public final class RawHeaders {
     return namesAndValues.get(fieldNameIndex);
   }
 
+  /** Returns an immutable case-insensitive set of header names. */
+  public Set<String> names() {
+    TreeSet<String> result = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
+    for (int i = 0; i < length(); i++) {
+      result.add(getFieldName(i));
+    }
+    return Collections.unmodifiableSet(result);
+  }
+
   /** Returns the value at {@code index} or null if that is out of range. */
   public String getValue(int index) {
     int valueIndex = index * 2 + 1;
@@ -267,6 +263,20 @@ public final class RawHeaders {
     return null;
   }
 
+  /** Returns an immutable list of the header values for {@code name}. */
+  public List<String> values(String name) {
+    List<String> result = null;
+    for (int i = 0; i < length(); i++) {
+      if (name.equalsIgnoreCase(getFieldName(i))) {
+        if (result == null) result = new ArrayList<String>(2);
+        result.add(getValue(i));
+      }
+    }
+    return result != null
+        ? Collections.unmodifiableList(result)
+        : Collections.<String>emptyList();
+  }
+
   /** @param fieldNames a case-insensitive set of HTTP header field names. */
   public RawHeaders getAll(Set<String> fieldNames) {
     RawHeaders result = new RawHeaders();
@@ -401,10 +411,13 @@ public final class RawHeaders {
     return result;
   }
 
-  public static RawHeaders fromNameValueBlock(List<String> nameValueBlock) {
+  /** Returns headers for a name value block containing a SPDY response. */
+  public static RawHeaders fromNameValueBlock(List<String> nameValueBlock) throws IOException {
     if (nameValueBlock.size() % 2 != 0) {
       throw new IllegalArgumentException("Unexpected name value block: " + nameValueBlock);
     }
+    String status = null;
+    String version = null;
     RawHeaders result = new RawHeaders();
     for (int i = 0; i < nameValueBlock.size(); i += 2) {
       String name = nameValueBlock.get(i);
@@ -414,11 +427,21 @@ public final class RawHeaders {
         if (end == -1) {
           end = values.length();
         }
-        result.namesAndValues.add(name);
-        result.namesAndValues.add(values.substring(start, end));
+        String value = values.substring(start, end);
+        if (":status".equals(name)) {
+          status = value;
+        } else if (":version".equals(name)) {
+          version = value;
+        } else {
+          result.namesAndValues.add(name);
+          result.namesAndValues.add(value);
+        }
         start = end + 1;
       }
     }
+    if (status == null) throw new ProtocolException("Expected ':status' header not present");
+    if (version == null) throw new ProtocolException("Expected ':version' header not present");
+    result.setStatusLine(version + " " + status);
     return result;
   }
 }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/RequestHeaders.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/RequestHeaders.java b/framework/src/com/squareup/okhttp/internal/http/RequestHeaders.java
old mode 100644
new mode 100755
index 5ec4fcc..71c3cd0
--- a/framework/src/com/squareup/okhttp/internal/http/RequestHeaders.java
+++ b/framework/src/com/squareup/okhttp/internal/http/RequestHeaders.java
@@ -48,7 +48,7 @@ public final class RequestHeaders {
    */
   private boolean hasAuthorization;
 
-  private int contentLength = -1;
+  private long contentLength = -1;
   private String transferEncoding;
   private String userAgent;
   private String host;
@@ -157,7 +157,7 @@ public final class RequestHeaders {
     return hasAuthorization;
   }
 
-  public int getContentLength() {
+  public long getContentLength() {
     return contentLength;
   }
 
@@ -205,14 +205,26 @@ public final class RequestHeaders {
     this.transferEncoding = "chunked";
   }
 
-  public void setContentLength(int contentLength) {
+  public void setContentLength(long contentLength) {
     if (this.contentLength != -1) {
       headers.removeAll("Content-Length");
     }
-    headers.add("Content-Length", Integer.toString(contentLength));
+    headers.add("Content-Length", Long.toString(contentLength));
     this.contentLength = contentLength;
   }
 
+  /**
+   * Remove the Content-Length headers. Call this when dropping the body on a
+   * request or response, such as when a redirect changes the method from POST
+   * to GET.
+   */
+  public void removeContentLength() {
+    if (contentLength != -1) {
+      headers.removeAll("Content-Length");
+      contentLength = -1;
+    }
+  }
+
   public void setUserAgent(String userAgent) {
     if (this.userAgent != null) {
       headers.removeAll("User-Agent");
@@ -282,9 +294,24 @@ public final class RequestHeaders {
   public void addCookies(Map<String, List<String>> allCookieHeaders) {
     for (Map.Entry<String, List<String>> entry : allCookieHeaders.entrySet()) {
       String key = entry.getKey();
-      if ("Cookie".equalsIgnoreCase(key) || "Cookie2".equalsIgnoreCase(key)) {
-        headers.addAll(key, entry.getValue());
+      if (("Cookie".equalsIgnoreCase(key) || "Cookie2".equalsIgnoreCase(key))
+          && !entry.getValue().isEmpty()) {
+        headers.add(key, buildCookieHeader(entry.getValue()));
       }
     }
   }
+
+  /**
+   * Send all cookies in one big header, as recommended by
+   * <a href="http://tools.ietf.org/html/rfc6265#section-4.2.1">RFC 6265</a>.
+   */
+  private String buildCookieHeader(List<String> cookies) {
+    if (cookies.size() == 1) return cookies.get(0);
+    StringBuilder sb = new StringBuilder();
+    for (int i = 0; i < cookies.size(); i++) {
+      if (i > 0) sb.append("; ");
+      sb.append(cookies.get(i));
+    }
+    return sb.toString();
+  }
 }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/ResponseHeaders.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/ResponseHeaders.java b/framework/src/com/squareup/okhttp/internal/http/ResponseHeaders.java
old mode 100644
new mode 100755
index 2ab564d..69e8656
--- a/framework/src/com/squareup/okhttp/internal/http/ResponseHeaders.java
+++ b/framework/src/com/squareup/okhttp/internal/http/ResponseHeaders.java
@@ -17,6 +17,7 @@
 package com.squareup.okhttp.internal.http;
 
 import com.squareup.okhttp.ResponseSource;
+import com.squareup.okhttp.internal.Platform;
 import java.io.IOException;
 import java.net.HttpURLConnection;
 import java.net.URI;
@@ -34,13 +35,16 @@ import static com.squareup.okhttp.internal.Util.equal;
 public final class ResponseHeaders {
 
   /** HTTP header name for the local time when the request was sent. */
-  private static final String SENT_MILLIS = "X-Android-Sent-Millis";
+  private static final String SENT_MILLIS = Platform.get().getPrefix() + "-Sent-Millis";
 
   /** HTTP header name for the local time when the response was received. */
-  private static final String RECEIVED_MILLIS = "X-Android-Received-Millis";
+  private static final String RECEIVED_MILLIS = Platform.get().getPrefix() + "-Received-Millis";
 
   /** HTTP synthetic header with the response source. */
-  static final String RESPONSE_SOURCE = "X-Android-Response-Source";
+  static final String RESPONSE_SOURCE = Platform.get().getPrefix() + "-Response-Source";
+
+  /** HTTP synthetic header with the selected transport (spdy/3, http/1.1, etc). */
+  static final String SELECTED_TRANSPORT = Platform.get().getPrefix() + "-Selected-Transport";
 
   private final URI uri;
   private final RawHeaders headers;
@@ -110,8 +114,9 @@ public final class ResponseHeaders {
 
   private String contentEncoding;
   private String transferEncoding;
-  private int contentLength = -1;
+  private long contentLength = -1;
   private String connection;
+  private String contentType;
 
   public ResponseHeaders(URI uri, RawHeaders headers) {
     this.uri = uri;
@@ -168,9 +173,11 @@ public final class ResponseHeaders {
         transferEncoding = value;
       } else if ("Content-Length".equalsIgnoreCase(fieldName)) {
         try {
-          contentLength = Integer.parseInt(value);
+          contentLength = Long.parseLong(value);
         } catch (NumberFormatException ignored) {
         }
+      } else if ("Content-Type".equalsIgnoreCase(fieldName)) {
+        contentType = value;
       } else if ("Connection".equalsIgnoreCase(fieldName)) {
         connection = value;
       } else if (SENT_MILLIS.equalsIgnoreCase(fieldName)) {
@@ -259,10 +266,14 @@ public final class ResponseHeaders {
     return contentEncoding;
   }
 
-  public int getContentLength() {
+  public long getContentLength() {
     return contentLength;
   }
 
+  public String getContentType() {
+    return contentType;
+  }
+
   public String getConnection() {
     return connection;
   }
@@ -278,6 +289,10 @@ public final class ResponseHeaders {
     headers.set(RESPONSE_SOURCE, responseSource.toString() + " " + headers.getResponseCode());
   }
 
+  public void setTransport(String transport) {
+    headers.set(SELECTED_TRANSPORT, transport);
+  }
+
   /**
    * Returns the current age of the response, in milliseconds. The calculation
    * is specified by RFC 2616, 13.2.3 Age Calculations.

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/RetryableOutputStream.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/RetryableOutputStream.java b/framework/src/com/squareup/okhttp/internal/http/RetryableOutputStream.java
old mode 100644
new mode 100755

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/RouteSelector.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/RouteSelector.java b/framework/src/com/squareup/okhttp/internal/http/RouteSelector.java
old mode 100644
new mode 100755
index ce0a71d..1055e4f
--- a/framework/src/com/squareup/okhttp/internal/http/RouteSelector.java
+++ b/framework/src/com/squareup/okhttp/internal/http/RouteSelector.java
@@ -19,6 +19,7 @@ import com.squareup.okhttp.Address;
 import com.squareup.okhttp.Connection;
 import com.squareup.okhttp.ConnectionPool;
 import com.squareup.okhttp.Route;
+import com.squareup.okhttp.RouteDatabase;
 import com.squareup.okhttp.internal.Dns;
 import java.io.IOException;
 import java.net.InetAddress;
@@ -32,8 +33,6 @@ import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.NoSuchElementException;
-import java.util.Set;
-import javax.net.ssl.SSLHandshakeException;
 
 import static com.squareup.okhttp.internal.Util.getEffectivePort;
 
@@ -55,7 +54,7 @@ public final class RouteSelector {
   private final ProxySelector proxySelector;
   private final ConnectionPool pool;
   private final Dns dns;
-  private final Set<Route> failedRoutes;
+  private final RouteDatabase routeDatabase;
 
   /* The most recently attempted route. */
   private Proxy lastProxy;
@@ -78,13 +77,13 @@ public final class RouteSelector {
   private final List<Route> postponedRoutes;
 
   public RouteSelector(Address address, URI uri, ProxySelector proxySelector, ConnectionPool pool,
-      Dns dns, Set<Route> failedRoutes) {
+      Dns dns, RouteDatabase routeDatabase) {
     this.address = address;
     this.uri = uri;
     this.proxySelector = proxySelector;
     this.pool = pool;
     this.dns = dns;
-    this.failedRoutes = failedRoutes;
+    this.routeDatabase = routeDatabase;
     this.postponedRoutes = new LinkedList<Route>();
 
     resetNextProxy(uri, address.getProxy());
@@ -103,11 +102,11 @@ public final class RouteSelector {
    *
    * @throws NoSuchElementException if there are no more routes to attempt.
    */
-  public Connection next() throws IOException {
+  public Connection next(String method) throws IOException {
     // Always prefer pooled connections over new connections.
-    Connection pooled = pool.get(address);
-    if (pooled != null) {
-      return pooled;
+    for (Connection pooled; (pooled = pool.get(address)) != null; ) {
+      if (method.equals("GET") || pooled.isReadable()) return pooled;
+      pooled.close();
     }
 
     // Compute the next route to attempt.
@@ -128,11 +127,11 @@ public final class RouteSelector {
 
     boolean modernTls = nextTlsMode() == TLS_MODE_MODERN;
     Route route = new Route(address, lastProxy, lastInetSocketAddress, modernTls);
-    if (failedRoutes.contains(route)) {
+    if (routeDatabase.shouldPostpone(route)) {
       postponedRoutes.add(route);
       // We will only recurse in order to skip previously failed routes. They will be
       // tried last.
-      return next();
+      return next(method);
     }
 
     return new Connection(route);
@@ -149,12 +148,7 @@ public final class RouteSelector {
       proxySelector.connectFailed(uri, failedRoute.getProxy().address(), failure);
     }
 
-    failedRoutes.add(failedRoute);
-    if (!(failure instanceof SSLHandshakeException)) {
-      // If the problem was not related to SSL then it will also fail with
-      // a different Tls mode therefore we can be proactive about it.
-      failedRoutes.add(failedRoute.flipTlsMode());
-    }
+    routeDatabase.failed(failedRoute, failure);
   }
 
   /** Resets {@link #nextProxy} to the first option. */

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/SpdyTransport.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/SpdyTransport.java b/framework/src/com/squareup/okhttp/internal/http/SpdyTransport.java
old mode 100644
new mode 100755
index 18ab566..471539a
--- a/framework/src/com/squareup/okhttp/internal/http/SpdyTransport.java
+++ b/framework/src/com/squareup/okhttp/internal/http/SpdyTransport.java
@@ -16,6 +16,7 @@
 
 package com.squareup.okhttp.internal.http;
 
+import com.squareup.okhttp.internal.spdy.ErrorCode;
 import com.squareup.okhttp.internal.spdy.SpdyConnection;
 import com.squareup.okhttp.internal.spdy.SpdyStream;
 import java.io.IOException;
@@ -36,6 +37,10 @@ public final class SpdyTransport implements Transport {
   }
 
   @Override public OutputStream createRequestBody() throws IOException {
+    long fixedContentLength = httpEngine.policy.getFixedContentLength();
+    if (fixedContentLength != -1) {
+      httpEngine.requestHeaders.setContentLength(fixedContentLength);
+    }
     // TODO: if we aren't streaming up to the server, we should buffer the whole request
     writeRequestHeaders();
     return stream.getOutputStream();
@@ -55,7 +60,7 @@ public final class SpdyTransport implements Transport {
     boolean hasResponseBody = true;
     stream = spdyConnection.newStream(requestHeaders.toNameValueBlock(), hasRequestBody,
         hasResponseBody);
-    stream.setReadTimeout(httpEngine.policy.getReadTimeout());
+    stream.setReadTimeout(httpEngine.client.getReadTimeout());
   }
 
   @Override public void writeRequestBody(RetryableOutputStream requestBody) throws IOException {
@@ -69,24 +74,26 @@ public final class SpdyTransport implements Transport {
   @Override public ResponseHeaders readResponseHeaders() throws IOException {
     List<String> nameValueBlock = stream.getResponseHeaders();
     RawHeaders rawHeaders = RawHeaders.fromNameValueBlock(nameValueBlock);
-    rawHeaders.computeResponseStatusLineFromSpdyHeaders();
     httpEngine.receiveHeaders(rawHeaders);
-    return new ResponseHeaders(httpEngine.uri, rawHeaders);
+
+    ResponseHeaders headers = new ResponseHeaders(httpEngine.uri, rawHeaders);
+    headers.setTransport("spdy/3");
+    return headers;
   }
 
   @Override public InputStream getTransferStream(CacheRequest cacheRequest) throws IOException {
     return new UnknownLengthHttpInputStream(stream.getInputStream(), cacheRequest, httpEngine);
   }
 
-  @Override public boolean makeReusable(boolean streamCancelled, OutputStream requestBodyOut,
+  @Override public boolean makeReusable(boolean streamCanceled, OutputStream requestBodyOut,
       InputStream responseBodyIn) {
-    if (streamCancelled) {
+    if (streamCanceled) {
       if (stream != null) {
-        stream.closeLater(SpdyStream.RST_CANCEL);
+        stream.closeLater(ErrorCode.CANCEL);
         return true;
       } else {
         // If stream is null, it either means that writeRequestHeaders wasn't called
-        // or that SpdyConnection#newStream threw an IOEXception. In both cases there's
+        // or that SpdyConnection#newStream threw an IOException. In both cases there's
         // nothing to do here and this stream can't be reused.
         return false;
       }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/Transport.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/Transport.java b/framework/src/com/squareup/okhttp/internal/http/Transport.java
old mode 100644
new mode 100755
index 518827e..d408bfe
--- a/framework/src/com/squareup/okhttp/internal/http/Transport.java
+++ b/framework/src/com/squareup/okhttp/internal/http/Transport.java
@@ -59,6 +59,6 @@ interface Transport {
   InputStream getTransferStream(CacheRequest cacheRequest) throws IOException;
 
   /** Returns true if the underlying connection can be recycled. */
-  boolean makeReusable(boolean streamReusable, OutputStream requestBodyOut,
+  boolean makeReusable(boolean streamCanceled, OutputStream requestBodyOut,
       InputStream responseBodyIn);
 }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/UnknownLengthHttpInputStream.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/UnknownLengthHttpInputStream.java b/framework/src/com/squareup/okhttp/internal/http/UnknownLengthHttpInputStream.java
old mode 100644
new mode 100755
index 729e0b9..ca6bb59
--- a/framework/src/com/squareup/okhttp/internal/http/UnknownLengthHttpInputStream.java
+++ b/framework/src/com/squareup/okhttp/internal/http/UnknownLengthHttpInputStream.java
@@ -25,9 +25,9 @@ import static com.squareup.okhttp.internal.Util.checkOffsetAndCount;
 final class UnknownLengthHttpInputStream extends AbstractHttpInputStream {
   private boolean inputExhausted;
 
-  UnknownLengthHttpInputStream(InputStream is, CacheRequest cacheRequest, HttpEngine httpEngine)
+  UnknownLengthHttpInputStream(InputStream in, CacheRequest cacheRequest, HttpEngine httpEngine)
       throws IOException {
-    super(is, httpEngine, cacheRequest);
+    super(in, httpEngine, cacheRequest);
   }
 
   @Override public int read(byte[] buffer, int offset, int count) throws IOException {
@@ -39,7 +39,7 @@ final class UnknownLengthHttpInputStream extends AbstractHttpInputStream {
     int read = in.read(buffer, offset, count);
     if (read == -1) {
       inputExhausted = true;
-      endOfInput(false);
+      endOfInput();
       return -1;
     }
     cacheWrite(buffer, offset, read);

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/spdy/ErrorCode.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/ErrorCode.java b/framework/src/com/squareup/okhttp/internal/spdy/ErrorCode.java
new file mode 100755
index 0000000..d3a32e1
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/internal/spdy/ErrorCode.java
@@ -0,0 +1,67 @@
+package com.squareup.okhttp.internal.spdy;
+
+public enum ErrorCode {
+  /** Not an error! For SPDY stream resets, prefer null over NO_ERROR. */
+  NO_ERROR(0, -1, 0),
+
+  PROTOCOL_ERROR(1, 1, 1),
+
+  /** A subtype of PROTOCOL_ERROR used by SPDY. */
+  INVALID_STREAM(1, 2, -1),
+
+  /** A subtype of PROTOCOL_ERROR used by SPDY. */
+  UNSUPPORTED_VERSION(1, 4, -1),
+
+  /** A subtype of PROTOCOL_ERROR used by SPDY. */
+  STREAM_IN_USE(1, 8, -1),
+
+  /** A subtype of PROTOCOL_ERROR used by SPDY. */
+  STREAM_ALREADY_CLOSED(1, 9, -1),
+
+  INTERNAL_ERROR(2, 6, 2),
+
+  FLOW_CONTROL_ERROR(3, 7, -1),
+
+  STREAM_CLOSED(5, -1, -1),
+
+  FRAME_TOO_LARGE(6, 11, -1),
+
+  REFUSED_STREAM(7, 3, -1),
+
+  CANCEL(8, 5, -1),
+
+  COMPRESSION_ERROR(9, -1, -1),
+
+  INVALID_CREDENTIALS(-1, 10, -1);
+
+  public final int httpCode;
+  public final int spdyRstCode;
+  public final int spdyGoAwayCode;
+
+  private ErrorCode(int httpCode, int spdyRstCode, int spdyGoAwayCode) {
+    this.httpCode = httpCode;
+    this.spdyRstCode = spdyRstCode;
+    this.spdyGoAwayCode = spdyGoAwayCode;
+  }
+
+  public static ErrorCode fromSpdy3Rst(int code) {
+    for (ErrorCode errorCode : ErrorCode.values()) {
+      if (errorCode.spdyRstCode == code) return errorCode;
+    }
+    return null;
+  }
+
+  public static ErrorCode fromHttp2(int code) {
+    for (ErrorCode errorCode : ErrorCode.values()) {
+      if (errorCode.httpCode == code) return errorCode;
+    }
+    return null;
+  }
+
+  public static ErrorCode fromSpdyGoAway(int code) {
+    for (ErrorCode errorCode : ErrorCode.values()) {
+      if (errorCode.spdyGoAwayCode == code) return errorCode;
+    }
+    return null;
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/spdy/FrameReader.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/FrameReader.java b/framework/src/com/squareup/okhttp/internal/spdy/FrameReader.java
new file mode 100755
index 0000000..1371262
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/internal/spdy/FrameReader.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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.internal.spdy;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+/** Reads transport frames for SPDY/3 or HTTP/2.0. */
+public interface FrameReader extends Closeable {
+  void readConnectionHeader() throws IOException;
+  boolean nextFrame(Handler handler) throws IOException;
+
+  public interface Handler {
+    void data(boolean inFinished, int streamId, InputStream in, int length) throws IOException;
+    /**
+     * Create or update incoming headers, creating the corresponding streams
+     * if necessary. Frames that trigger this are SPDY SYN_STREAM, HEADERS, and
+     * SYN_REPLY, and HTTP/2.0 HEADERS and PUSH_PROMISE.
+     *
+     * @param inFinished true if the sender will not send further frames.
+     * @param outFinished true if the receiver should not send further frames.
+     * @param streamId the stream owning these headers.
+     * @param associatedStreamId the stream that triggered the sender to create
+     *     this stream.
+     * @param priority or -1 for no priority. For SPDY, priorities range from 0
+     *     (highest) thru 7 (lowest). For HTTP/2.0, priorities range from 0
+     *     (highest) thru 2**31-1 (lowest).
+     */
+    void headers(boolean outFinished, boolean inFinished, int streamId, int associatedStreamId,
+        int priority, List<String> nameValueBlock, HeadersMode headersMode);
+    void rstStream(int streamId, ErrorCode errorCode);
+    void settings(boolean clearPrevious, Settings settings);
+    void noop();
+    void ping(boolean reply, int payload1, int payload2);
+    void goAway(int lastGoodStreamId, ErrorCode errorCode);
+    void windowUpdate(int streamId, int deltaWindowSize, boolean endFlowControl);
+    void priority(int streamId, int priority);
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/spdy/FrameWriter.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/FrameWriter.java b/framework/src/com/squareup/okhttp/internal/spdy/FrameWriter.java
new file mode 100755
index 0000000..354f43d
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/internal/spdy/FrameWriter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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.internal.spdy;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.List;
+
+/** Writes transport frames for SPDY/3 or HTTP/2.0. */
+public interface FrameWriter extends Closeable {
+  /** HTTP/2.0 only. */
+  void connectionHeader() throws IOException;
+
+  /** SPDY/3 only. */
+  void flush() throws IOException;
+  void synStream(boolean outFinished, boolean inFinished, int streamId, int associatedStreamId,
+      int priority, int slot, List<String> nameValueBlock) throws IOException;
+  void synReply(boolean outFinished, int streamId, List<String> nameValueBlock) throws IOException;
+  void headers(int streamId, List<String> nameValueBlock) throws IOException;
+  void rstStream(int streamId, ErrorCode errorCode) throws IOException;
+  void data(boolean outFinished, int streamId, byte[] data) throws IOException;
+  void data(boolean outFinished, int streamId, byte[] data, int offset, int byteCount)
+      throws IOException;
+  void settings(Settings settings) throws IOException;
+  void noop() throws IOException;
+  void ping(boolean reply, int payload1, int payload2) throws IOException;
+  void goAway(int lastGoodStreamId, ErrorCode errorCode) throws IOException;
+  void windowUpdate(int streamId, int deltaWindowSize) throws IOException;
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/spdy/HeadersMode.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/HeadersMode.java b/framework/src/com/squareup/okhttp/internal/spdy/HeadersMode.java
new file mode 100755
index 0000000..e16e176
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/internal/spdy/HeadersMode.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2013 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.internal.spdy;
+
+enum HeadersMode {
+  SPDY_SYN_STREAM,
+  SPDY_REPLY,
+  SPDY_HEADERS,
+  HTTP_20_HEADERS;
+
+  /** Returns true if it is an error these headers to create a new stream. */
+  public boolean failIfStreamAbsent() {
+    return this == SPDY_REPLY || this == SPDY_HEADERS;
+  }
+
+  /** Returns true if it is an error these headers to update an existing stream. */
+  public boolean failIfStreamPresent() {
+    return this == SPDY_SYN_STREAM;
+  }
+
+  /**
+   * Returns true if it is an error these headers to be the initial headers of a
+   * response.
+   */
+  public boolean failIfHeadersAbsent() {
+    return this == SPDY_HEADERS;
+  }
+
+  /**
+   * Returns true if it is an error these headers to be update existing headers
+   * of a response.
+   */
+  public boolean failIfHeadersPresent() {
+    return this == SPDY_REPLY;
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/spdy/Hpack.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/Hpack.java b/framework/src/com/squareup/okhttp/internal/spdy/Hpack.java
new file mode 100755
index 0000000..c3ca8f1
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/internal/spdy/Hpack.java
@@ -0,0 +1,371 @@
+package com.squareup.okhttp.internal.spdy;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.List;
+
+/**
+ * Read and write HPACK v03.
+ * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-03
+ */
+final class Hpack {
+
+  static class HeaderEntry {
+    private final String name;
+    private final String value;
+
+    HeaderEntry(String name, String value) {
+      this.name = name;
+      this.value = value;
+    }
+
+    // TODO: This needs to be the length in UTF-8 bytes, not the length in chars.
+    int length() {
+      return 32 + name.length() + value.length();
+    }
+  }
+
+  static final int PREFIX_5_BITS = 0x1f;
+  static final int PREFIX_6_BITS = 0x3f;
+  static final int PREFIX_7_BITS = 0x7f;
+  static final int PREFIX_8_BITS = 0xff;
+
+  static final List<HeaderEntry> INITIAL_CLIENT_TO_SERVER_HEADER_TABLE = Arrays.asList(
+      new HeaderEntry(":scheme", "http"),
+      new HeaderEntry(":scheme", "https"),
+      new HeaderEntry(":host", ""),
+      new HeaderEntry(":path", "/"),
+      new HeaderEntry(":method", "GET"),
+      new HeaderEntry("accept", ""),
+      new HeaderEntry("accept-charset", ""),
+      new HeaderEntry("accept-encoding", ""),
+      new HeaderEntry("accept-language", ""),
+      new HeaderEntry("cookie", ""),
+      new HeaderEntry("if-modified-since", ""),
+      new HeaderEntry("user-agent", ""),
+      new HeaderEntry("referer", ""),
+      new HeaderEntry("authorization", ""),
+      new HeaderEntry("allow", ""),
+      new HeaderEntry("cache-control", ""),
+      new HeaderEntry("connection", ""),
+      new HeaderEntry("content-length", ""),
+      new HeaderEntry("content-type", ""),
+      new HeaderEntry("date", ""),
+      new HeaderEntry("expect", ""),
+      new HeaderEntry("from", ""),
+      new HeaderEntry("if-match", ""),
+      new HeaderEntry("if-none-match", ""),
+      new HeaderEntry("if-range", ""),
+      new HeaderEntry("if-unmodified-since", ""),
+      new HeaderEntry("max-forwards", ""),
+      new HeaderEntry("proxy-authorization", ""),
+      new HeaderEntry("range", ""),
+      new HeaderEntry("via", "")
+  );
+
+  static final List<HeaderEntry> INITIAL_SERVER_TO_CLIENT_HEADER_TABLE = Arrays.asList(
+      new HeaderEntry(":status", "200"),
+      new HeaderEntry("age", ""),
+      new HeaderEntry("cache-control", ""),
+      new HeaderEntry("content-length", ""),
+      new HeaderEntry("content-type", ""),
+      new HeaderEntry("date", ""),
+      new HeaderEntry("etag", ""),
+      new HeaderEntry("expires", ""),
+      new HeaderEntry("last-modified", ""),
+      new HeaderEntry("server", ""),
+      new HeaderEntry("set-cookie", ""),
+      new HeaderEntry("vary", ""),
+      new HeaderEntry("via", ""),
+      new HeaderEntry("access-control-allow-origin", ""),
+      new HeaderEntry("accept-ranges", ""),
+      new HeaderEntry("allow", ""),
+      new HeaderEntry("connection", ""),
+      new HeaderEntry("content-disposition", ""),
+      new HeaderEntry("content-encoding", ""),
+      new HeaderEntry("content-language", ""),
+      new HeaderEntry("content-location", ""),
+      new HeaderEntry("content-range", ""),
+      new HeaderEntry("link", ""),
+      new HeaderEntry("location", ""),
+      new HeaderEntry("proxy-authenticate", ""),
+      new HeaderEntry("refresh", ""),
+      new HeaderEntry("retry-after", ""),
+      new HeaderEntry("strict-transport-security", ""),
+      new HeaderEntry("transfer-encoding", ""),
+      new HeaderEntry("www-authenticate", "")
+  );
+
+  // Update these when initial tables change to sum of each entry length.
+  static final int INITIAL_CLIENT_TO_SERVER_HEADER_TABLE_LENGTH = 1262;
+  static final int INITIAL_SERVER_TO_CLIENT_HEADER_TABLE_LENGTH = 1304;
+
+  private Hpack() {
+  }
+
+  static class Reader {
+    private final long maxBufferSize = 4096; // TODO: needs to come from settings.
+    private final DataInputStream in;
+
+    private final BitSet referenceSet = new BitSet();
+    private final List<HeaderEntry> headerTable;
+    private final List<String> emittedHeaders = new ArrayList<String>();
+    private long bufferSize = 0;
+    private long bytesLeft = 0;
+
+    Reader(DataInputStream in, boolean client) {
+      this.in = in;
+      if (client) {  // we are reading from the server
+        this.headerTable = new ArrayList<HeaderEntry>(INITIAL_SERVER_TO_CLIENT_HEADER_TABLE);
+        this.bufferSize = INITIAL_SERVER_TO_CLIENT_HEADER_TABLE_LENGTH;
+      } else {
+        this.headerTable = new ArrayList<HeaderEntry>(INITIAL_CLIENT_TO_SERVER_HEADER_TABLE);
+        this.bufferSize = INITIAL_CLIENT_TO_SERVER_HEADER_TABLE_LENGTH;
+      }
+    }
+
+    /**
+     * Read {@code byteCount} bytes of headers from the source stream into the
+     * set of emitted headers.
+     */
+    public void readHeaders(int byteCount) throws IOException {
+      bytesLeft += byteCount;
+      // TODO: limit to 'byteCount' bytes?
+
+      while (bytesLeft > 0) {
+        int b = readByte();
+
+        if ((b & 0x80) != 0) {
+          int index = readInt(b, PREFIX_7_BITS);
+          readIndexedHeader(index);
+        } else if (b == 0x60) {
+          readLiteralHeaderWithoutIndexingNewName();
+        } else if ((b & 0xe0) == 0x60) {
+          int index = readInt(b, PREFIX_5_BITS);
+          readLiteralHeaderWithoutIndexingIndexedName(index - 1);
+        } else if (b == 0x40) {
+          readLiteralHeaderWithIncrementalIndexingNewName();
+        } else if ((b & 0xe0) == 0x40) {
+          int index = readInt(b, PREFIX_5_BITS);
+          readLiteralHeaderWithIncrementalIndexingIndexedName(index - 1);
+        } else if (b == 0) {
+          readLiteralHeaderWithSubstitutionIndexingNewName();
+        } else if ((b & 0xc0) == 0) {
+          int index = readInt(b, PREFIX_6_BITS);
+          readLiteralHeaderWithSubstitutionIndexingIndexedName(index - 1);
+        } else {
+          throw new AssertionError();
+        }
+      }
+    }
+
+    public void emitReferenceSet() {
+      for (int i = referenceSet.nextSetBit(0); i != -1; i = referenceSet.nextSetBit(i + 1)) {
+        emittedHeaders.add(getName(i));
+        emittedHeaders.add(getValue(i));
+      }
+    }
+
+    /**
+     * Returns all headers emitted since they were last cleared, then clears the
+     * emitted headers.
+     */
+    public List<String> getAndReset() {
+      List<String> result = new ArrayList<String>(emittedHeaders);
+      emittedHeaders.clear();
+      return result;
+    }
+
+    private void readIndexedHeader(int index) {
+      if (referenceSet.get(index)) {
+        referenceSet.clear(index);
+      } else {
+        referenceSet.set(index);
+      }
+    }
+
+    private void readLiteralHeaderWithoutIndexingIndexedName(int index)
+        throws IOException {
+      String name = getName(index);
+      String value = readString();
+      emittedHeaders.add(name);
+      emittedHeaders.add(value);
+    }
+
+    private void readLiteralHeaderWithoutIndexingNewName()
+        throws IOException {
+      String name = readString();
+      String value = readString();
+      emittedHeaders.add(name);
+      emittedHeaders.add(value);
+    }
+
+    private void readLiteralHeaderWithIncrementalIndexingIndexedName(int nameIndex)
+        throws IOException {
+      String name = getName(nameIndex);
+      String value = readString();
+      int index = headerTable.size(); // append to tail
+      insertIntoHeaderTable(index, new HeaderEntry(name, value));
+    }
+
+    private void readLiteralHeaderWithIncrementalIndexingNewName() throws IOException {
+      String name = readString();
+      String value = readString();
+      int index = headerTable.size(); // append to tail
+      insertIntoHeaderTable(index, new HeaderEntry(name, value));
+    }
+
+    private void readLiteralHeaderWithSubstitutionIndexingIndexedName(int nameIndex)
+        throws IOException {
+      int index = readInt(readByte(), PREFIX_8_BITS);
+      String name = getName(nameIndex);
+      String value = readString();
+      insertIntoHeaderTable(index, new HeaderEntry(name, value));
+    }
+
+    private void readLiteralHeaderWithSubstitutionIndexingNewName() throws IOException {
+      String name = readString();
+      int index = readInt(readByte(), PREFIX_8_BITS);
+      String value = readString();
+      insertIntoHeaderTable(index, new HeaderEntry(name, value));
+    }
+
+    private String getName(int index) {
+      return headerTable.get(index).name;
+    }
+
+    private String getValue(int index) {
+      return headerTable.get(index).value;
+    }
+
+    private void insertIntoHeaderTable(int index, HeaderEntry entry) {
+      int delta = entry.length();
+      if (index != headerTable.size()) {
+        delta -= headerTable.get(index).length();
+      }
+
+      // if the new or replacement header is too big, drop all entries.
+      if (delta > maxBufferSize) {
+        headerTable.clear();
+        bufferSize = 0;
+        // emit the large header to the callback.
+        emittedHeaders.add(entry.name);
+        emittedHeaders.add(entry.value);
+        return;
+      }
+
+      // Prune headers to the required length.
+      while (bufferSize + delta > maxBufferSize) {
+        remove(0);
+        index--;
+      }
+
+      if (index < 0) { // we pruned it, so insert at beginning
+        index = 0;
+        headerTable.add(index, entry);
+      } else if (index == headerTable.size()) { // append to the end
+        headerTable.add(index, entry);
+      } else { // replace value at same position
+        headerTable.set(index, entry);
+      }
+
+      bufferSize += delta;
+      referenceSet.set(index);
+    }
+
+    private void remove(int index) {
+      bufferSize -= headerTable.remove(index).length();
+    }
+
+    private int readByte() throws IOException {
+      bytesLeft--;
+      return in.readByte() & 0xff;
+    }
+
+    int readInt(int firstByte, int prefixMask) throws IOException {
+      int prefix = firstByte & prefixMask;
+      if (prefix < prefixMask) {
+        return prefix; // This was a single byte value.
+      }
+
+      // This is a multibyte value. Read 7 bits at a time.
+      int result = prefixMask;
+      int shift = 0;
+      while (true) {
+        int b = readByte();
+        if ((b & 0x80) != 0) { // Equivalent to (b >= 128) since b is in [0..255].
+          result += (b & 0x7f) << shift;
+          shift += 7;
+        } else {
+          result += b << shift; // Last byte.
+          break;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * Reads a UTF-8 encoded string. Since ASCII is a subset of UTF-8, this method
+     * may be used to read strings that are known to be ASCII-only.
+     */
+    public String readString() throws IOException {
+      int firstByte = readByte();
+      int length = readInt(firstByte, PREFIX_8_BITS);
+      byte[] encoded = new byte[length];
+      bytesLeft -= length;
+      in.readFully(encoded);
+      return new String(encoded, "UTF-8");
+    }
+  }
+
+  static class Writer {
+    private final OutputStream out;
+
+    Writer(OutputStream out) {
+      this.out = out;
+    }
+
+    public void writeHeaders(List<String> nameValueBlock) throws IOException {
+      // TODO: implement a compression strategy.
+      for (int i = 0, size = nameValueBlock.size(); i < size; i += 2) {
+        out.write(0x60); // Literal Header without Indexing - New Name.
+        writeString(nameValueBlock.get(i));
+        writeString(nameValueBlock.get(i + 1));
+      }
+    }
+
+    public void writeInt(int value, int prefixMask, int bits) throws IOException {
+      // Write the raw value for a single byte value.
+      if (value < prefixMask) {
+        out.write(bits | value);
+        return;
+      }
+
+      // Write the mask to start a multibyte value.
+      out.write(bits | prefixMask);
+      value -= prefixMask;
+
+      // Write 7 bits at a time 'til we're done.
+      while (value >= 0x80) {
+        int b = value & 0x7f;
+        out.write(b | 0x80);
+        value >>>= 7;
+      }
+      out.write(value);
+    }
+
+    /**
+     * Writes a UTF-8 encoded string. Since ASCII is a subset of UTF-8, this
+     * method can be used to write strings that are known to be ASCII-only.
+     */
+    public void writeString(String headerName) throws IOException {
+      byte[] bytes = headerName.getBytes("UTF-8");
+      writeInt(bytes.length, PREFIX_8_BITS, 0);
+      out.write(bytes);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/spdy/Http20Draft06.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/Http20Draft06.java b/framework/src/com/squareup/okhttp/internal/spdy/Http20Draft06.java
new file mode 100755
index 0000000..3d53f48
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/internal/spdy/Http20Draft06.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2013 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.internal.spdy;
+
+import com.squareup.okhttp.internal.Util;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Read and write http/2 v06 frames.
+ * http://tools.ietf.org/html/draft-ietf-httpbis-http2-06
+ */
+final class Http20Draft06 implements Variant {
+  private static final byte[] CONNECTION_HEADER;
+  static {
+    try {
+      CONNECTION_HEADER = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".getBytes("UTF-8");
+    } catch (UnsupportedEncodingException e) {
+      throw new AssertionError();
+    }
+  }
+
+  static final int TYPE_DATA = 0x0;
+  static final int TYPE_HEADERS = 0x1;
+  static final int TYPE_PRIORITY = 0x2;
+  static final int TYPE_RST_STREAM = 0x3;
+  static final int TYPE_SETTINGS = 0x4;
+  static final int TYPE_PUSH_PROMISE = 0x5;
+  static final int TYPE_PING = 0x6;
+  static final int TYPE_GOAWAY = 0x7;
+  static final int TYPE_WINDOW_UPDATE = 0x9;
+  static final int TYPE_CONTINUATION = 0xa;
+
+  static final int FLAG_END_STREAM = 0x1;
+  /** Used for headers, push-promise and continuation. */
+  static final int FLAG_END_HEADERS = 0x4;
+  static final int FLAG_PRIORITY = 0x8;
+  static final int FLAG_PONG = 0x1;
+  static final int FLAG_END_FLOW_CONTROL = 0x1;
+
+  @Override public FrameReader newReader(InputStream in, boolean client) {
+    return new Reader(in, client);
+  }
+
+  @Override public FrameWriter newWriter(OutputStream out, boolean client) {
+    return new Writer(out, client);
+  }
+
+  static final class Reader implements FrameReader {
+    private final DataInputStream in;
+    private final boolean client;
+    private final Hpack.Reader hpackReader;
+
+    Reader(InputStream in, boolean client) {
+      this.in = new DataInputStream(in);
+      this.client = client;
+      this.hpackReader = new Hpack.Reader(this.in, client);
+    }
+
+    @Override public void readConnectionHeader() throws IOException {
+      if (client) return; // Nothing to read; servers don't send connection headers!
+      byte[] connectionHeader = new byte[CONNECTION_HEADER.length];
+      in.readFully(connectionHeader);
+      if (!Arrays.equals(connectionHeader, CONNECTION_HEADER)) {
+        throw ioException("Expected a connection header but was "
+            + Arrays.toString(connectionHeader));
+      }
+    }
+
+    @Override public boolean nextFrame(Handler handler) throws IOException {
+      int w1;
+      try {
+        w1 = in.readInt();
+      } catch (IOException e) {
+        return false; // This might be a normal socket close.
+      }
+      int w2 = in.readInt();
+
+      int length = (w1 & 0xffff0000) >> 16;
+      int type = (w1 & 0xff00) >> 8;
+      int flags = w1 & 0xff;
+      // boolean r = (w2 & 0x80000000) != 0; // Reserved.
+      int streamId = (w2 & 0x7fffffff);
+
+      switch (type) {
+        case TYPE_DATA:
+          readData(handler, flags, length, streamId);
+          return true;
+
+        case TYPE_HEADERS:
+          readHeaders(handler, flags, length, streamId);
+          return true;
+
+        case TYPE_PRIORITY:
+          readPriority(handler, flags, length, streamId);
+          return true;
+
+        case TYPE_RST_STREAM:
+          readRstStream(handler, flags, length, streamId);
+          return true;
+
+        case TYPE_SETTINGS:
+          readSettings(handler, flags, length, streamId);
+          return true;
+
+        case TYPE_PUSH_PROMISE:
+          readPushPromise(handler, flags, length, streamId);
+          return true;
+
+        case TYPE_PING:
+          readPing(handler, flags, length, streamId);
+          return true;
+
+        case TYPE_GOAWAY:
+          readGoAway(handler, flags, length, streamId);
+          return true;
+
+        case TYPE_WINDOW_UPDATE:
+          readWindowUpdate(handler, flags, length, streamId);
+          return true;
+      }
+
+      throw new UnsupportedOperationException("TODO");
+    }
+
+    private void readHeaders(Handler handler, int flags, int length, int streamId)
+        throws IOException {
+      if (streamId == 0) throw ioException("TYPE_HEADERS streamId == 0");
+
+      boolean inFinished = (flags & FLAG_END_STREAM) != 0;
+
+      while (true) {
+        hpackReader.readHeaders(length);
+
+        if ((flags & FLAG_END_HEADERS) != 0) {
+          hpackReader.emitReferenceSet();
+          List<String> namesAndValues = hpackReader.getAndReset();
+          int priority = -1; // TODO: priority
+          handler.headers(false, inFinished, streamId, -1, priority, namesAndValues,
+              HeadersMode.HTTP_20_HEADERS);
+          return;
+        }
+
+        // Read another continuation frame.
+        int w1 = in.readInt();
+        int w2 = in.readInt();
+
+        length = (w1 & 0xffff0000) >> 16;
+        int newType = (w1 & 0xff00) >> 8;
+        flags = w1 & 0xff;
+
+        // TODO: remove in draft 8: CONTINUATION no longer sets END_STREAM
+        inFinished = (flags & FLAG_END_STREAM) != 0;
+
+        // boolean u = (w2 & 0x80000000) != 0; // Unused.
+        int newStreamId = (w2 & 0x7fffffff);
+
+        if (newType != TYPE_CONTINUATION) {
+          throw ioException("TYPE_CONTINUATION didn't have FLAG_END_HEADERS");
+        }
+        if (newStreamId != streamId) throw ioException("TYPE_CONTINUATION streamId changed");
+      }
+    }
+
+    private void readData(Handler handler, int flags, int length, int streamId) throws IOException {
+      boolean inFinished = (flags & FLAG_END_STREAM) != 0;
+      handler.data(inFinished, streamId, in, length);
+    }
+
+    private void readPriority(Handler handler, int flags, int length, int streamId)
+        throws IOException {
+      if (length != 4) throw ioException("TYPE_PRIORITY length: %d != 4", length);
+      if (streamId == 0) throw ioException("TYPE_PRIORITY streamId == 0");
+      int w1 = in.readInt();
+      // boolean r = (w1 & 0x80000000) != 0; // Reserved.
+      int priority = (w1 & 0x7fffffff);
+      handler.priority(streamId, priority);
+    }
+
+    private void readRstStream(Handler handler, int flags, int length, int streamId)
+        throws IOException {
+      if (length != 4) throw ioException("TYPE_RST_STREAM length: %d != 4", length);
+      if (streamId == 0) throw ioException("TYPE_RST_STREAM streamId == 0");
+      int errorCodeInt = in.readInt();
+      ErrorCode errorCode = ErrorCode.fromHttp2(errorCodeInt);
+      if (errorCode == null) {
+        throw ioException("TYPE_RST_STREAM unexpected error code: %d", errorCodeInt);
+      }
+      handler.rstStream(streamId, errorCode);
+    }
+
+    private void readSettings(Handler handler, int flags, int length, int streamId)
+        throws IOException {
+      if (length % 8 != 0) throw ioException("TYPE_SETTINGS length %% 8 != 0: %s", length);
+      if (streamId != 0) throw ioException("TYPE_SETTINGS streamId != 0");
+      Settings settings = new Settings();
+      for (int i = 0; i < length; i += 8) {
+        int w1 = in.readInt();
+        int value = in.readInt();
+        // int r = (w1 & 0xff000000) >>> 24; // Reserved.
+        int id = w1 & 0xffffff;
+        settings.set(id, 0, value);
+      }
+      handler.settings(false, settings);
+    }
+
+    private void readPushPromise(Handler handler, int flags, int length, int streamId) {
+      // TODO:
+    }
+
+    private void readPing(Handler handler, int flags, int length, int streamId) throws IOException {
+      if (length != 8) throw ioException("TYPE_PING length != 8: %s", length);
+      if (streamId != 0) throw ioException("TYPE_PING streamId != 0");
+      int payload1 = in.readInt();
+      int payload2 = in.readInt();
+      boolean reply = (flags & FLAG_PONG) != 0;
+      handler.ping(reply, payload1, payload2);
+    }
+
+    private void readGoAway(Handler handler, int flags, int length, int streamId)
+        throws IOException {
+      if (length < 8) throw ioException("TYPE_GOAWAY length < 8: %s", length);
+      int lastStreamId = in.readInt();
+      int errorCodeInt = in.readInt();
+      int opaqueDataLength = length - 8;
+      ErrorCode errorCode = ErrorCode.fromHttp2(errorCodeInt);
+      if (errorCode == null) {
+        throw ioException("TYPE_RST_STREAM unexpected error code: %d", errorCodeInt);
+      }
+      if (Util.skipByReading(in, opaqueDataLength) != opaqueDataLength) {
+        throw new IOException("TYPE_GOAWAY opaque data was truncated");
+      }
+      handler.goAway(lastStreamId, errorCode);
+    }
+
+    private void readWindowUpdate(Handler handler, int flags, int length, int streamId)
+        throws IOException {
+      int w1 = in.readInt();
+      // boolean r = (w1 & 0x80000000) != 0; // Reserved.
+      int windowSizeIncrement = (w1 & 0x7fffffff);
+      boolean endFlowControl = (flags & FLAG_END_FLOW_CONTROL) != 0;
+      handler.windowUpdate(streamId, windowSizeIncrement, endFlowControl);
+    }
+
+    private static IOException ioException(String message, Object... args) throws IOException {
+      throw new IOException(String.format(message, args));
+    }
+
+    @Override public void close() throws IOException {
+      in.close();
+    }
+  }
+
+  static final class Writer implements FrameWriter {
+    private final DataOutputStream out;
+    private final boolean client;
+    private final ByteArrayOutputStream hpackBuffer;
+    private final Hpack.Writer hpackWriter;
+
+    Writer(OutputStream out, boolean client) {
+      this.out = new DataOutputStream(out);
+      this.client = client;
+      this.hpackBuffer = new ByteArrayOutputStream();
+      this.hpackWriter = new Hpack.Writer(hpackBuffer);
+    }
+
+    @Override public synchronized void flush() throws IOException {
+      out.flush();
+    }
+
+    @Override public synchronized void connectionHeader() throws IOException {
+      if (!client) return; // Nothing to write; servers don't send connection headers!
+      out.write(CONNECTION_HEADER);
+    }
+
+    @Override public synchronized void synStream(boolean outFinished, boolean inFinished,
+        int streamId, int associatedStreamId, int priority, int slot, List<String> nameValueBlock)
+        throws IOException {
+      if (inFinished) throw new UnsupportedOperationException();
+      headers(outFinished, streamId, priority, nameValueBlock);
+    }
+
+    @Override public synchronized void synReply(boolean outFinished, int streamId,
+        List<String> nameValueBlock) throws IOException {
+      headers(outFinished, streamId, -1, nameValueBlock);
+    }
+
+    @Override public synchronized void headers(int streamId, List<String> nameValueBlock)
+        throws IOException {
+      headers(false, streamId, -1, nameValueBlock);
+    }
+
+    private void headers(boolean outFinished, int streamId, int priority,
+        List<String> nameValueBlock) throws IOException {
+      hpackBuffer.reset();
+      hpackWriter.writeHeaders(nameValueBlock);
+      int type = TYPE_HEADERS;
+      // TODO: implement CONTINUATION
+      int length = hpackBuffer.size();
+      int flags = FLAG_END_HEADERS;
+      if (outFinished) flags |= FLAG_END_STREAM;
+      if (priority != -1) flags |= FLAG_PRIORITY;
+      out.writeInt((length & 0xffff) << 16 | (type & 0xff) << 8 | (flags & 0xff));
+      out.writeInt(streamId & 0x7fffffff);
+      if (priority != -1) out.writeInt(priority & 0x7fffffff);
+      hpackBuffer.writeTo(out);
+    }
+
+    @Override public synchronized void rstStream(int streamId, ErrorCode errorCode)
+        throws IOException {
+      throw new UnsupportedOperationException("TODO");
+    }
+
+    @Override public void data(boolean outFinished, int streamId, byte[] data) throws IOException {
+      data(outFinished, streamId, data, 0, data.length);
+    }
+
+    @Override public synchronized void data(boolean outFinished, int streamId, byte[] data,
+        int offset, int byteCount) throws IOException {
+      int type = TYPE_DATA;
+      int flags = 0;
+      if (outFinished) flags |= FLAG_END_STREAM;
+      out.writeInt((byteCount & 0xffff) << 16 | (type & 0xff) << 8 | (flags & 0xff));
+      out.writeInt(streamId & 0x7fffffff);
+      out.write(data, offset, byteCount);
+    }
+
+    @Override public synchronized void settings(Settings settings) throws IOException {
+      int type = TYPE_SETTINGS;
+      int length = settings.size() * 8;
+      int flags = 0;
+      int streamId = 0;
+      out.writeInt((length & 0xffff) << 16 | (type & 0xff) << 8 | (flags & 0xff));
+      out.writeInt(streamId & 0x7fffffff);
+      for (int i = 0; i < Settings.COUNT; i++) {
+        if (!settings.isSet(i)) continue;
+        out.writeInt(i & 0xffffff);
+        out.writeInt(settings.get(i));
+      }
+    }
+
+    @Override public synchronized void noop() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override public synchronized void ping(boolean reply, int payload1, int payload2)
+        throws IOException {
+      // TODO
+    }
+
+    @Override public synchronized void goAway(int lastGoodStreamId, ErrorCode errorCode)
+        throws IOException {
+      // TODO
+    }
+
+    @Override public synchronized void windowUpdate(int streamId, int deltaWindowSize)
+        throws IOException {
+      // TODO
+    }
+
+    @Override public void close() throws IOException {
+      out.close();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/spdy/IncomingStreamHandler.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/IncomingStreamHandler.java b/framework/src/com/squareup/okhttp/internal/spdy/IncomingStreamHandler.java
old mode 100644
new mode 100755
index 875fff0..44d4ea2
--- a/framework/src/com/squareup/okhttp/internal/spdy/IncomingStreamHandler.java
+++ b/framework/src/com/squareup/okhttp/internal/spdy/IncomingStreamHandler.java
@@ -22,7 +22,7 @@ import java.io.IOException;
 public interface IncomingStreamHandler {
   IncomingStreamHandler REFUSE_INCOMING_STREAMS = new IncomingStreamHandler() {
     @Override public void receive(SpdyStream stream) throws IOException {
-      stream.close(SpdyStream.RST_REFUSED_STREAM);
+      stream.close(ErrorCode.REFUSED_STREAM);
     }
   };
 

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/spdy/NameValueBlockReader.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/NameValueBlockReader.java b/framework/src/com/squareup/okhttp/internal/spdy/NameValueBlockReader.java
new file mode 100755
index 0000000..b95d013
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/internal/spdy/NameValueBlockReader.java
@@ -0,0 +1,123 @@
+package com.squareup.okhttp.internal.spdy;
+
+import com.squareup.okhttp.internal.Util;
+import java.io.Closeable;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+
+/**
+ * Reads a SPDY/3 Name/Value header block. This class is made complicated by the
+ * requirement that we're strict with which bytes we put in the compressed bytes
+ * buffer. We need to put all compressed bytes into that buffer -- but no other
+ * bytes.
+ */
+class NameValueBlockReader implements Closeable {
+  private final DataInputStream nameValueBlockIn;
+  private final FillableInflaterInputStream fillableInflaterInputStream;
+  private int compressedLimit;
+
+  NameValueBlockReader(final InputStream in) {
+    // Limit the inflater input stream to only those bytes in the Name/Value block. We cut the
+    // inflater off at its source because we can't predict the ratio of compressed bytes to
+    // uncompressed bytes.
+    InputStream throttleStream = new InputStream() {
+      @Override public int read() throws IOException {
+        return Util.readSingleByte(this);
+      }
+
+      @Override public int read(byte[] buffer, int offset, int byteCount) throws IOException {
+        byteCount = Math.min(byteCount, compressedLimit);
+        int consumed = in.read(buffer, offset, byteCount);
+        compressedLimit -= consumed;
+        return consumed;
+      }
+
+      @Override public void close() throws IOException {
+        in.close();
+      }
+    };
+
+    // Subclass inflater to install a dictionary when it's needed.
+    Inflater inflater = new Inflater() {
+      @Override public int inflate(byte[] buffer, int offset, int count)
+          throws DataFormatException {
+        int result = super.inflate(buffer, offset, count);
+        if (result == 0 && needsDictionary()) {
+          setDictionary(Spdy3.DICTIONARY);
+          result = super.inflate(buffer, offset, count);
+        }
+        return result;
+      }
+    };
+
+    fillableInflaterInputStream = new FillableInflaterInputStream(throttleStream, inflater);
+    nameValueBlockIn = new DataInputStream(fillableInflaterInputStream);
+  }
+
+  /** Extend the inflater stream so we can eagerly fill the compressed bytes buffer if necessary. */
+  static class FillableInflaterInputStream extends InflaterInputStream {
+    public FillableInflaterInputStream(InputStream in, Inflater inf) {
+      super(in, inf);
+    }
+
+    @Override public void fill() throws IOException {
+      super.fill(); // This method is protected in the superclass.
+    }
+  }
+
+  public List<String> readNameValueBlock(int length) throws IOException {
+    this.compressedLimit += length;
+    try {
+      int numberOfPairs = nameValueBlockIn.readInt();
+      if (numberOfPairs < 0) {
+        throw new IOException("numberOfPairs < 0: " + numberOfPairs);
+      }
+      if (numberOfPairs > 1024) {
+        throw new IOException("numberOfPairs > 1024: " + numberOfPairs);
+      }
+      List<String> entries = new ArrayList<String>(numberOfPairs * 2);
+      for (int i = 0; i < numberOfPairs; i++) {
+        String name = readString();
+        String values = readString();
+        if (name.length() == 0) throw new IOException("name.length == 0");
+        entries.add(name);
+        entries.add(values);
+      }
+
+      doneReading();
+
+      return entries;
+    } catch (DataFormatException e) {
+      throw new IOException(e.getMessage());
+    }
+  }
+
+  private void doneReading() throws IOException {
+    if (compressedLimit == 0) return;
+
+    // Read any outstanding unread bytes. One side-effect of deflate compression is that sometimes
+    // there are bytes remaining in the stream after we've consumed all of the content.
+    fillableInflaterInputStream.fill();
+
+    if (compressedLimit != 0) {
+      throw new IOException("compressedLimit > 0: " + compressedLimit);
+    }
+  }
+
+  private String readString() throws DataFormatException, IOException {
+    int length = nameValueBlockIn.readInt();
+    byte[] bytes = new byte[length];
+    Util.readFully(nameValueBlockIn, bytes);
+    return new String(bytes, 0, length, "UTF-8");
+  }
+
+  @Override public void close() throws IOException {
+    nameValueBlockIn.close();
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/spdy/Ping.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/Ping.java b/framework/src/com/squareup/okhttp/internal/spdy/Ping.java
old mode 100644
new mode 100755


[4/5] CB-5799 Update version of OkHTTP to 1.3

Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/Platform.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/Platform.java b/framework/src/com/squareup/okhttp/internal/Platform.java
old mode 100644
new mode 100755
index 6b4ac34..d5884b1
--- a/framework/src/com/squareup/okhttp/internal/Platform.java
+++ b/framework/src/com/squareup/okhttp/internal/Platform.java
@@ -16,7 +16,6 @@
  */
 package com.squareup.okhttp.internal;
 
-import com.squareup.okhttp.OkHttpClient;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
@@ -25,7 +24,7 @@ import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
-import java.net.NetworkInterface;
+import java.net.InetSocketAddress;
 import java.net.Socket;
 import java.net.SocketException;
 import java.net.URI;
@@ -57,6 +56,11 @@ public class Platform {
     return PLATFORM;
   }
 
+  /** Prefix used on custom headers. */
+  public String getPrefix() {
+    return "OkHttp";
+  }
+
   public void logW(String warning) {
     System.out.println(warning);
   }
@@ -99,6 +103,11 @@ public class Platform {
   public void setNpnProtocols(SSLSocket socket, byte[] npnProtocols) {
   }
 
+  public void connectSocket(Socket socket, InetSocketAddress address,
+      int connectTimeout) throws IOException {
+    socket.connect(address, connectTimeout);
+  }
+
   /**
    * Returns a deflater output stream that supports SYNC_FLUSH for SPDY name
    * value blocks. This throws an {@link UnsupportedOperationException} on
@@ -125,33 +134,21 @@ public class Platform {
     }
   }
 
-  /**
-   * Returns the maximum transmission unit of the network interface used by
-   * {@code socket}, or a reasonable default if this platform doesn't expose the
-   * MTU to the application layer.
-   *
-   * <p>The returned value should only be used as an optimization; such as to
-   * size buffers efficiently.
-   */
-  public int getMtu(Socket socket) throws IOException {
-    return 1400; // Smaller than 1500 to leave room for headers on interfaces like PPPoE.
-  }
-
   /** Attempt to match the host runtime to a capable Platform implementation. */
   private static Platform findPlatform() {
-    Method getMtu;
-    try {
-      getMtu = NetworkInterface.class.getMethod("getMTU");
-    } catch (NoSuchMethodException e) {
-      return new Platform(); // No Java 1.6 APIs. It's either Java 1.5, Android 2.2 or earlier.
-    }
-
     // Attempt to find Android 2.3+ APIs.
     Class<?> openSslSocketClass;
     Method setUseSessionTickets;
     Method setHostname;
     try {
-      openSslSocketClass = Class.forName("org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl");
+      try {
+        openSslSocketClass = Class.forName("com.android.org.conscrypt.OpenSSLSocketImpl");
+      } catch (ClassNotFoundException ignored) {
+        // Older platform before being unbundled.
+        openSslSocketClass = Class.forName(
+            "org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl");
+      }
+
       setUseSessionTickets = openSslSocketClass.getMethod("setUseSessionTickets", boolean.class);
       setHostname = openSslSocketClass.getMethod("setHostname", String.class);
 
@@ -159,10 +156,10 @@ public class Platform {
       try {
         Method setNpnProtocols = openSslSocketClass.getMethod("setNpnProtocols", byte[].class);
         Method getNpnSelectedProtocol = openSslSocketClass.getMethod("getNpnSelectedProtocol");
-        return new Android41(getMtu, openSslSocketClass, setUseSessionTickets, setHostname,
+        return new Android41(openSslSocketClass, setUseSessionTickets, setHostname,
             setNpnProtocols, getNpnSelectedProtocol);
       } catch (NoSuchMethodException ignored) {
-        return new Android23(getMtu, openSslSocketClass, setUseSessionTickets, setHostname);
+        return new Android23(openSslSocketClass, setUseSessionTickets, setHostname);
       }
     } catch (ClassNotFoundException ignored) {
       // This isn't an Android runtime.
@@ -179,55 +176,43 @@ public class Platform {
       Class<?> serverProviderClass = Class.forName(npnClassName + "$ServerProvider");
       Method putMethod = nextProtoNegoClass.getMethod("put", SSLSocket.class, providerClass);
       Method getMethod = nextProtoNegoClass.getMethod("get", SSLSocket.class);
-      return new JdkWithJettyNpnPlatform(getMtu, putMethod, getMethod, clientProviderClass,
-          serverProviderClass);
+      return new JdkWithJettyNpnPlatform(
+          putMethod, getMethod, clientProviderClass, serverProviderClass);
     } catch (ClassNotFoundException ignored) {
       // NPN isn't on the classpath.
     } catch (NoSuchMethodException ignored) {
       // The NPN version isn't what we expect.
     }
 
-    return getMtu != null ? new Java5(getMtu) : new Platform();
-  }
-
-  private static class Java5 extends Platform {
-    private final Method getMtu;
-
-    private Java5(Method getMtu) {
-      this.getMtu = getMtu;
-    }
-
-    @Override public int getMtu(Socket socket) throws IOException {
-      try {
-        NetworkInterface networkInterface = NetworkInterface.getByInetAddress(
-            socket.getLocalAddress());
-        return (Integer) getMtu.invoke(networkInterface);
-      } catch (IllegalAccessException e) {
-        throw new AssertionError(e);
-      } catch (InvocationTargetException e) {
-        if (e.getCause() instanceof IOException) throw (IOException) e.getCause();
-        throw new RuntimeException(e.getCause());
-      }
-    }
+    return new Platform();
   }
 
-  /**
-   * Android version 2.3 and newer support TLS session tickets and server name
-   * indication (SNI).
-   */
-  private static class Android23 extends Java5 {
+  /** Android version 2.3 and newer support TLS session tickets and server name indication (SNI). */
+  private static class Android23 extends Platform {
     protected final Class<?> openSslSocketClass;
     private final Method setUseSessionTickets;
     private final Method setHostname;
 
-    private Android23(Method getMtu, Class<?> openSslSocketClass, Method setUseSessionTickets,
-        Method setHostname) {
-      super(getMtu);
+    private Android23(
+        Class<?> openSslSocketClass, Method setUseSessionTickets, Method setHostname) {
       this.openSslSocketClass = openSslSocketClass;
       this.setUseSessionTickets = setUseSessionTickets;
       this.setHostname = setHostname;
     }
 
+    @Override public void connectSocket(Socket socket, InetSocketAddress address,
+        int connectTimeout) throws IOException {
+      try {
+        socket.connect(address, connectTimeout);
+      } catch (SecurityException se) {
+        // Before android 4.3, socket.connect could throw a SecurityException
+        // if opening a socket resulted in an EACCES error.
+        IOException ioException = new IOException("Exception in connect");
+        ioException.initCause(se);
+        throw ioException;
+      }
+    }
+
     @Override public void enableTlsExtensions(SSLSocket socket, String uriHost) {
       super.enableTlsExtensions(socket, uriHost);
       if (openSslSocketClass.isInstance(socket)) {
@@ -249,9 +234,9 @@ public class Platform {
     private final Method setNpnProtocols;
     private final Method getNpnSelectedProtocol;
 
-    private Android41(Method getMtu, Class<?> openSslSocketClass, Method setUseSessionTickets,
-        Method setHostname, Method setNpnProtocols, Method getNpnSelectedProtocol) {
-      super(getMtu, openSslSocketClass, setUseSessionTickets, setHostname);
+    private Android41(Class<?> openSslSocketClass, Method setUseSessionTickets, Method setHostname,
+        Method setNpnProtocols, Method getNpnSelectedProtocol) {
+      super(openSslSocketClass, setUseSessionTickets, setHostname);
       this.setNpnProtocols = setNpnProtocols;
       this.getNpnSelectedProtocol = getNpnSelectedProtocol;
     }
@@ -283,19 +268,15 @@ public class Platform {
     }
   }
 
-  /**
-   * OpenJDK 7 plus {@code org.mortbay.jetty.npn/npn-boot} on the boot class
-   * path.
-   */
-  private static class JdkWithJettyNpnPlatform extends Java5 {
+  /** OpenJDK 7 plus {@code org.mortbay.jetty.npn/npn-boot} on the boot class path. */
+  private static class JdkWithJettyNpnPlatform extends Platform {
     private final Method getMethod;
     private final Method putMethod;
     private final Class<?> clientProviderClass;
     private final Class<?> serverProviderClass;
 
-    public JdkWithJettyNpnPlatform(Method getMtu, Method putMethod, Method getMethod,
-        Class<?> clientProviderClass, Class<?> serverProviderClass) {
-      super(getMtu);
+    public JdkWithJettyNpnPlatform(Method putMethod, Method getMethod, Class<?> clientProviderClass,
+        Class<?> serverProviderClass) {
       this.putMethod = putMethod;
       this.getMethod = getMethod;
       this.clientProviderClass = clientProviderClass;
@@ -328,7 +309,7 @@ public class Platform {
         JettyNpnProvider provider =
             (JettyNpnProvider) Proxy.getInvocationHandler(getMethod.invoke(null, socket));
         if (!provider.unsupported && provider.selected == null) {
-          Logger logger = Logger.getLogger(OkHttpClient.class.getName());
+          Logger logger = Logger.getLogger("com.squareup.okhttp.OkHttpClient");
           logger.log(Level.INFO,
               "NPN callback dropped so SPDY is disabled. " + "Is npn-boot on the boot class path?");
           return null;

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/StrictLineReader.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/StrictLineReader.java b/framework/src/com/squareup/okhttp/internal/StrictLineReader.java
old mode 100644
new mode 100755
index 3ddc693..74af6fd
--- a/framework/src/com/squareup/okhttp/internal/StrictLineReader.java
+++ b/framework/src/com/squareup/okhttp/internal/StrictLineReader.java
@@ -146,8 +146,7 @@ public class StrictLineReader implements Closeable {
 
       // Let's anticipate up to 80 characters on top of those already read.
       ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) {
-        @Override
-        public String toString() {
+        @Override public String toString() {
           int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;
           try {
             return new String(buf, 0, length, charset.name());

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/Util.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/Util.java b/framework/src/com/squareup/okhttp/internal/Util.java
old mode 100644
new mode 100755
index 290e5ea..9c5b008
--- a/framework/src/com/squareup/okhttp/internal/Util.java
+++ b/framework/src/com/squareup/okhttp/internal/Util.java
@@ -24,11 +24,19 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.Reader;
 import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
 import java.net.Socket;
+import java.net.ServerSocket;
 import java.net.URI;
 import java.net.URL;
 import java.nio.ByteOrder;
 import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.atomic.AtomicReference;
 
 /** Junk drawer of utility methods. */
@@ -46,6 +54,9 @@ public final class Util {
   public static final Charset UTF_8 = Charset.forName("UTF-8");
   private static AtomicReference<byte[]> skipBuffer = new AtomicReference<byte[]>();
 
+  private static final char[] DIGITS =
+      { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
   private Util() {
   }
 
@@ -127,6 +138,21 @@ public final class Util {
   }
 
   /**
+   * Closes {@code serverSocket}, ignoring any checked exceptions. Does nothing if
+   * {@code serverSocket} is null.
+   */
+  public static void closeQuietly(ServerSocket serverSocket) {
+    if (serverSocket != null) {
+      try {
+        serverSocket.close();
+      } catch (RuntimeException rethrown) {
+        throw rethrown;
+      } catch (Exception ignored) {
+      }
+    }
+  }
+
+  /**
    * Closes {@code a} and {@code b}. If either close fails, this completes
    * the other close and rethrows the first encountered exception.
    */
@@ -258,6 +284,8 @@ public final class Util {
    * buffer.
    */
   public static long skipByReading(InputStream in, long byteCount) throws IOException {
+    if (byteCount == 0) return 0L;
+
     // acquire the shared skip buffer.
     byte[] buffer = skipBuffer.getAndSet(null);
     if (buffer == null) {
@@ -324,4 +352,43 @@ public final class Util {
     }
     return result.toString();
   }
+
+  /** Returns a 32 character string containing a hash of {@code s}. */
+  public static String hash(String s) {
+    try {
+      MessageDigest messageDigest = MessageDigest.getInstance("MD5");
+      byte[] md5bytes = messageDigest.digest(s.getBytes("UTF-8"));
+      return bytesToHexString(md5bytes);
+    } catch (NoSuchAlgorithmException e) {
+      throw new AssertionError(e);
+    } catch (UnsupportedEncodingException e) {
+      throw new AssertionError(e);
+    }
+  }
+
+  private static String bytesToHexString(byte[] bytes) {
+    char[] digits = DIGITS;
+    char[] buf = new char[bytes.length * 2];
+    int c = 0;
+    for (byte b : bytes) {
+      buf[c++] = digits[(b >> 4) & 0xf];
+      buf[c++] = digits[b & 0xf];
+    }
+    return new String(buf);
+  }
+
+  /** Returns an immutable copy of {@code list}. */
+  public static <T> List<T> immutableList(List<T> list) {
+    return Collections.unmodifiableList(new ArrayList<T>(list));
+  }
+
+  public static ThreadFactory daemonThreadFactory(final String name) {
+    return new ThreadFactory() {
+      @Override public Thread newThread(Runnable runnable) {
+        Thread result = new Thread(runnable, name);
+        result.setDaemon(true);
+        return result;
+      }
+    };
+  }
 }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/AbstractHttpInputStream.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/AbstractHttpInputStream.java b/framework/src/com/squareup/okhttp/internal/http/AbstractHttpInputStream.java
old mode 100644
new mode 100755
index 187f3b6..a5d39b3
--- a/framework/src/com/squareup/okhttp/internal/http/AbstractHttpInputStream.java
+++ b/framework/src/com/squareup/okhttp/internal/http/AbstractHttpInputStream.java
@@ -79,11 +79,11 @@ abstract class AbstractHttpInputStream extends InputStream {
    * Closes the cache entry and makes the socket available for reuse. This
    * should be invoked when the end of the body has been reached.
    */
-  protected final void endOfInput(boolean streamCancelled) throws IOException {
+  protected final void endOfInput() throws IOException {
     if (cacheRequest != null) {
       cacheBody.close();
     }
-    httpEngine.release(streamCancelled);
+    httpEngine.release(false);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/AbstractHttpOutputStream.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/AbstractHttpOutputStream.java b/framework/src/com/squareup/okhttp/internal/http/AbstractHttpOutputStream.java
deleted file mode 100644
index 90675b0..0000000
--- a/framework/src/com/squareup/okhttp/internal/http/AbstractHttpOutputStream.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * 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.internal.http;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * An output stream for the body of an HTTP request.
- *
- * <p>Since a single socket's output stream may be used to write multiple HTTP
- * requests to the same server, subclasses should not close the socket stream.
- */
-abstract class AbstractHttpOutputStream extends OutputStream {
-  protected boolean closed;
-
-  @Override public final void write(int data) throws IOException {
-    write(new byte[] { (byte) data });
-  }
-
-  protected final void checkNotClosed() throws IOException {
-    if (closed) {
-      throw new IOException("stream closed");
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/HeaderParser.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/HeaderParser.java b/framework/src/com/squareup/okhttp/internal/http/HeaderParser.java
old mode 100644
new mode 100755
index 12e6409..d5f0f4f
--- a/framework/src/com/squareup/okhttp/internal/http/HeaderParser.java
+++ b/framework/src/com/squareup/okhttp/internal/http/HeaderParser.java
@@ -27,11 +27,11 @@ final class HeaderParser {
     int pos = 0;
     while (pos < value.length()) {
       int tokenStart = pos;
-      pos = skipUntil(value, pos, "=,");
+      pos = skipUntil(value, pos, "=,;");
       String directive = value.substring(tokenStart, pos).trim();
 
-      if (pos == value.length() || value.charAt(pos) == ',') {
-        pos++; // consume ',' (if necessary)
+      if (pos == value.length() || value.charAt(pos) == ',' || value.charAt(pos) == ';') {
+        pos++; // consume ',' or ';' (if necessary)
         handler.handle(directive, null);
         continue;
       }
@@ -52,7 +52,7 @@ final class HeaderParser {
         // unquoted string
       } else {
         int parameterStart = pos;
-        pos = skipUntil(value, pos, ",");
+        pos = skipUntil(value, pos, ",;");
         parameter = value.substring(parameterStart, pos).trim();
       }
 

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/HttpAuthenticator.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/HttpAuthenticator.java b/framework/src/com/squareup/okhttp/internal/http/HttpAuthenticator.java
old mode 100644
new mode 100755
index 4ccd12a..1ad3689
--- a/framework/src/com/squareup/okhttp/internal/http/HttpAuthenticator.java
+++ b/framework/src/com/squareup/okhttp/internal/http/HttpAuthenticator.java
@@ -16,7 +16,8 @@
  */
 package com.squareup.okhttp.internal.http;
 
-import com.squareup.okhttp.internal.Base64;
+import com.squareup.okhttp.OkAuthenticator;
+import com.squareup.okhttp.OkAuthenticator.Challenge;
 import java.io.IOException;
 import java.net.Authenticator;
 import java.net.InetAddress;
@@ -27,11 +28,57 @@ import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
 
+import static com.squareup.okhttp.OkAuthenticator.Credential;
 import static java.net.HttpURLConnection.HTTP_PROXY_AUTH;
 import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
 
 /** Handles HTTP authentication headers from origin and proxy servers. */
 public final class HttpAuthenticator {
+  /** Uses the global authenticator to get the password. */
+  public static final OkAuthenticator SYSTEM_DEFAULT = new OkAuthenticator() {
+    @Override public Credential authenticate(
+        Proxy proxy, URL url, List<Challenge> challenges) throws IOException {
+      for (Challenge challenge : challenges) {
+        if (!"Basic".equalsIgnoreCase(challenge.getScheme())) {
+          continue;
+        }
+
+        PasswordAuthentication auth = Authenticator.requestPasswordAuthentication(url.getHost(),
+            getConnectToInetAddress(proxy, url), url.getPort(), url.getProtocol(),
+            challenge.getRealm(), challenge.getScheme(), url, Authenticator.RequestorType.SERVER);
+        if (auth != null) {
+          return Credential.basic(auth.getUserName(), new String(auth.getPassword()));
+        }
+      }
+      return null;
+    }
+
+    @Override public Credential authenticateProxy(
+        Proxy proxy, URL url, List<Challenge> challenges) throws IOException {
+      for (Challenge challenge : challenges) {
+        if (!"Basic".equalsIgnoreCase(challenge.getScheme())) {
+          continue;
+        }
+
+        InetSocketAddress proxyAddress = (InetSocketAddress) proxy.address();
+        PasswordAuthentication auth = Authenticator.requestPasswordAuthentication(
+            proxyAddress.getHostName(), getConnectToInetAddress(proxy, url), proxyAddress.getPort(),
+            url.getProtocol(), challenge.getRealm(), challenge.getScheme(), url,
+            Authenticator.RequestorType.PROXY);
+        if (auth != null) {
+          return Credential.basic(auth.getUserName(), new String(auth.getPassword()));
+        }
+      }
+      return null;
+    }
+
+    private InetAddress getConnectToInetAddress(Proxy proxy, URL url) throws IOException {
+      return (proxy != null && proxy.type() != Proxy.Type.DIRECT)
+          ? ((InetSocketAddress) proxy.address()).getAddress()
+          : InetAddress.getByName(url.getHost());
+    }
+  };
+
   private HttpAuthenticator() {
   }
 
@@ -41,68 +88,33 @@ public final class HttpAuthenticator {
    * @return true if credentials have been added to successorRequestHeaders
    *         and another request should be attempted.
    */
-  public static boolean processAuthHeader(int responseCode, RawHeaders responseHeaders,
-      RawHeaders successorRequestHeaders, Proxy proxy, URL url) throws IOException {
-    if (responseCode != HTTP_PROXY_AUTH && responseCode != HTTP_UNAUTHORIZED) {
-      throw new IllegalArgumentException();
+  public static boolean processAuthHeader(OkAuthenticator authenticator, int responseCode,
+      RawHeaders responseHeaders, RawHeaders successorRequestHeaders, Proxy proxy, URL url)
+      throws IOException {
+    String responseField;
+    String requestField;
+    if (responseCode == HTTP_UNAUTHORIZED) {
+      responseField = "WWW-Authenticate";
+      requestField = "Authorization";
+    } else if (responseCode == HTTP_PROXY_AUTH) {
+      responseField = "Proxy-Authenticate";
+      requestField = "Proxy-Authorization";
+    } else {
+      throw new IllegalArgumentException(); // TODO: ProtocolException?
     }
-
-    // Keep asking for username/password until authorized.
-    String challengeHeader =
-        responseCode == HTTP_PROXY_AUTH ? "Proxy-Authenticate" : "WWW-Authenticate";
-    String credentials = getCredentials(responseHeaders, challengeHeader, proxy, url);
-    if (credentials == null) {
-      return false; // Could not find credentials so end the request cycle.
-    }
-
-    // Add authorization credentials, bypassing the already-connected check.
-    String fieldName = responseCode == HTTP_PROXY_AUTH ? "Proxy-Authorization" : "Authorization";
-    successorRequestHeaders.set(fieldName, credentials);
-    return true;
-  }
-
-  /**
-   * Returns the authorization credentials that may satisfy the challenge.
-   * Returns null if a challenge header was not provided or if credentials
-   * were not available.
-   */
-  private static String getCredentials(RawHeaders responseHeaders, String challengeHeader,
-      Proxy proxy, URL url) throws IOException {
-    List<Challenge> challenges = parseChallenges(responseHeaders, challengeHeader);
+    List<Challenge> challenges = parseChallenges(responseHeaders, responseField);
     if (challenges.isEmpty()) {
-      return null;
+      return false; // Could not find a challenge so end the request cycle.
     }
-
-    for (Challenge challenge : challenges) {
-      // Use the global authenticator to get the password.
-      PasswordAuthentication auth;
-      if (responseHeaders.getResponseCode() == HTTP_PROXY_AUTH) {
-        InetSocketAddress proxyAddress = (InetSocketAddress) proxy.address();
-        auth = Authenticator.requestPasswordAuthentication(proxyAddress.getHostName(),
-            getConnectToInetAddress(proxy, url), proxyAddress.getPort(), url.getProtocol(),
-            challenge.realm, challenge.scheme, url, Authenticator.RequestorType.PROXY);
-      } else {
-        auth = Authenticator.requestPasswordAuthentication(url.getHost(),
-            getConnectToInetAddress(proxy, url), url.getPort(), url.getProtocol(), challenge.realm,
-            challenge.scheme, url, Authenticator.RequestorType.SERVER);
-      }
-      if (auth == null) {
-        continue;
-      }
-
-      // Use base64 to encode the username and password.
-      String usernameAndPassword = auth.getUserName() + ":" + new String(auth.getPassword());
-      byte[] bytes = usernameAndPassword.getBytes("ISO-8859-1");
-      String encoded = Base64.encode(bytes);
-      return challenge.scheme + " " + encoded;
+    Credential credential = responseHeaders.getResponseCode() == HTTP_PROXY_AUTH
+        ? authenticator.authenticateProxy(proxy, url, challenges)
+        : authenticator.authenticate(proxy, url, challenges);
+    if (credential == null) {
+      return false; // Could not satisfy the challenge so end the request cycle.
     }
-
-    return null;
-  }
-
-  private static InetAddress getConnectToInetAddress(Proxy proxy, URL url) throws IOException {
-    return (proxy != null && proxy.type() != Proxy.Type.DIRECT)
-        ? ((InetSocketAddress) proxy.address()).getAddress() : InetAddress.getByName(url.getHost());
+    // Add authorization credentials, bypassing the already-connected check.
+    successorRequestHeaders.set(requestField, credential.getHeaderValue());
+    return true;
   }
 
   /**
@@ -134,7 +146,7 @@ public final class HttpAuthenticator {
         //       It needs to be fixed to handle any scheme and any parameters
         //       http://code.google.com/p/android/issues/detail?id=11140
 
-        if (!value.regionMatches(pos, "realm=\"", 0, "realm=\"".length())) {
+        if (!value.regionMatches(true, pos, "realm=\"", 0, "realm=\"".length())) {
           break; // Unexpected challenge parameter; give up!
         }
 
@@ -151,25 +163,4 @@ public final class HttpAuthenticator {
     }
     return result;
   }
-
-  /** An RFC 2617 challenge. */
-  private static final class Challenge {
-    final String scheme;
-    final String realm;
-
-    Challenge(String scheme, String realm) {
-      this.scheme = scheme;
-      this.realm = realm;
-    }
-
-    @Override public boolean equals(Object o) {
-      return o instanceof Challenge
-          && ((Challenge) o).scheme.equals(scheme)
-          && ((Challenge) o).realm.equals(realm);
-    }
-
-    @Override public int hashCode() {
-      return scheme.hashCode() + 31 * realm.hashCode();
-    }
-  }
 }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/HttpDate.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/HttpDate.java b/framework/src/com/squareup/okhttp/internal/http/HttpDate.java
old mode 100644
new mode 100755
index acb5fda..b4d2c7c
--- a/framework/src/com/squareup/okhttp/internal/http/HttpDate.java
+++ b/framework/src/com/squareup/okhttp/internal/http/HttpDate.java
@@ -36,14 +36,13 @@ final class HttpDate {
       new ThreadLocal<DateFormat>() {
         @Override protected DateFormat initialValue() {
           DateFormat rfc1123 = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
-          rfc1123.setTimeZone(TimeZone.getTimeZone("UTC"));
+          rfc1123.setTimeZone(TimeZone.getTimeZone("GMT"));
           return rfc1123;
         }
       };
 
   /** If we fail to parse a date in a non-standard format, try each of these formats in sequence. */
-  private static final String[] BROWSER_COMPATIBLE_DATE_FORMATS = new String[] {
-            /* This list comes from  {@code org.apache.http.impl.cookie.BrowserCompatSpec}. */
+  private static final String[] BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS = new String[] {
       "EEEE, dd-MMM-yy HH:mm:ss zzz", // RFC 1036
       "EEE MMM d HH:mm:ss yyyy", // ANSI C asctime()
       "EEE, dd-MMM-yyyy HH:mm:ss z", "EEE, dd-MMM-yyyy HH-mm-ss z", "EEE, dd MMM yy HH:mm:ss z",
@@ -54,19 +53,26 @@ final class HttpDate {
             /* RI bug 6641315 claims a cookie of this format was once served by www.yahoo.com */
       "EEE MMM d yyyy HH:mm:ss z", };
 
-  /**
-   * Returns the date for {@code value}. Returns null if the value couldn't be
-   * parsed.
-   */
+  private static final DateFormat[] BROWSER_COMPATIBLE_DATE_FORMATS =
+      new DateFormat[BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS.length];
+
+  /** Returns the date for {@code value}. Returns null if the value couldn't be parsed. */
   public static Date parse(String value) {
     try {
       return STANDARD_DATE_FORMAT.get().parse(value);
-    } catch (ParseException ignore) {
+    } catch (ParseException ignored) {
     }
-    for (String formatString : BROWSER_COMPATIBLE_DATE_FORMATS) {
-      try {
-        return new SimpleDateFormat(formatString, Locale.US).parse(value);
-      } catch (ParseException ignore) {
+    synchronized (BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS) {
+      for (int i = 0, count = BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS.length; i < count; i++) {
+        DateFormat format = BROWSER_COMPATIBLE_DATE_FORMATS[i];
+        if (format == null) {
+          format = new SimpleDateFormat(BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS[i], Locale.US);
+          BROWSER_COMPATIBLE_DATE_FORMATS[i] = format;
+        }
+        try {
+          return format.parse(value);
+        } catch (ParseException ignored) {
+        }
       }
     }
     return null;

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/HttpEngine.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/HttpEngine.java b/framework/src/com/squareup/okhttp/internal/http/HttpEngine.java
old mode 100644
new mode 100755
index 7a06dca..4a2dad4
--- a/framework/src/com/squareup/okhttp/internal/http/HttpEngine.java
+++ b/framework/src/com/squareup/okhttp/internal/http/HttpEngine.java
@@ -19,6 +19,8 @@ package com.squareup.okhttp.internal.http;
 
 import com.squareup.okhttp.Address;
 import com.squareup.okhttp.Connection;
+import com.squareup.okhttp.OkHttpClient;
+import com.squareup.okhttp.OkResponseCache;
 import com.squareup.okhttp.ResponseSource;
 import com.squareup.okhttp.TunnelRequest;
 import com.squareup.okhttp.internal.Dns;
@@ -31,6 +33,7 @@ import java.io.OutputStream;
 import java.net.CacheRequest;
 import java.net.CacheResponse;
 import java.net.CookieHandler;
+import java.net.HttpURLConnection;
 import java.net.Proxy;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -85,7 +88,8 @@ public class HttpEngine {
   };
   public static final int HTTP_CONTINUE = 100;
 
-  protected final HttpURLConnectionImpl policy;
+  protected final Policy policy;
+  protected final OkHttpClient client;
 
   protected final String method;
 
@@ -106,6 +110,9 @@ public class HttpEngine {
   /** The time when the request headers were written, or -1 if they haven't been written yet. */
   long sentRequestMillis = -1;
 
+  /** Whether the connection has been established. */
+  boolean connected;
+
   /**
    * True if this client added an "Accept-Encoding: gzip" header field and is
    * therefore responsible for also decompressing the transfer stream.
@@ -137,14 +144,15 @@ public class HttpEngine {
 
   /**
    * @param requestHeaders the client's supplied request headers. This class
-   * creates a private copy that it can mutate.
+   *     creates a private copy that it can mutate.
    * @param connection the connection used for an intermediate response
-   * immediately prior to this request/response pair, such as a same-host
-   * redirect. This engine assumes ownership of the connection and must
-   * release it when it is unneeded.
+   *     immediately prior to this request/response pair, such as a same-host
+   *     redirect. This engine assumes ownership of the connection and must
+   *     release it when it is unneeded.
    */
-  public HttpEngine(HttpURLConnectionImpl policy, String method, RawHeaders requestHeaders,
+  public HttpEngine(OkHttpClient client, Policy policy, String method, RawHeaders requestHeaders,
       Connection connection, RetryableOutputStream requestBodyOut) throws IOException {
+    this.client = client;
     this.policy = policy;
     this.method = method;
     this.connection = connection;
@@ -175,8 +183,9 @@ public class HttpEngine {
 
     prepareRawRequestHeaders();
     initResponseSource();
-    if (policy.responseCache != null) {
-      policy.responseCache.trackResponse(responseSource);
+    OkResponseCache responseCache = client.getOkResponseCache();
+    if (responseCache != null) {
+      responseCache.trackResponse(responseSource);
     }
 
     // The raw response source may require the network, but the request
@@ -196,8 +205,7 @@ public class HttpEngine {
     if (responseSource.requiresConnection()) {
       sendSocketRequest();
     } else if (connection != null) {
-      policy.connectionPool.recycle(connection);
-      policy.getFailedRoutes().remove(connection.getRoute());
+      client.getConnectionPool().recycle(connection);
       connection = null;
     }
   }
@@ -208,15 +216,14 @@ public class HttpEngine {
    */
   private void initResponseSource() throws IOException {
     responseSource = ResponseSource.NETWORK;
-    if (!policy.getUseCaches() || policy.responseCache == null) {
-      return;
-    }
+    if (!policy.getUseCaches()) return;
 
-    CacheResponse candidate =
-        policy.responseCache.get(uri, method, requestHeaders.getHeaders().toMultimap(false));
-    if (candidate == null) {
-      return;
-    }
+    OkResponseCache responseCache = client.getOkResponseCache();
+    if (responseCache == null) return;
+
+    CacheResponse candidate = responseCache.get(
+        uri, method, requestHeaders.getHeaders().toMultimap(false));
+    if (candidate == null) return;
 
     Map<String, List<String>> responseHeadersMap = candidate.getHeaders();
     cachedResponseBody = candidate.getBody();
@@ -274,22 +281,24 @@ public class HttpEngine {
       SSLSocketFactory sslSocketFactory = null;
       HostnameVerifier hostnameVerifier = null;
       if (uri.getScheme().equalsIgnoreCase("https")) {
-        sslSocketFactory = policy.sslSocketFactory;
-        hostnameVerifier = policy.hostnameVerifier;
+        sslSocketFactory = client.getSslSocketFactory();
+        hostnameVerifier = client.getHostnameVerifier();
       }
       Address address = new Address(uriHost, getEffectivePort(uri), sslSocketFactory,
-          hostnameVerifier, policy.requestedProxy);
-      routeSelector = new RouteSelector(address, uri, policy.proxySelector, policy.connectionPool,
-          Dns.DEFAULT, policy.getFailedRoutes());
+          hostnameVerifier, client.getAuthenticator(), client.getProxy(), client.getTransports());
+      routeSelector = new RouteSelector(address, uri, client.getProxySelector(),
+          client.getConnectionPool(), Dns.DEFAULT, client.getRoutesDatabase());
     }
-    connection = routeSelector.next();
+    connection = routeSelector.next(method);
     if (!connection.isConnected()) {
-      connection.connect(policy.getConnectTimeout(), policy.getReadTimeout(), getTunnelConfig());
-      policy.connectionPool.maybeShare(connection);
-      policy.getFailedRoutes().remove(connection.getRoute());
+      connection.connect(client.getConnectTimeout(), client.getReadTimeout(), getTunnelConfig());
+      client.getConnectionPool().maybeShare(connection);
+      client.getRoutesDatabase().connected(connection.getRoute());
+    } else if (!connection.isSpdy()) {
+        connection.updateReadTimeout(client.getReadTimeout());
     }
     connected(connection);
-    if (connection.getRoute().getProxy() != policy.requestedProxy) {
+    if (connection.getRoute().getProxy() != client.getProxy()) {
       // Update the request line if the proxy changed; it may need a host name.
       requestHeaders.getHeaders().setRequestLine(getRequestLine());
     }
@@ -300,6 +309,8 @@ public class HttpEngine {
    * pool. Subclasses use this hook to get a reference to the TLS data.
    */
   protected void connected(Connection connection) {
+    policy.setSelectedProxy(connection.getRoute().getProxy());
+    connected = true;
   }
 
   /**
@@ -328,7 +339,7 @@ public class HttpEngine {
   }
 
   boolean hasRequestBody() {
-    return method.equals("POST") || method.equals("PUT");
+    return method.equals("POST") || method.equals("PUT") || method.equals("PATCH");
   }
 
   /** Returns the request body or null if this request doesn't have a body. */
@@ -387,17 +398,20 @@ public class HttpEngine {
 
   private void maybeCache() throws IOException {
     // Are we caching at all?
-    if (!policy.getUseCaches() || policy.responseCache == null) {
-      return;
-    }
+    if (!policy.getUseCaches()) return;
+    OkResponseCache responseCache = client.getOkResponseCache();
+    if (responseCache == null) return;
+
+    HttpURLConnection connectionToCache = policy.getHttpConnectionToCache();
 
     // Should we cache this response for this request?
     if (!responseHeaders.isCacheable(requestHeaders)) {
+      responseCache.maybeRemove(connectionToCache.getRequestMethod(), uri);
       return;
     }
 
     // Offer this request to the cache.
-    cacheRequest = policy.responseCache.put(uri, policy.getHttpConnectionToCache());
+    cacheRequest = responseCache.put(uri, connectionToCache);
   }
 
   /**
@@ -409,7 +423,7 @@ public class HttpEngine {
   public final void automaticallyReleaseConnectionToPool() {
     automaticallyReleaseConnectionToPool = true;
     if (connection != null && connectionReleased) {
-      policy.connectionPool.recycle(connection);
+      client.getConnectionPool().recycle(connection);
       connection = null;
     }
   }
@@ -419,7 +433,7 @@ public class HttpEngine {
    * closed. Also call {@link #automaticallyReleaseConnectionToPool} unless
    * the connection will be used to follow a redirect.
    */
-  public final void release(boolean streamCancelled) {
+  public final void release(boolean streamCanceled) {
     // If the response body comes from the cache, close it.
     if (responseBodyIn == cachedResponseBody) {
       Util.closeQuietly(responseBodyIn);
@@ -428,12 +442,12 @@ public class HttpEngine {
     if (!connectionReleased && connection != null) {
       connectionReleased = true;
 
-      if (transport == null || !transport.makeReusable(streamCancelled, requestBodyOut,
-          responseTransferIn)) {
+      if (transport == null
+          || !transport.makeReusable(streamCanceled, requestBodyOut, responseTransferIn)) {
         Util.closeQuietly(connection);
         connection = null;
       } else if (automaticallyReleaseConnectionToPool) {
-        policy.connectionPool.recycle(connection);
+        client.getConnectionPool().recycle(connection);
         connection = null;
       }
     }
@@ -521,7 +535,7 @@ public class HttpEngine {
       requestHeaders.setIfModifiedSince(new Date(ifModifiedSince));
     }
 
-    CookieHandler cookieHandler = policy.cookieHandler;
+    CookieHandler cookieHandler = client.getCookieHandler();
     if (cookieHandler != null) {
       requestHeaders.addCookies(
           cookieHandler.get(uri, requestHeaders.getHeaders().toMultimap(false)));
@@ -635,9 +649,17 @@ public class HttpEngine {
       if (cachedResponseHeaders.validate(responseHeaders)) {
         release(false);
         ResponseHeaders combinedHeaders = cachedResponseHeaders.combine(responseHeaders);
-        setResponse(combinedHeaders, cachedResponseBody);
-        policy.responseCache.trackConditionalCacheHit();
-        policy.responseCache.update(cacheResponse, policy.getHttpConnectionToCache());
+        this.responseHeaders = combinedHeaders;
+
+        // Update the cache after applying the combined headers but before initializing the content
+        // stream, otherwise the Content-Encoding header (if present) will be stripped from the
+        // combined headers and not end up in the cache file if transparent gzip compression is
+        // turned on.
+        OkResponseCache responseCache = client.getOkResponseCache();
+        responseCache.trackConditionalCacheHit();
+        responseCache.update(cacheResponse, policy.getHttpConnectionToCache());
+
+        initContentStream(cachedResponseBody);
         return;
       } else {
         Util.closeQuietly(cachedResponseBody);
@@ -656,7 +678,7 @@ public class HttpEngine {
   }
 
   public void receiveHeaders(RawHeaders headers) throws IOException {
-    CookieHandler cookieHandler = policy.cookieHandler;
+    CookieHandler cookieHandler = client.getCookieHandler();
     if (cookieHandler != null) {
       cookieHandler.put(uri, headers.toMultimap(true));
     }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/HttpResponseCache.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/HttpResponseCache.java b/framework/src/com/squareup/okhttp/internal/http/HttpResponseCache.java
deleted file mode 100644
index 8735166..0000000
--- a/framework/src/com/squareup/okhttp/internal/http/HttpResponseCache.java
+++ /dev/null
@@ -1,608 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * 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.internal.http;
-
-import com.squareup.okhttp.OkResponseCache;
-import com.squareup.okhttp.ResponseSource;
-import com.squareup.okhttp.internal.Base64;
-import com.squareup.okhttp.internal.DiskLruCache;
-import com.squareup.okhttp.internal.StrictLineReader;
-import com.squareup.okhttp.internal.Util;
-import java.io.BufferedWriter;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FilterInputStream;
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
-import java.io.Writer;
-import java.net.CacheRequest;
-import java.net.CacheResponse;
-import java.net.HttpURLConnection;
-import java.net.ResponseCache;
-import java.net.SecureCacheResponse;
-import java.net.URI;
-import java.net.URLConnection;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.Principal;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLPeerUnverifiedException;
-
-import static com.squareup.okhttp.internal.Util.US_ASCII;
-import static com.squareup.okhttp.internal.Util.UTF_8;
-
-/**
- * Cache responses in a directory on the file system. Most clients should use
- * {@code android.net.HttpResponseCache}, the stable, documented front end for
- * this.
- */
-public final class HttpResponseCache extends ResponseCache implements OkResponseCache {
-  private static final char[] DIGITS =
-      { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
-
-  // TODO: add APIs to iterate the cache?
-  private static final int VERSION = 201105;
-  private static final int ENTRY_METADATA = 0;
-  private static final int ENTRY_BODY = 1;
-  private static final int ENTRY_COUNT = 2;
-
-  private final DiskLruCache cache;
-
-  /* read and write statistics, all guarded by 'this' */
-  private int writeSuccessCount;
-  private int writeAbortCount;
-  private int networkCount;
-  private int hitCount;
-  private int requestCount;
-
-  public HttpResponseCache(File directory, long maxSize) throws IOException {
-    cache = DiskLruCache.open(directory, VERSION, ENTRY_COUNT, maxSize);
-  }
-
-  private String uriToKey(URI uri) {
-    try {
-      MessageDigest messageDigest = MessageDigest.getInstance("MD5");
-      byte[] md5bytes = messageDigest.digest(uri.toString().getBytes("UTF-8"));
-      return bytesToHexString(md5bytes);
-    } catch (NoSuchAlgorithmException e) {
-      throw new AssertionError(e);
-    } catch (UnsupportedEncodingException e) {
-      throw new AssertionError(e);
-    }
-  }
-
-  private static String bytesToHexString(byte[] bytes) {
-    char[] digits = DIGITS;
-    char[] buf = new char[bytes.length * 2];
-    int c = 0;
-    for (byte b : bytes) {
-      buf[c++] = digits[(b >> 4) & 0xf];
-      buf[c++] = digits[b & 0xf];
-    }
-    return new String(buf);
-  }
-
-  @Override public CacheResponse get(URI uri, String requestMethod,
-      Map<String, List<String>> requestHeaders) {
-    String key = uriToKey(uri);
-    DiskLruCache.Snapshot snapshot;
-    Entry entry;
-    try {
-      snapshot = cache.get(key);
-      if (snapshot == null) {
-        return null;
-      }
-      entry = new Entry(snapshot.getInputStream(ENTRY_METADATA));
-    } catch (IOException e) {
-      // Give up because the cache cannot be read.
-      return null;
-    }
-
-    if (!entry.matches(uri, requestMethod, requestHeaders)) {
-      snapshot.close();
-      return null;
-    }
-
-    return entry.isHttps() ? new EntrySecureCacheResponse(entry, snapshot)
-        : new EntryCacheResponse(entry, snapshot);
-  }
-
-  @Override public CacheRequest put(URI uri, URLConnection urlConnection) throws IOException {
-    if (!(urlConnection instanceof HttpURLConnection)) {
-      return null;
-    }
-
-    HttpURLConnection httpConnection = (HttpURLConnection) urlConnection;
-    String requestMethod = httpConnection.getRequestMethod();
-    String key = uriToKey(uri);
-
-    if (requestMethod.equals("POST") || requestMethod.equals("PUT") || requestMethod.equals(
-        "DELETE")) {
-      try {
-        cache.remove(key);
-      } catch (IOException ignored) {
-        // The cache cannot be written.
-      }
-      return null;
-    } else if (!requestMethod.equals("GET")) {
-      // Don't cache non-GET responses. We're technically allowed to cache
-      // HEAD requests and some POST requests, but the complexity of doing
-      // so is high and the benefit is low.
-      return null;
-    }
-
-    HttpEngine httpEngine = getHttpEngine(httpConnection);
-    if (httpEngine == null) {
-      // Don't cache unless the HTTP implementation is ours.
-      return null;
-    }
-
-    ResponseHeaders response = httpEngine.getResponseHeaders();
-    if (response.hasVaryAll()) {
-      return null;
-    }
-
-    RawHeaders varyHeaders =
-        httpEngine.getRequestHeaders().getHeaders().getAll(response.getVaryFields());
-    Entry entry = new Entry(uri, varyHeaders, httpConnection);
-    DiskLruCache.Editor editor = null;
-    try {
-      editor = cache.edit(key);
-      if (editor == null) {
-        return null;
-      }
-      entry.writeTo(editor);
-      return new CacheRequestImpl(editor);
-    } catch (IOException e) {
-      abortQuietly(editor);
-      return null;
-    }
-  }
-
-  /**
-   * Handles a conditional request hit by updating the stored cache response
-   * with the headers from {@code httpConnection}. The cached response body is
-   * not updated. If the stored response has changed since {@code
-   * conditionalCacheHit} was returned, this does nothing.
-   */
-  @Override public void update(CacheResponse conditionalCacheHit, HttpURLConnection httpConnection)
-      throws IOException {
-    HttpEngine httpEngine = getHttpEngine(httpConnection);
-    URI uri = httpEngine.getUri();
-    ResponseHeaders response = httpEngine.getResponseHeaders();
-    RawHeaders varyHeaders =
-        httpEngine.getRequestHeaders().getHeaders().getAll(response.getVaryFields());
-    Entry entry = new Entry(uri, varyHeaders, httpConnection);
-    DiskLruCache.Snapshot snapshot = (conditionalCacheHit instanceof EntryCacheResponse)
-        ? ((EntryCacheResponse) conditionalCacheHit).snapshot
-        : ((EntrySecureCacheResponse) conditionalCacheHit).snapshot;
-    DiskLruCache.Editor editor = null;
-    try {
-      editor = snapshot.edit(); // returns null if snapshot is not current
-      if (editor != null) {
-        entry.writeTo(editor);
-        editor.commit();
-      }
-    } catch (IOException e) {
-      abortQuietly(editor);
-    }
-  }
-
-  private void abortQuietly(DiskLruCache.Editor editor) {
-    // Give up because the cache cannot be written.
-    try {
-      if (editor != null) {
-        editor.abort();
-      }
-    } catch (IOException ignored) {
-    }
-  }
-
-  private HttpEngine getHttpEngine(URLConnection httpConnection) {
-    if (httpConnection instanceof HttpURLConnectionImpl) {
-      return ((HttpURLConnectionImpl) httpConnection).getHttpEngine();
-    } else if (httpConnection instanceof HttpsURLConnectionImpl) {
-      return ((HttpsURLConnectionImpl) httpConnection).getHttpEngine();
-    } else {
-      return null;
-    }
-  }
-
-  public DiskLruCache getCache() {
-    return cache;
-  }
-
-  public synchronized int getWriteAbortCount() {
-    return writeAbortCount;
-  }
-
-  public synchronized int getWriteSuccessCount() {
-    return writeSuccessCount;
-  }
-
-  public synchronized void trackResponse(ResponseSource source) {
-    requestCount++;
-
-    switch (source) {
-      case CACHE:
-        hitCount++;
-        break;
-      case CONDITIONAL_CACHE:
-      case NETWORK:
-        networkCount++;
-        break;
-    }
-  }
-
-  public synchronized void trackConditionalCacheHit() {
-    hitCount++;
-  }
-
-  public synchronized int getNetworkCount() {
-    return networkCount;
-  }
-
-  public synchronized int getHitCount() {
-    return hitCount;
-  }
-
-  public synchronized int getRequestCount() {
-    return requestCount;
-  }
-
-  private final class CacheRequestImpl extends CacheRequest {
-    private final DiskLruCache.Editor editor;
-    private OutputStream cacheOut;
-    private boolean done;
-    private OutputStream body;
-
-    public CacheRequestImpl(final DiskLruCache.Editor editor) throws IOException {
-      this.editor = editor;
-      this.cacheOut = editor.newOutputStream(ENTRY_BODY);
-      this.body = new FilterOutputStream(cacheOut) {
-        @Override public void close() throws IOException {
-          synchronized (HttpResponseCache.this) {
-            if (done) {
-              return;
-            }
-            done = true;
-            writeSuccessCount++;
-          }
-          super.close();
-          editor.commit();
-        }
-
-        @Override
-        public void write(byte[] buffer, int offset, int length) throws IOException {
-          // Since we don't override "write(int oneByte)", we can write directly to "out"
-          // and avoid the inefficient implementation from the FilterOutputStream.
-          out.write(buffer, offset, length);
-        }
-      };
-    }
-
-    @Override public void abort() {
-      synchronized (HttpResponseCache.this) {
-        if (done) {
-          return;
-        }
-        done = true;
-        writeAbortCount++;
-      }
-      Util.closeQuietly(cacheOut);
-      try {
-        editor.abort();
-      } catch (IOException ignored) {
-      }
-    }
-
-    @Override public OutputStream getBody() throws IOException {
-      return body;
-    }
-  }
-
-  private static final class Entry {
-    private final String uri;
-    private final RawHeaders varyHeaders;
-    private final String requestMethod;
-    private final RawHeaders responseHeaders;
-    private final String cipherSuite;
-    private final Certificate[] peerCertificates;
-    private final Certificate[] localCertificates;
-
-    /**
-     * Reads an entry from an input stream. A typical entry looks like this:
-     * <pre>{@code
-     *   http://google.com/foo
-     *   GET
-     *   2
-     *   Accept-Language: fr-CA
-     *   Accept-Charset: UTF-8
-     *   HTTP/1.1 200 OK
-     *   3
-     *   Content-Type: image/png
-     *   Content-Length: 100
-     *   Cache-Control: max-age=600
-     * }</pre>
-     *
-     * <p>A typical HTTPS file looks like this:
-     * <pre>{@code
-     *   https://google.com/foo
-     *   GET
-     *   2
-     *   Accept-Language: fr-CA
-     *   Accept-Charset: UTF-8
-     *   HTTP/1.1 200 OK
-     *   3
-     *   Content-Type: image/png
-     *   Content-Length: 100
-     *   Cache-Control: max-age=600
-     *
-     *   AES_256_WITH_MD5
-     *   2
-     *   base64-encoded peerCertificate[0]
-     *   base64-encoded peerCertificate[1]
-     *   -1
-     * }</pre>
-     * The file is newline separated. The first two lines are the URL and
-     * the request method. Next is the number of HTTP Vary request header
-     * lines, followed by those lines.
-     *
-     * <p>Next is the response status line, followed by the number of HTTP
-     * response header lines, followed by those lines.
-     *
-     * <p>HTTPS responses also contain SSL session information. This begins
-     * with a blank line, and then a line containing the cipher suite. Next
-     * is the length of the peer certificate chain. These certificates are
-     * base64-encoded and appear each on their own line. The next line
-     * contains the length of the local certificate chain. These
-     * certificates are also base64-encoded and appear each on their own
-     * line. A length of -1 is used to encode a null array.
-     */
-    public Entry(InputStream in) throws IOException {
-      try {
-        StrictLineReader reader = new StrictLineReader(in, US_ASCII);
-        uri = reader.readLine();
-        requestMethod = reader.readLine();
-        varyHeaders = new RawHeaders();
-        int varyRequestHeaderLineCount = reader.readInt();
-        for (int i = 0; i < varyRequestHeaderLineCount; i++) {
-          varyHeaders.addLine(reader.readLine());
-        }
-
-        responseHeaders = new RawHeaders();
-        responseHeaders.setStatusLine(reader.readLine());
-        int responseHeaderLineCount = reader.readInt();
-        for (int i = 0; i < responseHeaderLineCount; i++) {
-          responseHeaders.addLine(reader.readLine());
-        }
-
-        if (isHttps()) {
-          String blank = reader.readLine();
-          if (!blank.isEmpty()) {
-            throw new IOException("expected \"\" but was \"" + blank + "\"");
-          }
-          cipherSuite = reader.readLine();
-          peerCertificates = readCertArray(reader);
-          localCertificates = readCertArray(reader);
-        } else {
-          cipherSuite = null;
-          peerCertificates = null;
-          localCertificates = null;
-        }
-      } finally {
-        in.close();
-      }
-    }
-
-    public Entry(URI uri, RawHeaders varyHeaders, HttpURLConnection httpConnection)
-        throws IOException {
-      this.uri = uri.toString();
-      this.varyHeaders = varyHeaders;
-      this.requestMethod = httpConnection.getRequestMethod();
-      this.responseHeaders = RawHeaders.fromMultimap(httpConnection.getHeaderFields(), true);
-
-      if (isHttps()) {
-        HttpsURLConnection httpsConnection = (HttpsURLConnection) httpConnection;
-        cipherSuite = httpsConnection.getCipherSuite();
-        Certificate[] peerCertificatesNonFinal = null;
-        try {
-          peerCertificatesNonFinal = httpsConnection.getServerCertificates();
-        } catch (SSLPeerUnverifiedException ignored) {
-        }
-        peerCertificates = peerCertificatesNonFinal;
-        localCertificates = httpsConnection.getLocalCertificates();
-      } else {
-        cipherSuite = null;
-        peerCertificates = null;
-        localCertificates = null;
-      }
-    }
-
-    public void writeTo(DiskLruCache.Editor editor) throws IOException {
-      OutputStream out = editor.newOutputStream(ENTRY_METADATA);
-      Writer writer = new BufferedWriter(new OutputStreamWriter(out, UTF_8));
-
-      writer.write(uri + '\n');
-      writer.write(requestMethod + '\n');
-      writer.write(Integer.toString(varyHeaders.length()) + '\n');
-      for (int i = 0; i < varyHeaders.length(); i++) {
-        writer.write(varyHeaders.getFieldName(i) + ": " + varyHeaders.getValue(i) + '\n');
-      }
-
-      writer.write(responseHeaders.getStatusLine() + '\n');
-      writer.write(Integer.toString(responseHeaders.length()) + '\n');
-      for (int i = 0; i < responseHeaders.length(); i++) {
-        writer.write(responseHeaders.getFieldName(i) + ": " + responseHeaders.getValue(i) + '\n');
-      }
-
-      if (isHttps()) {
-        writer.write('\n');
-        writer.write(cipherSuite + '\n');
-        writeCertArray(writer, peerCertificates);
-        writeCertArray(writer, localCertificates);
-      }
-      writer.close();
-    }
-
-    private boolean isHttps() {
-      return uri.startsWith("https://");
-    }
-
-    private Certificate[] readCertArray(StrictLineReader reader) throws IOException {
-      int length = reader.readInt();
-      if (length == -1) {
-        return null;
-      }
-      try {
-        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
-        Certificate[] result = new Certificate[length];
-        for (int i = 0; i < result.length; i++) {
-          String line = reader.readLine();
-          byte[] bytes = Base64.decode(line.getBytes("US-ASCII"));
-          result[i] = certificateFactory.generateCertificate(new ByteArrayInputStream(bytes));
-        }
-        return result;
-      } catch (CertificateException e) {
-        throw new IOException(e);
-      }
-    }
-
-    private void writeCertArray(Writer writer, Certificate[] certificates) throws IOException {
-      if (certificates == null) {
-        writer.write("-1\n");
-        return;
-      }
-      try {
-        writer.write(Integer.toString(certificates.length) + '\n');
-        for (Certificate certificate : certificates) {
-          byte[] bytes = certificate.getEncoded();
-          String line = Base64.encode(bytes);
-          writer.write(line + '\n');
-        }
-      } catch (CertificateEncodingException e) {
-        throw new IOException(e);
-      }
-    }
-
-    public boolean matches(URI uri, String requestMethod,
-        Map<String, List<String>> requestHeaders) {
-      return this.uri.equals(uri.toString())
-          && this.requestMethod.equals(requestMethod)
-          && new ResponseHeaders(uri, responseHeaders).varyMatches(varyHeaders.toMultimap(false),
-          requestHeaders);
-    }
-  }
-
-  /**
-   * Returns an input stream that reads the body of a snapshot, closing the
-   * snapshot when the stream is closed.
-   */
-  private static InputStream newBodyInputStream(final DiskLruCache.Snapshot snapshot) {
-    return new FilterInputStream(snapshot.getInputStream(ENTRY_BODY)) {
-      @Override public void close() throws IOException {
-        snapshot.close();
-        super.close();
-      }
-    };
-  }
-
-  static class EntryCacheResponse extends CacheResponse {
-    private final Entry entry;
-    private final DiskLruCache.Snapshot snapshot;
-    private final InputStream in;
-
-    public EntryCacheResponse(Entry entry, DiskLruCache.Snapshot snapshot) {
-      this.entry = entry;
-      this.snapshot = snapshot;
-      this.in = newBodyInputStream(snapshot);
-    }
-
-    @Override public Map<String, List<String>> getHeaders() {
-      return entry.responseHeaders.toMultimap(true);
-    }
-
-    @Override public InputStream getBody() {
-      return in;
-    }
-  }
-
-  static class EntrySecureCacheResponse extends SecureCacheResponse {
-    private final Entry entry;
-    private final DiskLruCache.Snapshot snapshot;
-    private final InputStream in;
-
-    public EntrySecureCacheResponse(Entry entry, DiskLruCache.Snapshot snapshot) {
-      this.entry = entry;
-      this.snapshot = snapshot;
-      this.in = newBodyInputStream(snapshot);
-    }
-
-    @Override public Map<String, List<String>> getHeaders() {
-      return entry.responseHeaders.toMultimap(true);
-    }
-
-    @Override public InputStream getBody() {
-      return in;
-    }
-
-    @Override public String getCipherSuite() {
-      return entry.cipherSuite;
-    }
-
-    @Override public List<Certificate> getServerCertificateChain()
-        throws SSLPeerUnverifiedException {
-      if (entry.peerCertificates == null || entry.peerCertificates.length == 0) {
-        throw new SSLPeerUnverifiedException(null);
-      }
-      return Arrays.asList(entry.peerCertificates.clone());
-    }
-
-    @Override public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
-      if (entry.peerCertificates == null || entry.peerCertificates.length == 0) {
-        throw new SSLPeerUnverifiedException(null);
-      }
-      return ((X509Certificate) entry.peerCertificates[0]).getSubjectX500Principal();
-    }
-
-    @Override public List<Certificate> getLocalCertificateChain() {
-      if (entry.localCertificates == null || entry.localCertificates.length == 0) {
-        return null;
-      }
-      return Arrays.asList(entry.localCertificates.clone());
-    }
-
-    @Override public Principal getLocalPrincipal() {
-      if (entry.localCertificates == null || entry.localCertificates.length == 0) {
-        return null;
-      }
-      return ((X509Certificate) entry.localCertificates[0]).getSubjectX500Principal();
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/HttpTransport.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/HttpTransport.java b/framework/src/com/squareup/okhttp/internal/http/HttpTransport.java
old mode 100644
new mode 100755
index f6d77b2..c967830
--- a/framework/src/com/squareup/okhttp/internal/http/HttpTransport.java
+++ b/framework/src/com/squareup/okhttp/internal/http/HttpTransport.java
@@ -78,18 +78,23 @@ public final class HttpTransport implements Transport {
     }
 
     // Stream a request body of a known length.
-    int fixedContentLength = httpEngine.policy.getFixedContentLength();
+    long fixedContentLength = httpEngine.policy.getFixedContentLength();
     if (fixedContentLength != -1) {
       httpEngine.requestHeaders.setContentLength(fixedContentLength);
       writeRequestHeaders();
       return new FixedLengthOutputStream(requestOut, fixedContentLength);
     }
 
+    long contentLength = httpEngine.requestHeaders.getContentLength();
+    if (contentLength > Integer.MAX_VALUE) {
+      throw new IllegalArgumentException("Use setFixedLengthStreamingMode() or "
+          + "setChunkedStreamingMode() for requests larger than 2 GiB.");
+    }
+
     // Buffer a request body of a known length.
-    int contentLength = httpEngine.requestHeaders.getContentLength();
     if (contentLength != -1) {
       writeRequestHeaders();
-      return new RetryableOutputStream(contentLength);
+      return new RetryableOutputStream((int) contentLength);
     }
 
     // Buffer a request body of an unknown length. Don't write request
@@ -127,15 +132,18 @@ public final class HttpTransport implements Transport {
   }
 
   @Override public ResponseHeaders readResponseHeaders() throws IOException {
-    RawHeaders headers = RawHeaders.fromBytes(socketIn);
-    httpEngine.connection.setHttpMinorVersion(headers.getHttpMinorVersion());
-    httpEngine.receiveHeaders(headers);
-    return new ResponseHeaders(httpEngine.uri, headers);
+    RawHeaders rawHeaders = RawHeaders.fromBytes(socketIn);
+    httpEngine.connection.setHttpMinorVersion(rawHeaders.getHttpMinorVersion());
+    httpEngine.receiveHeaders(rawHeaders);
+
+    ResponseHeaders headers = new ResponseHeaders(httpEngine.uri, rawHeaders);
+    headers.setTransport("http/1.1");
+    return headers;
   }
 
-  public boolean makeReusable(boolean streamCancelled, OutputStream requestBodyOut,
+  public boolean makeReusable(boolean streamCanceled, OutputStream requestBodyOut,
       InputStream responseBodyIn) {
-    if (streamCancelled) {
+    if (streamCanceled) {
       return false;
     }
 
@@ -169,6 +177,10 @@ public final class HttpTransport implements Transport {
    * Discards the response body so that the connection can be reused. This
    * needs to be done judiciously, since it delays the current request in
    * order to speed up a potential future request that may never occur.
+   *
+   * <p>A stream may be discarded to encourage response caching (a response
+   * cannot be cached unless it is consumed completely) or to enable connection
+   * reuse.
    */
   private static boolean discardStream(HttpEngine httpEngine, InputStream responseBodyIn) {
     Connection connection = httpEngine.connection;
@@ -212,9 +224,9 @@ public final class HttpTransport implements Transport {
   /** An HTTP body with a fixed length known in advance. */
   private static final class FixedLengthOutputStream extends AbstractOutputStream {
     private final OutputStream socketOut;
-    private int bytesRemaining;
+    private long bytesRemaining;
 
-    private FixedLengthOutputStream(OutputStream socketOut, int bytesRemaining) {
+    private FixedLengthOutputStream(OutputStream socketOut, long bytesRemaining) {
       this.socketOut = socketOut;
       this.bytesRemaining = bytesRemaining;
     }
@@ -358,14 +370,14 @@ public final class HttpTransport implements Transport {
 
   /** An HTTP body with a fixed length specified in advance. */
   private static class FixedLengthInputStream extends AbstractHttpInputStream {
-    private int bytesRemaining;
+    private long bytesRemaining;
 
     public FixedLengthInputStream(InputStream is, CacheRequest cacheRequest, HttpEngine httpEngine,
-        int length) throws IOException {
+        long length) throws IOException {
       super(is, httpEngine, cacheRequest);
       bytesRemaining = length;
       if (bytesRemaining == 0) {
-        endOfInput(false);
+        endOfInput();
       }
     }
 
@@ -375,7 +387,7 @@ public final class HttpTransport implements Transport {
       if (bytesRemaining == 0) {
         return -1;
       }
-      int read = in.read(buffer, offset, Math.min(count, bytesRemaining));
+      int read = in.read(buffer, offset, (int) Math.min(count, bytesRemaining));
       if (read == -1) {
         unexpectedEndOfInput(); // the server didn't supply the promised content length
         throw new ProtocolException("unexpected end of stream");
@@ -383,14 +395,14 @@ public final class HttpTransport implements Transport {
       bytesRemaining -= read;
       cacheWrite(buffer, offset, read);
       if (bytesRemaining == 0) {
-        endOfInput(false);
+        endOfInput();
       }
       return read;
     }
 
     @Override public int available() throws IOException {
       checkNotClosed();
-      return bytesRemaining == 0 ? 0 : Math.min(in.available(), bytesRemaining);
+      return bytesRemaining == 0 ? 0 : (int) Math.min(in.available(), bytesRemaining);
     }
 
     @Override public void close() throws IOException {
@@ -460,7 +472,7 @@ public final class HttpTransport implements Transport {
         RawHeaders rawResponseHeaders = httpEngine.responseHeaders.getHeaders();
         RawHeaders.readHeaders(transport.socketIn, rawResponseHeaders);
         httpEngine.receiveHeaders(rawResponseHeaders);
-        endOfInput(false);
+        endOfInput();
       }
     }
 

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java b/framework/src/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java
old mode 100644
new mode 100755
index eabe649..fb4a704
--- a/framework/src/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java
+++ b/framework/src/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java
@@ -18,33 +18,28 @@
 package com.squareup.okhttp.internal.http;
 
 import com.squareup.okhttp.Connection;
-import com.squareup.okhttp.ConnectionPool;
 import com.squareup.okhttp.OkHttpClient;
-import com.squareup.okhttp.Route;
-import com.squareup.okhttp.internal.AbstractOutputStream;
-import com.squareup.okhttp.internal.FaultRecoveringOutputStream;
+import com.squareup.okhttp.internal.Platform;
 import com.squareup.okhttp.internal.Util;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.net.CookieHandler;
 import java.net.HttpRetryException;
 import java.net.HttpURLConnection;
 import java.net.InetSocketAddress;
 import java.net.ProtocolException;
 import java.net.Proxy;
-import java.net.ProxySelector;
 import java.net.SocketPermission;
 import java.net.URL;
 import java.security.Permission;
 import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
-import javax.net.ssl.HostnameVerifier;
+import java.util.concurrent.TimeUnit;
 import javax.net.ssl.SSLHandshakeException;
-import javax.net.ssl.SSLSocketFactory;
 
 import static com.squareup.okhttp.internal.Util.getEffectivePort;
 
@@ -62,10 +57,10 @@ import static com.squareup.okhttp.internal.Util.getEffectivePort;
  * connection} field on this class for null/non-null to determine of an instance
  * is currently connected to a server.
  */
-public class HttpURLConnectionImpl extends HttpURLConnection {
+public class HttpURLConnectionImpl extends HttpURLConnection implements Policy {
 
   /** Numeric status code, 307: Temporary Redirect. */
-  static final int HTTP_TEMP_REDIRECT = 307;
+  public static final int HTTP_TEMP_REDIRECT = 307;
 
   /**
    * How many redirects should we follow? Chrome follows 21; Firefox, curl,
@@ -73,51 +68,19 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
    */
   private static final int MAX_REDIRECTS = 20;
 
-  /**
-   * The minimum number of request body bytes to transmit before we're willing
-   * to let a routine {@link IOException} bubble up to the user. This is used to
-   * size a buffer for data that will be replayed upon error.
-   */
-  private static final int MAX_REPLAY_BUFFER_LENGTH = 8192;
-
-  private final boolean followProtocolRedirects;
-
-  /** The proxy requested by the client, or null for a proxy to be selected automatically. */
-  final Proxy requestedProxy;
-
-  final ProxySelector proxySelector;
-  final CookieHandler cookieHandler;
-  final OkResponseCache responseCache;
-  final ConnectionPool connectionPool;
-  /* SSL configuration; necessary for HTTP requests that get redirected to HTTPS. */
-  SSLSocketFactory sslSocketFactory;
-  HostnameVerifier hostnameVerifier;
-  final Set<Route> failedRoutes;
+  final OkHttpClient client;
 
   private final RawHeaders rawRequestHeaders = new RawHeaders();
-
+  /** Like the superclass field of the same name, but a long and available on all platforms. */
+  private long fixedContentLength = -1;
   private int redirectionCount;
-  private FaultRecoveringOutputStream faultRecoveringRequestBody;
-
   protected IOException httpEngineFailure;
   protected HttpEngine httpEngine;
+  private Proxy selectedProxy;
 
-  public HttpURLConnectionImpl(URL url, OkHttpClient client, OkResponseCache responseCache,
-      Set<Route> failedRoutes) {
+  public HttpURLConnectionImpl(URL url, OkHttpClient client) {
     super(url);
-    this.followProtocolRedirects = client.getFollowProtocolRedirects();
-    this.failedRoutes = failedRoutes;
-    this.requestedProxy = client.getProxy();
-    this.proxySelector = client.getProxySelector();
-    this.cookieHandler = client.getCookieHandler();
-    this.connectionPool = client.getConnectionPool();
-    this.sslSocketFactory = client.getSslSocketFactory();
-    this.hostnameVerifier = client.getHostnameVerifier();
-    this.responseCache = responseCache;
-  }
-
-  Set<Route> getFailedRoutes() {
-    return failedRoutes;
+    this.client = client;
   }
 
   @Override public final void connect() throws IOException {
@@ -197,7 +160,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
     try {
       return getResponse().getResponseHeaders().getHeaders().toMultimap(true);
     } catch (IOException e) {
-      return null;
+      return Collections.emptyMap();
     }
   }
 
@@ -241,29 +204,14 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
       throw new ProtocolException("cannot write request body after response has been read");
     }
 
-    if (faultRecoveringRequestBody == null) {
-      faultRecoveringRequestBody = new FaultRecoveringOutputStream(MAX_REPLAY_BUFFER_LENGTH, out) {
-        @Override protected OutputStream replacementStream(IOException e) throws IOException {
-          if (httpEngine.getRequestBody() instanceof AbstractOutputStream
-              && ((AbstractOutputStream) httpEngine.getRequestBody()).isClosed()) {
-            return null; // Don't recover once the underlying stream has been closed.
-          }
-          if (handleFailure(e)) {
-            return httpEngine.getRequestBody();
-          }
-          return null; // This is a permanent failure.
-        }
-      };
-    }
-
-    return faultRecoveringRequestBody;
+    return out;
   }
 
   @Override public final Permission getPermission() throws IOException {
     String hostName = getURL().getHost();
     int hostPort = Util.getEffectivePort(getURL());
     if (usingProxy()) {
-      InetSocketAddress proxyAddress = (InetSocketAddress) requestedProxy.address();
+      InetSocketAddress proxyAddress = (InetSocketAddress) client.getProxy().address();
       hostName = proxyAddress.getHostName();
       hostPort = proxyAddress.getPort();
     }
@@ -277,6 +225,22 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
     return rawRequestHeaders.get(field);
   }
 
+  @Override public void setConnectTimeout(int timeoutMillis) {
+    client.setConnectTimeout(timeoutMillis, TimeUnit.MILLISECONDS);
+  }
+
+  @Override public int getConnectTimeout() {
+    return client.getConnectTimeout();
+  }
+
+  @Override public void setReadTimeout(int timeoutMillis) {
+    client.setReadTimeout(timeoutMillis, TimeUnit.MILLISECONDS);
+  }
+
+  @Override public int getReadTimeout() {
+    return client.getReadTimeout();
+  }
+
   private void initHttpEngine() throws IOException {
     if (httpEngineFailure != null) {
       throw httpEngineFailure;
@@ -290,8 +254,8 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
         if (method.equals("GET")) {
           // they are requesting a stream to write to. This implies a POST method
           method = "POST";
-        } else if (!method.equals("POST") && !method.equals("PUT")) {
-          // If the request method is neither POST nor PUT, then you're not writing
+        } else if (!method.equals("POST") && !method.equals("PUT") && !method.equals("PATCH")) {
+          // If the request method is neither POST nor PUT nor PATCH, then you're not writing
           throw new ProtocolException(method + " does not support writing");
         }
       }
@@ -302,17 +266,16 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
     }
   }
 
-  protected HttpURLConnection getHttpConnectionToCache() {
+  @Override public HttpURLConnection getHttpConnectionToCache() {
     return this;
   }
 
   private HttpEngine newHttpEngine(String method, RawHeaders requestHeaders,
       Connection connection, RetryableOutputStream requestBody) throws IOException {
     if (url.getProtocol().equals("http")) {
-      return new HttpEngine(this, method, requestHeaders, connection, requestBody);
+      return new HttpEngine(client, this, method, requestHeaders, connection, requestBody);
     } else if (url.getProtocol().equals("https")) {
-      return new HttpsURLConnectionImpl.HttpsEngine(
-          this, method, requestHeaders, connection, requestBody);
+      return new HttpsEngine(client, this, method, requestHeaders, connection, requestBody);
     } else {
       throw new AssertionError();
     }
@@ -348,7 +311,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
       // Although RFC 2616 10.3.2 specifies that a HTTP_MOVED_PERM
       // redirect should keep the same method, Chrome, Firefox and the
       // RI all issue GETs when following any redirect.
-      int responseCode = getResponseCode();
+      int responseCode = httpEngine.getResponseCode();
       if (responseCode == HTTP_MULT_CHOICE
           || responseCode == HTTP_MOVED_PERM
           || responseCode == HTTP_MOVED_TEMP
@@ -358,8 +321,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
       }
 
       if (requestBody != null && !(requestBody instanceof RetryableOutputStream)) {
-        throw new HttpRetryException("Cannot retry streamed HTTP body",
-            httpEngine.getResponseCode());
+        throw new HttpRetryException("Cannot retry streamed HTTP body", responseCode);
       }
 
       if (retry == Retry.DIFFERENT_CONNECTION) {
@@ -370,6 +332,11 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
 
       httpEngine = newHttpEngine(retryMethod, rawRequestHeaders, httpEngine.getConnection(),
           (RetryableOutputStream) requestBody);
+
+      if (requestBody == null) {
+        // Drop the Content-Length header when redirected from POST to GET.
+        httpEngine.getRequestHeaders().removeContentLength();
+      }
     }
   }
 
@@ -384,6 +351,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
       if (readResponse) {
         httpEngine.readResponse();
       }
+
       return true;
     } catch (IOException e) {
       if (handleFailure(e)) {
@@ -407,8 +375,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
 
     OutputStream requestBody = httpEngine.getRequestBody();
     boolean canRetryRequestBody = requestBody == null
-        || requestBody instanceof RetryableOutputStream
-        || (faultRecoveringRequestBody != null && faultRecoveringRequestBody.isRecoverable());
+        || requestBody instanceof RetryableOutputStream;
     if (routeSelector == null && httpEngine.connection == null // No connection.
         || routeSelector != null && !routeSelector.hasNext() // No more routes to attempt.
         || !isRecoverable(e)
@@ -418,15 +385,9 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
     }
 
     httpEngine.release(true);
-    RetryableOutputStream retryableOutputStream = requestBody instanceof RetryableOutputStream
-        ? (RetryableOutputStream) requestBody
-        : null;
+    RetryableOutputStream retryableOutputStream = (RetryableOutputStream) requestBody;
     httpEngine = newHttpEngine(method, rawRequestHeaders, null, retryableOutputStream);
     httpEngine.routeSelector = routeSelector; // Keep the same routeSelector.
-    if (faultRecoveringRequestBody != null && faultRecoveringRequestBody.isRecoverable()) {
-      httpEngine.sendRequest();
-      faultRecoveringRequestBody.replaceStream(httpEngine.getRequestBody());
-    }
     return true;
   }
 
@@ -451,13 +412,13 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
 
   /**
    * Returns the retry action to take for the current response headers. The
-   * headers, proxy and target URL or this connection may be adjusted to
+   * headers, proxy and target URL for this connection may be adjusted to
    * prepare for a follow up request.
    */
   private Retry processResponseHeaders() throws IOException {
     Proxy selectedProxy = httpEngine.connection != null
         ? httpEngine.connection.getRoute().getProxy()
-        : requestedProxy;
+        : client.getProxy();
     final int responseCode = getResponseCode();
     switch (responseCode) {
       case HTTP_PROXY_AUTH:
@@ -466,8 +427,9 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
         }
         // fall-through
       case HTTP_UNAUTHORIZED:
-        boolean credentialsFound = HttpAuthenticator.processAuthHeader(getResponseCode(),
-            httpEngine.getResponseHeaders().getHeaders(), rawRequestHeaders, selectedProxy, url);
+        boolean credentialsFound = HttpAuthenticator.processAuthHeader(client.getAuthenticator(),
+            getResponseCode(), httpEngine.getResponseHeaders().getHeaders(), rawRequestHeaders,
+            selectedProxy, url);
         return credentialsFound ? Retry.SAME_CONNECTION : Retry.NONE;
 
       case HTTP_MULT_CHOICE:
@@ -496,7 +458,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
           return Retry.NONE; // Don't follow redirects to unsupported protocols.
         }
         boolean sameProtocol = previousUrl.getProtocol().equals(url.getProtocol());
-        if (!sameProtocol && !followProtocolRedirects) {
+        if (!sameProtocol && !client.getFollowProtocolRedirects()) {
           return Retry.NONE; // This client doesn't follow redirects across protocols.
         }
         boolean sameHost = previousUrl.getHost().equals(url.getHost());
@@ -513,17 +475,29 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
   }
 
   /** @see java.net.HttpURLConnection#setFixedLengthStreamingMode(int) */
-  final int getFixedContentLength() {
+  @Override public final long getFixedContentLength() {
     return fixedContentLength;
   }
 
-  /** @see java.net.HttpURLConnection#setChunkedStreamingMode(int) */
-  final int getChunkLength() {
+  @Override public final int getChunkLength() {
     return chunkLength;
   }
 
   @Override public final boolean usingProxy() {
-    return (requestedProxy != null && requestedProxy.type() != Proxy.Type.DIRECT);
+    if (selectedProxy != null) {
+      return isValidNonDirectProxy(selectedProxy);
+    }
+
+    // This behavior is a bit odd (but is probably justified by the
+    // oddness of the APIs involved). Before a connection is established,
+    // this method will return true only if this connection was explicitly
+    // opened with a Proxy. We don't attempt to query the ProxySelector
+    // at all.
+    return isValidNonDirectProxy(client.getProxy());
+  }
+
+  private static boolean isValidNonDirectProxy(Proxy proxy) {
+    return proxy != null && proxy.type() != Proxy.Type.DIRECT;
   }
 
   @Override public String getResponseMessage() throws IOException {
@@ -541,7 +515,21 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
     if (field == null) {
       throw new NullPointerException("field == null");
     }
-    rawRequestHeaders.set(field, newValue);
+    if (newValue == null) {
+      // Silently ignore null header values for backwards compatibility with older
+      // android versions as well as with other URLConnection implementations.
+      //
+      // Some implementations send a malformed HTTP header when faced with
+      // such requests, we respect the spec and ignore the header.
+      Platform.get().logW("Ignoring header " + field + " because its value was null.");
+      return;
+    }
+
+    if ("X-Android-Transports".equals(field)) {
+      setTransports(newValue, false /* append */);
+    } else {
+      rawRequestHeaders.set(field, newValue);
+    }
   }
 
   @Override public final void addRequestProperty(String field, String value) {
@@ -551,6 +539,52 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
     if (field == null) {
       throw new NullPointerException("field == null");
     }
-    rawRequestHeaders.add(field, value);
+    if (value == null) {
+      // Silently ignore null header values for backwards compatibility with older
+      // android versions as well as with other URLConnection implementations.
+      //
+      // Some implementations send a malformed HTTP header when faced with
+      // such requests, we respect the spec and ignore the header.
+      Platform.get().logW("Ignoring header " + field + " because its value was null.");
+      return;
+    }
+
+    if ("X-Android-Transports".equals(field)) {
+      setTransports(value, true /* append */);
+    } else {
+      rawRequestHeaders.add(field, value);
+    }
+  }
+
+  /*
+   * Splits and validates a comma-separated string of transports.
+   * When append == false, we require that the transport list contains "http/1.1".
+   */
+  private void setTransports(String transportsString, boolean append) {
+    List<String> transportsList = new ArrayList<String>();
+    if (append) {
+      transportsList.addAll(client.getTransports());
+    }
+    for (String transport : transportsString.split(",", -1)) {
+      transportsList.add(transport);
+    }
+    client.setTransports(transportsList);
+  }
+
+  @Override public void setFixedLengthStreamingMode(int contentLength) {
+    setFixedLengthStreamingMode((long) contentLength);
+  }
+
+  // @Override Don't override: this overload method doesn't exist prior to Java 1.7.
+  public void setFixedLengthStreamingMode(long contentLength) {
+    if (super.connected) throw new IllegalStateException("Already connected");
+    if (chunkLength > 0) throw new IllegalStateException("Already in chunked mode");
+    if (contentLength < 0) throw new IllegalArgumentException("contentLength < 0");
+    this.fixedContentLength = contentLength;
+    super.fixedContentLength = (int) Math.min(contentLength, Integer.MAX_VALUE);
+  }
+
+  @Override public final void setSelectedProxy(Proxy proxy) {
+    this.selectedProxy = proxy;
   }
 }


[2/5] CB-5799 Update version of OkHTTP to 1.3

Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/spdy/Settings.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/Settings.java b/framework/src/com/squareup/okhttp/internal/spdy/Settings.java
old mode 100644
new mode 100755
index 774d791..05380e2
--- a/framework/src/com/squareup/okhttp/internal/spdy/Settings.java
+++ b/framework/src/com/squareup/okhttp/internal/spdy/Settings.java
@@ -31,23 +31,29 @@ final class Settings {
   static final int PERSISTED = 0x2;
 
   /** Sender's estimate of max incoming kbps. */
-  static final int UPLOAD_BANDWIDTH = 0x1;
+  static final int UPLOAD_BANDWIDTH = 1;
   /** Sender's estimate of max outgoing kbps. */
-  static final int DOWNLOAD_BANDWIDTH = 0x2;
+  static final int DOWNLOAD_BANDWIDTH = 2;
   /** Sender's estimate of milliseconds between sending a request and receiving a response. */
-  static final int ROUND_TRIP_TIME = 0x3;
+  static final int ROUND_TRIP_TIME = 3;
   /** Sender's maximum number of concurrent streams. */
-  static final int MAX_CONCURRENT_STREAMS = 0x4;
+  static final int MAX_CONCURRENT_STREAMS = 4;
   /** Current CWND in Packets. */
-  static final int CURRENT_CWND = 0x5;
+  static final int CURRENT_CWND = 5;
   /** Retransmission rate. Percentage */
-  static final int DOWNLOAD_RETRANS_RATE = 0x6;
+  static final int DOWNLOAD_RETRANS_RATE = 6;
   /** Window size in bytes. */
-  static final int INITIAL_WINDOW_SIZE = 0x7;
+  static final int INITIAL_WINDOW_SIZE = 7;
   /** Window size in bytes. */
-  static final int CLIENT_CERTIFICATE_VECTOR_SIZE = 0x8;
+  static final int CLIENT_CERTIFICATE_VECTOR_SIZE = 8;
+  /** Flow control options. */
+  static final int FLOW_CONTROL_OPTIONS = 9;
+
   /** Total number of settings. */
-  static final int COUNT = 0x9;
+  static final int COUNT = 10;
+
+  /** If set, flow control is disabled for streams directed to the sender of these settings. */
+  static final int FLOW_CONTROL_OPTIONS_DISABLED = 0x1;
 
   /** Bitfield of which flags that values. */
   private int set;
@@ -146,6 +152,13 @@ final class Settings {
     return (bit & set) != 0 ? values[CLIENT_CERTIFICATE_VECTOR_SIZE] : defaultValue;
   }
 
+  // TODO: honor this setting.
+  boolean isFlowControlDisabled() {
+    int bit = 1 << FLOW_CONTROL_OPTIONS;
+    int value = (bit & set) != 0 ? values[FLOW_CONTROL_OPTIONS] : 0;
+    return (value & FLOW_CONTROL_OPTIONS_DISABLED) != 0;
+  }
+
   /**
    * Returns true if this user agent should use this setting in future SPDY
    * connections to the same host.

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/spdy/Spdy3.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/Spdy3.java b/framework/src/com/squareup/okhttp/internal/spdy/Spdy3.java
new file mode 100755
index 0000000..5d9a49b
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/internal/spdy/Spdy3.java
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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.internal.spdy;
+
+import com.squareup.okhttp.internal.Platform;
+import com.squareup.okhttp.internal.Util;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.ProtocolException;
+import java.util.List;
+import java.util.zip.Deflater;
+
+final class Spdy3 implements Variant {
+  static final int TYPE_DATA = 0x0;
+  static final int TYPE_SYN_STREAM = 0x1;
+  static final int TYPE_SYN_REPLY = 0x2;
+  static final int TYPE_RST_STREAM = 0x3;
+  static final int TYPE_SETTINGS = 0x4;
+  static final int TYPE_NOOP = 0x5;
+  static final int TYPE_PING = 0x6;
+  static final int TYPE_GOAWAY = 0x7;
+  static final int TYPE_HEADERS = 0x8;
+  static final int TYPE_WINDOW_UPDATE = 0x9;
+  static final int TYPE_CREDENTIAL = 0x10;
+
+  static final int FLAG_FIN = 0x1;
+  static final int FLAG_UNIDIRECTIONAL = 0x2;
+
+  static final int VERSION = 3;
+
+  static final byte[] DICTIONARY;
+  static {
+    try {
+      DICTIONARY = ("\u0000\u0000\u0000\u0007options\u0000\u0000\u0000\u0004hea"
+          + "d\u0000\u0000\u0000\u0004post\u0000\u0000\u0000\u0003put\u0000\u0000\u0000\u0006dele"
+          + "te\u0000\u0000\u0000\u0005trace\u0000\u0000\u0000\u0006accept\u0000\u0000\u0000"
+          + "\u000Eaccept-charset\u0000\u0000\u0000\u000Faccept-encoding\u0000\u0000\u0000\u000Fa"
+          + "ccept-language\u0000\u0000\u0000\raccept-ranges\u0000\u0000\u0000\u0003age\u0000"
+          + "\u0000\u0000\u0005allow\u0000\u0000\u0000\rauthorization\u0000\u0000\u0000\rcache-co"
+          + "ntrol\u0000\u0000\u0000\nconnection\u0000\u0000\u0000\fcontent-base\u0000\u0000"
+          + "\u0000\u0010content-encoding\u0000\u0000\u0000\u0010content-language\u0000\u0000"
+          + "\u0000\u000Econtent-length\u0000\u0000\u0000\u0010content-location\u0000\u0000\u0000"
+          + "\u000Bcontent-md5\u0000\u0000\u0000\rcontent-range\u0000\u0000\u0000\fcontent-type"
+          + "\u0000\u0000\u0000\u0004date\u0000\u0000\u0000\u0004etag\u0000\u0000\u0000\u0006expe"
+          + "ct\u0000\u0000\u0000\u0007expires\u0000\u0000\u0000\u0004from\u0000\u0000\u0000"
+          + "\u0004host\u0000\u0000\u0000\bif-match\u0000\u0000\u0000\u0011if-modified-since"
+          + "\u0000\u0000\u0000\rif-none-match\u0000\u0000\u0000\bif-range\u0000\u0000\u0000"
+          + "\u0013if-unmodified-since\u0000\u0000\u0000\rlast-modified\u0000\u0000\u0000\blocati"
+          + "on\u0000\u0000\u0000\fmax-forwards\u0000\u0000\u0000\u0006pragma\u0000\u0000\u0000"
+          + "\u0012proxy-authenticate\u0000\u0000\u0000\u0013proxy-authorization\u0000\u0000"
+          + "\u0000\u0005range\u0000\u0000\u0000\u0007referer\u0000\u0000\u0000\u000Bretry-after"
+          + "\u0000\u0000\u0000\u0006server\u0000\u0000\u0000\u0002te\u0000\u0000\u0000\u0007trai"
+          + "ler\u0000\u0000\u0000\u0011transfer-encoding\u0000\u0000\u0000\u0007upgrade\u0000"
+          + "\u0000\u0000\nuser-agent\u0000\u0000\u0000\u0004vary\u0000\u0000\u0000\u0003via"
+          + "\u0000\u0000\u0000\u0007warning\u0000\u0000\u0000\u0010www-authenticate\u0000\u0000"
+          + "\u0000\u0006method\u0000\u0000\u0000\u0003get\u0000\u0000\u0000\u0006status\u0000"
+          + "\u0000\u0000\u0006200 OK\u0000\u0000\u0000\u0007version\u0000\u0000\u0000\bHTTP/1.1"
+          + "\u0000\u0000\u0000\u0003url\u0000\u0000\u0000\u0006public\u0000\u0000\u0000\nset-coo"
+          + "kie\u0000\u0000\u0000\nkeep-alive\u0000\u0000\u0000\u0006origin100101201202205206300"
+          + "302303304305306307402405406407408409410411412413414415416417502504505203 Non-Authori"
+          + "tative Information204 No Content301 Moved Permanently400 Bad Request401 Unauthorized"
+          + "403 Forbidden404 Not Found500 Internal Server Error501 Not Implemented503 Service Un"
+          + "availableJan Feb Mar Apr May Jun Jul Aug Sept Oct Nov Dec 00:00:00 Mon, Tue, Wed, Th"
+          + "u, Fri, Sat, Sun, GMTchunked,text/html,image/png,image/jpg,image/gif,application/xml"
+          + ",application/xhtml+xml,text/plain,text/javascript,publicprivatemax-age=gzip,deflate,"
+          + "sdchcharset=utf-8charset=iso-8859-1,utf-,*,enq=0.").getBytes(Util.UTF_8.name());
+    } catch (UnsupportedEncodingException e) {
+      throw new AssertionError();
+    }
+  }
+
+  @Override public FrameReader newReader(InputStream in, boolean client) {
+    return new Reader(in, client);
+  }
+
+  @Override public FrameWriter newWriter(OutputStream out, boolean client) {
+    return new Writer(out, client);
+  }
+
+  /** Read spdy/3 frames. */
+  static final class Reader implements FrameReader {
+    private final DataInputStream in;
+    private final boolean client;
+    private final NameValueBlockReader nameValueBlockReader;
+
+    Reader(InputStream in, boolean client) {
+      this.in = new DataInputStream(in);
+      this.nameValueBlockReader = new NameValueBlockReader(in);
+      this.client = client;
+    }
+
+    @Override public void readConnectionHeader() {
+    }
+
+    /**
+     * Send the next frame to {@code handler}. Returns true unless there are no
+     * more frames on the stream.
+     */
+    @Override public boolean nextFrame(Handler handler) throws IOException {
+      int w1;
+      try {
+        w1 = in.readInt();
+      } catch (IOException e) {
+        return false; // This might be a normal socket close.
+      }
+      int w2 = in.readInt();
+
+      boolean control = (w1 & 0x80000000) != 0;
+      int flags = (w2 & 0xff000000) >>> 24;
+      int length = (w2 & 0xffffff);
+
+      if (control) {
+        int version = (w1 & 0x7fff0000) >>> 16;
+        int type = (w1 & 0xffff);
+
+        if (version != 3) {
+          throw new ProtocolException("version != 3: " + version);
+        }
+
+        switch (type) {
+          case TYPE_SYN_STREAM:
+            readSynStream(handler, flags, length);
+            return true;
+
+          case TYPE_SYN_REPLY:
+            readSynReply(handler, flags, length);
+            return true;
+
+          case TYPE_RST_STREAM:
+            readRstStream(handler, flags, length);
+            return true;
+
+          case TYPE_SETTINGS:
+            readSettings(handler, flags, length);
+            return true;
+
+          case TYPE_NOOP:
+            if (length != 0) throw ioException("TYPE_NOOP length: %d != 0", length);
+            handler.noop();
+            return true;
+
+          case TYPE_PING:
+            readPing(handler, flags, length);
+            return true;
+
+          case TYPE_GOAWAY:
+            readGoAway(handler, flags, length);
+            return true;
+
+          case TYPE_HEADERS:
+            readHeaders(handler, flags, length);
+            return true;
+
+          case TYPE_WINDOW_UPDATE:
+            readWindowUpdate(handler, flags, length);
+            return true;
+
+          case TYPE_CREDENTIAL:
+            Util.skipByReading(in, length);
+            throw new UnsupportedOperationException("TODO"); // TODO: implement
+
+          default:
+            throw new IOException("Unexpected frame");
+        }
+      } else {
+        int streamId = w1 & 0x7fffffff;
+        boolean inFinished = (flags & FLAG_FIN) != 0;
+        handler.data(inFinished, streamId, in, length);
+        return true;
+      }
+    }
+
+    private void readSynStream(Handler handler, int flags, int length) throws IOException {
+      int w1 = in.readInt();
+      int w2 = in.readInt();
+      int s3 = in.readShort();
+      int streamId = w1 & 0x7fffffff;
+      int associatedStreamId = w2 & 0x7fffffff;
+      int priority = (s3 & 0xe000) >>> 13;
+      int slot = s3 & 0xff;
+      List<String> nameValueBlock = nameValueBlockReader.readNameValueBlock(length - 10);
+
+      boolean inFinished = (flags & FLAG_FIN) != 0;
+      boolean outFinished = (flags & FLAG_UNIDIRECTIONAL) != 0;
+      handler.headers(outFinished, inFinished, streamId, associatedStreamId, priority,
+          nameValueBlock, HeadersMode.SPDY_SYN_STREAM);
+    }
+
+    private void readSynReply(Handler handler, int flags, int length) throws IOException {
+      int w1 = in.readInt();
+      int streamId = w1 & 0x7fffffff;
+      List<String> nameValueBlock = nameValueBlockReader.readNameValueBlock(length - 4);
+      boolean inFinished = (flags & FLAG_FIN) != 0;
+      handler.headers(false, inFinished, streamId, -1, -1, nameValueBlock, HeadersMode.SPDY_REPLY);
+    }
+
+    private void readRstStream(Handler handler, int flags, int length) throws IOException {
+      if (length != 8) throw ioException("TYPE_RST_STREAM length: %d != 8", length);
+      int streamId = in.readInt() & 0x7fffffff;
+      int errorCodeInt = in.readInt();
+      ErrorCode errorCode = ErrorCode.fromSpdy3Rst(errorCodeInt);
+      if (errorCode == null) {
+        throw ioException("TYPE_RST_STREAM unexpected error code: %d", errorCodeInt);
+      }
+      handler.rstStream(streamId, errorCode);
+    }
+
+    private void readHeaders(Handler handler, int flags, int length) throws IOException {
+      int w1 = in.readInt();
+      int streamId = w1 & 0x7fffffff;
+      List<String> nameValueBlock = nameValueBlockReader.readNameValueBlock(length - 4);
+      handler.headers(false, false, streamId, -1, -1, nameValueBlock, HeadersMode.SPDY_HEADERS);
+    }
+
+    private void readWindowUpdate(Handler handler, int flags, int length) throws IOException {
+      if (length != 8) throw ioException("TYPE_WINDOW_UPDATE length: %d != 8", length);
+      int w1 = in.readInt();
+      int w2 = in.readInt();
+      int streamId = w1 & 0x7fffffff;
+      int deltaWindowSize = w2 & 0x7fffffff;
+      handler.windowUpdate(streamId, deltaWindowSize, false);
+    }
+
+    private void readPing(Handler handler, int flags, int length) throws IOException {
+      if (length != 4) throw ioException("TYPE_PING length: %d != 4", length);
+      int id = in.readInt();
+      boolean reply = client == ((id % 2) == 1);
+      handler.ping(reply, id, 0);
+    }
+
+    private void readGoAway(Handler handler, int flags, int length) throws IOException {
+      if (length != 8) throw ioException("TYPE_GOAWAY length: %d != 8", length);
+      int lastGoodStreamId = in.readInt() & 0x7fffffff;
+      int errorCodeInt = in.readInt();
+      ErrorCode errorCode = ErrorCode.fromSpdyGoAway(errorCodeInt);
+      if (errorCode == null) {
+        throw ioException("TYPE_GOAWAY unexpected error code: %d", errorCodeInt);
+      }
+      handler.goAway(lastGoodStreamId, errorCode);
+    }
+
+    private void readSettings(Handler handler, int flags, int length) throws IOException {
+      int numberOfEntries = in.readInt();
+      if (length != 4 + 8 * numberOfEntries) {
+        throw ioException("TYPE_SETTINGS length: %d != 4 + 8 * %d", length, numberOfEntries);
+      }
+      Settings settings = new Settings();
+      for (int i = 0; i < numberOfEntries; i++) {
+        int w1 = in.readInt();
+        int value = in.readInt();
+        int idFlags = (w1 & 0xff000000) >>> 24;
+        int id = w1 & 0xffffff;
+        settings.set(id, idFlags, value);
+      }
+      boolean clearPrevious = (flags & Settings.FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS) != 0;
+      handler.settings(clearPrevious, settings);
+    }
+
+    private static IOException ioException(String message, Object... args) throws IOException {
+      throw new IOException(String.format(message, args));
+    }
+
+    @Override public void close() throws IOException {
+      Util.closeAll(in, nameValueBlockReader);
+    }
+  }
+
+  /** Write spdy/3 frames. */
+  static final class Writer implements FrameWriter {
+    private final DataOutputStream out;
+    private final ByteArrayOutputStream nameValueBlockBuffer;
+    private final DataOutputStream nameValueBlockOut;
+    private final boolean client;
+
+    Writer(OutputStream out, boolean client) {
+      this.out = new DataOutputStream(out);
+      this.client = client;
+
+      Deflater deflater = new Deflater();
+      deflater.setDictionary(DICTIONARY);
+      nameValueBlockBuffer = new ByteArrayOutputStream();
+      nameValueBlockOut = new DataOutputStream(
+          Platform.get().newDeflaterOutputStream(nameValueBlockBuffer, deflater, true));
+    }
+
+    @Override public synchronized void connectionHeader() {
+      // Do nothing: no connection header for SPDY/3.
+    }
+
+    @Override public synchronized void flush() throws IOException {
+      out.flush();
+    }
+
+    @Override public synchronized void synStream(boolean outFinished, boolean inFinished,
+        int streamId, int associatedStreamId, int priority, int slot, List<String> nameValueBlock)
+        throws IOException {
+      writeNameValueBlockToBuffer(nameValueBlock);
+      int length = 10 + nameValueBlockBuffer.size();
+      int type = TYPE_SYN_STREAM;
+      int flags = (outFinished ? FLAG_FIN : 0) | (inFinished ? FLAG_UNIDIRECTIONAL : 0);
+
+      int unused = 0;
+      out.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff);
+      out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+      out.writeInt(streamId & 0x7fffffff);
+      out.writeInt(associatedStreamId & 0x7fffffff);
+      out.writeShort((priority & 0x7) << 13 | (unused & 0x1f) << 8 | (slot & 0xff));
+      nameValueBlockBuffer.writeTo(out);
+      out.flush();
+    }
+
+    @Override public synchronized void synReply(
+        boolean outFinished, int streamId, List<String> nameValueBlock) throws IOException {
+      writeNameValueBlockToBuffer(nameValueBlock);
+      int type = TYPE_SYN_REPLY;
+      int flags = (outFinished ? FLAG_FIN : 0);
+      int length = nameValueBlockBuffer.size() + 4;
+
+      out.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff);
+      out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+      out.writeInt(streamId & 0x7fffffff);
+      nameValueBlockBuffer.writeTo(out);
+      out.flush();
+    }
+
+    @Override public synchronized void headers(int streamId, List<String> nameValueBlock)
+        throws IOException {
+      writeNameValueBlockToBuffer(nameValueBlock);
+      int flags = 0;
+      int type = TYPE_HEADERS;
+      int length = nameValueBlockBuffer.size() + 4;
+
+      out.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff);
+      out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+      out.writeInt(streamId & 0x7fffffff);
+      nameValueBlockBuffer.writeTo(out);
+      out.flush();
+    }
+
+    @Override public synchronized void rstStream(int streamId, ErrorCode errorCode)
+        throws IOException {
+      if (errorCode.spdyRstCode == -1) throw new IllegalArgumentException();
+      int flags = 0;
+      int type = TYPE_RST_STREAM;
+      int length = 8;
+      out.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff);
+      out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+      out.writeInt(streamId & 0x7fffffff);
+      out.writeInt(errorCode.spdyRstCode);
+      out.flush();
+    }
+
+    @Override public synchronized void data(boolean outFinished, int streamId, byte[] data)
+        throws IOException {
+      data(outFinished, streamId, data, 0, data.length);
+    }
+
+    @Override public synchronized void data(boolean outFinished, int streamId, byte[] data,
+        int offset, int byteCount) throws IOException {
+      int flags = (outFinished ? FLAG_FIN : 0);
+      out.writeInt(streamId & 0x7fffffff);
+      out.writeInt((flags & 0xff) << 24 | byteCount & 0xffffff);
+      out.write(data, offset, byteCount);
+    }
+
+    private void writeNameValueBlockToBuffer(List<String> nameValueBlock) throws IOException {
+      nameValueBlockBuffer.reset();
+      int numberOfPairs = nameValueBlock.size() / 2;
+      nameValueBlockOut.writeInt(numberOfPairs);
+      for (String s : nameValueBlock) {
+        nameValueBlockOut.writeInt(s.length());
+        nameValueBlockOut.write(s.getBytes("UTF-8"));
+      }
+      nameValueBlockOut.flush();
+    }
+
+    @Override public synchronized void settings(Settings settings) throws IOException {
+      int type = TYPE_SETTINGS;
+      int flags = 0;
+      int size = settings.size();
+      int length = 4 + size * 8;
+      out.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff);
+      out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+      out.writeInt(size);
+      for (int i = 0; i <= Settings.COUNT; i++) {
+        if (!settings.isSet(i)) continue;
+        int settingsFlags = settings.flags(i);
+        out.writeInt((settingsFlags & 0xff) << 24 | (i & 0xffffff));
+        out.writeInt(settings.get(i));
+      }
+      out.flush();
+    }
+
+    @Override public synchronized void noop() throws IOException {
+      int type = TYPE_NOOP;
+      int length = 0;
+      int flags = 0;
+      out.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff);
+      out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+      out.flush();
+    }
+
+    @Override public synchronized void ping(boolean reply, int payload1, int payload2)
+        throws IOException {
+      boolean payloadIsReply = client != ((payload1 % 2) == 1);
+      if (reply != payloadIsReply) throw new IllegalArgumentException("payload != reply");
+      int type = TYPE_PING;
+      int flags = 0;
+      int length = 4;
+      out.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff);
+      out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+      out.writeInt(payload1);
+      out.flush();
+    }
+
+    @Override public synchronized void goAway(int lastGoodStreamId, ErrorCode errorCode)
+        throws IOException {
+      if (errorCode.spdyGoAwayCode == -1) throw new IllegalArgumentException();
+      int type = TYPE_GOAWAY;
+      int flags = 0;
+      int length = 8;
+      out.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff);
+      out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+      out.writeInt(lastGoodStreamId);
+      out.writeInt(errorCode.spdyGoAwayCode);
+      out.flush();
+    }
+
+    @Override public synchronized void windowUpdate(int streamId, int deltaWindowSize)
+        throws IOException {
+      int type = TYPE_WINDOW_UPDATE;
+      int flags = 0;
+      int length = 8;
+      out.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff);
+      out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+      out.writeInt(streamId);
+      out.writeInt(deltaWindowSize);
+      out.flush();
+    }
+
+    @Override public void close() throws IOException {
+      Util.closeAll(out, nameValueBlockOut);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/spdy/SpdyConnection.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/SpdyConnection.java b/framework/src/com/squareup/okhttp/internal/spdy/SpdyConnection.java
old mode 100644
new mode 100755
index fccd14f..41724f0
--- a/framework/src/com/squareup/okhttp/internal/spdy/SpdyConnection.java
+++ b/framework/src/com/squareup/okhttp/internal/spdy/SpdyConnection.java
@@ -32,8 +32,6 @@ import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 
-import static java.util.concurrent.Executors.defaultThreadFactory;
-
 /**
  * A socket connection to a remote peer. A connection hosts streams which can
  * send and receive data.
@@ -48,38 +46,21 @@ public final class SpdyConnection implements Closeable {
   // Internal state of this connection is guarded by 'this'. No blocking
   // operations may be performed while holding this lock!
   //
-  // Socket writes are guarded by spdyWriter.
+  // Socket writes are guarded by frameWriter.
   //
   // Socket reads are unguarded but are only made by the reader thread.
   //
   // Certain operations (like SYN_STREAM) need to synchronize on both the
-  // spdyWriter (to do blocking I/O) and this (to create streams). Such
+  // frameWriter (to do blocking I/O) and this (to create streams). Such
   // operations must synchronize on 'this' last. This ensures that we never
   // wait for a blocking operation while holding 'this'.
 
-  static final int FLAG_FIN = 0x1;
-  static final int FLAG_UNIDIRECTIONAL = 0x2;
-
-  static final int TYPE_DATA = 0x0;
-  static final int TYPE_SYN_STREAM = 0x1;
-  static final int TYPE_SYN_REPLY = 0x2;
-  static final int TYPE_RST_STREAM = 0x3;
-  static final int TYPE_SETTINGS = 0x4;
-  static final int TYPE_NOOP = 0x5;
-  static final int TYPE_PING = 0x6;
-  static final int TYPE_GOAWAY = 0x7;
-  static final int TYPE_HEADERS = 0x8;
-  static final int TYPE_WINDOW_UPDATE = 0x9;
-  static final int TYPE_CREDENTIAL = 0x10;
-  static final int VERSION = 3;
-
-  static final int GOAWAY_OK = 0;
-  static final int GOAWAY_PROTOCOL_ERROR = 1;
-  static final int GOAWAY_INTERNAL_ERROR = 2;
-
-  private static final ExecutorService executor =
-      new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
-          new SynchronousQueue<Runnable>(), defaultThreadFactory());
+  private static final ExecutorService executor = new ThreadPoolExecutor(0,
+      Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(),
+      Util.daemonThreadFactory("OkHttp SpdyConnection"));
+
+  /** The protocol variant, like SPDY/3 or HTTP-draft-06/2.0. */
+  final Variant variant;
 
   /** True if this peer initiated the connection. */
   final boolean client;
@@ -89,8 +70,8 @@ public final class SpdyConnection implements Closeable {
    * run on the callback executor.
    */
   private final IncomingStreamHandler handler;
-  private final SpdyReader spdyReader;
-  private final SpdyWriter spdyWriter;
+  private final FrameReader frameReader;
+  private final FrameWriter frameWriter;
 
   private final Map<Integer, SpdyStream> streams = new HashMap<Integer, SpdyStream>();
   private final String hostName;
@@ -103,14 +84,15 @@ public final class SpdyConnection implements Closeable {
   private Map<Integer, Ping> pings;
   private int nextPingId;
 
-  /** Lazily-created settings for this connection. */
+  /** Lazily-created settings for the peer. */
   Settings settings;
 
   private SpdyConnection(Builder builder) {
+    variant = builder.variant;
     client = builder.client;
     handler = builder.handler;
-    spdyReader = new SpdyReader(builder.in);
-    spdyWriter = new SpdyWriter(builder.out);
+    frameReader = variant.newReader(builder.in, client);
+    frameWriter = variant.newWriter(builder.out, client);
     nextStreamId = builder.client ? 1 : 2;
     nextPingId = builder.client ? 1 : 2;
 
@@ -140,15 +122,18 @@ public final class SpdyConnection implements Closeable {
   }
 
   private synchronized void setIdle(boolean value) {
-    idleStartTimeNs = value ? System.nanoTime() : 0L;
+    idleStartTimeNs = value ? System.nanoTime() : Long.MAX_VALUE;
   }
 
   /** Returns true if this connection is idle. */
   public synchronized boolean isIdle() {
-    return idleStartTimeNs != 0L;
+    return idleStartTimeNs != Long.MAX_VALUE;
   }
 
-  /** Returns the time in ns when this connection became idle or 0L if connection is not idle. */
+  /**
+   * Returns the time in ns when this connection became idle or Long.MAX_VALUE
+   * if connection is not idle.
+   */
   public synchronized long getIdleStartTimeNs() {
     return idleStartTimeNs;
   }
@@ -157,80 +142,80 @@ public final class SpdyConnection implements Closeable {
    * Returns a new locally-initiated stream.
    *
    * @param out true to create an output stream that we can use to send data
-   * to the remote peer. Corresponds to {@code FLAG_FIN}.
+   *     to the remote peer. Corresponds to {@code FLAG_FIN}.
    * @param in true to create an input stream that the remote peer can use to
-   * send data to us. Corresponds to {@code FLAG_UNIDIRECTIONAL}.
+   *     send data to us. Corresponds to {@code FLAG_UNIDIRECTIONAL}.
    */
   public SpdyStream newStream(List<String> requestHeaders, boolean out, boolean in)
       throws IOException {
-    int flags = (out ? 0 : FLAG_FIN) | (in ? 0 : FLAG_UNIDIRECTIONAL);
+    boolean outFinished = !out;
+    boolean inFinished = !in;
     int associatedStreamId = 0;  // TODO: permit the caller to specify an associated stream?
     int priority = 0; // TODO: permit the caller to specify a priority?
     int slot = 0; // TODO: permit the caller to specify a slot?
     SpdyStream stream;
     int streamId;
 
-    synchronized (spdyWriter) {
+    synchronized (frameWriter) {
       synchronized (this) {
         if (shutdown) {
           throw new IOException("shutdown");
         }
         streamId = nextStreamId;
         nextStreamId += 2;
-        stream = new SpdyStream(streamId, this, flags, priority, slot, requestHeaders, settings);
+        stream = new SpdyStream(
+            streamId, this, outFinished, inFinished, priority, requestHeaders, settings);
         if (stream.isOpen()) {
           streams.put(streamId, stream);
           setIdle(false);
         }
       }
 
-      spdyWriter.synStream(flags, streamId, associatedStreamId, priority, slot, requestHeaders);
+      frameWriter.synStream(outFinished, inFinished, streamId, associatedStreamId, priority, slot,
+          requestHeaders);
     }
 
     return stream;
   }
 
-  void writeSynReply(int streamId, int flags, List<String> alternating) throws IOException {
-    spdyWriter.synReply(flags, streamId, alternating);
+  void writeSynReply(int streamId, boolean outFinished, List<String> alternating)
+      throws IOException {
+    frameWriter.synReply(outFinished, streamId, alternating);
   }
 
-  /** Writes a complete data frame. */
-  void writeFrame(byte[] bytes, int offset, int length) throws IOException {
-    synchronized (spdyWriter) {
-      spdyWriter.out.write(bytes, offset, length);
-    }
+  public void writeData(int streamId, boolean outFinished, byte[] buffer, int offset, int byteCount)
+      throws IOException {
+    frameWriter.data(outFinished, streamId, buffer, offset, byteCount);
   }
 
-  void writeSynResetLater(final int streamId, final int statusCode) {
-    executor.submit(
-        new NamedRunnable(String.format("Spdy Writer %s stream %d", hostName, streamId)) {
-          @Override public void execute() {
-            try {
-              writeSynReset(streamId, statusCode);
-            } catch (IOException ignored) {
-            }
-          }
-        });
+  void writeSynResetLater(final int streamId, final ErrorCode errorCode) {
+    executor.submit(new NamedRunnable("OkHttp SPDY Writer %s stream %d", hostName, streamId) {
+      @Override public void execute() {
+        try {
+          writeSynReset(streamId, errorCode);
+        } catch (IOException ignored) {
+        }
+      }
+    });
   }
 
-  void writeSynReset(int streamId, int statusCode) throws IOException {
-    spdyWriter.rstStream(streamId, statusCode);
+  void writeSynReset(int streamId, ErrorCode statusCode) throws IOException {
+    frameWriter.rstStream(streamId, statusCode);
   }
 
   void writeWindowUpdateLater(final int streamId, final int deltaWindowSize) {
-    executor.submit(
-        new NamedRunnable(String.format("Spdy Writer %s stream %d", hostName, streamId)) {
-          @Override public void execute() {
-            try {
-              writeWindowUpdate(streamId, deltaWindowSize);
-            } catch (IOException ignored) {
-            }
-          }
-        });
+    executor.submit(new NamedRunnable("OkHttp SPDY Writer %s stream %d", hostName, streamId) {
+      @Override public void execute() {
+        try {
+          writeWindowUpdate(streamId, deltaWindowSize);
+        } catch (IOException ignored) {
+        }
+      }
+    });
   }
 
   void writeWindowUpdate(int streamId, int deltaWindowSize) throws IOException {
-    spdyWriter.windowUpdate(streamId, deltaWindowSize);
+    frameWriter.windowUpdate(streamId, deltaWindowSize);
   }
 
   /**
@@ -249,26 +234,28 @@ public final class SpdyConnection implements Closeable {
       if (pings == null) pings = new HashMap<Integer, Ping>();
       pings.put(pingId, ping);
     }
-    writePing(pingId, ping);
+    writePing(false, pingId, 0x4f4b6f6b /* ASCII "OKok" */, ping);
     return ping;
   }
 
-  private void writePingLater(final int streamId, final Ping ping) {
-    executor.submit(new NamedRunnable(String.format("Spdy Writer %s ping %d", hostName, streamId)) {
+  private void writePingLater(
+      final boolean reply, final int payload1, final int payload2, final Ping ping) {
+    executor.submit(new NamedRunnable("OkHttp SPDY Writer %s ping %08x%08x",
+        hostName, payload1, payload2) {
       @Override public void execute() {
         try {
-          writePing(streamId, ping);
+          writePing(reply, payload1, payload2, ping);
         } catch (IOException ignored) {
         }
       }
     });
   }
 
-  private void writePing(int id, Ping ping) throws IOException {
-    synchronized (spdyWriter) {
+  private void writePing(boolean reply, int payload1, int payload2, Ping ping) throws IOException {
+    synchronized (frameWriter) {
       // Observe the sent time immediately before performing I/O.
       if (ping != null) ping.send();
-      spdyWriter.ping(0, id);
+      frameWriter.ping(reply, payload1, payload2);
     }
   }
 
@@ -278,13 +265,11 @@ public final class SpdyConnection implements Closeable {
 
   /** Sends a noop frame to the peer. */
   public void noop() throws IOException {
-    spdyWriter.noop();
+    frameWriter.noop();
   }
 
   public void flush() throws IOException {
-    synchronized (spdyWriter) {
-      spdyWriter.out.flush();
-    }
+    frameWriter.flush();
   }
 
   /**
@@ -292,12 +277,9 @@ public final class SpdyConnection implements Closeable {
    * locally, nor accepted from the remote peer. Existing streams are not
    * impacted. This is intended to permit an endpoint to gracefully stop
    * accepting new requests without harming previously established streams.
-   *
-   * @param statusCode one of {@link #GOAWAY_OK}, {@link
-   * #GOAWAY_INTERNAL_ERROR} or {@link #GOAWAY_PROTOCOL_ERROR}.
    */
-  public void shutdown(int statusCode) throws IOException {
-    synchronized (spdyWriter) {
+  public void shutdown(ErrorCode statusCode) throws IOException {
+    synchronized (frameWriter) {
       int lastGoodStreamId;
       synchronized (this) {
         if (shutdown) {
@@ -306,7 +288,7 @@ public final class SpdyConnection implements Closeable {
         shutdown = true;
         lastGoodStreamId = this.lastGoodStreamId;
       }
-      spdyWriter.goAway(0, lastGoodStreamId, statusCode);
+      frameWriter.goAway(lastGoodStreamId, statusCode);
     }
   }
 
@@ -316,14 +298,14 @@ public final class SpdyConnection implements Closeable {
    * internal executor services.
    */
   @Override public void close() throws IOException {
-    close(GOAWAY_OK, SpdyStream.RST_CANCEL);
+    close(ErrorCode.NO_ERROR, ErrorCode.CANCEL);
   }
 
-  private void close(int shutdownStatusCode, int rstStatusCode) throws IOException {
+  private void close(ErrorCode connectionCode, ErrorCode streamCode) throws IOException {
     assert (!Thread.holdsLock(this));
     IOException thrown = null;
     try {
-      shutdown(shutdownStatusCode);
+      shutdown(connectionCode);
     } catch (IOException e) {
       thrown = e;
     }
@@ -345,7 +327,7 @@ public final class SpdyConnection implements Closeable {
     if (streamsToClose != null) {
       for (SpdyStream stream : streamsToClose) {
         try {
-          stream.close(rstStatusCode);
+          stream.close(streamCode);
         } catch (IOException e) {
           if (thrown != null) thrown = e;
         }
@@ -359,12 +341,12 @@ public final class SpdyConnection implements Closeable {
     }
 
     try {
-      spdyReader.close();
+      frameReader.close();
     } catch (IOException e) {
       thrown = e;
     }
     try {
-      spdyWriter.close();
+      frameWriter.close();
     } catch (IOException e) {
       if (thrown == null) thrown = e;
     }
@@ -372,12 +354,30 @@ public final class SpdyConnection implements Closeable {
     if (thrown != null) throw thrown;
   }
 
+  /**
+   * Sends a connection header if the current variant requires it. This should
+   * be called after {@link Builder#build} for all new connections.
+   */
+  public void sendConnectionHeader() throws IOException {
+    frameWriter.connectionHeader();
+    frameWriter.settings(new Settings());
+  }
+
+  /**
+   * Reads a connection header if the current variant requires it. This should
+   * be called after {@link Builder#build} for all new connections.
+   */
+  public void readConnectionHeader() throws IOException {
+    frameReader.readConnectionHeader();
+  }
+
   public static class Builder {
     private String hostName;
     private InputStream in;
     private OutputStream out;
     private IncomingStreamHandler handler = IncomingStreamHandler.REFUSE_INCOMING_STREAMS;
-    public boolean client;
+    private Variant variant = Variant.SPDY3;
+    private boolean client;
 
     public Builder(boolean client, Socket socket) throws IOException {
       this("", client, socket.getInputStream(), socket.getOutputStream());
@@ -411,110 +411,119 @@ public final class SpdyConnection implements Closeable {
       return this;
     }
 
+    public Builder spdy3() {
+      this.variant = Variant.SPDY3;
+      return this;
+    }
+
+    public Builder http20Draft06() {
+      this.variant = Variant.HTTP_20_DRAFT_06;
+      return this;
+    }
+
     public SpdyConnection build() {
       return new SpdyConnection(this);
     }
   }
 
-  private class Reader implements Runnable, SpdyReader.Handler {
+  private class Reader implements Runnable, FrameReader.Handler {
     @Override public void run() {
-      int shutdownStatusCode = GOAWAY_INTERNAL_ERROR;
-      int rstStatusCode = SpdyStream.RST_INTERNAL_ERROR;
+      ErrorCode connectionErrorCode = ErrorCode.INTERNAL_ERROR;
+      ErrorCode streamErrorCode = ErrorCode.INTERNAL_ERROR;
       try {
-        while (spdyReader.nextFrame(this)) {
+        while (frameReader.nextFrame(this)) {
         }
-        shutdownStatusCode = GOAWAY_OK;
-        rstStatusCode = SpdyStream.RST_CANCEL;
+        connectionErrorCode = ErrorCode.NO_ERROR;
+        streamErrorCode = ErrorCode.CANCEL;
       } catch (IOException e) {
-        shutdownStatusCode = GOAWAY_PROTOCOL_ERROR;
-        rstStatusCode = SpdyStream.RST_PROTOCOL_ERROR;
+        connectionErrorCode = ErrorCode.PROTOCOL_ERROR;
+        streamErrorCode = ErrorCode.PROTOCOL_ERROR;
       } finally {
         try {
-          close(shutdownStatusCode, rstStatusCode);
+          close(connectionErrorCode, streamErrorCode);
         } catch (IOException ignored) {
         }
       }
     }
 
-    @Override public void data(int flags, int streamId, InputStream in, int length)
+    @Override public void data(boolean inFinished, int streamId, InputStream in, int length)
         throws IOException {
       SpdyStream dataStream = getStream(streamId);
       if (dataStream == null) {
-        writeSynResetLater(streamId, SpdyStream.RST_INVALID_STREAM);
+        writeSynResetLater(streamId, ErrorCode.INVALID_STREAM);
         Util.skipByReading(in, length);
         return;
       }
       dataStream.receiveData(in, length);
-      if ((flags & SpdyConnection.FLAG_FIN) != 0) {
+      if (inFinished) {
         dataStream.receiveFin();
       }
     }
 
-    @Override
-    public void synStream(int flags, int streamId, int associatedStreamId, int priority, int slot,
-        List<String> nameValueBlock) {
-      final SpdyStream synStream;
-      final SpdyStream previous;
+    @Override public void headers(boolean outFinished, boolean inFinished, int streamId,
+        int associatedStreamId, int priority, List<String> nameValueBlock,
+        HeadersMode headersMode) {
+      SpdyStream stream;
       synchronized (SpdyConnection.this) {
-        synStream =
-            new SpdyStream(streamId, SpdyConnection.this, flags, priority, slot, nameValueBlock,
-                settings);
-        if (shutdown) {
-          return;
-        }
-        lastGoodStreamId = streamId;
-        previous = streams.put(streamId, synStream);
-      }
-      if (previous != null) {
-        previous.closeLater(SpdyStream.RST_PROTOCOL_ERROR);
-        removeStream(streamId);
-        return;
-      }
+        // If we're shutdown, don't bother with this stream.
+        if (shutdown) return;
+
+        stream = getStream(streamId);
 
-      executor.submit(
-          new NamedRunnable(String.format("Callback %s stream %d", hostName, streamId)) {
-        @Override public void execute() {
-          try {
-            handler.receive(synStream);
-          } catch (IOException e) {
-            throw new RuntimeException(e);
+        if (stream == null) {
+          // The headers claim to be for an existing stream, but we don't have one.
+          if (headersMode.failIfStreamAbsent()) {
+            writeSynResetLater(streamId, ErrorCode.INVALID_STREAM);
+            return;
           }
+
+          // If the stream ID is less than the last created ID, assume it's already closed.
+          if (streamId <= lastGoodStreamId) return;
+
+          // If the stream ID is in the client's namespace, assume it's already closed.
+          if (streamId % 2 == nextStreamId % 2) return;
+
+          // Create a stream.
+          final SpdyStream newStream = new SpdyStream(streamId, SpdyConnection.this, outFinished,
+              inFinished, priority, nameValueBlock, settings);
+          lastGoodStreamId = streamId;
+          streams.put(streamId, newStream);
+          executor.submit(new NamedRunnable("OkHttp Callback %s stream %d", hostName, streamId) {
+            @Override public void execute() {
+              try {
+                handler.receive(newStream);
+              } catch (IOException e) {
+                throw new RuntimeException(e);
+              }
+            }
+          });
+          return;
         }
-      });
-    }
+      }
 
-    @Override public void synReply(int flags, int streamId, List<String> nameValueBlock)
-        throws IOException {
-      SpdyStream replyStream = getStream(streamId);
-      if (replyStream == null) {
-        writeSynResetLater(streamId, SpdyStream.RST_INVALID_STREAM);
+      // The headers claim to be for a new stream, but we already have one.
+      if (headersMode.failIfStreamPresent()) {
+        stream.closeLater(ErrorCode.PROTOCOL_ERROR);
+        removeStream(streamId);
         return;
       }
-      replyStream.receiveReply(nameValueBlock);
-      if ((flags & SpdyConnection.FLAG_FIN) != 0) {
-        replyStream.receiveFin();
-      }
-    }
 
-    @Override public void headers(int flags, int streamId, List<String> nameValueBlock)
-        throws IOException {
-      SpdyStream replyStream = getStream(streamId);
-      if (replyStream != null) {
-        replyStream.receiveHeaders(nameValueBlock);
-      }
+      // Update an existing stream.
+      stream.receiveHeaders(nameValueBlock, headersMode);
+      if (inFinished) stream.receiveFin();
     }
 
-    @Override public void rstStream(int flags, int streamId, int statusCode) {
+    @Override public void rstStream(int streamId, ErrorCode errorCode) {
       SpdyStream rstStream = removeStream(streamId);
       if (rstStream != null) {
-        rstStream.receiveRstStream(statusCode);
+        rstStream.receiveRstStream(errorCode);
       }
     }
 
-    @Override public void settings(int flags, Settings newSettings) {
+    @Override public void settings(boolean clearPrevious, Settings newSettings) {
       SpdyStream[] streamsToNotify = null;
       synchronized (SpdyConnection.this) {
-        if (settings == null || (flags & Settings.FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS) != 0) {
+        if (settings == null || clearPrevious) {
           settings = newSettings;
         } else {
           settings.merge(newSettings);
@@ -528,8 +537,9 @@ public final class SpdyConnection implements Closeable {
           // The synchronization here is ugly. We need to synchronize on 'this' to guard
           // reads to 'settings'. We synchronize on 'stream' to guard the state change.
           // And we need to acquire the 'stream' lock first, since that may block.
+          // TODO: this can block the reader thread until a write completes. That's bad!
           synchronized (stream) {
-            synchronized (this) {
+            synchronized (SpdyConnection.this) {
               stream.receiveSettings(settings);
             }
           }
@@ -540,19 +550,19 @@ public final class SpdyConnection implements Closeable {
     @Override public void noop() {
     }
 
-    @Override public void ping(int flags, int streamId) {
-      if (client != (streamId % 2 == 1)) {
-        // Respond to a client ping if this is a server and vice versa.
-        writePingLater(streamId, null);
-      } else {
-        Ping ping = removePing(streamId);
+    @Override public void ping(boolean reply, int payload1, int payload2) {
+      if (reply) {
+        Ping ping = removePing(payload1);
         if (ping != null) {
           ping.receive();
         }
+      } else {
+        // Send a reply to a client ping if this is a server and vice versa.
+        writePingLater(true, payload1, payload2, null);
       }
     }
 
-    @Override public void goAway(int flags, int lastGoodStreamId, int statusCode) {
+    @Override public void goAway(int lastGoodStreamId, ErrorCode errorCode) {
       synchronized (SpdyConnection.this) {
         shutdown = true;
 
@@ -562,18 +572,28 @@ public final class SpdyConnection implements Closeable {
           Map.Entry<Integer, SpdyStream> entry = i.next();
           int streamId = entry.getKey();
           if (streamId > lastGoodStreamId && entry.getValue().isLocallyInitiated()) {
-            entry.getValue().receiveRstStream(SpdyStream.RST_REFUSED_STREAM);
+            entry.getValue().receiveRstStream(ErrorCode.REFUSED_STREAM);
             i.remove();
           }
         }
       }
     }
 
-    @Override public void windowUpdate(int flags, int streamId, int deltaWindowSize) {
+    @Override public void windowUpdate(int streamId, int deltaWindowSize, boolean endFlowControl) {
+      if (streamId == 0) {
+        // TODO: honor whole-stream flow control
+        return;
+      }
+
+      // TODO: honor endFlowControl
       SpdyStream stream = getStream(streamId);
       if (stream != null) {
         stream.receiveWindowUpdate(deltaWindowSize);
       }
     }
+
+    @Override public void priority(int streamId, int priority) {
+      // TODO: honor priority.
+    }
   }
 }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/spdy/SpdyReader.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/SpdyReader.java b/framework/src/com/squareup/okhttp/internal/spdy/SpdyReader.java
deleted file mode 100644
index 7d3f2bd..0000000
--- a/framework/src/com/squareup/okhttp/internal/spdy/SpdyReader.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * 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.internal.spdy;
-
-import com.squareup.okhttp.internal.Util;
-import java.io.Closeable;
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.ProtocolException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.logging.Logger;
-import java.util.zip.DataFormatException;
-import java.util.zip.Inflater;
-import java.util.zip.InflaterInputStream;
-
-/** Read spdy/3 frames. */
-final class SpdyReader implements Closeable {
-  static final byte[] DICTIONARY;
-  static {
-    try {
-      DICTIONARY = ("\u0000\u0000\u0000\u0007options\u0000\u0000\u0000\u0004hea"
-          + "d\u0000\u0000\u0000\u0004post\u0000\u0000\u0000\u0003put\u0000\u0000\u0000\u0006dele"
-          + "te\u0000\u0000\u0000\u0005trace\u0000\u0000\u0000\u0006accept\u0000\u0000\u0000"
-          + "\u000Eaccept-charset\u0000\u0000\u0000\u000Faccept-encoding\u0000\u0000\u0000\u000Fa"
-          + "ccept-language\u0000\u0000\u0000\raccept-ranges\u0000\u0000\u0000\u0003age\u0000"
-          + "\u0000\u0000\u0005allow\u0000\u0000\u0000\rauthorization\u0000\u0000\u0000\rcache-co"
-          + "ntrol\u0000\u0000\u0000\nconnection\u0000\u0000\u0000\fcontent-base\u0000\u0000"
-          + "\u0000\u0010content-encoding\u0000\u0000\u0000\u0010content-language\u0000\u0000"
-          + "\u0000\u000Econtent-length\u0000\u0000\u0000\u0010content-location\u0000\u0000\u0000"
-          + "\u000Bcontent-md5\u0000\u0000\u0000\rcontent-range\u0000\u0000\u0000\fcontent-type"
-          + "\u0000\u0000\u0000\u0004date\u0000\u0000\u0000\u0004etag\u0000\u0000\u0000\u0006expe"
-          + "ct\u0000\u0000\u0000\u0007expires\u0000\u0000\u0000\u0004from\u0000\u0000\u0000"
-          + "\u0004host\u0000\u0000\u0000\bif-match\u0000\u0000\u0000\u0011if-modified-since"
-          + "\u0000\u0000\u0000\rif-none-match\u0000\u0000\u0000\bif-range\u0000\u0000\u0000"
-          + "\u0013if-unmodified-since\u0000\u0000\u0000\rlast-modified\u0000\u0000\u0000\blocati"
-          + "on\u0000\u0000\u0000\fmax-forwards\u0000\u0000\u0000\u0006pragma\u0000\u0000\u0000"
-          + "\u0012proxy-authenticate\u0000\u0000\u0000\u0013proxy-authorization\u0000\u0000"
-          + "\u0000\u0005range\u0000\u0000\u0000\u0007referer\u0000\u0000\u0000\u000Bretry-after"
-          + "\u0000\u0000\u0000\u0006server\u0000\u0000\u0000\u0002te\u0000\u0000\u0000\u0007trai"
-          + "ler\u0000\u0000\u0000\u0011transfer-encoding\u0000\u0000\u0000\u0007upgrade\u0000"
-          + "\u0000\u0000\nuser-agent\u0000\u0000\u0000\u0004vary\u0000\u0000\u0000\u0003via"
-          + "\u0000\u0000\u0000\u0007warning\u0000\u0000\u0000\u0010www-authenticate\u0000\u0000"
-          + "\u0000\u0006method\u0000\u0000\u0000\u0003get\u0000\u0000\u0000\u0006status\u0000"
-          + "\u0000\u0000\u0006200 OK\u0000\u0000\u0000\u0007version\u0000\u0000\u0000\bHTTP/1.1"
-          + "\u0000\u0000\u0000\u0003url\u0000\u0000\u0000\u0006public\u0000\u0000\u0000\nset-coo"
-          + "kie\u0000\u0000\u0000\nkeep-alive\u0000\u0000\u0000\u0006origin100101201202205206300"
-          + "302303304305306307402405406407408409410411412413414415416417502504505203 Non-Authori"
-          + "tative Information204 No Content301 Moved Permanently400 Bad Request401 Unauthorized"
-          + "403 Forbidden404 Not Found500 Internal Server Error501 Not Implemented503 Service Un"
-          + "availableJan Feb Mar Apr May Jun Jul Aug Sept Oct Nov Dec 00:00:00 Mon, Tue, Wed, Th"
-          + "u, Fri, Sat, Sun, GMTchunked,text/html,image/png,image/jpg,image/gif,application/xml"
-          + ",application/xhtml+xml,text/plain,text/javascript,publicprivatemax-age=gzip,deflate,"
-          + "sdchcharset=utf-8charset=iso-8859-1,utf-,*,enq=0.").getBytes(Util.UTF_8.name());
-    } catch (UnsupportedEncodingException e) {
-      throw new AssertionError();
-    }
-  }
-
-  private final DataInputStream in;
-  private final DataInputStream nameValueBlockIn;
-  private int compressedLimit;
-
-  SpdyReader(InputStream in) {
-    this.in = new DataInputStream(in);
-    this.nameValueBlockIn = newNameValueBlockStream();
-  }
-
-  /**
-   * Send the next frame to {@code handler}. Returns true unless there are no
-   * more frames on the stream.
-   */
-  public boolean nextFrame(Handler handler) throws IOException {
-    int w1;
-    try {
-      w1 = in.readInt();
-    } catch (IOException e) {
-      return false; // This might be a normal socket close.
-    }
-    int w2 = in.readInt();
-
-    boolean control = (w1 & 0x80000000) != 0;
-    int flags = (w2 & 0xff000000) >>> 24;
-    int length = (w2 & 0xffffff);
-
-    if (control) {
-      int version = (w1 & 0x7fff0000) >>> 16;
-      int type = (w1 & 0xffff);
-
-      if (version != 3) {
-        throw new ProtocolException("version != 3: " + version);
-      }
-
-      switch (type) {
-        case SpdyConnection.TYPE_SYN_STREAM:
-          readSynStream(handler, flags, length);
-          return true;
-
-        case SpdyConnection.TYPE_SYN_REPLY:
-          readSynReply(handler, flags, length);
-          return true;
-
-        case SpdyConnection.TYPE_RST_STREAM:
-          readRstStream(handler, flags, length);
-          return true;
-
-        case SpdyConnection.TYPE_SETTINGS:
-          readSettings(handler, flags, length);
-          return true;
-
-        case SpdyConnection.TYPE_NOOP:
-          if (length != 0) throw ioException("TYPE_NOOP length: %d != 0", length);
-          handler.noop();
-          return true;
-
-        case SpdyConnection.TYPE_PING:
-          readPing(handler, flags, length);
-          return true;
-
-        case SpdyConnection.TYPE_GOAWAY:
-          readGoAway(handler, flags, length);
-          return true;
-
-        case SpdyConnection.TYPE_HEADERS:
-          readHeaders(handler, flags, length);
-          return true;
-
-        case SpdyConnection.TYPE_WINDOW_UPDATE:
-          readWindowUpdate(handler, flags, length);
-          return true;
-
-        case SpdyConnection.TYPE_CREDENTIAL:
-          Util.skipByReading(in, length);
-          throw new UnsupportedOperationException("TODO"); // TODO: implement
-
-        default:
-          throw new IOException("Unexpected frame");
-      }
-    } else {
-      int streamId = w1 & 0x7fffffff;
-      handler.data(flags, streamId, in, length);
-      return true;
-    }
-  }
-
-  private void readSynStream(Handler handler, int flags, int length) throws IOException {
-    int w1 = in.readInt();
-    int w2 = in.readInt();
-    int s3 = in.readShort();
-    int streamId = w1 & 0x7fffffff;
-    int associatedStreamId = w2 & 0x7fffffff;
-    int priority = (s3 & 0xe000) >>> 13;
-    int slot = s3 & 0xff;
-    List<String> nameValueBlock = readNameValueBlock(length - 10);
-    handler.synStream(flags, streamId, associatedStreamId, priority, slot, nameValueBlock);
-  }
-
-  private void readSynReply(Handler handler, int flags, int length) throws IOException {
-    int w1 = in.readInt();
-    int streamId = w1 & 0x7fffffff;
-    List<String> nameValueBlock = readNameValueBlock(length - 4);
-    handler.synReply(flags, streamId, nameValueBlock);
-  }
-
-  private void readRstStream(Handler handler, int flags, int length) throws IOException {
-    if (length != 8) throw ioException("TYPE_RST_STREAM length: %d != 8", length);
-    int streamId = in.readInt() & 0x7fffffff;
-    int statusCode = in.readInt();
-    handler.rstStream(flags, streamId, statusCode);
-  }
-
-  private void readHeaders(Handler handler, int flags, int length) throws IOException {
-    int w1 = in.readInt();
-    int streamId = w1 & 0x7fffffff;
-    List<String> nameValueBlock = readNameValueBlock(length - 4);
-    handler.headers(flags, streamId, nameValueBlock);
-  }
-
-  private void readWindowUpdate(Handler handler, int flags, int length) throws IOException {
-    if (length != 8) throw ioException("TYPE_WINDOW_UPDATE length: %d != 8", length);
-    int w1 = in.readInt();
-    int w2 = in.readInt();
-    int streamId = w1 & 0x7fffffff;
-    int deltaWindowSize = w2 & 0x7fffffff;
-    handler.windowUpdate(flags, streamId, deltaWindowSize);
-  }
-
-  private DataInputStream newNameValueBlockStream() {
-    // Limit the inflater input stream to only those bytes in the Name/Value block.
-    final InputStream throttleStream = new InputStream() {
-      @Override public int read() throws IOException {
-        return Util.readSingleByte(this);
-      }
-
-      @Override public int read(byte[] buffer, int offset, int byteCount) throws IOException {
-        byteCount = Math.min(byteCount, compressedLimit);
-        int consumed = in.read(buffer, offset, byteCount);
-        compressedLimit -= consumed;
-        return consumed;
-      }
-
-      @Override public void close() throws IOException {
-        in.close();
-      }
-    };
-
-    // Subclass inflater to install a dictionary when it's needed.
-    Inflater inflater = new Inflater() {
-      @Override
-      public int inflate(byte[] buffer, int offset, int count) throws DataFormatException {
-        int result = super.inflate(buffer, offset, count);
-        if (result == 0 && needsDictionary()) {
-          setDictionary(DICTIONARY);
-          result = super.inflate(buffer, offset, count);
-        }
-        return result;
-      }
-    };
-
-    return new DataInputStream(new InflaterInputStream(throttleStream, inflater));
-  }
-
-  private List<String> readNameValueBlock(int length) throws IOException {
-    this.compressedLimit += length;
-    try {
-      int numberOfPairs = nameValueBlockIn.readInt();
-      if (numberOfPairs < 0) {
-        Logger.getLogger(getClass().getName()).warning("numberOfPairs < 0: " + numberOfPairs);
-        throw ioException("numberOfPairs < 0");
-      }
-      List<String> entries = new ArrayList<String>(numberOfPairs * 2);
-      for (int i = 0; i < numberOfPairs; i++) {
-        String name = readString();
-        String values = readString();
-        if (name.length() == 0) throw ioException("name.length == 0");
-        if (values.length() == 0) throw ioException("values.length == 0");
-        entries.add(name);
-        entries.add(values);
-      }
-
-      if (compressedLimit != 0) {
-        Logger.getLogger(getClass().getName()).warning("compressedLimit > 0: " + compressedLimit);
-      }
-
-      return entries;
-    } catch (DataFormatException e) {
-      throw new IOException(e.getMessage());
-    }
-  }
-
-  private String readString() throws DataFormatException, IOException {
-    int length = nameValueBlockIn.readInt();
-    byte[] bytes = new byte[length];
-    Util.readFully(nameValueBlockIn, bytes);
-    return new String(bytes, 0, length, "UTF-8");
-  }
-
-  private void readPing(Handler handler, int flags, int length) throws IOException {
-    if (length != 4) throw ioException("TYPE_PING length: %d != 4", length);
-    int id = in.readInt();
-    handler.ping(flags, id);
-  }
-
-  private void readGoAway(Handler handler, int flags, int length) throws IOException {
-    if (length != 8) throw ioException("TYPE_GOAWAY length: %d != 8", length);
-    int lastGoodStreamId = in.readInt() & 0x7fffffff;
-    int statusCode = in.readInt();
-    handler.goAway(flags, lastGoodStreamId, statusCode);
-  }
-
-  private void readSettings(Handler handler, int flags, int length) throws IOException {
-    int numberOfEntries = in.readInt();
-    if (length != 4 + 8 * numberOfEntries) {
-      throw ioException("TYPE_SETTINGS length: %d != 4 + 8 * %d", length, numberOfEntries);
-    }
-    Settings settings = new Settings();
-    for (int i = 0; i < numberOfEntries; i++) {
-      int w1 = in.readInt();
-      int value = in.readInt();
-      int idFlags = (w1 & 0xff000000) >>> 24;
-      int id = w1 & 0xffffff;
-      settings.set(id, idFlags, value);
-    }
-    handler.settings(flags, settings);
-  }
-
-  private static IOException ioException(String message, Object... args) throws IOException {
-    throw new IOException(String.format(message, args));
-  }
-
-  @Override public void close() throws IOException {
-    Util.closeAll(in, nameValueBlockIn);
-  }
-
-  public interface Handler {
-    void data(int flags, int streamId, InputStream in, int length) throws IOException;
-
-    void synStream(int flags, int streamId, int associatedStreamId, int priority, int slot,
-        List<String> nameValueBlock);
-
-    void synReply(int flags, int streamId, List<String> nameValueBlock) throws IOException;
-    void headers(int flags, int streamId, List<String> nameValueBlock) throws IOException;
-    void rstStream(int flags, int streamId, int statusCode);
-    void settings(int flags, Settings settings);
-    void noop();
-    void ping(int flags, int streamId);
-    void goAway(int flags, int lastGoodStreamId, int statusCode);
-    void windowUpdate(int flags, int streamId, int deltaWindowSize);
-  }
-}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/spdy/SpdyStream.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/SpdyStream.java b/framework/src/com/squareup/okhttp/internal/spdy/SpdyStream.java
old mode 100644
new mode 100755
index 744a04e..a3ab3a4
--- a/framework/src/com/squareup/okhttp/internal/spdy/SpdyStream.java
+++ b/framework/src/com/squareup/okhttp/internal/spdy/SpdyStream.java
@@ -26,8 +26,6 @@ import java.util.ArrayList;
 import java.util.List;
 
 import static com.squareup.okhttp.internal.Util.checkOffsetAndCount;
-import static com.squareup.okhttp.internal.Util.pokeInt;
-import static java.nio.ByteOrder.BIG_ENDIAN;
 
 /** A logical bidirectional stream. */
 public final class SpdyStream {
@@ -35,35 +33,6 @@ public final class SpdyStream {
   // Internal state is guarded by this. No long-running or potentially
   // blocking operations are performed while the lock is held.
 
-  private static final int DATA_FRAME_HEADER_LENGTH = 8;
-
-  private static final String[] STATUS_CODE_NAMES = {
-      null,
-      "PROTOCOL_ERROR",
-      "INVALID_STREAM",
-      "REFUSED_STREAM",
-      "UNSUPPORTED_VERSION",
-      "CANCEL",
-      "INTERNAL_ERROR",
-      "FLOW_CONTROL_ERROR",
-      "STREAM_IN_USE",
-      "STREAM_ALREADY_CLOSED",
-      "INVALID_CREDENTIALS",
-      "FRAME_TOO_LARGE"
-  };
-
-  public static final int RST_PROTOCOL_ERROR = 1;
-  public static final int RST_INVALID_STREAM = 2;
-  public static final int RST_REFUSED_STREAM = 3;
-  public static final int RST_UNSUPPORTED_VERSION = 4;
-  public static final int RST_CANCEL = 5;
-  public static final int RST_INTERNAL_ERROR = 6;
-  public static final int RST_FLOW_CONTROL_ERROR = 7;
-  public static final int RST_STREAM_IN_USE = 8;
-  public static final int RST_STREAM_ALREADY_CLOSED = 9;
-  public static final int RST_INVALID_CREDENTIALS = 10;
-  public static final int RST_FRAME_TOO_LARGE = 11;
-
   /**
    * The number of unacknowledged bytes at which the input stream will send
    * the peer a {@code WINDOW_UPDATE} frame. Must be less than this client's
@@ -75,7 +44,6 @@ public final class SpdyStream {
   private final int id;
   private final SpdyConnection connection;
   private final int priority;
-  private final int slot;
   private long readTimeoutMillis = 0;
   private int writeWindowSize;
 
@@ -93,28 +61,19 @@ public final class SpdyStream {
    * reasons to abnormally close this stream (such as both peers closing it
    * near-simultaneously) then this is the first reason known to this peer.
    */
-  private int rstStatusCode = -1;
+  private ErrorCode errorCode = null;
 
-  SpdyStream(int id, SpdyConnection connection, int flags, int priority, int slot,
-      List<String> requestHeaders, Settings settings) {
+  SpdyStream(int id, SpdyConnection connection, boolean outFinished, boolean inFinished,
+      int priority, List<String> requestHeaders, Settings settings) {
     if (connection == null) throw new NullPointerException("connection == null");
     if (requestHeaders == null) throw new NullPointerException("requestHeaders == null");
     this.id = id;
     this.connection = connection;
+    this.in.finished = inFinished;
+    this.out.finished = outFinished;
     this.priority = priority;
-    this.slot = slot;
     this.requestHeaders = requestHeaders;
 
-    if (isLocallyInitiated()) {
-      // I am the sender
-      in.finished = (flags & SpdyConnection.FLAG_UNIDIRECTIONAL) != 0;
-      out.finished = (flags & SpdyConnection.FLAG_FIN) != 0;
-    } else {
-      // I am the receiver
-      in.finished = (flags & SpdyConnection.FLAG_FIN) != 0;
-      out.finished = (flags & SpdyConnection.FLAG_UNIDIRECTIONAL) != 0;
-    }
-
     setSettings(settings);
   }
 
@@ -129,7 +88,7 @@ public final class SpdyStream {
    * reports itself as not open. This is because input data is buffered.
    */
   public synchronized boolean isOpen() {
-    if (rstStatusCode != -1) {
+    if (errorCode != null) {
       return false;
     }
     if ((in.finished || in.closed) && (out.finished || out.closed) && responseHeaders != null) {
@@ -157,14 +116,28 @@ public final class SpdyStream {
    * have not been received yet.
    */
   public synchronized List<String> getResponseHeaders() throws IOException {
+    long remaining = 0;
+    long start = 0;
+    if (readTimeoutMillis != 0) {
+      start = (System.nanoTime() / 1000000);
+      remaining = readTimeoutMillis;
+    }
     try {
-      while (responseHeaders == null && rstStatusCode == -1) {
-        wait();
+      while (responseHeaders == null && errorCode == null) {
+        if (readTimeoutMillis == 0) { // No timeout configured.
+          wait();
+        } else if (remaining > 0) {
+          wait(remaining);
+          remaining = start + readTimeoutMillis - (System.nanoTime() / 1000000);
+        } else {
+          throw new SocketTimeoutException("Read response header timeout. readTimeoutMillis: "
+                            + readTimeoutMillis);
+        }
       }
       if (responseHeaders != null) {
         return responseHeaders;
       }
-      throw new IOException("stream was reset: " + rstStatusString());
+      throw new IOException("stream was reset: " + errorCode);
     } catch (InterruptedException e) {
       InterruptedIOException rethrow = new InterruptedIOException();
       rethrow.initCause(e);
@@ -173,15 +146,11 @@ public final class SpdyStream {
   }
 
   /**
-   * Returns the reason why this stream was closed, or -1 if it closed
-   * normally or has not yet been closed. Valid reasons are {@link
-   * #RST_PROTOCOL_ERROR}, {@link #RST_INVALID_STREAM}, {@link
-   * #RST_REFUSED_STREAM}, {@link #RST_UNSUPPORTED_VERSION}, {@link
-   * #RST_CANCEL}, {@link #RST_INTERNAL_ERROR} and {@link
-   * #RST_FLOW_CONTROL_ERROR}.
+   * Returns the reason why this stream was closed, or null if it closed
+   * normally or has not yet been closed.
    */
-  public synchronized int getRstStatusCode() {
-    return rstStatusCode;
+  public synchronized ErrorCode getErrorCode() {
+    return errorCode;
   }
 
   /**
@@ -192,7 +161,7 @@ public final class SpdyStream {
    */
   public void reply(List<String> responseHeaders, boolean out) throws IOException {
     assert (!Thread.holdsLock(SpdyStream.this));
-    int flags = 0;
+    boolean outFinished = false;
     synchronized (this) {
       if (responseHeaders == null) {
         throw new NullPointerException("responseHeaders == null");
@@ -206,10 +175,10 @@ public final class SpdyStream {
       this.responseHeaders = responseHeaders;
       if (!out) {
         this.out.finished = true;
-        flags |= SpdyConnection.FLAG_FIN;
+        outFinished = true;
       }
     }
-    connection.writeSynReply(id, flags, responseHeaders);
+    connection.writeSynReply(id, outFinished, responseHeaders);
   }
 
   /**
@@ -248,7 +217,7 @@ public final class SpdyStream {
    * Abnormally terminate this stream. This blocks until the {@code RST_STREAM}
    * frame has been transmitted.
    */
-  public void close(int rstStatusCode) throws IOException {
+  public void close(ErrorCode rstStatusCode) throws IOException {
     if (!closeInternal(rstStatusCode)) {
       return; // Already closed.
     }
@@ -259,68 +228,61 @@ public final class SpdyStream {
    * Abnormally terminate this stream. This enqueues a {@code RST_STREAM}
    * frame and returns immediately.
    */
-  public void closeLater(int rstStatusCode) {
-    if (!closeInternal(rstStatusCode)) {
+  public void closeLater(ErrorCode errorCode) {
+    if (!closeInternal(errorCode)) {
       return; // Already closed.
     }
-    connection.writeSynResetLater(id, rstStatusCode);
+    connection.writeSynResetLater(id, errorCode);
   }
 
   /** Returns true if this stream was closed. */
-  private boolean closeInternal(int rstStatusCode) {
+  private boolean closeInternal(ErrorCode errorCode) {
     assert (!Thread.holdsLock(this));
     synchronized (this) {
-      if (this.rstStatusCode != -1) {
+      if (this.errorCode != null) {
         return false;
       }
       if (in.finished && out.finished) {
         return false;
       }
-      this.rstStatusCode = rstStatusCode;
+      this.errorCode = errorCode;
       notifyAll();
     }
     connection.removeStream(id);
     return true;
   }
 
-  void receiveReply(List<String> strings) throws IOException {
+  void receiveHeaders(List<String> headers, HeadersMode headersMode) {
     assert (!Thread.holdsLock(SpdyStream.this));
-    boolean streamInUseError = false;
+    ErrorCode errorCode = null;
     boolean open = true;
     synchronized (this) {
-      if (isLocallyInitiated() && responseHeaders == null) {
-        responseHeaders = strings;
-        open = isOpen();
-        notifyAll();
+      if (responseHeaders == null) {
+        if (headersMode.failIfHeadersAbsent()) {
+          errorCode = ErrorCode.PROTOCOL_ERROR;
+        } else {
+          responseHeaders = headers;
+          open = isOpen();
+          notifyAll();
+        }
       } else {
-        streamInUseError = true;
+        if (headersMode.failIfHeadersPresent()) {
+          errorCode = ErrorCode.STREAM_IN_USE;
+        } else {
+          List<String> newHeaders = new ArrayList<String>();
+          newHeaders.addAll(responseHeaders);
+          newHeaders.addAll(headers);
+          this.responseHeaders = newHeaders;
+        }
       }
     }
-    if (streamInUseError) {
-      closeLater(SpdyStream.RST_STREAM_IN_USE);
+    if (errorCode != null) {
+      closeLater(errorCode);
     } else if (!open) {
       connection.removeStream(id);
     }
   }
 
-  void receiveHeaders(List<String> headers) throws IOException {
-    assert (!Thread.holdsLock(SpdyStream.this));
-    boolean protocolError = false;
-    synchronized (this) {
-      if (responseHeaders != null) {
-        List<String> newHeaders = new ArrayList<String>();
-        newHeaders.addAll(responseHeaders);
-        newHeaders.addAll(headers);
-        this.responseHeaders = newHeaders;
-      } else {
-        protocolError = true;
-      }
-    }
-    if (protocolError) {
-      closeLater(SpdyStream.RST_PROTOCOL_ERROR);
-    }
-  }
-
   void receiveData(InputStream in, int length) throws IOException {
     assert (!Thread.holdsLock(SpdyStream.this));
     this.in.receive(in, length);
@@ -339,18 +301,20 @@ public final class SpdyStream {
     }
   }
 
-  synchronized void receiveRstStream(int statusCode) {
-    if (rstStatusCode == -1) {
-      rstStatusCode = statusCode;
+  synchronized void receiveRstStream(ErrorCode errorCode) {
+    if (this.errorCode == null) {
+      this.errorCode = errorCode;
       notifyAll();
     }
   }
 
   private void setSettings(Settings settings) {
+    // TODO: For HTTP/2.0, also adjust the stream flow control window size
+    // by the difference between the new value and the old value.
     assert (Thread.holdsLock(connection)); // Because 'settings' is guarded by 'connection'.
-    this.writeWindowSize =
-        settings != null ? settings.getInitialWindowSize(Settings.DEFAULT_INITIAL_WINDOW_SIZE)
-            : Settings.DEFAULT_INITIAL_WINDOW_SIZE;
+    this.writeWindowSize = settings != null
+        ? settings.getInitialWindowSize(Settings.DEFAULT_INITIAL_WINDOW_SIZE)
+        : Settings.DEFAULT_INITIAL_WINDOW_SIZE;
   }
 
   void receiveSettings(Settings settings) {
@@ -364,19 +328,10 @@ public final class SpdyStream {
     notifyAll();
   }
 
-  private String rstStatusString() {
-    return rstStatusCode > 0 && rstStatusCode < STATUS_CODE_NAMES.length
-        ? STATUS_CODE_NAMES[rstStatusCode] : Integer.toString(rstStatusCode);
-  }
-
   int getPriority() {
     return priority;
   }
 
-  int getSlot() {
-    return slot;
-  }
-
   /**
    * An input stream that reads the incoming data frames of a stream. Although
    * this class uses synchronization to safely receive incoming data frames,
@@ -496,7 +451,7 @@ public final class SpdyStream {
         remaining = readTimeoutMillis;
       }
       try {
-        while (pos == -1 && !finished && !closed && rstStatusCode == -1) {
+        while (pos == -1 && !finished && !closed && errorCode == null) {
           if (readTimeoutMillis == 0) {
             SpdyStream.this.wait();
           } else if (remaining > 0) {
@@ -534,7 +489,7 @@ public final class SpdyStream {
       // If the peer sends more data than we can handle, discard it and close the connection.
       if (flowControlError) {
         Util.skipByReading(in, byteCount);
-        closeLater(SpdyStream.RST_FLOW_CONTROL_ERROR);
+        closeLater(ErrorCode.FLOW_CONTROL_ERROR);
         return;
       }
 
@@ -583,8 +538,8 @@ public final class SpdyStream {
       if (closed) {
         throw new IOException("stream closed");
       }
-      if (rstStatusCode != -1) {
-        throw new IOException("stream was reset: " + rstStatusString());
+      if (errorCode != null) {
+        throw new IOException("stream was reset: " + errorCode);
       }
     }
   }
@@ -602,7 +557,7 @@ public final class SpdyStream {
       // is safe because the input stream is closed (we won't use any
       // further bytes) and the output stream is either finished or closed
       // (so RSTing both streams doesn't cause harm).
-      SpdyStream.this.close(RST_CANCEL);
+      SpdyStream.this.close(ErrorCode.CANCEL);
     } else if (!open) {
       connection.removeStream(id);
     }
@@ -614,7 +569,7 @@ public final class SpdyStream {
    */
   private final class SpdyDataOutputStream extends OutputStream {
     private final byte[] buffer = new byte[8192];
-    private int pos = DATA_FRAME_HEADER_LENGTH;
+    private int pos = 0;
 
     /** True if the caller has closed this stream. */
     private boolean closed;
@@ -656,7 +611,7 @@ public final class SpdyStream {
     @Override public void flush() throws IOException {
       assert (!Thread.holdsLock(SpdyStream.this));
       checkNotClosed();
-      if (pos > DATA_FRAME_HEADER_LENGTH) {
+      if (pos > 0) {
         writeFrame(false);
         connection.flush();
       }
@@ -670,27 +625,23 @@ public final class SpdyStream {
         }
         closed = true;
       }
-      writeFrame(true);
+      if (!out.finished) {
+        writeFrame(true);
+      }
       connection.flush();
       cancelStreamIfNecessary();
     }
 
-    private void writeFrame(boolean last) throws IOException {
+    private void writeFrame(boolean outFinished) throws IOException {
       assert (!Thread.holdsLock(SpdyStream.this));
 
-      int length = pos - DATA_FRAME_HEADER_LENGTH;
+      int length = pos;
       synchronized (SpdyStream.this) {
-        waitUntilWritable(length, last);
+        waitUntilWritable(length, outFinished);
         unacknowledgedBytes += length;
       }
-      int flags = 0;
-      if (last) {
-        flags |= SpdyConnection.FLAG_FIN;
-      }
-      pokeInt(buffer, 0, id & 0x7fffffff, BIG_ENDIAN);
-      pokeInt(buffer, 4, (flags & 0xff) << 24 | length & 0xffffff, BIG_ENDIAN);
-      connection.writeFrame(buffer, 0, pos);
-      pos = DATA_FRAME_HEADER_LENGTH;
+      connection.writeData(id, outFinished, buffer, 0, pos);
+      pos = 0;
     }
 
     /**
@@ -709,8 +660,8 @@ public final class SpdyStream {
             throw new IOException("stream closed");
           } else if (finished) {
             throw new IOException("stream finished");
-          } else if (rstStatusCode != -1) {
-            throw new IOException("stream was reset: " + rstStatusString());
+          } else if (errorCode != null) {
+            throw new IOException("stream was reset: " + errorCode);
           }
         }
       } catch (InterruptedException e) {
@@ -724,8 +675,8 @@ public final class SpdyStream {
           throw new IOException("stream closed");
         } else if (finished) {
           throw new IOException("stream finished");
-        } else if (rstStatusCode != -1) {
-          throw new IOException("stream was reset: " + rstStatusString());
+        } else if (errorCode != null) {
+          throw new IOException("stream was reset: " + errorCode);
         }
       }
     }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/spdy/SpdyWriter.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/SpdyWriter.java b/framework/src/com/squareup/okhttp/internal/spdy/SpdyWriter.java
deleted file mode 100644
index b3d1d1f..0000000
--- a/framework/src/com/squareup/okhttp/internal/spdy/SpdyWriter.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * 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.internal.spdy;
-
-import com.squareup.okhttp.internal.Platform;
-import com.squareup.okhttp.internal.Util;
-import java.io.ByteArrayOutputStream;
-import java.io.Closeable;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.List;
-import java.util.zip.Deflater;
-
-/** Write spdy/3 frames. */
-final class SpdyWriter implements Closeable {
-  final DataOutputStream out;
-  private final ByteArrayOutputStream nameValueBlockBuffer;
-  private final DataOutputStream nameValueBlockOut;
-
-  SpdyWriter(OutputStream out) {
-    this.out = new DataOutputStream(out);
-
-    Deflater deflater = new Deflater();
-    deflater.setDictionary(SpdyReader.DICTIONARY);
-    nameValueBlockBuffer = new ByteArrayOutputStream();
-    nameValueBlockOut = new DataOutputStream(
-        Platform.get().newDeflaterOutputStream(nameValueBlockBuffer, deflater, true));
-  }
-
-  public synchronized void synStream(int flags, int streamId, int associatedStreamId, int priority,
-      int slot, List<String> nameValueBlock) throws IOException {
-    writeNameValueBlockToBuffer(nameValueBlock);
-    int length = 10 + nameValueBlockBuffer.size();
-    int type = SpdyConnection.TYPE_SYN_STREAM;
-
-    int unused = 0;
-    out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
-    out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
-    out.writeInt(streamId & 0x7fffffff);
-    out.writeInt(associatedStreamId & 0x7fffffff);
-    out.writeShort((priority & 0x7) << 13 | (unused & 0x1f) << 8 | (slot & 0xff));
-    nameValueBlockBuffer.writeTo(out);
-    out.flush();
-  }
-
-  public synchronized void synReply(int flags, int streamId, List<String> nameValueBlock)
-      throws IOException {
-    writeNameValueBlockToBuffer(nameValueBlock);
-    int type = SpdyConnection.TYPE_SYN_REPLY;
-    int length = nameValueBlockBuffer.size() + 4;
-
-    out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
-    out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
-    out.writeInt(streamId & 0x7fffffff);
-    nameValueBlockBuffer.writeTo(out);
-    out.flush();
-  }
-
-  public synchronized void headers(int flags, int streamId, List<String> nameValueBlock)
-      throws IOException {
-    writeNameValueBlockToBuffer(nameValueBlock);
-    int type = SpdyConnection.TYPE_HEADERS;
-    int length = nameValueBlockBuffer.size() + 4;
-
-    out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
-    out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
-    out.writeInt(streamId & 0x7fffffff);
-    nameValueBlockBuffer.writeTo(out);
-    out.flush();
-  }
-
-  public synchronized void rstStream(int streamId, int statusCode) throws IOException {
-    int flags = 0;
-    int type = SpdyConnection.TYPE_RST_STREAM;
-    int length = 8;
-    out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
-    out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
-    out.writeInt(streamId & 0x7fffffff);
-    out.writeInt(statusCode);
-    out.flush();
-  }
-
-  public synchronized void data(int flags, int streamId, byte[] data) throws IOException {
-    int length = data.length;
-    out.writeInt(streamId & 0x7fffffff);
-    out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
-    out.write(data);
-    out.flush();
-  }
-
-  private void writeNameValueBlockToBuffer(List<String> nameValueBlock) throws IOException {
-    nameValueBlockBuffer.reset();
-    int numberOfPairs = nameValueBlock.size() / 2;
-    nameValueBlockOut.writeInt(numberOfPairs);
-    for (String s : nameValueBlock) {
-      nameValueBlockOut.writeInt(s.length());
-      nameValueBlockOut.write(s.getBytes("UTF-8"));
-    }
-    nameValueBlockOut.flush();
-  }
-
-  public synchronized void settings(int flags, Settings settings) throws IOException {
-    int type = SpdyConnection.TYPE_SETTINGS;
-    int size = settings.size();
-    int length = 4 + size * 8;
-    out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
-    out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
-    out.writeInt(size);
-    for (int i = 0; i <= Settings.COUNT; i++) {
-      if (!settings.isSet(i)) continue;
-      int settingsFlags = settings.flags(i);
-      out.writeInt((settingsFlags & 0xff) << 24 | (i & 0xffffff));
-      out.writeInt(settings.get(i));
-    }
-    out.flush();
-  }
-
-  public synchronized void noop() throws IOException {
-    int type = SpdyConnection.TYPE_NOOP;
-    int length = 0;
-    int flags = 0;
-    out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
-    out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
-    out.flush();
-  }
-
-  public synchronized void ping(int flags, int id) throws IOException {
-    int type = SpdyConnection.TYPE_PING;
-    int length = 4;
-    out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
-    out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
-    out.writeInt(id);
-    out.flush();
-  }
-
-  public synchronized void goAway(int flags, int lastGoodStreamId, int statusCode)
-      throws IOException {
-    int type = SpdyConnection.TYPE_GOAWAY;
-    int length = 8;
-    out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
-    out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
-    out.writeInt(lastGoodStreamId);
-    out.writeInt(statusCode);
-    out.flush();
-  }
-
-  public synchronized void windowUpdate(int streamId, int deltaWindowSize) throws IOException {
-    int type = SpdyConnection.TYPE_WINDOW_UPDATE;
-    int flags = 0;
-    int length = 8;
-    out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
-    out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
-    out.writeInt(streamId);
-    out.writeInt(deltaWindowSize);
-    out.flush();
-  }
-
-  @Override public void close() throws IOException {
-    Util.closeAll(out, nameValueBlockOut);
-  }
-}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/spdy/Variant.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/Variant.java b/framework/src/com/squareup/okhttp/internal/spdy/Variant.java
new file mode 100755
index 0000000..8f48bcd
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/internal/spdy/Variant.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 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.internal.spdy;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/** A version and dialect of the framed socket protocol. */
+interface Variant {
+  Variant SPDY3 = new Spdy3();
+  Variant HTTP_20_DRAFT_06 = new Http20Draft06();
+
+  /**
+   * @param client true if this is the HTTP client's reader, reading frames from
+   *     a peer SPDY or HTTP/2 server.
+   */
+  FrameReader newReader(InputStream in, boolean client);
+
+  /**
+   * @param client true if this is the HTTP client's writer, writing frames to a
+   *     peer SPDY or HTTP/2 server.
+   */
+  FrameWriter newWriter(OutputStream out, boolean client);
+}