Return-Path: X-Original-To: apmail-cordova-commits-archive@www.apache.org Delivered-To: apmail-cordova-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 218C2CB8D for ; Thu, 13 Jun 2013 18:21:33 +0000 (UTC) Received: (qmail 56784 invoked by uid 500); 13 Jun 2013 18:21:27 -0000 Delivered-To: apmail-cordova-commits-archive@cordova.apache.org Received: (qmail 56718 invoked by uid 500); 13 Jun 2013 18:21:27 -0000 Mailing-List: contact commits-help@cordova.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cordova.apache.org Delivered-To: mailing list commits@cordova.apache.org Received: (qmail 55912 invoked by uid 99); 13 Jun 2013 18:21:26 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 13 Jun 2013 18:21:26 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 3ED418A4E28; Thu, 13 Jun 2013 18:21:26 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: filmaj@apache.org To: commits@cordova.apache.org Date: Thu, 13 Jun 2013 18:22:05 -0000 Message-Id: In-Reply-To: <437c04f0ec1b446b93fcdeac865562cf@git.apache.org> References: <437c04f0ec1b446b93fcdeac865562cf@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [42/78] [partial] start of lazy loading: axe all vendored-in libs http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/67fa7ebb/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/AbstractHttpInputStream.java ---------------------------------------------------------------------- diff --git a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/AbstractHttpInputStream.java b/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/AbstractHttpInputStream.java deleted file mode 100644 index 187f3b6..0000000 --- a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/AbstractHttpInputStream.java +++ /dev/null @@ -1,107 +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.internal.Util; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.CacheRequest; - -/** - * An input stream for the body of an HTTP response. - * - *

Since a single socket's input stream may be used to read multiple HTTP - * responses from the same server, subclasses shouldn't close the socket stream. - * - *

A side effect of reading an HTTP response is that the response cache - * is populated. If the stream is closed early, that cache entry will be - * invalidated. - */ -abstract class AbstractHttpInputStream extends InputStream { - protected final InputStream in; - protected final HttpEngine httpEngine; - private final CacheRequest cacheRequest; - private final OutputStream cacheBody; - protected boolean closed; - - AbstractHttpInputStream(InputStream in, HttpEngine httpEngine, CacheRequest cacheRequest) - throws IOException { - this.in = in; - this.httpEngine = httpEngine; - - OutputStream cacheBody = cacheRequest != null ? cacheRequest.getBody() : null; - - // some apps return a null body; for compatibility we treat that like a null cache request - if (cacheBody == null) { - cacheRequest = null; - } - - this.cacheBody = cacheBody; - this.cacheRequest = cacheRequest; - } - - /** - * read() is implemented using read(byte[], int, int) so subclasses only - * need to override the latter. - */ - @Override public final int read() throws IOException { - return Util.readSingleByte(this); - } - - protected final void checkNotClosed() throws IOException { - if (closed) { - throw new IOException("stream closed"); - } - } - - protected final void cacheWrite(byte[] buffer, int offset, int count) throws IOException { - if (cacheBody != null) { - cacheBody.write(buffer, offset, count); - } - } - - /** - * 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 { - if (cacheRequest != null) { - cacheBody.close(); - } - httpEngine.release(streamCancelled); - } - - /** - * Calls abort on the cache entry and disconnects the socket. This - * should be invoked when the connection is closed unexpectedly to - * invalidate the cache entry and to prevent the HTTP connection from - * being reused. HTTP messages are sent in serial so whenever a message - * cannot be read to completion, subsequent messages cannot be read - * either and the connection must be discarded. - * - *

An earlier implementation skipped the remaining bytes, but this - * requires that the entire transfer be completed. If the intention was - * to cancel the transfer, closing the connection is the only solution. - */ - protected final void unexpectedEndOfInput() { - if (cacheRequest != null) { - cacheRequest.abort(); - } - httpEngine.release(true); - } -} http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/67fa7ebb/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/AbstractHttpOutputStream.java ---------------------------------------------------------------------- diff --git a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/AbstractHttpOutputStream.java b/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/AbstractHttpOutputStream.java deleted file mode 100644 index 90675b0..0000000 --- a/lib/cordova-android/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. - * - *

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-cli/blob/67fa7ebb/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HeaderParser.java ---------------------------------------------------------------------- diff --git a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HeaderParser.java b/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HeaderParser.java deleted file mode 100644 index 12e6409..0000000 --- a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HeaderParser.java +++ /dev/null @@ -1,112 +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.http; - -final class HeaderParser { - - public interface CacheControlHandler { - void handle(String directive, String parameter); - } - - /** Parse a comma-separated list of cache control header values. */ - public static void parseCacheControl(String value, CacheControlHandler handler) { - int pos = 0; - while (pos < value.length()) { - int tokenStart = pos; - pos = skipUntil(value, pos, "=,"); - String directive = value.substring(tokenStart, pos).trim(); - - if (pos == value.length() || value.charAt(pos) == ',') { - pos++; // consume ',' (if necessary) - handler.handle(directive, null); - continue; - } - - pos++; // consume '=' - pos = skipWhitespace(value, pos); - - String parameter; - - // quoted string - if (pos < value.length() && value.charAt(pos) == '\"') { - pos++; // consume '"' open quote - int parameterStart = pos; - pos = skipUntil(value, pos, "\""); - parameter = value.substring(parameterStart, pos); - pos++; // consume '"' close quote (if necessary) - - // unquoted string - } else { - int parameterStart = pos; - pos = skipUntil(value, pos, ","); - parameter = value.substring(parameterStart, pos).trim(); - } - - handler.handle(directive, parameter); - } - } - - /** - * Returns the next index in {@code input} at or after {@code pos} that - * contains a character from {@code characters}. Returns the input length if - * none of the requested characters can be found. - */ - public static int skipUntil(String input, int pos, String characters) { - for (; pos < input.length(); pos++) { - if (characters.indexOf(input.charAt(pos)) != -1) { - break; - } - } - return pos; - } - - /** - * Returns the next non-whitespace character in {@code input} that is white - * space. Result is undefined if input contains newline characters. - */ - public static int skipWhitespace(String input, int pos) { - for (; pos < input.length(); pos++) { - char c = input.charAt(pos); - if (c != ' ' && c != '\t') { - break; - } - } - return pos; - } - - /** - * Returns {@code value} as a positive integer, or 0 if it is negative, or - * -1 if it cannot be parsed. - */ - public static int parseSeconds(String value) { - try { - long seconds = Long.parseLong(value); - if (seconds > Integer.MAX_VALUE) { - return Integer.MAX_VALUE; - } else if (seconds < 0) { - return 0; - } else { - return (int) seconds; - } - } catch (NumberFormatException e) { - return -1; - } - } - - private HeaderParser() { - } -} http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/67fa7ebb/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpAuthenticator.java ---------------------------------------------------------------------- diff --git a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpAuthenticator.java b/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpAuthenticator.java deleted file mode 100644 index 4ccd12a..0000000 --- a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpAuthenticator.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2012 Square, Inc. - * 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.http; - -import com.squareup.okhttp.internal.Base64; -import java.io.IOException; -import java.net.Authenticator; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.PasswordAuthentication; -import java.net.Proxy; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -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 { - private HttpAuthenticator() { - } - - /** - * React to a failed authorization response by looking up new credentials. - * - * @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(); - } - - // 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 challenges = parseChallenges(responseHeaders, challengeHeader); - if (challenges.isEmpty()) { - return null; - } - - 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; - } - - 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()); - } - - /** - * Parse RFC 2617 challenges. This API is only interested in the scheme - * name and realm. - */ - private static List parseChallenges(RawHeaders responseHeaders, - String challengeHeader) { - // auth-scheme = token - // auth-param = token "=" ( token | quoted-string ) - // challenge = auth-scheme 1*SP 1#auth-param - // realm = "realm" "=" realm-value - // realm-value = quoted-string - List result = new ArrayList(); - for (int h = 0; h < responseHeaders.length(); h++) { - if (!challengeHeader.equalsIgnoreCase(responseHeaders.getFieldName(h))) { - continue; - } - String value = responseHeaders.getValue(h); - int pos = 0; - while (pos < value.length()) { - int tokenStart = pos; - pos = HeaderParser.skipUntil(value, pos, " "); - - String scheme = value.substring(tokenStart, pos).trim(); - pos = HeaderParser.skipWhitespace(value, pos); - - // TODO: This currently only handles schemes with a 'realm' parameter; - // 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())) { - break; // Unexpected challenge parameter; give up! - } - - pos += "realm=\"".length(); - int realmStart = pos; - pos = HeaderParser.skipUntil(value, pos, "\""); - String realm = value.substring(realmStart, pos); - pos++; // Consume '"' close quote. - pos = HeaderParser.skipUntil(value, pos, ","); - pos++; // Consume ',' comma. - pos = HeaderParser.skipWhitespace(value, pos); - result.add(new Challenge(scheme, realm)); - } - } - 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-cli/blob/67fa7ebb/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpDate.java ---------------------------------------------------------------------- diff --git a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpDate.java b/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpDate.java deleted file mode 100644 index acb5fda..0000000 --- a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpDate.java +++ /dev/null @@ -1,82 +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.http; - -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.TimeZone; - -/** - * Best-effort parser for HTTP dates. - */ -final class HttpDate { - - /** - * Most websites serve cookies in the blessed format. Eagerly create the parser to ensure such - * cookies are on the fast path. - */ - private static final ThreadLocal STANDARD_DATE_FORMAT = - new ThreadLocal() { - @Override protected DateFormat initialValue() { - DateFormat rfc1123 = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US); - rfc1123.setTimeZone(TimeZone.getTimeZone("UTC")); - 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}. */ - "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", - "EEE dd-MMM-yyyy HH:mm:ss z", "EEE dd MMM yyyy HH:mm:ss z", "EEE dd-MMM-yyyy HH-mm-ss z", - "EEE dd-MMM-yy HH:mm:ss z", "EEE dd MMM yy HH:mm:ss z", "EEE,dd-MMM-yy HH:mm:ss z", - "EEE,dd-MMM-yyyy HH:mm:ss z", "EEE, dd-MM-yyyy HH:mm:ss z", - - /* 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. - */ - public static Date parse(String value) { - try { - return STANDARD_DATE_FORMAT.get().parse(value); - } catch (ParseException ignore) { - } - for (String formatString : BROWSER_COMPATIBLE_DATE_FORMATS) { - try { - return new SimpleDateFormat(formatString, Locale.US).parse(value); - } catch (ParseException ignore) { - } - } - return null; - } - - /** Returns the string for {@code value}. */ - public static String format(Date value) { - return STANDARD_DATE_FORMAT.get().format(value); - } - - private HttpDate() { - } -} http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/67fa7ebb/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpEngine.java ---------------------------------------------------------------------- diff --git a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpEngine.java b/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpEngine.java deleted file mode 100644 index 7a06dca..0000000 --- a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpEngine.java +++ /dev/null @@ -1,664 +0,0 @@ -/* - * 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.Address; -import com.squareup.okhttp.Connection; -import com.squareup.okhttp.ResponseSource; -import com.squareup.okhttp.TunnelRequest; -import com.squareup.okhttp.internal.Dns; -import com.squareup.okhttp.internal.Platform; -import com.squareup.okhttp.internal.Util; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.CacheRequest; -import java.net.CacheResponse; -import java.net.CookieHandler; -import java.net.Proxy; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.UnknownHostException; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.zip.GZIPInputStream; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.SSLSocketFactory; - -import static com.squareup.okhttp.internal.Util.EMPTY_BYTE_ARRAY; -import static com.squareup.okhttp.internal.Util.getDefaultPort; -import static com.squareup.okhttp.internal.Util.getEffectivePort; - -/** - * Handles a single HTTP request/response pair. Each HTTP engine follows this - * lifecycle: - *

    - *
  1. It is created. - *
  2. The HTTP request message is sent with sendRequest(). Once the request - * is sent it is an error to modify the request headers. After - * sendRequest() has been called the request body can be written to if - * it exists. - *
  3. The HTTP response message is read with readResponse(). After the - * response has been read the response headers and body can be read. - * All responses have a response body input stream, though in some - * instances this stream is empty. - *
- * - *

The request and response may be served by the HTTP response cache, by the - * network, or by both in the event of a conditional GET. - * - *

This class may hold a socket connection that needs to be released or - * recycled. By default, this socket connection is held when the last byte of - * the response is consumed. To release the connection when it is no longer - * required, use {@link #automaticallyReleaseConnectionToPool()}. - */ -public class HttpEngine { - private static final CacheResponse GATEWAY_TIMEOUT_RESPONSE = new CacheResponse() { - @Override public Map> getHeaders() throws IOException { - Map> result = new HashMap>(); - result.put(null, Collections.singletonList("HTTP/1.1 504 Gateway Timeout")); - return result; - } - @Override public InputStream getBody() throws IOException { - return new ByteArrayInputStream(EMPTY_BYTE_ARRAY); - } - }; - public static final int HTTP_CONTINUE = 100; - - protected final HttpURLConnectionImpl policy; - - protected final String method; - - private ResponseSource responseSource; - - protected Connection connection; - protected RouteSelector routeSelector; - private OutputStream requestBodyOut; - - private Transport transport; - - private InputStream responseTransferIn; - private InputStream responseBodyIn; - - private CacheResponse cacheResponse; - private CacheRequest cacheRequest; - - /** The time when the request headers were written, or -1 if they haven't been written yet. */ - long sentRequestMillis = -1; - - /** - * True if this client added an "Accept-Encoding: gzip" header field and is - * therefore responsible for also decompressing the transfer stream. - */ - private boolean transparentGzip; - - final URI uri; - - final RequestHeaders requestHeaders; - - /** Null until a response is received from the network or the cache. */ - ResponseHeaders responseHeaders; - - // The cache response currently being validated on a conditional get. Null - // if the cached response doesn't exist or doesn't need validation. If the - // conditional get succeeds, these will be used for the response headers and - // body. If it fails, these be closed and set to null. - private ResponseHeaders cachedResponseHeaders; - private InputStream cachedResponseBody; - - /** - * True if the socket connection should be released to the connection pool - * when the response has been fully read. - */ - private boolean automaticallyReleaseConnectionToPool; - - /** True if the socket connection is no longer needed by this engine. */ - private boolean connectionReleased; - - /** - * @param requestHeaders the client's supplied request headers. This class - * 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. - */ - public HttpEngine(HttpURLConnectionImpl policy, String method, RawHeaders requestHeaders, - Connection connection, RetryableOutputStream requestBodyOut) throws IOException { - this.policy = policy; - this.method = method; - this.connection = connection; - this.requestBodyOut = requestBodyOut; - - try { - uri = Platform.get().toUriLenient(policy.getURL()); - } catch (URISyntaxException e) { - throw new IOException(e.getMessage()); - } - - this.requestHeaders = new RequestHeaders(uri, new RawHeaders(requestHeaders)); - } - - public URI getUri() { - return uri; - } - - /** - * Figures out what the response source will be, and opens a socket to that - * source if necessary. Prepares the request headers and gets ready to start - * writing the request body if it exists. - */ - public final void sendRequest() throws IOException { - if (responseSource != null) { - return; - } - - prepareRawRequestHeaders(); - initResponseSource(); - if (policy.responseCache != null) { - policy.responseCache.trackResponse(responseSource); - } - - // The raw response source may require the network, but the request - // headers may forbid network use. In that case, dispose of the network - // response and use a GATEWAY_TIMEOUT response instead, as specified - // by http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4. - if (requestHeaders.isOnlyIfCached() && responseSource.requiresConnection()) { - if (responseSource == ResponseSource.CONDITIONAL_CACHE) { - Util.closeQuietly(cachedResponseBody); - } - this.responseSource = ResponseSource.CACHE; - this.cacheResponse = GATEWAY_TIMEOUT_RESPONSE; - RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(cacheResponse.getHeaders(), true); - setResponse(new ResponseHeaders(uri, rawResponseHeaders), cacheResponse.getBody()); - } - - if (responseSource.requiresConnection()) { - sendSocketRequest(); - } else if (connection != null) { - policy.connectionPool.recycle(connection); - policy.getFailedRoutes().remove(connection.getRoute()); - connection = null; - } - } - - /** - * Initialize the source for this response. It may be corrected later if the - * request headers forbids network use. - */ - private void initResponseSource() throws IOException { - responseSource = ResponseSource.NETWORK; - if (!policy.getUseCaches() || policy.responseCache == null) { - return; - } - - CacheResponse candidate = - policy.responseCache.get(uri, method, requestHeaders.getHeaders().toMultimap(false)); - if (candidate == null) { - return; - } - - Map> responseHeadersMap = candidate.getHeaders(); - cachedResponseBody = candidate.getBody(); - if (!acceptCacheResponseType(candidate) - || responseHeadersMap == null - || cachedResponseBody == null) { - Util.closeQuietly(cachedResponseBody); - return; - } - - RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(responseHeadersMap, true); - cachedResponseHeaders = new ResponseHeaders(uri, rawResponseHeaders); - long now = System.currentTimeMillis(); - this.responseSource = cachedResponseHeaders.chooseResponseSource(now, requestHeaders); - if (responseSource == ResponseSource.CACHE) { - this.cacheResponse = candidate; - setResponse(cachedResponseHeaders, cachedResponseBody); - } else if (responseSource == ResponseSource.CONDITIONAL_CACHE) { - this.cacheResponse = candidate; - } else if (responseSource == ResponseSource.NETWORK) { - Util.closeQuietly(cachedResponseBody); - } else { - throw new AssertionError(); - } - } - - private void sendSocketRequest() throws IOException { - if (connection == null) { - connect(); - } - - if (transport != null) { - throw new IllegalStateException(); - } - - transport = (Transport) connection.newTransport(this); - - if (hasRequestBody() && requestBodyOut == null) { - // Create a request body if we don't have one already. We'll already - // have one if we're retrying a failed POST. - requestBodyOut = transport.createRequestBody(); - } - } - - /** Connect to the origin server either directly or via a proxy. */ - protected final void connect() throws IOException { - if (connection != null) { - return; - } - if (routeSelector == null) { - String uriHost = uri.getHost(); - if (uriHost == null) { - throw new UnknownHostException(uri.toString()); - } - SSLSocketFactory sslSocketFactory = null; - HostnameVerifier hostnameVerifier = null; - if (uri.getScheme().equalsIgnoreCase("https")) { - sslSocketFactory = policy.sslSocketFactory; - hostnameVerifier = policy.hostnameVerifier; - } - Address address = new Address(uriHost, getEffectivePort(uri), sslSocketFactory, - hostnameVerifier, policy.requestedProxy); - routeSelector = new RouteSelector(address, uri, policy.proxySelector, policy.connectionPool, - Dns.DEFAULT, policy.getFailedRoutes()); - } - connection = routeSelector.next(); - if (!connection.isConnected()) { - connection.connect(policy.getConnectTimeout(), policy.getReadTimeout(), getTunnelConfig()); - policy.connectionPool.maybeShare(connection); - policy.getFailedRoutes().remove(connection.getRoute()); - } - connected(connection); - if (connection.getRoute().getProxy() != policy.requestedProxy) { - // Update the request line if the proxy changed; it may need a host name. - requestHeaders.getHeaders().setRequestLine(getRequestLine()); - } - } - - /** - * Called after a socket connection has been created or retrieved from the - * pool. Subclasses use this hook to get a reference to the TLS data. - */ - protected void connected(Connection connection) { - } - - /** - * Called immediately before the transport transmits HTTP request headers. - * This is used to observe the sent time should the request be cached. - */ - public void writingRequestHeaders() { - if (sentRequestMillis != -1) { - throw new IllegalStateException(); - } - sentRequestMillis = System.currentTimeMillis(); - } - - /** - * @param body the response body, or null if it doesn't exist or isn't - * available. - */ - private void setResponse(ResponseHeaders headers, InputStream body) throws IOException { - if (this.responseBodyIn != null) { - throw new IllegalStateException(); - } - this.responseHeaders = headers; - if (body != null) { - initContentStream(body); - } - } - - boolean hasRequestBody() { - return method.equals("POST") || method.equals("PUT"); - } - - /** Returns the request body or null if this request doesn't have a body. */ - public final OutputStream getRequestBody() { - if (responseSource == null) { - throw new IllegalStateException(); - } - return requestBodyOut; - } - - public final boolean hasResponse() { - return responseHeaders != null; - } - - public final RequestHeaders getRequestHeaders() { - return requestHeaders; - } - - public final ResponseHeaders getResponseHeaders() { - if (responseHeaders == null) { - throw new IllegalStateException(); - } - return responseHeaders; - } - - public final int getResponseCode() { - if (responseHeaders == null) { - throw new IllegalStateException(); - } - return responseHeaders.getHeaders().getResponseCode(); - } - - public final InputStream getResponseBody() { - if (responseHeaders == null) { - throw new IllegalStateException(); - } - return responseBodyIn; - } - - public final CacheResponse getCacheResponse() { - return cacheResponse; - } - - public final Connection getConnection() { - return connection; - } - - /** - * Returns true if {@code cacheResponse} is of the right type. This - * condition is necessary but not sufficient for the cached response to - * be used. - */ - protected boolean acceptCacheResponseType(CacheResponse cacheResponse) { - return true; - } - - private void maybeCache() throws IOException { - // Are we caching at all? - if (!policy.getUseCaches() || policy.responseCache == null) { - return; - } - - // Should we cache this response for this request? - if (!responseHeaders.isCacheable(requestHeaders)) { - return; - } - - // Offer this request to the cache. - cacheRequest = policy.responseCache.put(uri, policy.getHttpConnectionToCache()); - } - - /** - * Cause the socket connection to be released to the connection pool when - * it is no longer needed. If it is already unneeded, it will be pooled - * immediately. Otherwise the connection is held so that redirects can be - * handled by the same connection. - */ - public final void automaticallyReleaseConnectionToPool() { - automaticallyReleaseConnectionToPool = true; - if (connection != null && connectionReleased) { - policy.connectionPool.recycle(connection); - connection = null; - } - } - - /** - * Releases this engine so that its resources may be either reused or - * closed. Also call {@link #automaticallyReleaseConnectionToPool} unless - * the connection will be used to follow a redirect. - */ - public final void release(boolean streamCancelled) { - // If the response body comes from the cache, close it. - if (responseBodyIn == cachedResponseBody) { - Util.closeQuietly(responseBodyIn); - } - - if (!connectionReleased && connection != null) { - connectionReleased = true; - - if (transport == null || !transport.makeReusable(streamCancelled, requestBodyOut, - responseTransferIn)) { - Util.closeQuietly(connection); - connection = null; - } else if (automaticallyReleaseConnectionToPool) { - policy.connectionPool.recycle(connection); - connection = null; - } - } - } - - private void initContentStream(InputStream transferStream) throws IOException { - responseTransferIn = transferStream; - if (transparentGzip && responseHeaders.isContentEncodingGzip()) { - // If the response was transparently gzipped, remove the gzip header field - // so clients don't double decompress. http://b/3009828 - // - // Also remove the Content-Length in this case because it contains the - // length 528 of the gzipped response. This isn't terribly useful and is - // dangerous because 529 clients can query the content length, but not - // the content encoding. - responseHeaders.stripContentEncoding(); - responseHeaders.stripContentLength(); - responseBodyIn = new GZIPInputStream(transferStream); - } else { - responseBodyIn = transferStream; - } - } - - /** - * Returns true if the response must have a (possibly 0-length) body. - * See RFC 2616 section 4.3. - */ - public final boolean hasResponseBody() { - int responseCode = responseHeaders.getHeaders().getResponseCode(); - - // HEAD requests never yield a body regardless of the response headers. - if (method.equals("HEAD")) { - return false; - } - - if ((responseCode < HTTP_CONTINUE || responseCode >= 200) - && responseCode != HttpURLConnectionImpl.HTTP_NO_CONTENT - && responseCode != HttpURLConnectionImpl.HTTP_NOT_MODIFIED) { - return true; - } - - // If the Content-Length or Transfer-Encoding headers disagree with the - // response code, the response is malformed. For best compatibility, we - // honor the headers. - if (responseHeaders.getContentLength() != -1 || responseHeaders.isChunked()) { - return true; - } - - return false; - } - - /** - * Populates requestHeaders with defaults and cookies. - * - *

This client doesn't specify a default {@code Accept} header because it - * doesn't know what content types the application is interested in. - */ - private void prepareRawRequestHeaders() throws IOException { - requestHeaders.getHeaders().setRequestLine(getRequestLine()); - - if (requestHeaders.getUserAgent() == null) { - requestHeaders.setUserAgent(getDefaultUserAgent()); - } - - if (requestHeaders.getHost() == null) { - requestHeaders.setHost(getOriginAddress(policy.getURL())); - } - - if ((connection == null || connection.getHttpMinorVersion() != 0) - && requestHeaders.getConnection() == null) { - requestHeaders.setConnection("Keep-Alive"); - } - - if (requestHeaders.getAcceptEncoding() == null) { - transparentGzip = true; - requestHeaders.setAcceptEncoding("gzip"); - } - - if (hasRequestBody() && requestHeaders.getContentType() == null) { - requestHeaders.setContentType("application/x-www-form-urlencoded"); - } - - long ifModifiedSince = policy.getIfModifiedSince(); - if (ifModifiedSince != 0) { - requestHeaders.setIfModifiedSince(new Date(ifModifiedSince)); - } - - CookieHandler cookieHandler = policy.cookieHandler; - if (cookieHandler != null) { - requestHeaders.addCookies( - cookieHandler.get(uri, requestHeaders.getHeaders().toMultimap(false))); - } - } - - /** - * Returns the request status line, like "GET / HTTP/1.1". This is exposed - * to the application by {@link HttpURLConnectionImpl#getHeaderFields}, so - * it needs to be set even if the transport is SPDY. - */ - String getRequestLine() { - String protocol = - (connection == null || connection.getHttpMinorVersion() != 0) ? "HTTP/1.1" : "HTTP/1.0"; - return method + " " + requestString() + " " + protocol; - } - - private String requestString() { - URL url = policy.getURL(); - if (includeAuthorityInRequestLine()) { - return url.toString(); - } else { - return requestPath(url); - } - } - - /** - * Returns the path to request, like the '/' in 'GET / HTTP/1.1'. Never - * empty, even if the request URL is. Includes the query component if it - * exists. - */ - public static String requestPath(URL url) { - String fileOnly = url.getFile(); - if (fileOnly == null) { - return "/"; - } else if (!fileOnly.startsWith("/")) { - return "/" + fileOnly; - } else { - return fileOnly; - } - } - - /** - * Returns true if the request line should contain the full URL with host - * and port (like "GET http://android.com/foo HTTP/1.1") or only the path - * (like "GET /foo HTTP/1.1"). - * - *

This is non-final because for HTTPS it's never necessary to supply the - * full URL, even if a proxy is in use. - */ - protected boolean includeAuthorityInRequestLine() { - return connection == null - ? policy.usingProxy() // A proxy was requested. - : connection.getRoute().getProxy().type() == Proxy.Type.HTTP; // A proxy was selected. - } - - public static String getDefaultUserAgent() { - String agent = System.getProperty("http.agent"); - return agent != null ? agent : ("Java" + System.getProperty("java.version")); - } - - public static String getOriginAddress(URL url) { - int port = url.getPort(); - String result = url.getHost(); - if (port > 0 && port != getDefaultPort(url.getProtocol())) { - result = result + ":" + port; - } - return result; - } - - /** - * Flushes the remaining request header and body, parses the HTTP response - * headers and starts reading the HTTP response body if it exists. - */ - public final void readResponse() throws IOException { - if (hasResponse()) { - responseHeaders.setResponseSource(responseSource); - return; - } - - if (responseSource == null) { - throw new IllegalStateException("readResponse() without sendRequest()"); - } - - if (!responseSource.requiresConnection()) { - return; - } - - if (sentRequestMillis == -1) { - if (requestBodyOut instanceof RetryableOutputStream) { - int contentLength = ((RetryableOutputStream) requestBodyOut).contentLength(); - requestHeaders.setContentLength(contentLength); - } - transport.writeRequestHeaders(); - } - - if (requestBodyOut != null) { - requestBodyOut.close(); - if (requestBodyOut instanceof RetryableOutputStream) { - transport.writeRequestBody((RetryableOutputStream) requestBodyOut); - } - } - - transport.flushRequest(); - - responseHeaders = transport.readResponseHeaders(); - responseHeaders.setLocalTimestamps(sentRequestMillis, System.currentTimeMillis()); - responseHeaders.setResponseSource(responseSource); - - if (responseSource == ResponseSource.CONDITIONAL_CACHE) { - if (cachedResponseHeaders.validate(responseHeaders)) { - release(false); - ResponseHeaders combinedHeaders = cachedResponseHeaders.combine(responseHeaders); - setResponse(combinedHeaders, cachedResponseBody); - policy.responseCache.trackConditionalCacheHit(); - policy.responseCache.update(cacheResponse, policy.getHttpConnectionToCache()); - return; - } else { - Util.closeQuietly(cachedResponseBody); - } - } - - if (hasResponseBody()) { - maybeCache(); // reentrant. this calls into user code which may call back into this! - } - - initContentStream(transport.getTransferStream(cacheRequest)); - } - - protected TunnelRequest getTunnelConfig() { - return null; - } - - public void receiveHeaders(RawHeaders headers) throws IOException { - CookieHandler cookieHandler = policy.cookieHandler; - if (cookieHandler != null) { - cookieHandler.put(uri, headers.toMultimap(true)); - } - } -} http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/67fa7ebb/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpResponseCache.java ---------------------------------------------------------------------- diff --git a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpResponseCache.java b/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpResponseCache.java deleted file mode 100644 index 8735166..0000000 --- a/lib/cordova-android/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> 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: - *

{@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
-     * }
- * - *

A typical HTTPS file looks like this: - *

{@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
-     * }
- * 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. - * - *

Next is the response status line, followed by the number of HTTP - * response header lines, followed by those lines. - * - *

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> 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> 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> getHeaders() { - return entry.responseHeaders.toMultimap(true); - } - - @Override public InputStream getBody() { - return in; - } - - @Override public String getCipherSuite() { - return entry.cipherSuite; - } - - @Override public List 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 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-cli/blob/67fa7ebb/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpTransport.java ---------------------------------------------------------------------- diff --git a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpTransport.java b/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpTransport.java deleted file mode 100644 index f6d77b2..0000000 --- a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/http/HttpTransport.java +++ /dev/null @@ -1,485 +0,0 @@ -/* - * Copyright (C) 2012 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.Connection; -import com.squareup.okhttp.internal.AbstractOutputStream; -import com.squareup.okhttp.internal.Util; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.CacheRequest; -import java.net.ProtocolException; -import java.net.Socket; - -import static com.squareup.okhttp.internal.Util.checkOffsetAndCount; - -public final class HttpTransport implements Transport { - /** - * The timeout to use while discarding a stream of input data. Since this is - * used for connection reuse, this timeout should be significantly less than - * the time it takes to establish a new connection. - */ - private static final int DISCARD_STREAM_TIMEOUT_MILLIS = 100; - - public static final int DEFAULT_CHUNK_LENGTH = 1024; - - private final HttpEngine httpEngine; - private final InputStream socketIn; - private final OutputStream socketOut; - - /** - * This stream buffers the request headers and the request body when their - * combined size is less than MAX_REQUEST_BUFFER_LENGTH. By combining them - * we can save socket writes, which in turn saves a packet transmission. - * This is socketOut if the request size is large or unknown. - */ - private OutputStream requestOut; - - public HttpTransport(HttpEngine httpEngine, OutputStream outputStream, InputStream inputStream) { - this.httpEngine = httpEngine; - this.socketOut = outputStream; - this.requestOut = outputStream; - this.socketIn = inputStream; - } - - @Override public OutputStream createRequestBody() throws IOException { - boolean chunked = httpEngine.requestHeaders.isChunked(); - if (!chunked - && httpEngine.policy.getChunkLength() > 0 - && httpEngine.connection.getHttpMinorVersion() != 0) { - httpEngine.requestHeaders.setChunked(); - chunked = true; - } - - // Stream a request body of unknown length. - if (chunked) { - int chunkLength = httpEngine.policy.getChunkLength(); - if (chunkLength == -1) { - chunkLength = DEFAULT_CHUNK_LENGTH; - } - writeRequestHeaders(); - return new ChunkedOutputStream(requestOut, chunkLength); - } - - // Stream a request body of a known length. - int fixedContentLength = httpEngine.policy.getFixedContentLength(); - if (fixedContentLength != -1) { - httpEngine.requestHeaders.setContentLength(fixedContentLength); - writeRequestHeaders(); - return new FixedLengthOutputStream(requestOut, fixedContentLength); - } - - // Buffer a request body of a known length. - int contentLength = httpEngine.requestHeaders.getContentLength(); - if (contentLength != -1) { - writeRequestHeaders(); - return new RetryableOutputStream(contentLength); - } - - // Buffer a request body of an unknown length. Don't write request - // headers until the entire body is ready; otherwise we can't set the - // Content-Length header correctly. - return new RetryableOutputStream(); - } - - @Override public void flushRequest() throws IOException { - requestOut.flush(); - requestOut = socketOut; - } - - @Override public void writeRequestBody(RetryableOutputStream requestBody) throws IOException { - requestBody.writeToSocket(requestOut); - } - - /** - * Prepares the HTTP headers and sends them to the server. - * - *

For streaming requests with a body, headers must be prepared - * before the output stream has been written to. Otherwise - * the body would need to be buffered! - * - *

For non-streaming requests with a body, headers must be prepared - * after the output stream has been written to and closed. - * This ensures that the {@code Content-Length} header field receives the - * proper value. - */ - public void writeRequestHeaders() throws IOException { - httpEngine.writingRequestHeaders(); - RawHeaders headersToSend = httpEngine.requestHeaders.getHeaders(); - byte[] bytes = headersToSend.toBytes(); - requestOut.write(bytes); - } - - @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); - } - - public boolean makeReusable(boolean streamCancelled, OutputStream requestBodyOut, - InputStream responseBodyIn) { - if (streamCancelled) { - return false; - } - - // We cannot reuse sockets that have incomplete output. - if (requestBodyOut != null && !((AbstractOutputStream) requestBodyOut).isClosed()) { - return false; - } - - // If the request specified that the connection shouldn't be reused, don't reuse it. - if (httpEngine.requestHeaders.hasConnectionClose()) { - return false; - } - - // If the response specified that the connection shouldn't be reused, don't reuse it. - if (httpEngine.responseHeaders != null && httpEngine.responseHeaders.hasConnectionClose()) { - return false; - } - - if (responseBodyIn instanceof UnknownLengthHttpInputStream) { - return false; - } - - if (responseBodyIn != null) { - return discardStream(httpEngine, responseBodyIn); - } - - return true; - } - - /** - * 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. - */ - private static boolean discardStream(HttpEngine httpEngine, InputStream responseBodyIn) { - Connection connection = httpEngine.connection; - if (connection == null) return false; - Socket socket = connection.getSocket(); - if (socket == null) return false; - try { - int socketTimeout = socket.getSoTimeout(); - socket.setSoTimeout(DISCARD_STREAM_TIMEOUT_MILLIS); - try { - Util.skipAll(responseBodyIn); - return true; - } finally { - socket.setSoTimeout(socketTimeout); - } - } catch (IOException e) { - return false; - } - } - - @Override public InputStream getTransferStream(CacheRequest cacheRequest) throws IOException { - if (!httpEngine.hasResponseBody()) { - return new FixedLengthInputStream(socketIn, cacheRequest, httpEngine, 0); - } - - if (httpEngine.responseHeaders.isChunked()) { - return new ChunkedInputStream(socketIn, cacheRequest, this); - } - - if (httpEngine.responseHeaders.getContentLength() != -1) { - return new FixedLengthInputStream(socketIn, cacheRequest, httpEngine, - httpEngine.responseHeaders.getContentLength()); - } - - // Wrap the input stream from the connection (rather than just returning - // "socketIn" directly here), so that we can control its use after the - // reference escapes. - return new UnknownLengthHttpInputStream(socketIn, cacheRequest, httpEngine); - } - - /** 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 FixedLengthOutputStream(OutputStream socketOut, int bytesRemaining) { - this.socketOut = socketOut; - this.bytesRemaining = bytesRemaining; - } - - @Override public void write(byte[] buffer, int offset, int count) throws IOException { - checkNotClosed(); - checkOffsetAndCount(buffer.length, offset, count); - if (count > bytesRemaining) { - throw new ProtocolException("expected " + bytesRemaining + " bytes but received " + count); - } - socketOut.write(buffer, offset, count); - bytesRemaining -= count; - } - - @Override public void flush() throws IOException { - if (closed) { - return; // don't throw; this stream might have been closed on the caller's behalf - } - socketOut.flush(); - } - - @Override public void close() throws IOException { - if (closed) { - return; - } - closed = true; - if (bytesRemaining > 0) { - throw new ProtocolException("unexpected end of stream"); - } - } - } - - /** - * An HTTP body with alternating chunk sizes and chunk bodies. Chunks are - * buffered until {@code maxChunkLength} bytes are ready, at which point the - * chunk is written and the buffer is cleared. - */ - private static final class ChunkedOutputStream extends AbstractOutputStream { - private static final byte[] CRLF = { '\r', '\n' }; - private static final byte[] HEX_DIGITS = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' - }; - private static final byte[] FINAL_CHUNK = new byte[] { '0', '\r', '\n', '\r', '\n' }; - - /** Scratch space for up to 8 hex digits, and then a constant CRLF. */ - private final byte[] hex = { 0, 0, 0, 0, 0, 0, 0, 0, '\r', '\n' }; - - private final OutputStream socketOut; - private final int maxChunkLength; - private final ByteArrayOutputStream bufferedChunk; - - private ChunkedOutputStream(OutputStream socketOut, int maxChunkLength) { - this.socketOut = socketOut; - this.maxChunkLength = Math.max(1, dataLength(maxChunkLength)); - this.bufferedChunk = new ByteArrayOutputStream(maxChunkLength); - } - - /** - * Returns the amount of data that can be transmitted in a chunk whose total - * length (data+headers) is {@code dataPlusHeaderLength}. This is presumably - * useful to match sizes with wire-protocol packets. - */ - private int dataLength(int dataPlusHeaderLength) { - int headerLength = 4; // "\r\n" after the size plus another "\r\n" after the data - for (int i = dataPlusHeaderLength - headerLength; i > 0; i >>= 4) { - headerLength++; - } - return dataPlusHeaderLength - headerLength; - } - - @Override public synchronized void write(byte[] buffer, int offset, int count) - throws IOException { - checkNotClosed(); - checkOffsetAndCount(buffer.length, offset, count); - - while (count > 0) { - int numBytesWritten; - - if (bufferedChunk.size() > 0 || count < maxChunkLength) { - // fill the buffered chunk and then maybe write that to the stream - numBytesWritten = Math.min(count, maxChunkLength - bufferedChunk.size()); - // TODO: skip unnecessary copies from buffer->bufferedChunk? - bufferedChunk.write(buffer, offset, numBytesWritten); - if (bufferedChunk.size() == maxChunkLength) { - writeBufferedChunkToSocket(); - } - } else { - // write a single chunk of size maxChunkLength to the stream - numBytesWritten = maxChunkLength; - writeHex(numBytesWritten); - socketOut.write(buffer, offset, numBytesWritten); - socketOut.write(CRLF); - } - - offset += numBytesWritten; - count -= numBytesWritten; - } - } - - /** - * Equivalent to, but cheaper than writing Integer.toHexString().getBytes() - * followed by CRLF. - */ - private void writeHex(int i) throws IOException { - int cursor = 8; - do { - hex[--cursor] = HEX_DIGITS[i & 0xf]; - } while ((i >>>= 4) != 0); - socketOut.write(hex, cursor, hex.length - cursor); - } - - @Override public synchronized void flush() throws IOException { - if (closed) { - return; // don't throw; this stream might have been closed on the caller's behalf - } - writeBufferedChunkToSocket(); - socketOut.flush(); - } - - @Override public synchronized void close() throws IOException { - if (closed) { - return; - } - closed = true; - writeBufferedChunkToSocket(); - socketOut.write(FINAL_CHUNK); - } - - private void writeBufferedChunkToSocket() throws IOException { - int size = bufferedChunk.size(); - if (size <= 0) { - return; - } - - writeHex(size); - bufferedChunk.writeTo(socketOut); - bufferedChunk.reset(); - socketOut.write(CRLF); - } - } - - /** An HTTP body with a fixed length specified in advance. */ - private static class FixedLengthInputStream extends AbstractHttpInputStream { - private int bytesRemaining; - - public FixedLengthInputStream(InputStream is, CacheRequest cacheRequest, HttpEngine httpEngine, - int length) throws IOException { - super(is, httpEngine, cacheRequest); - bytesRemaining = length; - if (bytesRemaining == 0) { - endOfInput(false); - } - } - - @Override public int read(byte[] buffer, int offset, int count) throws IOException { - checkOffsetAndCount(buffer.length, offset, count); - checkNotClosed(); - if (bytesRemaining == 0) { - return -1; - } - int read = in.read(buffer, offset, Math.min(count, bytesRemaining)); - if (read == -1) { - unexpectedEndOfInput(); // the server didn't supply the promised content length - throw new ProtocolException("unexpected end of stream"); - } - bytesRemaining -= read; - cacheWrite(buffer, offset, read); - if (bytesRemaining == 0) { - endOfInput(false); - } - return read; - } - - @Override public int available() throws IOException { - checkNotClosed(); - return bytesRemaining == 0 ? 0 : Math.min(in.available(), bytesRemaining); - } - - @Override public void close() throws IOException { - if (closed) { - return; - } - if (bytesRemaining != 0 && !discardStream(httpEngine, this)) { - unexpectedEndOfInput(); - } - closed = true; - } - } - - /** An HTTP body with alternating chunk sizes and chunk bodies. */ - private static class ChunkedInputStream extends AbstractHttpInputStream { - private static final int NO_CHUNK_YET = -1; - private final HttpTransport transport; - private int bytesRemainingInChunk = NO_CHUNK_YET; - private boolean hasMoreChunks = true; - - ChunkedInputStream(InputStream is, CacheRequest cacheRequest, HttpTransport transport) - throws IOException { - super(is, transport.httpEngine, cacheRequest); - this.transport = transport; - } - - @Override public int read(byte[] buffer, int offset, int count) throws IOException { - checkOffsetAndCount(buffer.length, offset, count); - checkNotClosed(); - - if (!hasMoreChunks) { - return -1; - } - if (bytesRemainingInChunk == 0 || bytesRemainingInChunk == NO_CHUNK_YET) { - readChunkSize(); - if (!hasMoreChunks) { - return -1; - } - } - int read = in.read(buffer, offset, Math.min(count, bytesRemainingInChunk)); - if (read == -1) { - unexpectedEndOfInput(); // the server didn't supply the promised chunk length - throw new IOException("unexpected end of stream"); - } - bytesRemainingInChunk -= read; - cacheWrite(buffer, offset, read); - return read; - } - - private void readChunkSize() throws IOException { - // read the suffix of the previous chunk - if (bytesRemainingInChunk != NO_CHUNK_YET) { - Util.readAsciiLine(in); - } - String chunkSizeString = Util.readAsciiLine(in); - int index = chunkSizeString.indexOf(";"); - if (index != -1) { - chunkSizeString = chunkSizeString.substring(0, index); - } - try { - bytesRemainingInChunk = Integer.parseInt(chunkSizeString.trim(), 16); - } catch (NumberFormatException e) { - throw new ProtocolException("Expected a hex chunk size but was " + chunkSizeString); - } - if (bytesRemainingInChunk == 0) { - hasMoreChunks = false; - RawHeaders rawResponseHeaders = httpEngine.responseHeaders.getHeaders(); - RawHeaders.readHeaders(transport.socketIn, rawResponseHeaders); - httpEngine.receiveHeaders(rawResponseHeaders); - endOfInput(false); - } - } - - @Override public int available() throws IOException { - checkNotClosed(); - if (!hasMoreChunks || bytesRemainingInChunk == NO_CHUNK_YET) { - return 0; - } - return Math.min(in.available(), bytesRemainingInChunk); - } - - @Override public void close() throws IOException { - if (closed) { - return; - } - if (hasMoreChunks && !discardStream(httpEngine, this)) { - unexpectedEndOfInput(); - } - closed = true; - } - } -}