diff options
Diffstat (limited to 'core/java/android')
22 files changed, 45 insertions, 5651 deletions
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java index c125544..e61664c 100644 --- a/core/java/android/database/DatabaseUtils.java +++ b/core/java/android/database/DatabaseUtils.java @@ -16,8 +16,6 @@ package android.database; -import org.apache.commons.codec.binary.Hex; - import android.content.ContentValues; import android.content.Context; import android.content.OperationApplicationException; @@ -416,11 +414,33 @@ public class DatabaseUtils { * @return the collation key in hex format */ public static String getHexCollationKey(String name) { - byte [] arr = getCollationKeyInBytes(name); - char[] keys = Hex.encodeHex(arr); + byte[] arr = getCollationKeyInBytes(name); + char[] keys = encodeHex(arr); return new String(keys, 0, getKeyLen(arr) * 2); } + + /** + * Used building output as Hex + */ + private static final char[] DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + private static char[] encodeHex(byte[] input) { + int l = input.length; + char[] out = new char[l << 1]; + + // two characters form the hex value. + for (int i = 0, j = 0; i < l; i++) { + out[j++] = DIGITS[(0xF0 & input[i]) >>> 4 ]; + out[j++] = DIGITS[ 0x0F & input[i] ]; + } + + return out; + } + private static int getKeyLen(byte[] arr) { if (arr[arr.length - 1] != 0) { return arr.length; diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java index a3cad77..2c90909 100644 --- a/core/java/android/net/ProxyInfo.java +++ b/core/java/android/net/ProxyInfo.java @@ -21,8 +21,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; -import org.apache.http.client.HttpClient; - import java.net.InetSocketAddress; import java.net.URLConnection; import java.util.List; @@ -31,8 +29,9 @@ import java.util.Locale; /** * Describes a proxy configuration. * - * Proxy configurations are already integrated within the Apache HTTP stack. - * So {@link URLConnection} and {@link HttpClient} will use them automatically. + * Proxy configurations are already integrated within the {@code java.net} and + * Apache HTTP stack. So {@link URLConnection} and Apache's {@code HttpClient} will use + * them automatically. * * Other HTTP stacks will need to obtain the proxy info from * {@link Proxy#PROXY_CHANGE_ACTION} broadcast as the extra {@link Proxy#EXTRA_PROXY_INFO}. diff --git a/core/java/android/net/http/AndroidHttpClient.java b/core/java/android/net/http/AndroidHttpClient.java deleted file mode 100644 index a262076..0000000 --- a/core/java/android/net/http/AndroidHttpClient.java +++ /dev/null @@ -1,527 +0,0 @@ -/* - * Copyright (C) 2007 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 android.net.http; - -import com.android.internal.http.HttpDateTime; - -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpEntityEnclosingRequest; -import org.apache.http.HttpException; -import org.apache.http.HttpHost; -import org.apache.http.HttpRequest; -import org.apache.http.HttpRequestInterceptor; -import org.apache.http.HttpResponse; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.HttpClient; -import org.apache.http.client.ResponseHandler; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.client.params.HttpClientParams; -import org.apache.http.client.protocol.ClientContext; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.scheme.PlainSocketFactory; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.entity.AbstractHttpEntity; -import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.client.RequestWrapper; -import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.apache.http.params.HttpProtocolParams; -import org.apache.http.protocol.BasicHttpContext; -import org.apache.http.protocol.BasicHttpProcessor; -import org.apache.http.protocol.HttpContext; - -import android.content.ContentResolver; -import android.content.Context; -import android.net.SSLCertificateSocketFactory; -import android.net.SSLSessionCache; -import android.os.Looper; -import android.util.Base64; -import android.util.Log; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URI; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; - -/** - * Implementation of the Apache {@link DefaultHttpClient} that is configured with - * reasonable default settings and registered schemes for Android. - * Don't create this directly, use the {@link #newInstance} factory method. - * - * <p>This client processes cookies but does not retain them by default. - * To retain cookies, simply add a cookie store to the HttpContext:</p> - * - * <pre>context.setAttribute(ClientContext.COOKIE_STORE, cookieStore);</pre> - * - * @deprecated Please use {@link java.net.URLConnection} and friends instead. - * The Apache HTTP client is no longer maintained and may be removed in a future - * release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> - * for further details. - */ -@Deprecated -public final class AndroidHttpClient implements HttpClient { - - // Gzip of data shorter than this probably won't be worthwhile - public static long DEFAULT_SYNC_MIN_GZIP_BYTES = 256; - - // Default connection and socket timeout of 60 seconds. Tweak to taste. - private static final int SOCKET_OPERATION_TIMEOUT = 60 * 1000; - - private static final String TAG = "AndroidHttpClient"; - - private static String[] textContentTypes = new String[] { - "text/", - "application/xml", - "application/json" - }; - - /** Interceptor throws an exception if the executing thread is blocked */ - private static final HttpRequestInterceptor sThreadCheckInterceptor = - new HttpRequestInterceptor() { - public void process(HttpRequest request, HttpContext context) { - // Prevent the HttpRequest from being sent on the main thread - if (Looper.myLooper() != null && Looper.myLooper() == Looper.getMainLooper() ) { - throw new RuntimeException("This thread forbids HTTP requests"); - } - } - }; - - /** - * Create a new HttpClient with reasonable defaults (which you can update). - * - * @param userAgent to report in your HTTP requests - * @param context to use for caching SSL sessions (may be null for no caching) - * @return AndroidHttpClient for you to use for all your requests. - * - * @deprecated Please use {@link java.net.URLConnection} and friends instead. See - * {@link android.net.SSLCertificateSocketFactory} for SSL cache support. If you'd - * like to set a custom useragent, please use {@link java.net.URLConnection#setRequestProperty(String, String)} - * with {@code field} set to {@code User-Agent}. - */ - @Deprecated - public static AndroidHttpClient newInstance(String userAgent, Context context) { - HttpParams params = new BasicHttpParams(); - - // Turn off stale checking. Our connections break all the time anyway, - // and it's not worth it to pay the penalty of checking every time. - HttpConnectionParams.setStaleCheckingEnabled(params, false); - - HttpConnectionParams.setConnectionTimeout(params, SOCKET_OPERATION_TIMEOUT); - HttpConnectionParams.setSoTimeout(params, SOCKET_OPERATION_TIMEOUT); - HttpConnectionParams.setSocketBufferSize(params, 8192); - - // Don't handle redirects -- return them to the caller. Our code - // often wants to re-POST after a redirect, which we must do ourselves. - HttpClientParams.setRedirecting(params, false); - - // Use a session cache for SSL sockets - SSLSessionCache sessionCache = context == null ? null : new SSLSessionCache(context); - - // Set the specified user agent and register standard protocols. - HttpProtocolParams.setUserAgent(params, userAgent); - SchemeRegistry schemeRegistry = new SchemeRegistry(); - schemeRegistry.register(new Scheme("http", - PlainSocketFactory.getSocketFactory(), 80)); - schemeRegistry.register(new Scheme("https", - SSLCertificateSocketFactory.getHttpSocketFactory( - SOCKET_OPERATION_TIMEOUT, sessionCache), 443)); - - ClientConnectionManager manager = - new ThreadSafeClientConnManager(params, schemeRegistry); - - // We use a factory method to modify superclass initialization - // parameters without the funny call-a-static-method dance. - return new AndroidHttpClient(manager, params); - } - - /** - * Create a new HttpClient with reasonable defaults (which you can update). - * @param userAgent to report in your HTTP requests. - * @return AndroidHttpClient for you to use for all your requests. - * - * @deprecated Please use {@link java.net.URLConnection} and friends instead. See - * {@link android.net.SSLCertificateSocketFactory} for SSL cache support. If you'd - * like to set a custom useragent, please use {@link java.net.URLConnection#setRequestProperty(String, String)} - * with {@code field} set to {@code User-Agent}. - */ - @Deprecated - public static AndroidHttpClient newInstance(String userAgent) { - return newInstance(userAgent, null /* session cache */); - } - - private final HttpClient delegate; - - private RuntimeException mLeakedException = new IllegalStateException( - "AndroidHttpClient created and never closed"); - - private AndroidHttpClient(ClientConnectionManager ccm, HttpParams params) { - this.delegate = new DefaultHttpClient(ccm, params) { - @Override - protected BasicHttpProcessor createHttpProcessor() { - // Add interceptor to prevent making requests from main thread. - BasicHttpProcessor processor = super.createHttpProcessor(); - processor.addRequestInterceptor(sThreadCheckInterceptor); - processor.addRequestInterceptor(new CurlLogger()); - - return processor; - } - - @Override - protected HttpContext createHttpContext() { - // Same as DefaultHttpClient.createHttpContext() minus the - // cookie store. - HttpContext context = new BasicHttpContext(); - context.setAttribute( - ClientContext.AUTHSCHEME_REGISTRY, - getAuthSchemes()); - context.setAttribute( - ClientContext.COOKIESPEC_REGISTRY, - getCookieSpecs()); - context.setAttribute( - ClientContext.CREDS_PROVIDER, - getCredentialsProvider()); - return context; - } - }; - } - - @Override - protected void finalize() throws Throwable { - super.finalize(); - if (mLeakedException != null) { - Log.e(TAG, "Leak found", mLeakedException); - mLeakedException = null; - } - } - - /** - * Modifies a request to indicate to the server that we would like a - * gzipped response. (Uses the "Accept-Encoding" HTTP header.) - * @param request the request to modify - * @see #getUngzippedContent - */ - public static void modifyRequestToAcceptGzipResponse(HttpRequest request) { - request.addHeader("Accept-Encoding", "gzip"); - } - - /** - * Gets the input stream from a response entity. If the entity is gzipped - * then this will get a stream over the uncompressed data. - * - * @param entity the entity whose content should be read - * @return the input stream to read from - * @throws IOException - */ - public static InputStream getUngzippedContent(HttpEntity entity) - throws IOException { - InputStream responseStream = entity.getContent(); - if (responseStream == null) return responseStream; - Header header = entity.getContentEncoding(); - if (header == null) return responseStream; - String contentEncoding = header.getValue(); - if (contentEncoding == null) return responseStream; - if (contentEncoding.contains("gzip")) responseStream - = new GZIPInputStream(responseStream); - return responseStream; - } - - /** - * Release resources associated with this client. You must call this, - * or significant resources (sockets and memory) may be leaked. - */ - public void close() { - if (mLeakedException != null) { - getConnectionManager().shutdown(); - mLeakedException = null; - } - } - - public HttpParams getParams() { - return delegate.getParams(); - } - - public ClientConnectionManager getConnectionManager() { - return delegate.getConnectionManager(); - } - - public HttpResponse execute(HttpUriRequest request) throws IOException { - return delegate.execute(request); - } - - public HttpResponse execute(HttpUriRequest request, HttpContext context) - throws IOException { - return delegate.execute(request, context); - } - - public HttpResponse execute(HttpHost target, HttpRequest request) - throws IOException { - return delegate.execute(target, request); - } - - public HttpResponse execute(HttpHost target, HttpRequest request, - HttpContext context) throws IOException { - return delegate.execute(target, request, context); - } - - public <T> T execute(HttpUriRequest request, - ResponseHandler<? extends T> responseHandler) - throws IOException, ClientProtocolException { - return delegate.execute(request, responseHandler); - } - - public <T> T execute(HttpUriRequest request, - ResponseHandler<? extends T> responseHandler, HttpContext context) - throws IOException, ClientProtocolException { - return delegate.execute(request, responseHandler, context); - } - - public <T> T execute(HttpHost target, HttpRequest request, - ResponseHandler<? extends T> responseHandler) throws IOException, - ClientProtocolException { - return delegate.execute(target, request, responseHandler); - } - - public <T> T execute(HttpHost target, HttpRequest request, - ResponseHandler<? extends T> responseHandler, HttpContext context) - throws IOException, ClientProtocolException { - return delegate.execute(target, request, responseHandler, context); - } - - /** - * Compress data to send to server. - * Creates a Http Entity holding the gzipped data. - * The data will not be compressed if it is too short. - * @param data The bytes to compress - * @return Entity holding the data - */ - public static AbstractHttpEntity getCompressedEntity(byte data[], ContentResolver resolver) - throws IOException { - AbstractHttpEntity entity; - if (data.length < getMinGzipSize(resolver)) { - entity = new ByteArrayEntity(data); - } else { - ByteArrayOutputStream arr = new ByteArrayOutputStream(); - OutputStream zipper = new GZIPOutputStream(arr); - zipper.write(data); - zipper.close(); - entity = new ByteArrayEntity(arr.toByteArray()); - entity.setContentEncoding("gzip"); - } - return entity; - } - - /** - * Retrieves the minimum size for compressing data. - * Shorter data will not be compressed. - */ - public static long getMinGzipSize(ContentResolver resolver) { - return DEFAULT_SYNC_MIN_GZIP_BYTES; // For now, this is just a constant. - } - - /* cURL logging support. */ - - /** - * Logging tag and level. - */ - private static class LoggingConfiguration { - - private final String tag; - private final int level; - - private LoggingConfiguration(String tag, int level) { - this.tag = tag; - this.level = level; - } - - /** - * Returns true if logging is turned on for this configuration. - */ - private boolean isLoggable() { - return Log.isLoggable(tag, level); - } - - /** - * Prints a message using this configuration. - */ - private void println(String message) { - Log.println(level, tag, message); - } - } - - /** cURL logging configuration. */ - private volatile LoggingConfiguration curlConfiguration; - - /** - * Enables cURL request logging for this client. - * - * @param name to log messages with - * @param level at which to log messages (see {@link android.util.Log}) - */ - public void enableCurlLogging(String name, int level) { - if (name == null) { - throw new NullPointerException("name"); - } - if (level < Log.VERBOSE || level > Log.ASSERT) { - throw new IllegalArgumentException("Level is out of range [" - + Log.VERBOSE + ".." + Log.ASSERT + "]"); - } - - curlConfiguration = new LoggingConfiguration(name, level); - } - - /** - * Disables cURL logging for this client. - */ - public void disableCurlLogging() { - curlConfiguration = null; - } - - /** - * Logs cURL commands equivalent to requests. - */ - private class CurlLogger implements HttpRequestInterceptor { - public void process(HttpRequest request, HttpContext context) - throws HttpException, IOException { - LoggingConfiguration configuration = curlConfiguration; - if (configuration != null - && configuration.isLoggable() - && request instanceof HttpUriRequest) { - // Never print auth token -- we used to check ro.secure=0 to - // enable that, but can't do that in unbundled code. - configuration.println(toCurl((HttpUriRequest) request, false)); - } - } - } - - /** - * Generates a cURL command equivalent to the given request. - */ - private static String toCurl(HttpUriRequest request, boolean logAuthToken) throws IOException { - StringBuilder builder = new StringBuilder(); - - builder.append("curl "); - - // add in the method - builder.append("-X "); - builder.append(request.getMethod()); - builder.append(" "); - - for (Header header: request.getAllHeaders()) { - if (!logAuthToken - && (header.getName().equals("Authorization") || - header.getName().equals("Cookie"))) { - continue; - } - builder.append("--header \""); - builder.append(header.toString().trim()); - builder.append("\" "); - } - - URI uri = request.getURI(); - - // If this is a wrapped request, use the URI from the original - // request instead. getURI() on the wrapper seems to return a - // relative URI. We want an absolute URI. - if (request instanceof RequestWrapper) { - HttpRequest original = ((RequestWrapper) request).getOriginal(); - if (original instanceof HttpUriRequest) { - uri = ((HttpUriRequest) original).getURI(); - } - } - - builder.append("\""); - builder.append(uri); - builder.append("\""); - - if (request instanceof HttpEntityEnclosingRequest) { - HttpEntityEnclosingRequest entityRequest = - (HttpEntityEnclosingRequest) request; - HttpEntity entity = entityRequest.getEntity(); - if (entity != null && entity.isRepeatable()) { - if (entity.getContentLength() < 1024) { - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - entity.writeTo(stream); - - if (isBinaryContent(request)) { - String base64 = Base64.encodeToString(stream.toByteArray(), Base64.NO_WRAP); - builder.insert(0, "echo '" + base64 + "' | base64 -d > /tmp/$$.bin; "); - builder.append(" --data-binary @/tmp/$$.bin"); - } else { - String entityString = stream.toString(); - builder.append(" --data-ascii \"") - .append(entityString) - .append("\""); - } - } else { - builder.append(" [TOO MUCH DATA TO INCLUDE]"); - } - } - } - - return builder.toString(); - } - - private static boolean isBinaryContent(HttpUriRequest request) { - Header[] headers; - headers = request.getHeaders(Headers.CONTENT_ENCODING); - if (headers != null) { - for (Header header : headers) { - if ("gzip".equalsIgnoreCase(header.getValue())) { - return true; - } - } - } - - headers = request.getHeaders(Headers.CONTENT_TYPE); - if (headers != null) { - for (Header header : headers) { - for (String contentType : textContentTypes) { - if (header.getValue().startsWith(contentType)) { - return false; - } - } - } - } - return true; - } - - /** - * Returns the date of the given HTTP date string. This method can identify - * and parse the date formats emitted by common HTTP servers, such as - * <a href="http://www.ietf.org/rfc/rfc0822.txt">RFC 822</a>, - * <a href="http://www.ietf.org/rfc/rfc0850.txt">RFC 850</a>, - * <a href="http://www.ietf.org/rfc/rfc1036.txt">RFC 1036</a>, - * <a href="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</a> and - * <a href="http://www.opengroup.org/onlinepubs/007908799/xsh/asctime.html">ANSI - * C's asctime()</a>. - * - * @return the number of milliseconds since Jan. 1, 1970, midnight GMT. - * @throws IllegalArgumentException if {@code dateString} is not a date or - * of an unsupported format. - */ - public static long parseDate(String dateString) { - return HttpDateTime.parse(dateString); - } -} diff --git a/core/java/android/net/http/AndroidHttpClientConnection.java b/core/java/android/net/http/AndroidHttpClientConnection.java deleted file mode 100644 index 6d48fce..0000000 --- a/core/java/android/net/http/AndroidHttpClientConnection.java +++ /dev/null @@ -1,460 +0,0 @@ -/* - * Copyright (C) 2008 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 android.net.http; - -import org.apache.http.HttpConnection; -import org.apache.http.HttpClientConnection; -import org.apache.http.HttpConnectionMetrics; -import org.apache.http.HttpEntity; -import org.apache.http.HttpEntityEnclosingRequest; -import org.apache.http.HttpException; -import org.apache.http.HttpInetConnection; -import org.apache.http.HttpRequest; -import org.apache.http.HttpResponse; -import org.apache.http.NoHttpResponseException; -import org.apache.http.StatusLine; -import org.apache.http.entity.BasicHttpEntity; -import org.apache.http.entity.ContentLengthStrategy; -import org.apache.http.impl.HttpConnectionMetricsImpl; -import org.apache.http.impl.entity.EntitySerializer; -import org.apache.http.impl.entity.StrictContentLengthStrategy; -import org.apache.http.impl.io.ChunkedInputStream; -import org.apache.http.impl.io.ContentLengthInputStream; -import org.apache.http.impl.io.HttpRequestWriter; -import org.apache.http.impl.io.IdentityInputStream; -import org.apache.http.impl.io.SocketInputBuffer; -import org.apache.http.impl.io.SocketOutputBuffer; -import org.apache.http.io.HttpMessageWriter; -import org.apache.http.io.SessionInputBuffer; -import org.apache.http.io.SessionOutputBuffer; -import org.apache.http.message.BasicLineParser; -import org.apache.http.message.ParserCursor; -import org.apache.http.params.CoreConnectionPNames; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.apache.http.ParseException; -import org.apache.http.util.CharArrayBuffer; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.Socket; -import java.net.SocketException; - -/** - * A alternate class for (@link DefaultHttpClientConnection). - * It has better performance than DefaultHttpClientConnection - * - * {@hide} - */ -public class AndroidHttpClientConnection - implements HttpInetConnection, HttpConnection { - - private SessionInputBuffer inbuffer = null; - private SessionOutputBuffer outbuffer = null; - private int maxHeaderCount; - // store CoreConnectionPNames.MAX_LINE_LENGTH for performance - private int maxLineLength; - - private final EntitySerializer entityserializer; - - private HttpMessageWriter requestWriter = null; - private HttpConnectionMetricsImpl metrics = null; - private volatile boolean open; - private Socket socket = null; - - public AndroidHttpClientConnection() { - this.entityserializer = new EntitySerializer( - new StrictContentLengthStrategy()); - } - - /** - * Bind socket and set HttpParams to AndroidHttpClientConnection - * @param socket outgoing socket - * @param params HttpParams - * @throws IOException - */ - public void bind( - final Socket socket, - final HttpParams params) throws IOException { - if (socket == null) { - throw new IllegalArgumentException("Socket may not be null"); - } - if (params == null) { - throw new IllegalArgumentException("HTTP parameters may not be null"); - } - assertNotOpen(); - socket.setTcpNoDelay(HttpConnectionParams.getTcpNoDelay(params)); - socket.setSoTimeout(HttpConnectionParams.getSoTimeout(params)); - - int linger = HttpConnectionParams.getLinger(params); - if (linger >= 0) { - socket.setSoLinger(linger > 0, linger); - } - this.socket = socket; - - int buffersize = HttpConnectionParams.getSocketBufferSize(params); - this.inbuffer = new SocketInputBuffer(socket, buffersize, params); - this.outbuffer = new SocketOutputBuffer(socket, buffersize, params); - - maxHeaderCount = params.getIntParameter( - CoreConnectionPNames.MAX_HEADER_COUNT, -1); - maxLineLength = params.getIntParameter( - CoreConnectionPNames.MAX_LINE_LENGTH, -1); - - this.requestWriter = new HttpRequestWriter(outbuffer, null, params); - - this.metrics = new HttpConnectionMetricsImpl( - inbuffer.getMetrics(), - outbuffer.getMetrics()); - - this.open = true; - } - - @Override - public String toString() { - StringBuilder buffer = new StringBuilder(); - buffer.append(getClass().getSimpleName()).append("["); - if (isOpen()) { - buffer.append(getRemotePort()); - } else { - buffer.append("closed"); - } - buffer.append("]"); - return buffer.toString(); - } - - - private void assertNotOpen() { - if (this.open) { - throw new IllegalStateException("Connection is already open"); - } - } - - private void assertOpen() { - if (!this.open) { - throw new IllegalStateException("Connection is not open"); - } - } - - public boolean isOpen() { - // to make this method useful, we want to check if the socket is connected - return (this.open && this.socket != null && this.socket.isConnected()); - } - - public InetAddress getLocalAddress() { - if (this.socket != null) { - return this.socket.getLocalAddress(); - } else { - return null; - } - } - - public int getLocalPort() { - if (this.socket != null) { - return this.socket.getLocalPort(); - } else { - return -1; - } - } - - public InetAddress getRemoteAddress() { - if (this.socket != null) { - return this.socket.getInetAddress(); - } else { - return null; - } - } - - public int getRemotePort() { - if (this.socket != null) { - return this.socket.getPort(); - } else { - return -1; - } - } - - public void setSocketTimeout(int timeout) { - assertOpen(); - if (this.socket != null) { - try { - this.socket.setSoTimeout(timeout); - } catch (SocketException ignore) { - // It is not quite clear from the original documentation if there are any - // other legitimate cases for a socket exception to be thrown when setting - // SO_TIMEOUT besides the socket being already closed - } - } - } - - public int getSocketTimeout() { - if (this.socket != null) { - try { - return this.socket.getSoTimeout(); - } catch (SocketException ignore) { - return -1; - } - } else { - return -1; - } - } - - public void shutdown() throws IOException { - this.open = false; - Socket tmpsocket = this.socket; - if (tmpsocket != null) { - tmpsocket.close(); - } - } - - public void close() throws IOException { - if (!this.open) { - return; - } - this.open = false; - doFlush(); - try { - try { - this.socket.shutdownOutput(); - } catch (IOException ignore) { - } - try { - this.socket.shutdownInput(); - } catch (IOException ignore) { - } - } catch (UnsupportedOperationException ignore) { - // if one isn't supported, the other one isn't either - } - this.socket.close(); - } - - /** - * Sends the request line and all headers over the connection. - * @param request the request whose headers to send. - * @throws HttpException - * @throws IOException - */ - public void sendRequestHeader(final HttpRequest request) - throws HttpException, IOException { - if (request == null) { - throw new IllegalArgumentException("HTTP request may not be null"); - } - assertOpen(); - this.requestWriter.write(request); - this.metrics.incrementRequestCount(); - } - - /** - * Sends the request entity over the connection. - * @param request the request whose entity to send. - * @throws HttpException - * @throws IOException - */ - public void sendRequestEntity(final HttpEntityEnclosingRequest request) - throws HttpException, IOException { - if (request == null) { - throw new IllegalArgumentException("HTTP request may not be null"); - } - assertOpen(); - if (request.getEntity() == null) { - return; - } - this.entityserializer.serialize( - this.outbuffer, - request, - request.getEntity()); - } - - protected void doFlush() throws IOException { - this.outbuffer.flush(); - } - - public void flush() throws IOException { - assertOpen(); - doFlush(); - } - - /** - * Parses the response headers and adds them to the - * given {@code headers} object, and returns the response StatusLine - * @param headers store parsed header to headers. - * @throws IOException - * @return StatusLine - * @see HttpClientConnection#receiveResponseHeader() - */ - public StatusLine parseResponseHeader(Headers headers) - throws IOException, ParseException { - assertOpen(); - - CharArrayBuffer current = new CharArrayBuffer(64); - - if (inbuffer.readLine(current) == -1) { - throw new NoHttpResponseException("The target server failed to respond"); - } - - // Create the status line from the status string - StatusLine statusline = BasicLineParser.DEFAULT.parseStatusLine( - current, new ParserCursor(0, current.length())); - - if (HttpLog.LOGV) HttpLog.v("read: " + statusline); - int statusCode = statusline.getStatusCode(); - - // Parse header body - CharArrayBuffer previous = null; - int headerNumber = 0; - while(true) { - if (current == null) { - current = new CharArrayBuffer(64); - } else { - // This must be he buffer used to parse the status - current.clear(); - } - int l = inbuffer.readLine(current); - if (l == -1 || current.length() < 1) { - break; - } - // Parse the header name and value - // Check for folded headers first - // Detect LWS-char see HTTP/1.0 or HTTP/1.1 Section 2.2 - // discussion on folded headers - char first = current.charAt(0); - if ((first == ' ' || first == '\t') && previous != null) { - // we have continuation folded header - // so append value - int start = 0; - int length = current.length(); - while (start < length) { - char ch = current.charAt(start); - if (ch != ' ' && ch != '\t') { - break; - } - start++; - } - if (maxLineLength > 0 && - previous.length() + 1 + current.length() - start > - maxLineLength) { - throw new IOException("Maximum line length limit exceeded"); - } - previous.append(' '); - previous.append(current, start, current.length() - start); - } else { - if (previous != null) { - headers.parseHeader(previous); - } - headerNumber++; - previous = current; - current = null; - } - if (maxHeaderCount > 0 && headerNumber >= maxHeaderCount) { - throw new IOException("Maximum header count exceeded"); - } - } - - if (previous != null) { - headers.parseHeader(previous); - } - - if (statusCode >= 200) { - this.metrics.incrementResponseCount(); - } - return statusline; - } - - /** - * Return the next response entity. - * @param headers contains values for parsing entity - * @see HttpClientConnection#receiveResponseEntity(HttpResponse response) - */ - public HttpEntity receiveResponseEntity(final Headers headers) { - assertOpen(); - BasicHttpEntity entity = new BasicHttpEntity(); - - long len = determineLength(headers); - if (len == ContentLengthStrategy.CHUNKED) { - entity.setChunked(true); - entity.setContentLength(-1); - entity.setContent(new ChunkedInputStream(inbuffer)); - } else if (len == ContentLengthStrategy.IDENTITY) { - entity.setChunked(false); - entity.setContentLength(-1); - entity.setContent(new IdentityInputStream(inbuffer)); - } else { - entity.setChunked(false); - entity.setContentLength(len); - entity.setContent(new ContentLengthInputStream(inbuffer, len)); - } - - String contentTypeHeader = headers.getContentType(); - if (contentTypeHeader != null) { - entity.setContentType(contentTypeHeader); - } - String contentEncodingHeader = headers.getContentEncoding(); - if (contentEncodingHeader != null) { - entity.setContentEncoding(contentEncodingHeader); - } - - return entity; - } - - private long determineLength(final Headers headers) { - long transferEncoding = headers.getTransferEncoding(); - // We use Transfer-Encoding if present and ignore Content-Length. - // RFC2616, 4.4 item number 3 - if (transferEncoding < Headers.NO_TRANSFER_ENCODING) { - return transferEncoding; - } else { - long contentlen = headers.getContentLength(); - if (contentlen > Headers.NO_CONTENT_LENGTH) { - return contentlen; - } else { - return ContentLengthStrategy.IDENTITY; - } - } - } - - /** - * Checks whether this connection has gone down. - * Network connections may get closed during some time of inactivity - * for several reasons. The next time a read is attempted on such a - * connection it will throw an IOException. - * This method tries to alleviate this inconvenience by trying to - * find out if a connection is still usable. Implementations may do - * that by attempting a read with a very small timeout. Thus this - * method may block for a small amount of time before returning a result. - * It is therefore an <i>expensive</i> operation. - * - * @return <code>true</code> if attempts to use this connection are - * likely to succeed, or <code>false</code> if they are likely - * to fail and this connection should be closed - */ - public boolean isStale() { - assertOpen(); - try { - this.inbuffer.isDataAvailable(1); - return false; - } catch (IOException ex) { - return true; - } - } - - /** - * Returns a collection of connection metrcis - * @return HttpConnectionMetrics - */ - public HttpConnectionMetrics getMetrics() { - return this.metrics; - } -} diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java deleted file mode 100644 index bf3fe02..0000000 --- a/core/java/android/net/http/CertificateChainValidator.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2008 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 android.net.http; - -import com.android.org.conscrypt.SSLParametersImpl; -import com.android.org.conscrypt.TrustManagerImpl; - -import android.util.Slog; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.lang.reflect.Method; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; - -/** - * Class responsible for all server certificate validation functionality - * - * {@hide} - */ -public class CertificateChainValidator { - private static final String TAG = "CertificateChainValidator"; - - private static class NoPreloadHolder { - /** - * The singleton instance of the certificate chain validator. - */ - private static final CertificateChainValidator sInstance = new CertificateChainValidator(); - - /** - * The singleton instance of the hostname verifier. - */ - private static final HostnameVerifier sVerifier = HttpsURLConnection - .getDefaultHostnameVerifier(); - } - - private X509TrustManager mTrustManager; - - /** - * @return The singleton instance of the certificates chain validator - */ - public static CertificateChainValidator getInstance() { - return NoPreloadHolder.sInstance; - } - - /** - * Creates a new certificate chain validator. This is a private constructor. - * If you need a Certificate chain validator, call getInstance(). - */ - private CertificateChainValidator() { - try { - TrustManagerFactory tmf = TrustManagerFactory.getInstance("X.509"); - tmf.init((KeyStore) null); - for (TrustManager tm : tmf.getTrustManagers()) { - if (tm instanceof X509TrustManager) { - mTrustManager = (X509TrustManager) tm; - } - } - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("X.509 TrustManagerFactory must be available", e); - } catch (KeyStoreException e) { - throw new RuntimeException("X.509 TrustManagerFactory cannot be initialized", e); - } - - if (mTrustManager == null) { - throw new RuntimeException( - "None of the X.509 TrustManagers are X509TrustManager"); - } - } - - /** - * Performs the handshake and server certificates validation - * Notice a new chain will be rebuilt by tracing the issuer and subject - * before calling checkServerTrusted(). - * And if the last traced certificate is self issued and it is expired, it - * will be dropped. - * @param sslSocket The secure connection socket - * @param domain The website domain - * @return An SSL error object if there is an error and null otherwise - */ - public SslError doHandshakeAndValidateServerCertificates( - HttpsConnection connection, SSLSocket sslSocket, String domain) - throws IOException { - // get a valid SSLSession, close the socket if we fail - SSLSession sslSession = sslSocket.getSession(); - if (!sslSession.isValid()) { - closeSocketThrowException(sslSocket, "failed to perform SSL handshake"); - } - - // retrieve the chain of the server peer certificates - Certificate[] peerCertificates = - sslSocket.getSession().getPeerCertificates(); - - if (peerCertificates == null || peerCertificates.length == 0) { - closeSocketThrowException( - sslSocket, "failed to retrieve peer certificates"); - } else { - // update the SSL certificate associated with the connection - if (connection != null) { - if (peerCertificates[0] != null) { - connection.setCertificate( - new SslCertificate((X509Certificate)peerCertificates[0])); - } - } - } - - return verifyServerDomainAndCertificates((X509Certificate[]) peerCertificates, domain, "RSA"); - } - - /** - * Similar to doHandshakeAndValidateServerCertificates but exposed to JNI for use - * by Chromium HTTPS stack to validate the cert chain. - * @param certChain The bytes for certificates in ASN.1 DER encoded certificates format. - * @param domain The full website hostname and domain - * @param authType The authentication type for the cert chain - * @return An SSL error object if there is an error and null otherwise - */ - public static SslError verifyServerCertificates( - byte[][] certChain, String domain, String authType) - throws IOException { - - if (certChain == null || certChain.length == 0) { - throw new IllegalArgumentException("bad certificate chain"); - } - - X509Certificate[] serverCertificates = new X509Certificate[certChain.length]; - - try { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - for (int i = 0; i < certChain.length; ++i) { - serverCertificates[i] = (X509Certificate) cf.generateCertificate( - new ByteArrayInputStream(certChain[i])); - } - } catch (CertificateException e) { - throw new IOException("can't read certificate", e); - } - - return verifyServerDomainAndCertificates(serverCertificates, domain, authType); - } - - /** - * Handles updates to credential storage. - */ - public static void handleTrustStorageUpdate() { - TrustManagerFactory tmf; - try { - tmf = TrustManagerFactory.getInstance("X.509"); - tmf.init((KeyStore) null); - } catch (NoSuchAlgorithmException e) { - Slog.w(TAG, "Couldn't find default X.509 TrustManagerFactory"); - return; - } catch (KeyStoreException e) { - Slog.w(TAG, "Couldn't initialize default X.509 TrustManagerFactory", e); - return; - } - - TrustManager[] tms = tmf.getTrustManagers(); - boolean sentUpdate = false; - for (TrustManager tm : tms) { - try { - Method updateMethod = tm.getClass().getDeclaredMethod("handleTrustStorageUpdate"); - updateMethod.setAccessible(true); - updateMethod.invoke(tm); - sentUpdate = true; - } catch (Exception e) { - } - } - if (!sentUpdate) { - Slog.w(TAG, "Didn't find a TrustManager to handle CA list update"); - } - } - - /** - * Common code of doHandshakeAndValidateServerCertificates and verifyServerCertificates. - * Calls DomainNamevalidator to verify the domain, and TrustManager to verify the certs. - * @param chain the cert chain in X509 cert format. - * @param domain The full website hostname and domain - * @param authType The authentication type for the cert chain - * @return An SSL error object if there is an error and null otherwise - */ - private static SslError verifyServerDomainAndCertificates( - X509Certificate[] chain, String domain, String authType) - throws IOException { - // check if the first certificate in the chain is for this site - X509Certificate currCertificate = chain[0]; - if (currCertificate == null) { - throw new IllegalArgumentException("certificate for this site is null"); - } - - boolean valid = domain != null - && !domain.isEmpty() - && NoPreloadHolder.sVerifier.verify(domain, - new DelegatingSSLSession.CertificateWrap(currCertificate)); - if (!valid) { - if (HttpLog.LOGV) { - HttpLog.v("certificate not for this host: " + domain); - } - return new SslError(SslError.SSL_IDMISMATCH, currCertificate); - } - - try { - X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultX509TrustManager(); - if (x509TrustManager instanceof TrustManagerImpl) { - TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager; - trustManager.checkServerTrusted(chain, authType, domain); - } else { - x509TrustManager.checkServerTrusted(chain, authType); - } - return null; // No errors. - } catch (GeneralSecurityException e) { - if (HttpLog.LOGV) { - HttpLog.v("failed to validate the certificate chain, error: " + - e.getMessage()); - } - return new SslError(SslError.SSL_UNTRUSTED, currCertificate); - } - } - - /** - * Returns the platform default {@link X509TrustManager}. - */ - private X509TrustManager getTrustManager() { - return mTrustManager; - } - - private void closeSocketThrowException( - SSLSocket socket, String errorMessage, String defaultErrorMessage) - throws IOException { - closeSocketThrowException( - socket, errorMessage != null ? errorMessage : defaultErrorMessage); - } - - private void closeSocketThrowException(SSLSocket socket, - String errorMessage) throws IOException { - if (HttpLog.LOGV) { - HttpLog.v("validation error: " + errorMessage); - } - - if (socket != null) { - SSLSession session = socket.getSession(); - if (session != null) { - session.invalidate(); - } - - socket.close(); - } - - throw new SSLHandshakeException(errorMessage); - } -} diff --git a/core/java/android/net/http/Connection.java b/core/java/android/net/http/Connection.java deleted file mode 100644 index 831bd0e..0000000 --- a/core/java/android/net/http/Connection.java +++ /dev/null @@ -1,575 +0,0 @@ -/* - * Copyright (C) 2007 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 android.net.http; - -import android.content.Context; -import android.os.SystemClock; - -import java.io.IOException; -import java.net.UnknownHostException; -import java.util.LinkedList; - -import javax.net.ssl.SSLHandshakeException; - -import org.apache.http.ConnectionReuseStrategy; -import org.apache.http.HttpEntity; -import org.apache.http.HttpException; -import org.apache.http.HttpHost; -import org.apache.http.HttpVersion; -import org.apache.http.ParseException; -import org.apache.http.ProtocolVersion; -import org.apache.http.protocol.ExecutionContext; -import org.apache.http.protocol.HttpContext; -import org.apache.http.protocol.BasicHttpContext; - -/** - * {@hide} - */ -abstract class Connection { - - /** - * Allow a TCP connection 60 idle seconds before erroring out - */ - static final int SOCKET_TIMEOUT = 60000; - - private static final int SEND = 0; - private static final int READ = 1; - private static final int DRAIN = 2; - private static final int DONE = 3; - private static final String[] states = {"SEND", "READ", "DRAIN", "DONE"}; - - Context mContext; - - /** The low level connection */ - protected AndroidHttpClientConnection mHttpClientConnection = null; - - /** - * The server SSL certificate associated with this connection - * (null if the connection is not secure) - * It would be nice to store the whole certificate chain, but - * we want to keep things as light-weight as possible - */ - protected SslCertificate mCertificate = null; - - /** - * The host this connection is connected to. If using proxy, - * this is set to the proxy address - */ - HttpHost mHost; - - /** true if the connection can be reused for sending more requests */ - private boolean mCanPersist; - - /** context required by ConnectionReuseStrategy. */ - private HttpContext mHttpContext; - - /** set when cancelled */ - private static int STATE_NORMAL = 0; - private static int STATE_CANCEL_REQUESTED = 1; - private int mActive = STATE_NORMAL; - - /** The number of times to try to re-connect (if connect fails). */ - private final static int RETRY_REQUEST_LIMIT = 2; - - private static final int MIN_PIPE = 2; - private static final int MAX_PIPE = 3; - - /** - * Doesn't seem to exist anymore in the new HTTP client, so copied here. - */ - private static final String HTTP_CONNECTION = "http.connection"; - - RequestFeeder mRequestFeeder; - - /** - * Buffer for feeding response blocks to webkit. One block per - * connection reduces memory churn. - */ - private byte[] mBuf; - - protected Connection(Context context, HttpHost host, - RequestFeeder requestFeeder) { - mContext = context; - mHost = host; - mRequestFeeder = requestFeeder; - - mCanPersist = false; - mHttpContext = new BasicHttpContext(null); - } - - HttpHost getHost() { - return mHost; - } - - /** - * connection factory: returns an HTTP or HTTPS connection as - * necessary - */ - static Connection getConnection( - Context context, HttpHost host, HttpHost proxy, - RequestFeeder requestFeeder) { - - if (host.getSchemeName().equals("http")) { - return new HttpConnection(context, host, requestFeeder); - } - - // Otherwise, default to https - return new HttpsConnection(context, host, proxy, requestFeeder); - } - - /** - * @return The server SSL certificate associated with this - * connection (null if the connection is not secure) - */ - /* package */ SslCertificate getCertificate() { - return mCertificate; - } - - /** - * Close current network connection - * Note: this runs in non-network thread - */ - void cancel() { - mActive = STATE_CANCEL_REQUESTED; - closeConnection(); - if (HttpLog.LOGV) HttpLog.v( - "Connection.cancel(): connection closed " + mHost); - } - - /** - * Process requests in queue - * pipelines requests - */ - void processRequests(Request firstRequest) { - Request req = null; - boolean empty; - int error = EventHandler.OK; - Exception exception = null; - - LinkedList<Request> pipe = new LinkedList<Request>(); - - int minPipe = MIN_PIPE, maxPipe = MAX_PIPE; - int state = SEND; - - while (state != DONE) { - if (HttpLog.LOGV) HttpLog.v( - states[state] + " pipe " + pipe.size()); - - /* If a request was cancelled, give other cancel requests - some time to go through so we don't uselessly restart - connections */ - if (mActive == STATE_CANCEL_REQUESTED) { - try { - Thread.sleep(100); - } catch (InterruptedException x) { /* ignore */ } - mActive = STATE_NORMAL; - } - - switch (state) { - case SEND: { - if (pipe.size() == maxPipe) { - state = READ; - break; - } - /* get a request */ - if (firstRequest == null) { - req = mRequestFeeder.getRequest(mHost); - } else { - req = firstRequest; - firstRequest = null; - } - if (req == null) { - state = DRAIN; - break; - } - req.setConnection(this); - - /* Don't work on cancelled requests. */ - if (req.mCancelled) { - if (HttpLog.LOGV) HttpLog.v( - "processRequests(): skipping cancelled request " - + req); - req.complete(); - break; - } - - if (mHttpClientConnection == null || - !mHttpClientConnection.isOpen()) { - /* If this call fails, the address is bad or - the net is down. Punt for now. - - FIXME: blow out entire queue here on - connection failure if net up? */ - - if (!openHttpConnection(req)) { - state = DONE; - break; - } - } - - /* we have a connection, let the event handler - * know of any associated certificate, - * potentially none. - */ - req.mEventHandler.certificate(mCertificate); - - try { - /* FIXME: don't increment failure count if old - connection? There should not be a penalty for - attempting to reuse an old connection */ - req.sendRequest(mHttpClientConnection); - } catch (HttpException e) { - exception = e; - error = EventHandler.ERROR; - } catch (IOException e) { - exception = e; - error = EventHandler.ERROR_IO; - } catch (IllegalStateException e) { - exception = e; - error = EventHandler.ERROR_IO; - } - if (exception != null) { - if (httpFailure(req, error, exception) && - !req.mCancelled) { - /* retry request if not permanent failure - or cancelled */ - pipe.addLast(req); - } - exception = null; - state = clearPipe(pipe) ? DONE : SEND; - minPipe = maxPipe = 1; - break; - } - - pipe.addLast(req); - if (!mCanPersist) state = READ; - break; - - } - case DRAIN: - case READ: { - empty = !mRequestFeeder.haveRequest(mHost); - int pipeSize = pipe.size(); - if (state != DRAIN && pipeSize < minPipe && - !empty && mCanPersist) { - state = SEND; - break; - } else if (pipeSize == 0) { - /* Done if no other work to do */ - state = empty ? DONE : SEND; - break; - } - - req = (Request)pipe.removeFirst(); - if (HttpLog.LOGV) HttpLog.v( - "processRequests() reading " + req); - - try { - req.readResponse(mHttpClientConnection); - } catch (ParseException e) { - exception = e; - error = EventHandler.ERROR_IO; - } catch (IOException e) { - exception = e; - error = EventHandler.ERROR_IO; - } catch (IllegalStateException e) { - exception = e; - error = EventHandler.ERROR_IO; - } - if (exception != null) { - if (httpFailure(req, error, exception) && - !req.mCancelled) { - /* retry request if not permanent failure - or cancelled */ - req.reset(); - pipe.addFirst(req); - } - exception = null; - mCanPersist = false; - } - if (!mCanPersist) { - if (HttpLog.LOGV) HttpLog.v( - "processRequests(): no persist, closing " + - mHost); - - closeConnection(); - - mHttpContext.removeAttribute(HTTP_CONNECTION); - clearPipe(pipe); - minPipe = maxPipe = 1; - state = SEND; - } - break; - } - } - } - } - - /** - * After a send/receive failure, any pipelined requests must be - * cleared back to the mRequest queue - * @return true if mRequests is empty after pipe cleared - */ - private boolean clearPipe(LinkedList<Request> pipe) { - boolean empty = true; - if (HttpLog.LOGV) HttpLog.v( - "Connection.clearPipe(): clearing pipe " + pipe.size()); - synchronized (mRequestFeeder) { - Request tReq; - while (!pipe.isEmpty()) { - tReq = (Request)pipe.removeLast(); - if (HttpLog.LOGV) HttpLog.v( - "clearPipe() adding back " + mHost + " " + tReq); - mRequestFeeder.requeueRequest(tReq); - empty = false; - } - if (empty) empty = !mRequestFeeder.haveRequest(mHost); - } - return empty; - } - - /** - * @return true on success - */ - private boolean openHttpConnection(Request req) { - - long now = SystemClock.uptimeMillis(); - int error = EventHandler.OK; - Exception exception = null; - - try { - // reset the certificate to null before opening a connection - mCertificate = null; - mHttpClientConnection = openConnection(req); - if (mHttpClientConnection != null) { - mHttpClientConnection.setSocketTimeout(SOCKET_TIMEOUT); - mHttpContext.setAttribute(HTTP_CONNECTION, - mHttpClientConnection); - } else { - // we tried to do SSL tunneling, failed, - // and need to drop the request; - // we have already informed the handler - req.mFailCount = RETRY_REQUEST_LIMIT; - return false; - } - } catch (UnknownHostException e) { - if (HttpLog.LOGV) HttpLog.v("Failed to open connection"); - error = EventHandler.ERROR_LOOKUP; - exception = e; - } catch (IllegalArgumentException e) { - if (HttpLog.LOGV) HttpLog.v("Illegal argument exception"); - error = EventHandler.ERROR_CONNECT; - req.mFailCount = RETRY_REQUEST_LIMIT; - exception = e; - } catch (SSLConnectionClosedByUserException e) { - // hack: if we have an SSL connection failure, - // we don't want to reconnect - req.mFailCount = RETRY_REQUEST_LIMIT; - // no error message - return false; - } catch (SSLHandshakeException e) { - // hack: if we have an SSL connection failure, - // we don't want to reconnect - req.mFailCount = RETRY_REQUEST_LIMIT; - if (HttpLog.LOGV) HttpLog.v( - "SSL exception performing handshake"); - error = EventHandler.ERROR_FAILED_SSL_HANDSHAKE; - exception = e; - } catch (IOException e) { - error = EventHandler.ERROR_CONNECT; - exception = e; - } - - if (HttpLog.LOGV) { - long now2 = SystemClock.uptimeMillis(); - HttpLog.v("Connection.openHttpConnection() " + - (now2 - now) + " " + mHost); - } - - if (error == EventHandler.OK) { - return true; - } else { - if (req.mFailCount < RETRY_REQUEST_LIMIT) { - // requeue - mRequestFeeder.requeueRequest(req); - req.mFailCount++; - } else { - httpFailure(req, error, exception); - } - return error == EventHandler.OK; - } - } - - /** - * Helper. Calls the mEventHandler's error() method only if - * request failed permanently. Increments mFailcount on failure. - * - * Increments failcount only if the network is believed to be - * connected - * - * @return true if request can be retried (less than - * RETRY_REQUEST_LIMIT failures have occurred). - */ - private boolean httpFailure(Request req, int errorId, Exception e) { - boolean ret = true; - - // e.printStackTrace(); - if (HttpLog.LOGV) HttpLog.v( - "httpFailure() ******* " + e + " count " + req.mFailCount + - " " + mHost + " " + req.getUri()); - - if (++req.mFailCount >= RETRY_REQUEST_LIMIT) { - ret = false; - String error; - if (errorId < 0) { - error = getEventHandlerErrorString(errorId); - } else { - Throwable cause = e.getCause(); - error = cause != null ? cause.toString() : e.getMessage(); - } - req.mEventHandler.error(errorId, error); - req.complete(); - } - - closeConnection(); - mHttpContext.removeAttribute(HTTP_CONNECTION); - - return ret; - } - - private static String getEventHandlerErrorString(int errorId) { - switch (errorId) { - case EventHandler.OK: - return "OK"; - - case EventHandler.ERROR: - return "ERROR"; - - case EventHandler.ERROR_LOOKUP: - return "ERROR_LOOKUP"; - - case EventHandler.ERROR_UNSUPPORTED_AUTH_SCHEME: - return "ERROR_UNSUPPORTED_AUTH_SCHEME"; - - case EventHandler.ERROR_AUTH: - return "ERROR_AUTH"; - - case EventHandler.ERROR_PROXYAUTH: - return "ERROR_PROXYAUTH"; - - case EventHandler.ERROR_CONNECT: - return "ERROR_CONNECT"; - - case EventHandler.ERROR_IO: - return "ERROR_IO"; - - case EventHandler.ERROR_TIMEOUT: - return "ERROR_TIMEOUT"; - - case EventHandler.ERROR_REDIRECT_LOOP: - return "ERROR_REDIRECT_LOOP"; - - case EventHandler.ERROR_UNSUPPORTED_SCHEME: - return "ERROR_UNSUPPORTED_SCHEME"; - - case EventHandler.ERROR_FAILED_SSL_HANDSHAKE: - return "ERROR_FAILED_SSL_HANDSHAKE"; - - case EventHandler.ERROR_BAD_URL: - return "ERROR_BAD_URL"; - - case EventHandler.FILE_ERROR: - return "FILE_ERROR"; - - case EventHandler.FILE_NOT_FOUND_ERROR: - return "FILE_NOT_FOUND_ERROR"; - - case EventHandler.TOO_MANY_REQUESTS_ERROR: - return "TOO_MANY_REQUESTS_ERROR"; - - default: - return "UNKNOWN_ERROR"; - } - } - - HttpContext getHttpContext() { - return mHttpContext; - } - - /** - * Use same logic as ConnectionReuseStrategy - * @see ConnectionReuseStrategy - */ - private boolean keepAlive(HttpEntity entity, - ProtocolVersion ver, int connType, final HttpContext context) { - org.apache.http.HttpConnection conn = (org.apache.http.HttpConnection) - context.getAttribute(ExecutionContext.HTTP_CONNECTION); - - if (conn != null && !conn.isOpen()) - return false; - // do NOT check for stale connection, that is an expensive operation - - if (entity != null) { - if (entity.getContentLength() < 0) { - if (!entity.isChunked() || ver.lessEquals(HttpVersion.HTTP_1_0)) { - // if the content length is not known and is not chunk - // encoded, the connection cannot be reused - return false; - } - } - } - // Check for 'Connection' directive - if (connType == Headers.CONN_CLOSE) { - return false; - } else if (connType == Headers.CONN_KEEP_ALIVE) { - return true; - } - // Resorting to protocol version default close connection policy - return !ver.lessEquals(HttpVersion.HTTP_1_0); - } - - void setCanPersist(HttpEntity entity, ProtocolVersion ver, int connType) { - mCanPersist = keepAlive(entity, ver, connType, mHttpContext); - } - - void setCanPersist(boolean canPersist) { - mCanPersist = canPersist; - } - - boolean getCanPersist() { - return mCanPersist; - } - - /** typically http or https... set by subclass */ - abstract String getScheme(); - abstract void closeConnection(); - abstract AndroidHttpClientConnection openConnection(Request req) throws IOException; - - /** - * Prints request queue to log, for debugging. - * returns request count - */ - public synchronized String toString() { - return mHost.toString(); - } - - byte[] getBuf() { - if (mBuf == null) mBuf = new byte[8192]; - return mBuf; - } - -} diff --git a/core/java/android/net/http/ConnectionThread.java b/core/java/android/net/http/ConnectionThread.java deleted file mode 100644 index d825530..0000000 --- a/core/java/android/net/http/ConnectionThread.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2008 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 android.net.http; - -import android.content.Context; -import android.os.SystemClock; - -import java.lang.Thread; - -/** - * {@hide} - */ -class ConnectionThread extends Thread { - - static final int WAIT_TIMEOUT = 5000; - static final int WAIT_TICK = 1000; - - // Performance probe - long mCurrentThreadTime; - long mTotalThreadTime; - - private boolean mWaiting; - private volatile boolean mRunning = true; - private Context mContext; - private RequestQueue.ConnectionManager mConnectionManager; - private RequestFeeder mRequestFeeder; - - private int mId; - Connection mConnection; - - ConnectionThread(Context context, - int id, - RequestQueue.ConnectionManager connectionManager, - RequestFeeder requestFeeder) { - super(); - mContext = context; - setName("http" + id); - mId = id; - mConnectionManager = connectionManager; - mRequestFeeder = requestFeeder; - } - - void requestStop() { - synchronized (mRequestFeeder) { - mRunning = false; - mRequestFeeder.notify(); - } - } - - /** - * Loop until app shutdown. Runs connections in priority - * order. - */ - public void run() { - android.os.Process.setThreadPriority( - android.os.Process.THREAD_PRIORITY_DEFAULT + - android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE); - - // these are used to get performance data. When it is not in the timing, - // mCurrentThreadTime is 0. When it starts timing, mCurrentThreadTime is - // first set to -1, it will be set to the current thread time when the - // next request starts. - mCurrentThreadTime = 0; - mTotalThreadTime = 0; - - while (mRunning) { - if (mCurrentThreadTime == -1) { - mCurrentThreadTime = SystemClock.currentThreadTimeMillis(); - } - - Request request; - - /* Get a request to process */ - request = mRequestFeeder.getRequest(); - - /* wait for work */ - if (request == null) { - synchronized(mRequestFeeder) { - if (HttpLog.LOGV) HttpLog.v("ConnectionThread: Waiting for work"); - mWaiting = true; - try { - mRequestFeeder.wait(); - } catch (InterruptedException e) { - } - mWaiting = false; - if (mCurrentThreadTime != 0) { - mCurrentThreadTime = SystemClock - .currentThreadTimeMillis(); - } - } - } else { - if (HttpLog.LOGV) HttpLog.v("ConnectionThread: new request " + - request.mHost + " " + request ); - - mConnection = mConnectionManager.getConnection(mContext, - request.mHost); - mConnection.processRequests(request); - if (mConnection.getCanPersist()) { - if (!mConnectionManager.recycleConnection(mConnection)) { - mConnection.closeConnection(); - } - } else { - mConnection.closeConnection(); - } - mConnection = null; - - if (mCurrentThreadTime > 0) { - long start = mCurrentThreadTime; - mCurrentThreadTime = SystemClock.currentThreadTimeMillis(); - mTotalThreadTime += mCurrentThreadTime - start; - } - } - - } - } - - public synchronized String toString() { - String con = mConnection == null ? "" : mConnection.toString(); - String active = mWaiting ? "w" : "a"; - return "cid " + mId + " " + active + " " + con; - } - -} diff --git a/core/java/android/net/http/DelegatingSSLSession.java b/core/java/android/net/http/DelegatingSSLSession.java deleted file mode 100644 index 98fbe21..0000000 --- a/core/java/android/net/http/DelegatingSSLSession.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2014 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 android.net.http; - -import java.security.Principal; -import java.security.cert.Certificate; -import java.security.cert.X509Certificate; - -import javax.net.ssl.SSLPeerUnverifiedException; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSessionContext; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.X509TrustManager; - -/** - * This is only used when a {@code certificate} is available but usage - * requires a {@link SSLSession}. - * - * @hide - */ -public class DelegatingSSLSession implements SSLSession { - protected DelegatingSSLSession() { - } - - public static class CertificateWrap extends DelegatingSSLSession { - private final Certificate mCertificate; - - public CertificateWrap(Certificate certificate) { - mCertificate = certificate; - } - - @Override - public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { - return new Certificate[] { mCertificate }; - } - } - - - @Override - public int getApplicationBufferSize() { - throw new UnsupportedOperationException(); - } - - @Override - public String getCipherSuite() { - throw new UnsupportedOperationException(); - } - - @Override - public long getCreationTime() { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] getId() { - throw new UnsupportedOperationException(); - } - - @Override - public long getLastAccessedTime() { - throw new UnsupportedOperationException(); - } - - @Override - public Certificate[] getLocalCertificates() { - throw new UnsupportedOperationException(); - } - - @Override - public Principal getLocalPrincipal() { - throw new UnsupportedOperationException(); - } - - @Override - public int getPacketBufferSize() { - throw new UnsupportedOperationException(); - } - - @Override - public javax.security.cert.X509Certificate[] getPeerCertificateChain() - throws SSLPeerUnverifiedException { - throw new UnsupportedOperationException(); - } - - @Override - public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { - throw new UnsupportedOperationException(); - } - - @Override - public String getPeerHost() { - throw new UnsupportedOperationException(); - } - - @Override - public int getPeerPort() { - throw new UnsupportedOperationException(); - } - - @Override - public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { - throw new UnsupportedOperationException(); - } - - @Override - public String getProtocol() { - throw new UnsupportedOperationException(); - } - - @Override - public SSLSessionContext getSessionContext() { - throw new UnsupportedOperationException(); - } - - @Override - public Object getValue(String name) { - throw new UnsupportedOperationException(); - } - - @Override - public String[] getValueNames() { - throw new UnsupportedOperationException(); - } - - @Override - public void invalidate() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isValid() { - throw new UnsupportedOperationException(); - } - - @Override - public void putValue(String name, Object value) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeValue(String name) { - throw new UnsupportedOperationException(); - } -} diff --git a/core/java/android/net/http/EventHandler.java b/core/java/android/net/http/EventHandler.java deleted file mode 100644 index 3fd471d..0000000 --- a/core/java/android/net/http/EventHandler.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2006 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 android.net.http; - - -/** - * Callbacks in this interface are made as an HTTP request is - * processed. The normal order of callbacks is status(), headers(), - * then multiple data() then endData(). handleSslErrorRequest(), if - * there is an SSL certificate error. error() can occur anywhere - * in the transaction. - * - * {@hide} - */ - -public interface EventHandler { - - /** - * Error codes used in the error() callback. Positive error codes - * are reserved for codes sent by http servers. Negative error - * codes are connection/parsing failures, etc. - */ - - /** Success */ - public static final int OK = 0; - /** Generic error */ - public static final int ERROR = -1; - /** Server or proxy hostname lookup failed */ - public static final int ERROR_LOOKUP = -2; - /** Unsupported authentication scheme (ie, not basic or digest) */ - public static final int ERROR_UNSUPPORTED_AUTH_SCHEME = -3; - /** User authentication failed on server */ - public static final int ERROR_AUTH = -4; - /** User authentication failed on proxy */ - public static final int ERROR_PROXYAUTH = -5; - /** Could not connect to server */ - public static final int ERROR_CONNECT = -6; - /** Failed to write to or read from server */ - public static final int ERROR_IO = -7; - /** Connection timed out */ - public static final int ERROR_TIMEOUT = -8; - /** Too many redirects */ - public static final int ERROR_REDIRECT_LOOP = -9; - /** Unsupported URI scheme (ie, not http, https, etc) */ - public static final int ERROR_UNSUPPORTED_SCHEME = -10; - /** Failed to perform SSL handshake */ - public static final int ERROR_FAILED_SSL_HANDSHAKE = -11; - /** Bad URL */ - public static final int ERROR_BAD_URL = -12; - /** Generic file error for file:/// loads */ - public static final int FILE_ERROR = -13; - /** File not found error for file:/// loads */ - public static final int FILE_NOT_FOUND_ERROR = -14; - /** Too many requests queued */ - public static final int TOO_MANY_REQUESTS_ERROR = -15; - - /** - * Called after status line has been sucessfully processed. - * @param major_version HTTP version advertised by server. major - * is the part before the "." - * @param minor_version HTTP version advertised by server. minor - * is the part after the "." - * @param code HTTP Status code. See RFC 2616. - * @param reason_phrase Textual explanation sent by server - */ - public void status(int major_version, - int minor_version, - int code, - String reason_phrase); - - /** - * Called after all headers are successfully processed. - */ - public void headers(Headers headers); - - /** - * An array containing all or part of the http body as read from - * the server. - * @param data A byte array containing the content - * @param len The length of valid content in data - * - * Note: chunked and compressed encodings are handled within - * android.net.http. Decoded data is passed through this - * interface. - */ - public void data(byte[] data, int len); - - /** - * Called when the document is completely read. No more data() - * callbacks will be made after this call - */ - public void endData(); - - /** - * SSL certificate callback called before resource request is - * made, which will be null for insecure connection. - */ - public void certificate(SslCertificate certificate); - - /** - * There was trouble. - * @param id One of the error codes defined below - * @param description of error - */ - public void error(int id, String description); - - /** - * SSL certificate error callback. Handles SSL error(s) on the way - * up to the user. The callback has to make sure that restartConnection() is called, - * otherwise the connection will be suspended indefinitely. - * @return True if the callback can handle the error, which means it will - * call restartConnection() to unblock the thread later, - * otherwise return false. - */ - public boolean handleSslErrorRequest(SslError error); - -} diff --git a/core/java/android/net/http/Headers.java b/core/java/android/net/http/Headers.java deleted file mode 100644 index 0f8b105..0000000 --- a/core/java/android/net/http/Headers.java +++ /dev/null @@ -1,521 +0,0 @@ -/* - * Copyright (C) 2006 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 android.net.http; - -import android.util.Log; - -import java.util.ArrayList; - -import org.apache.http.HeaderElement; -import org.apache.http.entity.ContentLengthStrategy; -import org.apache.http.message.BasicHeaderValueParser; -import org.apache.http.message.ParserCursor; -import org.apache.http.protocol.HTTP; -import org.apache.http.util.CharArrayBuffer; - -/** - * Manages received headers - * - * {@hide} - */ -public final class Headers { - private static final String LOGTAG = "Http"; - - // header parsing constant - /** - * indicate HTTP 1.0 connection close after the response - */ - public final static int CONN_CLOSE = 1; - /** - * indicate HTTP 1.1 connection keep alive - */ - public final static int CONN_KEEP_ALIVE = 2; - - // initial values. - public final static int NO_CONN_TYPE = 0; - public final static long NO_TRANSFER_ENCODING = 0; - public final static long NO_CONTENT_LENGTH = -1; - - // header strings - public final static String TRANSFER_ENCODING = "transfer-encoding"; - public final static String CONTENT_LEN = "content-length"; - public final static String CONTENT_TYPE = "content-type"; - public final static String CONTENT_ENCODING = "content-encoding"; - public final static String CONN_DIRECTIVE = "connection"; - - public final static String LOCATION = "location"; - public final static String PROXY_CONNECTION = "proxy-connection"; - - public final static String WWW_AUTHENTICATE = "www-authenticate"; - public final static String PROXY_AUTHENTICATE = "proxy-authenticate"; - public final static String CONTENT_DISPOSITION = "content-disposition"; - public final static String ACCEPT_RANGES = "accept-ranges"; - public final static String EXPIRES = "expires"; - public final static String CACHE_CONTROL = "cache-control"; - public final static String LAST_MODIFIED = "last-modified"; - public final static String ETAG = "etag"; - public final static String SET_COOKIE = "set-cookie"; - public final static String PRAGMA = "pragma"; - public final static String REFRESH = "refresh"; - public final static String X_PERMITTED_CROSS_DOMAIN_POLICIES = "x-permitted-cross-domain-policies"; - - // following hash are generated by String.hashCode() - private final static int HASH_TRANSFER_ENCODING = 1274458357; - private final static int HASH_CONTENT_LEN = -1132779846; - private final static int HASH_CONTENT_TYPE = 785670158; - private final static int HASH_CONTENT_ENCODING = 2095084583; - private final static int HASH_CONN_DIRECTIVE = -775651618; - private final static int HASH_LOCATION = 1901043637; - private final static int HASH_PROXY_CONNECTION = 285929373; - private final static int HASH_WWW_AUTHENTICATE = -243037365; - private final static int HASH_PROXY_AUTHENTICATE = -301767724; - private final static int HASH_CONTENT_DISPOSITION = -1267267485; - private final static int HASH_ACCEPT_RANGES = 1397189435; - private final static int HASH_EXPIRES = -1309235404; - private final static int HASH_CACHE_CONTROL = -208775662; - private final static int HASH_LAST_MODIFIED = 150043680; - private final static int HASH_ETAG = 3123477; - private final static int HASH_SET_COOKIE = 1237214767; - private final static int HASH_PRAGMA = -980228804; - private final static int HASH_REFRESH = 1085444827; - private final static int HASH_X_PERMITTED_CROSS_DOMAIN_POLICIES = -1345594014; - - // keep any headers that require direct access in a presized - // string array - private final static int IDX_TRANSFER_ENCODING = 0; - private final static int IDX_CONTENT_LEN = 1; - private final static int IDX_CONTENT_TYPE = 2; - private final static int IDX_CONTENT_ENCODING = 3; - private final static int IDX_CONN_DIRECTIVE = 4; - private final static int IDX_LOCATION = 5; - private final static int IDX_PROXY_CONNECTION = 6; - private final static int IDX_WWW_AUTHENTICATE = 7; - private final static int IDX_PROXY_AUTHENTICATE = 8; - private final static int IDX_CONTENT_DISPOSITION = 9; - private final static int IDX_ACCEPT_RANGES = 10; - private final static int IDX_EXPIRES = 11; - private final static int IDX_CACHE_CONTROL = 12; - private final static int IDX_LAST_MODIFIED = 13; - private final static int IDX_ETAG = 14; - private final static int IDX_SET_COOKIE = 15; - private final static int IDX_PRAGMA = 16; - private final static int IDX_REFRESH = 17; - private final static int IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES = 18; - - private final static int HEADER_COUNT = 19; - - /* parsed values */ - private long transferEncoding; - private long contentLength; // Content length of the incoming data - private int connectionType; - private ArrayList<String> cookies = new ArrayList<String>(2); - - private String[] mHeaders = new String[HEADER_COUNT]; - private final static String[] sHeaderNames = { - TRANSFER_ENCODING, - CONTENT_LEN, - CONTENT_TYPE, - CONTENT_ENCODING, - CONN_DIRECTIVE, - LOCATION, - PROXY_CONNECTION, - WWW_AUTHENTICATE, - PROXY_AUTHENTICATE, - CONTENT_DISPOSITION, - ACCEPT_RANGES, - EXPIRES, - CACHE_CONTROL, - LAST_MODIFIED, - ETAG, - SET_COOKIE, - PRAGMA, - REFRESH, - X_PERMITTED_CROSS_DOMAIN_POLICIES - }; - - // Catch-all for headers not explicitly handled - private ArrayList<String> mExtraHeaderNames = new ArrayList<String>(4); - private ArrayList<String> mExtraHeaderValues = new ArrayList<String>(4); - - public Headers() { - transferEncoding = NO_TRANSFER_ENCODING; - contentLength = NO_CONTENT_LENGTH; - connectionType = NO_CONN_TYPE; - } - - public void parseHeader(CharArrayBuffer buffer) { - int pos = setLowercaseIndexOf(buffer, ':'); - if (pos == -1) { - return; - } - String name = buffer.substringTrimmed(0, pos); - if (name.length() == 0) { - return; - } - pos++; - - String val = buffer.substringTrimmed(pos, buffer.length()); - if (HttpLog.LOGV) { - HttpLog.v("hdr " + buffer.length() + " " + buffer); - } - - switch (name.hashCode()) { - case HASH_TRANSFER_ENCODING: - if (name.equals(TRANSFER_ENCODING)) { - mHeaders[IDX_TRANSFER_ENCODING] = val; - HeaderElement[] encodings = BasicHeaderValueParser.DEFAULT - .parseElements(buffer, new ParserCursor(pos, - buffer.length())); - // The chunked encoding must be the last one applied RFC2616, - // 14.41 - int len = encodings.length; - if (HTTP.IDENTITY_CODING.equalsIgnoreCase(val)) { - transferEncoding = ContentLengthStrategy.IDENTITY; - } else if ((len > 0) - && (HTTP.CHUNK_CODING - .equalsIgnoreCase(encodings[len - 1].getName()))) { - transferEncoding = ContentLengthStrategy.CHUNKED; - } else { - transferEncoding = ContentLengthStrategy.IDENTITY; - } - } - break; - case HASH_CONTENT_LEN: - if (name.equals(CONTENT_LEN)) { - mHeaders[IDX_CONTENT_LEN] = val; - try { - contentLength = Long.parseLong(val); - } catch (NumberFormatException e) { - if (false) { - Log.v(LOGTAG, "Headers.headers(): error parsing" - + " content length: " + buffer.toString()); - } - } - } - break; - case HASH_CONTENT_TYPE: - if (name.equals(CONTENT_TYPE)) { - mHeaders[IDX_CONTENT_TYPE] = val; - } - break; - case HASH_CONTENT_ENCODING: - if (name.equals(CONTENT_ENCODING)) { - mHeaders[IDX_CONTENT_ENCODING] = val; - } - break; - case HASH_CONN_DIRECTIVE: - if (name.equals(CONN_DIRECTIVE)) { - mHeaders[IDX_CONN_DIRECTIVE] = val; - setConnectionType(buffer, pos); - } - break; - case HASH_LOCATION: - if (name.equals(LOCATION)) { - mHeaders[IDX_LOCATION] = val; - } - break; - case HASH_PROXY_CONNECTION: - if (name.equals(PROXY_CONNECTION)) { - mHeaders[IDX_PROXY_CONNECTION] = val; - setConnectionType(buffer, pos); - } - break; - case HASH_WWW_AUTHENTICATE: - if (name.equals(WWW_AUTHENTICATE)) { - mHeaders[IDX_WWW_AUTHENTICATE] = val; - } - break; - case HASH_PROXY_AUTHENTICATE: - if (name.equals(PROXY_AUTHENTICATE)) { - mHeaders[IDX_PROXY_AUTHENTICATE] = val; - } - break; - case HASH_CONTENT_DISPOSITION: - if (name.equals(CONTENT_DISPOSITION)) { - mHeaders[IDX_CONTENT_DISPOSITION] = val; - } - break; - case HASH_ACCEPT_RANGES: - if (name.equals(ACCEPT_RANGES)) { - mHeaders[IDX_ACCEPT_RANGES] = val; - } - break; - case HASH_EXPIRES: - if (name.equals(EXPIRES)) { - mHeaders[IDX_EXPIRES] = val; - } - break; - case HASH_CACHE_CONTROL: - if (name.equals(CACHE_CONTROL)) { - // In case where we receive more than one header, create a ',' separated list. - // This should be ok, according to RFC 2616 chapter 4.2 - if (mHeaders[IDX_CACHE_CONTROL] != null && - mHeaders[IDX_CACHE_CONTROL].length() > 0) { - mHeaders[IDX_CACHE_CONTROL] += (',' + val); - } else { - mHeaders[IDX_CACHE_CONTROL] = val; - } - } - break; - case HASH_LAST_MODIFIED: - if (name.equals(LAST_MODIFIED)) { - mHeaders[IDX_LAST_MODIFIED] = val; - } - break; - case HASH_ETAG: - if (name.equals(ETAG)) { - mHeaders[IDX_ETAG] = val; - } - break; - case HASH_SET_COOKIE: - if (name.equals(SET_COOKIE)) { - mHeaders[IDX_SET_COOKIE] = val; - cookies.add(val); - } - break; - case HASH_PRAGMA: - if (name.equals(PRAGMA)) { - mHeaders[IDX_PRAGMA] = val; - } - break; - case HASH_REFRESH: - if (name.equals(REFRESH)) { - mHeaders[IDX_REFRESH] = val; - } - break; - case HASH_X_PERMITTED_CROSS_DOMAIN_POLICIES: - if (name.equals(X_PERMITTED_CROSS_DOMAIN_POLICIES)) { - mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES] = val; - } - break; - default: - mExtraHeaderNames.add(name); - mExtraHeaderValues.add(val); - } - } - - public long getTransferEncoding() { - return transferEncoding; - } - - public long getContentLength() { - return contentLength; - } - - public int getConnectionType() { - return connectionType; - } - - public String getContentType() { - return mHeaders[IDX_CONTENT_TYPE]; - } - - public String getContentEncoding() { - return mHeaders[IDX_CONTENT_ENCODING]; - } - - public String getLocation() { - return mHeaders[IDX_LOCATION]; - } - - public String getWwwAuthenticate() { - return mHeaders[IDX_WWW_AUTHENTICATE]; - } - - public String getProxyAuthenticate() { - return mHeaders[IDX_PROXY_AUTHENTICATE]; - } - - public String getContentDisposition() { - return mHeaders[IDX_CONTENT_DISPOSITION]; - } - - public String getAcceptRanges() { - return mHeaders[IDX_ACCEPT_RANGES]; - } - - public String getExpires() { - return mHeaders[IDX_EXPIRES]; - } - - public String getCacheControl() { - return mHeaders[IDX_CACHE_CONTROL]; - } - - public String getLastModified() { - return mHeaders[IDX_LAST_MODIFIED]; - } - - public String getEtag() { - return mHeaders[IDX_ETAG]; - } - - public ArrayList<String> getSetCookie() { - return this.cookies; - } - - public String getPragma() { - return mHeaders[IDX_PRAGMA]; - } - - public String getRefresh() { - return mHeaders[IDX_REFRESH]; - } - - public String getXPermittedCrossDomainPolicies() { - return mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES]; - } - - public void setContentLength(long value) { - this.contentLength = value; - } - - public void setContentType(String value) { - mHeaders[IDX_CONTENT_TYPE] = value; - } - - public void setContentEncoding(String value) { - mHeaders[IDX_CONTENT_ENCODING] = value; - } - - public void setLocation(String value) { - mHeaders[IDX_LOCATION] = value; - } - - public void setWwwAuthenticate(String value) { - mHeaders[IDX_WWW_AUTHENTICATE] = value; - } - - public void setProxyAuthenticate(String value) { - mHeaders[IDX_PROXY_AUTHENTICATE] = value; - } - - public void setContentDisposition(String value) { - mHeaders[IDX_CONTENT_DISPOSITION] = value; - } - - public void setAcceptRanges(String value) { - mHeaders[IDX_ACCEPT_RANGES] = value; - } - - public void setExpires(String value) { - mHeaders[IDX_EXPIRES] = value; - } - - public void setCacheControl(String value) { - mHeaders[IDX_CACHE_CONTROL] = value; - } - - public void setLastModified(String value) { - mHeaders[IDX_LAST_MODIFIED] = value; - } - - public void setEtag(String value) { - mHeaders[IDX_ETAG] = value; - } - - public void setXPermittedCrossDomainPolicies(String value) { - mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES] = value; - } - - public interface HeaderCallback { - public void header(String name, String value); - } - - /** - * Reports all non-null headers to the callback - */ - public void getHeaders(HeaderCallback hcb) { - for (int i = 0; i < HEADER_COUNT; i++) { - String h = mHeaders[i]; - if (h != null) { - hcb.header(sHeaderNames[i], h); - } - } - int extraLen = mExtraHeaderNames.size(); - for (int i = 0; i < extraLen; i++) { - if (false) { - HttpLog.v("Headers.getHeaders() extra: " + i + " " + - mExtraHeaderNames.get(i) + " " + mExtraHeaderValues.get(i)); - } - hcb.header(mExtraHeaderNames.get(i), - mExtraHeaderValues.get(i)); - } - - } - - private void setConnectionType(CharArrayBuffer buffer, int pos) { - if (containsIgnoreCaseTrimmed(buffer, pos, HTTP.CONN_CLOSE)) { - connectionType = CONN_CLOSE; - } else if (containsIgnoreCaseTrimmed( - buffer, pos, HTTP.CONN_KEEP_ALIVE)) { - connectionType = CONN_KEEP_ALIVE; - } - } - - - /** - * Returns true if the buffer contains the given string. Ignores leading - * whitespace and case. - * - * @param buffer to search - * @param beginIndex index at which we should start - * @param str to search for - */ - static boolean containsIgnoreCaseTrimmed(CharArrayBuffer buffer, - int beginIndex, final String str) { - int len = buffer.length(); - char[] chars = buffer.buffer(); - while (beginIndex < len && HTTP.isWhitespace(chars[beginIndex])) { - beginIndex++; - } - int size = str.length(); - boolean ok = len >= (beginIndex + size); - for (int j=0; ok && (j < size); j++) { - char a = chars[beginIndex + j]; - char b = str.charAt(j); - if (a != b) { - a = Character.toLowerCase(a); - b = Character.toLowerCase(b); - ok = a == b; - } - } - - return true; - } - - /** - * Returns index of first occurence ch. Lower cases characters leading up - * to first occurrence of ch. - */ - static int setLowercaseIndexOf(CharArrayBuffer buffer, final int ch) { - - int beginIndex = 0; - int endIndex = buffer.length(); - char[] chars = buffer.buffer(); - - for (int i = beginIndex; i < endIndex; i++) { - char current = chars[i]; - if (current == ch) { - return i; - } else { - chars[i] = Character.toLowerCase(current); - } - } - return -1; - } -} diff --git a/core/java/android/net/http/HttpAuthHeader.java b/core/java/android/net/http/HttpAuthHeader.java deleted file mode 100644 index 3abac23..0000000 --- a/core/java/android/net/http/HttpAuthHeader.java +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Copyright (C) 2007 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 android.net.http; - -import java.util.Locale; - -/** - * HttpAuthHeader: a class to store HTTP authentication-header parameters. - * For more information, see: RFC 2617: HTTP Authentication. - * - * {@hide} - */ -public class HttpAuthHeader { - /** - * Possible HTTP-authentication header tokens to search for: - */ - public final static String BASIC_TOKEN = "Basic"; - public final static String DIGEST_TOKEN = "Digest"; - - private final static String REALM_TOKEN = "realm"; - private final static String NONCE_TOKEN = "nonce"; - private final static String STALE_TOKEN = "stale"; - private final static String OPAQUE_TOKEN = "opaque"; - private final static String QOP_TOKEN = "qop"; - private final static String ALGORITHM_TOKEN = "algorithm"; - - /** - * An authentication scheme. We currently support two different schemes: - * HttpAuthHeader.BASIC - basic, and - * HttpAuthHeader.DIGEST - digest (algorithm=MD5, QOP="auth"). - */ - private int mScheme; - - public static final int UNKNOWN = 0; - public static final int BASIC = 1; - public static final int DIGEST = 2; - - /** - * A flag, indicating that the previous request from the client was - * rejected because the nonce value was stale. If stale is TRUE - * (case-insensitive), the client may wish to simply retry the request - * with a new encrypted response, without reprompting the user for a - * new username and password. - */ - private boolean mStale; - - /** - * A string to be displayed to users so they know which username and - * password to use. - */ - private String mRealm; - - /** - * A server-specified data string which should be uniquely generated - * each time a 401 response is made. - */ - private String mNonce; - - /** - * A string of data, specified by the server, which should be returned - * by the client unchanged in the Authorization header of subsequent - * requests with URIs in the same protection space. - */ - private String mOpaque; - - /** - * This directive is optional, but is made so only for backward - * compatibility with RFC 2069 [6]; it SHOULD be used by all - * implementations compliant with this version of the Digest scheme. - * If present, it is a quoted string of one or more tokens indicating - * the "quality of protection" values supported by the server. The - * value "auth" indicates authentication; the value "auth-int" - * indicates authentication with integrity protection. - */ - private String mQop; - - /** - * A string indicating a pair of algorithms used to produce the digest - * and a checksum. If this is not present it is assumed to be "MD5". - */ - private String mAlgorithm; - - /** - * Is this authentication request a proxy authentication request? - */ - private boolean mIsProxy; - - /** - * Username string we get from the user. - */ - private String mUsername; - - /** - * Password string we get from the user. - */ - private String mPassword; - - /** - * Creates a new HTTP-authentication header object from the - * input header string. - * The header string is assumed to contain parameters of at - * most one authentication-scheme (ensured by the caller). - */ - public HttpAuthHeader(String header) { - if (header != null) { - parseHeader(header); - } - } - - /** - * @return True iff this is a proxy authentication header. - */ - public boolean isProxy() { - return mIsProxy; - } - - /** - * Marks this header as a proxy authentication header. - */ - public void setProxy() { - mIsProxy = true; - } - - /** - * @return The username string. - */ - public String getUsername() { - return mUsername; - } - - /** - * Sets the username string. - */ - public void setUsername(String username) { - mUsername = username; - } - - /** - * @return The password string. - */ - public String getPassword() { - return mPassword; - } - - /** - * Sets the password string. - */ - public void setPassword(String password) { - mPassword = password; - } - - /** - * @return True iff this is the BASIC-authentication request. - */ - public boolean isBasic () { - return mScheme == BASIC; - } - - /** - * @return True iff this is the DIGEST-authentication request. - */ - public boolean isDigest() { - return mScheme == DIGEST; - } - - /** - * @return The authentication scheme requested. We currently - * support two schemes: - * HttpAuthHeader.BASIC - basic, and - * HttpAuthHeader.DIGEST - digest (algorithm=MD5, QOP="auth"). - */ - public int getScheme() { - return mScheme; - } - - /** - * @return True if indicating that the previous request from - * the client was rejected because the nonce value was stale. - */ - public boolean getStale() { - return mStale; - } - - /** - * @return The realm value or null if there is none. - */ - public String getRealm() { - return mRealm; - } - - /** - * @return The nonce value or null if there is none. - */ - public String getNonce() { - return mNonce; - } - - /** - * @return The opaque value or null if there is none. - */ - public String getOpaque() { - return mOpaque; - } - - /** - * @return The QOP ("quality-of_protection") value or null if - * there is none. The QOP value is always lower-case. - */ - public String getQop() { - return mQop; - } - - /** - * @return The name of the algorithm used or null if there is - * none. By default, MD5 is used. - */ - public String getAlgorithm() { - return mAlgorithm; - } - - /** - * @return True iff the authentication scheme requested by the - * server is supported; currently supported schemes: - * BASIC, - * DIGEST (only algorithm="md5", no qop or qop="auth). - */ - public boolean isSupportedScheme() { - // it is a good idea to enforce non-null realms! - if (mRealm != null) { - if (mScheme == BASIC) { - return true; - } else { - if (mScheme == DIGEST) { - return - mAlgorithm.equals("md5") && - (mQop == null || mQop.equals("auth")); - } - } - } - - return false; - } - - /** - * Parses the header scheme name and then scheme parameters if - * the scheme is supported. - */ - private void parseHeader(String header) { - if (HttpLog.LOGV) { - HttpLog.v("HttpAuthHeader.parseHeader(): header: " + header); - } - - if (header != null) { - String parameters = parseScheme(header); - if (parameters != null) { - // if we have a supported scheme - if (mScheme != UNKNOWN) { - parseParameters(parameters); - } - } - } - } - - /** - * Parses the authentication scheme name. If we have a Digest - * scheme, sets the algorithm value to the default of MD5. - * @return The authentication scheme parameters string to be - * parsed later (if the scheme is supported) or null if failed - * to parse the scheme (the header value is null?). - */ - private String parseScheme(String header) { - if (header != null) { - int i = header.indexOf(' '); - if (i >= 0) { - String scheme = header.substring(0, i).trim(); - if (scheme.equalsIgnoreCase(DIGEST_TOKEN)) { - mScheme = DIGEST; - - // md5 is the default algorithm!!! - mAlgorithm = "md5"; - } else { - if (scheme.equalsIgnoreCase(BASIC_TOKEN)) { - mScheme = BASIC; - } - } - - return header.substring(i + 1); - } - } - - return null; - } - - /** - * Parses a comma-separated list of authentification scheme - * parameters. - */ - private void parseParameters(String parameters) { - if (HttpLog.LOGV) { - HttpLog.v("HttpAuthHeader.parseParameters():" + - " parameters: " + parameters); - } - - if (parameters != null) { - int i; - do { - i = parameters.indexOf(','); - if (i < 0) { - // have only one parameter - parseParameter(parameters); - } else { - parseParameter(parameters.substring(0, i)); - parameters = parameters.substring(i + 1); - } - } while (i >= 0); - } - } - - /** - * Parses a single authentication scheme parameter. The parameter - * string is expected to follow the format: PARAMETER=VALUE. - */ - private void parseParameter(String parameter) { - if (parameter != null) { - // here, we are looking for the 1st occurence of '=' only!!! - int i = parameter.indexOf('='); - if (i >= 0) { - String token = parameter.substring(0, i).trim(); - String value = - trimDoubleQuotesIfAny(parameter.substring(i + 1).trim()); - - if (HttpLog.LOGV) { - HttpLog.v("HttpAuthHeader.parseParameter():" + - " token: " + token + - " value: " + value); - } - - if (token.equalsIgnoreCase(REALM_TOKEN)) { - mRealm = value; - } else { - if (mScheme == DIGEST) { - parseParameter(token, value); - } - } - } - } - } - - /** - * If the token is a known parameter name, parses and initializes - * the token value. - */ - private void parseParameter(String token, String value) { - if (token != null && value != null) { - if (token.equalsIgnoreCase(NONCE_TOKEN)) { - mNonce = value; - return; - } - - if (token.equalsIgnoreCase(STALE_TOKEN)) { - parseStale(value); - return; - } - - if (token.equalsIgnoreCase(OPAQUE_TOKEN)) { - mOpaque = value; - return; - } - - if (token.equalsIgnoreCase(QOP_TOKEN)) { - mQop = value.toLowerCase(Locale.ROOT); - return; - } - - if (token.equalsIgnoreCase(ALGORITHM_TOKEN)) { - mAlgorithm = value.toLowerCase(Locale.ROOT); - return; - } - } - } - - /** - * Parses and initializes the 'stale' paramer value. Any value - * different from case-insensitive "true" is considered "false". - */ - private void parseStale(String value) { - if (value != null) { - if (value.equalsIgnoreCase("true")) { - mStale = true; - } - } - } - - /** - * Trims double-quotes around a parameter value if there are any. - * @return The string value without the outermost pair of double- - * quotes or null if the original value is null. - */ - static private String trimDoubleQuotesIfAny(String value) { - if (value != null) { - int len = value.length(); - if (len > 2 && - value.charAt(0) == '\"' && value.charAt(len - 1) == '\"') { - return value.substring(1, len - 1); - } - } - - return value; - } -} diff --git a/core/java/android/net/http/HttpConnection.java b/core/java/android/net/http/HttpConnection.java deleted file mode 100644 index edf8fed3..0000000 --- a/core/java/android/net/http/HttpConnection.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2007 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 android.net.http; - -import android.content.Context; - -import java.net.Socket; -import java.io.IOException; - -import org.apache.http.HttpHost; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; - -/** - * A requestConnection connecting to a normal (non secure) http server - * - * {@hide} - */ -class HttpConnection extends Connection { - - HttpConnection(Context context, HttpHost host, - RequestFeeder requestFeeder) { - super(context, host, requestFeeder); - } - - /** - * Opens the connection to a http server - * - * @return the opened low level connection - * @throws IOException if the connection fails for any reason. - */ - @Override - AndroidHttpClientConnection openConnection(Request req) throws IOException { - - // Update the certificate info (connection not secure - set to null) - EventHandler eventHandler = req.getEventHandler(); - mCertificate = null; - eventHandler.certificate(mCertificate); - - AndroidHttpClientConnection conn = new AndroidHttpClientConnection(); - BasicHttpParams params = new BasicHttpParams(); - Socket sock = new Socket(mHost.getHostName(), mHost.getPort()); - params.setIntParameter(HttpConnectionParams.SOCKET_BUFFER_SIZE, 8192); - conn.bind(sock, params); - return conn; - } - - /** - * Closes the low level connection. - * - * If an exception is thrown then it is assumed that the - * connection will have been closed (to the extent possible) - * anyway and the caller does not need to take any further action. - * - */ - void closeConnection() { - try { - if (mHttpClientConnection != null && mHttpClientConnection.isOpen()) { - mHttpClientConnection.close(); - } - } catch (IOException e) { - if (HttpLog.LOGV) HttpLog.v( - "closeConnection(): failed closing connection " + - mHost); - e.printStackTrace(); - } - } - - /** - * Restart a secure connection suspended waiting for user interaction. - */ - void restartConnection(boolean abort) { - // not required for plain http connections - } - - String getScheme() { - return "http"; - } -} diff --git a/core/java/android/net/http/HttpLog.java b/core/java/android/net/http/HttpLog.java deleted file mode 100644 index 0934664..0000000 --- a/core/java/android/net/http/HttpLog.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2007 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-level logging flag - */ - -package android.net.http; - -import android.os.SystemClock; - -import android.util.Log; - -/** - * {@hide} - */ -class HttpLog { - private final static String LOGTAG = "http"; - - private static final boolean DEBUG = false; - static final boolean LOGV = false; - - static void v(String logMe) { - Log.v(LOGTAG, SystemClock.uptimeMillis() + " " + Thread.currentThread().getName() + " " + logMe); - } - - static void e(String logMe) { - Log.e(LOGTAG, logMe); - } -} diff --git a/core/java/android/net/http/HttpResponseCache.java b/core/java/android/net/http/HttpResponseCache.java index c6c22e7..188287f 100644 --- a/core/java/android/net/http/HttpResponseCache.java +++ b/core/java/android/net/http/HttpResponseCache.java @@ -35,8 +35,8 @@ import java.util.Map; * Caches HTTP and HTTPS responses to the filesystem so they may be reused, * saving time and bandwidth. This class supports {@link * java.net.HttpURLConnection} and {@link javax.net.ssl.HttpsURLConnection}; - * there is no platform-provided cache for {@link - * org.apache.http.impl.client.DefaultHttpClient} or {@link AndroidHttpClient}. + * there is no platform-provided cache for {@code DefaultHttpClient} or + * {@code AndroidHttpClient}. * * <h3>Installing an HTTP response cache</h3> * Enable caching of all of your application's HTTP requests by installing the diff --git a/core/java/android/net/http/HttpsConnection.java b/core/java/android/net/http/HttpsConnection.java deleted file mode 100644 index a8674de..0000000 --- a/core/java/android/net/http/HttpsConnection.java +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Copyright (C) 2007 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 android.net.http; - -import android.content.Context; -import android.util.Log; -import com.android.org.conscrypt.FileClientSessionCache; -import com.android.org.conscrypt.OpenSSLContextImpl; -import com.android.org.conscrypt.SSLClientSessionCache; -import org.apache.http.Header; -import org.apache.http.HttpException; -import org.apache.http.HttpHost; -import org.apache.http.HttpStatus; -import org.apache.http.ParseException; -import org.apache.http.ProtocolVersion; -import org.apache.http.StatusLine; -import org.apache.http.message.BasicHttpRequest; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; - -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; -import java.io.File; -import java.io.IOException; -import java.net.Socket; -import java.security.KeyManagementException; -import java.security.cert.X509Certificate; -import java.util.Locale; - -/** - * A Connection connecting to a secure http server or tunneling through - * a http proxy server to a https server. - * - * @hide - */ -public class HttpsConnection extends Connection { - - /** - * SSL socket factory - */ - private static SSLSocketFactory mSslSocketFactory = null; - - static { - // This initialization happens in the zygote. It triggers some - // lazy initialization that can will benefit later invocations of - // initializeEngine(). - initializeEngine(null); - } - - /** - * @hide - * - * @param sessionDir directory to cache SSL sessions - */ - public static void initializeEngine(File sessionDir) { - try { - SSLClientSessionCache cache = null; - if (sessionDir != null) { - Log.d("HttpsConnection", "Caching SSL sessions in " - + sessionDir + "."); - cache = FileClientSessionCache.usingDirectory(sessionDir); - } - - OpenSSLContextImpl sslContext = OpenSSLContextImpl.getPreferred(); - - // here, trust managers is a single trust-all manager - TrustManager[] trustManagers = new TrustManager[] { - new X509TrustManager() { - public X509Certificate[] getAcceptedIssuers() { - return null; - } - - public void checkClientTrusted( - X509Certificate[] certs, String authType) { - } - - public void checkServerTrusted( - X509Certificate[] certs, String authType) { - } - } - }; - - sslContext.engineInit(null, trustManagers, null); - sslContext.engineGetClientSessionContext().setPersistentCache(cache); - - synchronized (HttpsConnection.class) { - mSslSocketFactory = sslContext.engineGetSocketFactory(); - } - } catch (KeyManagementException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private synchronized static SSLSocketFactory getSocketFactory() { - return mSslSocketFactory; - } - - /** - * Object to wait on when suspending the SSL connection - */ - private Object mSuspendLock = new Object(); - - /** - * True if the connection is suspended pending the result of asking the - * user about an error. - */ - private boolean mSuspended = false; - - /** - * True if the connection attempt should be aborted due to an ssl - * error. - */ - private boolean mAborted = false; - - // Used when connecting through a proxy. - private HttpHost mProxyHost; - - /** - * Contructor for a https connection. - */ - HttpsConnection(Context context, HttpHost host, HttpHost proxy, - RequestFeeder requestFeeder) { - super(context, host, requestFeeder); - mProxyHost = proxy; - } - - /** - * Sets the server SSL certificate associated with this - * connection. - * @param certificate The SSL certificate - */ - /* package */ void setCertificate(SslCertificate certificate) { - mCertificate = certificate; - } - - /** - * Opens the connection to a http server or proxy. - * - * @return the opened low level connection - * @throws IOException if the connection fails for any reason. - */ - @Override - AndroidHttpClientConnection openConnection(Request req) throws IOException { - SSLSocket sslSock = null; - - if (mProxyHost != null) { - // If we have a proxy set, we first send a CONNECT request - // to the proxy; if the proxy returns 200 OK, we negotiate - // a secure connection to the target server via the proxy. - // If the request fails, we drop it, but provide the event - // handler with the response status and headers. The event - // handler is then responsible for cancelling the load or - // issueing a new request. - AndroidHttpClientConnection proxyConnection = null; - Socket proxySock = null; - try { - proxySock = new Socket - (mProxyHost.getHostName(), mProxyHost.getPort()); - - proxySock.setSoTimeout(60 * 1000); - - proxyConnection = new AndroidHttpClientConnection(); - HttpParams params = new BasicHttpParams(); - HttpConnectionParams.setSocketBufferSize(params, 8192); - - proxyConnection.bind(proxySock, params); - } catch(IOException e) { - if (proxyConnection != null) { - proxyConnection.close(); - } - - String errorMessage = e.getMessage(); - if (errorMessage == null) { - errorMessage = - "failed to establish a connection to the proxy"; - } - - throw new IOException(errorMessage); - } - - StatusLine statusLine = null; - int statusCode = 0; - Headers headers = new Headers(); - try { - BasicHttpRequest proxyReq = new BasicHttpRequest - ("CONNECT", mHost.toHostString()); - - // add all 'proxy' headers from the original request, we also need - // to add 'host' header unless we want proxy to answer us with a - // 400 Bad Request - for (Header h : req.mHttpRequest.getAllHeaders()) { - String headerName = h.getName().toLowerCase(Locale.ROOT); - if (headerName.startsWith("proxy") || headerName.equals("keep-alive") - || headerName.equals("host")) { - proxyReq.addHeader(h); - } - } - - proxyConnection.sendRequestHeader(proxyReq); - proxyConnection.flush(); - - // it is possible to receive informational status - // codes prior to receiving actual headers; - // all those status codes are smaller than OK 200 - // a loop is a standard way of dealing with them - do { - statusLine = proxyConnection.parseResponseHeader(headers); - statusCode = statusLine.getStatusCode(); - } while (statusCode < HttpStatus.SC_OK); - } catch (ParseException e) { - String errorMessage = e.getMessage(); - if (errorMessage == null) { - errorMessage = - "failed to send a CONNECT request"; - } - - throw new IOException(errorMessage); - } catch (HttpException e) { - String errorMessage = e.getMessage(); - if (errorMessage == null) { - errorMessage = - "failed to send a CONNECT request"; - } - - throw new IOException(errorMessage); - } catch (IOException e) { - String errorMessage = e.getMessage(); - if (errorMessage == null) { - errorMessage = - "failed to send a CONNECT request"; - } - - throw new IOException(errorMessage); - } - - if (statusCode == HttpStatus.SC_OK) { - try { - sslSock = (SSLSocket) getSocketFactory().createSocket( - proxySock, mHost.getHostName(), mHost.getPort(), true); - } catch(IOException e) { - if (sslSock != null) { - sslSock.close(); - } - - String errorMessage = e.getMessage(); - if (errorMessage == null) { - errorMessage = - "failed to create an SSL socket"; - } - throw new IOException(errorMessage); - } - } else { - // if the code is not OK, inform the event handler - ProtocolVersion version = statusLine.getProtocolVersion(); - - req.mEventHandler.status(version.getMajor(), - version.getMinor(), - statusCode, - statusLine.getReasonPhrase()); - req.mEventHandler.headers(headers); - req.mEventHandler.endData(); - - proxyConnection.close(); - - // here, we return null to indicate that the original - // request needs to be dropped - return null; - } - } else { - // if we do not have a proxy, we simply connect to the host - try { - sslSock = (SSLSocket) getSocketFactory().createSocket( - mHost.getHostName(), mHost.getPort()); - sslSock.setSoTimeout(SOCKET_TIMEOUT); - } catch(IOException e) { - if (sslSock != null) { - sslSock.close(); - } - - String errorMessage = e.getMessage(); - if (errorMessage == null) { - errorMessage = "failed to create an SSL socket"; - } - - throw new IOException(errorMessage); - } - } - - // do handshake and validate server certificates - SslError error = CertificateChainValidator.getInstance(). - doHandshakeAndValidateServerCertificates(this, sslSock, mHost.getHostName()); - - // Inform the user if there is a problem - if (error != null) { - // handleSslErrorRequest may immediately unsuspend if it wants to - // allow the certificate anyway. - // So we mark the connection as suspended, call handleSslErrorRequest - // then check if we're still suspended and only wait if we actually - // need to. - synchronized (mSuspendLock) { - mSuspended = true; - } - // don't hold the lock while calling out to the event handler - boolean canHandle = req.getEventHandler().handleSslErrorRequest(error); - if(!canHandle) { - throw new IOException("failed to handle "+ error); - } - synchronized (mSuspendLock) { - if (mSuspended) { - try { - // Put a limit on how long we are waiting; if the timeout - // expires (which should never happen unless you choose - // to ignore the SSL error dialog for a very long time), - // we wake up the thread and abort the request. This is - // to prevent us from stalling the network if things go - // very bad. - mSuspendLock.wait(10 * 60 * 1000); - if (mSuspended) { - // mSuspended is true if we have not had a chance to - // restart the connection yet (ie, the wait timeout - // has expired) - mSuspended = false; - mAborted = true; - if (HttpLog.LOGV) { - HttpLog.v("HttpsConnection.openConnection():" + - " SSL timeout expired and request was cancelled!!!"); - } - } - } catch (InterruptedException e) { - // ignore - } - } - if (mAborted) { - // The user decided not to use this unverified connection - // so close it immediately. - sslSock.close(); - throw new SSLConnectionClosedByUserException("connection closed by the user"); - } - } - } - - // All went well, we have an open, verified connection. - AndroidHttpClientConnection conn = new AndroidHttpClientConnection(); - BasicHttpParams params = new BasicHttpParams(); - params.setIntParameter(HttpConnectionParams.SOCKET_BUFFER_SIZE, 8192); - conn.bind(sslSock, params); - - return conn; - } - - /** - * Closes the low level connection. - * - * If an exception is thrown then it is assumed that the connection will - * have been closed (to the extent possible) anyway and the caller does not - * need to take any further action. - * - */ - @Override - void closeConnection() { - // if the connection has been suspended due to an SSL error - if (mSuspended) { - // wake up the network thread - restartConnection(false); - } - - try { - if (mHttpClientConnection != null && mHttpClientConnection.isOpen()) { - mHttpClientConnection.close(); - } - } catch (IOException e) { - if (HttpLog.LOGV) - HttpLog.v("HttpsConnection.closeConnection():" + - " failed closing connection " + mHost); - e.printStackTrace(); - } - } - - /** - * Restart a secure connection suspended waiting for user interaction. - */ - void restartConnection(boolean proceed) { - if (HttpLog.LOGV) { - HttpLog.v("HttpsConnection.restartConnection():" + - " proceed: " + proceed); - } - - synchronized (mSuspendLock) { - if (mSuspended) { - mSuspended = false; - mAborted = !proceed; - mSuspendLock.notify(); - } - } - } - - @Override - String getScheme() { - return "https"; - } -} - -/** - * Simple exception we throw if the SSL connection is closed by the user. - * - * {@hide} - */ -class SSLConnectionClosedByUserException extends SSLException { - - public SSLConnectionClosedByUserException(String reason) { - super(reason); - } -} diff --git a/core/java/android/net/http/IdleCache.java b/core/java/android/net/http/IdleCache.java deleted file mode 100644 index fda6009..0000000 --- a/core/java/android/net/http/IdleCache.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2008 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. - */ - -/** - * Hangs onto idle live connections for a little while - */ - -package android.net.http; - -import org.apache.http.HttpHost; - -import android.os.SystemClock; - -/** - * {@hide} - */ -class IdleCache { - - class Entry { - HttpHost mHost; - Connection mConnection; - long mTimeout; - }; - - private final static int IDLE_CACHE_MAX = 8; - - /* Allow five consecutive empty queue checks before shutdown */ - private final static int EMPTY_CHECK_MAX = 5; - - /* six second timeout for connections */ - private final static int TIMEOUT = 6 * 1000; - private final static int CHECK_INTERVAL = 2 * 1000; - private Entry[] mEntries = new Entry[IDLE_CACHE_MAX]; - - private int mCount = 0; - - private IdleReaper mThread = null; - - /* stats */ - private int mCached = 0; - private int mReused = 0; - - IdleCache() { - for (int i = 0; i < IDLE_CACHE_MAX; i++) { - mEntries[i] = new Entry(); - } - } - - /** - * Caches connection, if there is room. - * @return true if connection cached - */ - synchronized boolean cacheConnection( - HttpHost host, Connection connection) { - - boolean ret = false; - - if (HttpLog.LOGV) { - HttpLog.v("IdleCache size " + mCount + " host " + host); - } - - if (mCount < IDLE_CACHE_MAX) { - long time = SystemClock.uptimeMillis(); - for (int i = 0; i < IDLE_CACHE_MAX; i++) { - Entry entry = mEntries[i]; - if (entry.mHost == null) { - entry.mHost = host; - entry.mConnection = connection; - entry.mTimeout = time + TIMEOUT; - mCount++; - if (HttpLog.LOGV) mCached++; - ret = true; - if (mThread == null) { - mThread = new IdleReaper(); - mThread.start(); - } - break; - } - } - } - return ret; - } - - synchronized Connection getConnection(HttpHost host) { - Connection ret = null; - - if (mCount > 0) { - for (int i = 0; i < IDLE_CACHE_MAX; i++) { - Entry entry = mEntries[i]; - HttpHost eHost = entry.mHost; - if (eHost != null && eHost.equals(host)) { - ret = entry.mConnection; - entry.mHost = null; - entry.mConnection = null; - mCount--; - if (HttpLog.LOGV) mReused++; - break; - } - } - } - return ret; - } - - synchronized void clear() { - for (int i = 0; mCount > 0 && i < IDLE_CACHE_MAX; i++) { - Entry entry = mEntries[i]; - if (entry.mHost != null) { - entry.mHost = null; - entry.mConnection.closeConnection(); - entry.mConnection = null; - mCount--; - } - } - } - - private synchronized void clearIdle() { - if (mCount > 0) { - long time = SystemClock.uptimeMillis(); - for (int i = 0; i < IDLE_CACHE_MAX; i++) { - Entry entry = mEntries[i]; - if (entry.mHost != null && time > entry.mTimeout) { - entry.mHost = null; - entry.mConnection.closeConnection(); - entry.mConnection = null; - mCount--; - } - } - } - } - - private class IdleReaper extends Thread { - - public void run() { - int check = 0; - - setName("IdleReaper"); - android.os.Process.setThreadPriority( - android.os.Process.THREAD_PRIORITY_BACKGROUND); - synchronized (IdleCache.this) { - while (check < EMPTY_CHECK_MAX) { - try { - IdleCache.this.wait(CHECK_INTERVAL); - } catch (InterruptedException ex) { - } - if (mCount == 0) { - check++; - } else { - check = 0; - clearIdle(); - } - } - mThread = null; - } - if (HttpLog.LOGV) { - HttpLog.v("IdleCache IdleReaper shutdown: cached " + mCached + - " reused " + mReused); - mCached = 0; - mReused = 0; - } - } - } -} diff --git a/core/java/android/net/http/LoggingEventHandler.java b/core/java/android/net/http/LoggingEventHandler.java deleted file mode 100644 index bdafa0b..0000000 --- a/core/java/android/net/http/LoggingEventHandler.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -/** - * A test EventHandler: Logs everything received - */ - -package android.net.http; - -import android.net.http.Headers; - -/** - * {@hide} - */ -public class LoggingEventHandler implements EventHandler { - - public void requestSent() { - HttpLog.v("LoggingEventHandler:requestSent()"); - } - - public void status(int major_version, - int minor_version, - int code, /* Status-Code value */ - String reason_phrase) { - if (HttpLog.LOGV) { - HttpLog.v("LoggingEventHandler:status() major: " + major_version + - " minor: " + minor_version + - " code: " + code + - " reason: " + reason_phrase); - } - } - - public void headers(Headers headers) { - if (HttpLog.LOGV) { - HttpLog.v("LoggingEventHandler:headers()"); - HttpLog.v(headers.toString()); - } - } - - public void locationChanged(String newLocation, boolean permanent) { - if (HttpLog.LOGV) { - HttpLog.v("LoggingEventHandler: locationChanged() " + newLocation + - " permanent " + permanent); - } - } - - public void data(byte[] data, int len) { - if (HttpLog.LOGV) { - HttpLog.v("LoggingEventHandler: data() " + len + " bytes"); - } - // HttpLog.v(new String(data, 0, len)); - } - public void endData() { - if (HttpLog.LOGV) { - HttpLog.v("LoggingEventHandler: endData() called"); - } - } - - public void certificate(SslCertificate certificate) { - if (HttpLog.LOGV) { - HttpLog.v("LoggingEventHandler: certificate(): " + certificate); - } - } - - public void error(int id, String description) { - if (HttpLog.LOGV) { - HttpLog.v("LoggingEventHandler: error() called Id:" + id + - " description " + description); - } - } - - public boolean handleSslErrorRequest(SslError error) { - if (HttpLog.LOGV) { - HttpLog.v("LoggingEventHandler: handleSslErrorRequest():" + error); - } - // return false so that the caller thread won't wait forever - return false; - } -} diff --git a/core/java/android/net/http/Request.java b/core/java/android/net/http/Request.java deleted file mode 100644 index 76d7bb9..0000000 --- a/core/java/android/net/http/Request.java +++ /dev/null @@ -1,526 +0,0 @@ -/* - * Copyright (C) 2006 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 android.net.http; - -import java.io.EOFException; -import java.io.InputStream; -import java.io.IOException; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.zip.GZIPInputStream; - -import org.apache.http.entity.InputStreamEntity; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpEntityEnclosingRequest; -import org.apache.http.HttpException; -import org.apache.http.HttpHost; -import org.apache.http.HttpRequest; -import org.apache.http.HttpStatus; -import org.apache.http.ParseException; -import org.apache.http.ProtocolVersion; - -import org.apache.http.StatusLine; -import org.apache.http.message.BasicHttpRequest; -import org.apache.http.message.BasicHttpEntityEnclosingRequest; -import org.apache.http.protocol.RequestContent; - -/** - * Represents an HTTP request for a given host. - * - * {@hide} - */ - -class Request { - - /** The eventhandler to call as the request progresses */ - EventHandler mEventHandler; - - private Connection mConnection; - - /** The Apache http request */ - BasicHttpRequest mHttpRequest; - - /** The path component of this request */ - String mPath; - - /** Host serving this request */ - HttpHost mHost; - - /** Set if I'm using a proxy server */ - HttpHost mProxyHost; - - /** True if request has been cancelled */ - volatile boolean mCancelled = false; - - int mFailCount = 0; - - // This will be used to set the Range field if we retry a connection. This - // is http/1.1 feature. - private int mReceivedBytes = 0; - - private InputStream mBodyProvider; - private int mBodyLength; - - private final static String HOST_HEADER = "Host"; - private final static String ACCEPT_ENCODING_HEADER = "Accept-Encoding"; - private final static String CONTENT_LENGTH_HEADER = "content-length"; - - /* Used to synchronize waitUntilComplete() requests */ - private final Object mClientResource = new Object(); - - /** True if loading should be paused **/ - private boolean mLoadingPaused = false; - - /** - * Processor used to set content-length and transfer-encoding - * headers. - */ - private static RequestContent requestContentProcessor = - new RequestContent(); - - /** - * Instantiates a new Request. - * @param method GET/POST/PUT - * @param host The server that will handle this request - * @param path path part of URI - * @param bodyProvider InputStream providing HTTP body, null if none - * @param bodyLength length of body, must be 0 if bodyProvider is null - * @param eventHandler request will make progress callbacks on - * this interface - * @param headers reqeust headers - */ - Request(String method, HttpHost host, HttpHost proxyHost, String path, - InputStream bodyProvider, int bodyLength, - EventHandler eventHandler, - Map<String, String> headers) { - mEventHandler = eventHandler; - mHost = host; - mProxyHost = proxyHost; - mPath = path; - mBodyProvider = bodyProvider; - mBodyLength = bodyLength; - - if (bodyProvider == null && !"POST".equalsIgnoreCase(method)) { - mHttpRequest = new BasicHttpRequest(method, getUri()); - } else { - mHttpRequest = new BasicHttpEntityEnclosingRequest( - method, getUri()); - // it is ok to have null entity for BasicHttpEntityEnclosingRequest. - // By using BasicHttpEntityEnclosingRequest, it will set up the - // correct content-length, content-type and content-encoding. - if (bodyProvider != null) { - setBodyProvider(bodyProvider, bodyLength); - } - } - addHeader(HOST_HEADER, getHostPort()); - - /* FIXME: if webcore will make the root document a - high-priority request, we can ask for gzip encoding only on - high priority reqs (saving the trouble for images, etc) */ - addHeader(ACCEPT_ENCODING_HEADER, "gzip"); - addHeaders(headers); - } - - /** - * @param pause True if the load should be paused. - */ - synchronized void setLoadingPaused(boolean pause) { - mLoadingPaused = pause; - - // Wake up the paused thread if we're unpausing the load. - if (!mLoadingPaused) { - notify(); - } - } - - /** - * @param connection Request served by this connection - */ - void setConnection(Connection connection) { - mConnection = connection; - } - - /* package */ EventHandler getEventHandler() { - return mEventHandler; - } - - /** - * Add header represented by given pair to request. Header will - * be formatted in request as "name: value\r\n". - * @param name of header - * @param value of header - */ - void addHeader(String name, String value) { - if (name == null) { - String damage = "Null http header name"; - HttpLog.e(damage); - throw new NullPointerException(damage); - } - if (value == null || value.length() == 0) { - String damage = "Null or empty value for header \"" + name + "\""; - HttpLog.e(damage); - throw new RuntimeException(damage); - } - mHttpRequest.addHeader(name, value); - } - - /** - * Add all headers in given map to this request. This is a helper - * method: it calls addHeader for each pair in the map. - */ - void addHeaders(Map<String, String> headers) { - if (headers == null) { - return; - } - - Entry<String, String> entry; - Iterator<Entry<String, String>> i = headers.entrySet().iterator(); - while (i.hasNext()) { - entry = i.next(); - addHeader(entry.getKey(), entry.getValue()); - } - } - - /** - * Send the request line and headers - */ - void sendRequest(AndroidHttpClientConnection httpClientConnection) - throws HttpException, IOException { - - if (mCancelled) return; // don't send cancelled requests - - if (HttpLog.LOGV) { - HttpLog.v("Request.sendRequest() " + mHost.getSchemeName() + "://" + getHostPort()); - // HttpLog.v(mHttpRequest.getRequestLine().toString()); - if (false) { - Iterator i = mHttpRequest.headerIterator(); - while (i.hasNext()) { - Header header = (Header)i.next(); - HttpLog.v(header.getName() + ": " + header.getValue()); - } - } - } - - requestContentProcessor.process(mHttpRequest, - mConnection.getHttpContext()); - httpClientConnection.sendRequestHeader(mHttpRequest); - if (mHttpRequest instanceof HttpEntityEnclosingRequest) { - httpClientConnection.sendRequestEntity( - (HttpEntityEnclosingRequest) mHttpRequest); - } - - if (HttpLog.LOGV) { - HttpLog.v("Request.requestSent() " + mHost.getSchemeName() + "://" + getHostPort() + mPath); - } - } - - - /** - * Receive a single http response. - * - * @param httpClientConnection the request to receive the response for. - */ - void readResponse(AndroidHttpClientConnection httpClientConnection) - throws IOException, ParseException { - - if (mCancelled) return; // don't send cancelled requests - - StatusLine statusLine = null; - boolean hasBody = false; - httpClientConnection.flush(); - int statusCode = 0; - - Headers header = new Headers(); - do { - statusLine = httpClientConnection.parseResponseHeader(header); - statusCode = statusLine.getStatusCode(); - } while (statusCode < HttpStatus.SC_OK); - if (HttpLog.LOGV) HttpLog.v( - "Request.readResponseStatus() " + - statusLine.toString().length() + " " + statusLine); - - ProtocolVersion v = statusLine.getProtocolVersion(); - mEventHandler.status(v.getMajor(), v.getMinor(), - statusCode, statusLine.getReasonPhrase()); - mEventHandler.headers(header); - HttpEntity entity = null; - hasBody = canResponseHaveBody(mHttpRequest, statusCode); - - if (hasBody) - entity = httpClientConnection.receiveResponseEntity(header); - - // restrict the range request to the servers claiming that they are - // accepting ranges in bytes - boolean supportPartialContent = "bytes".equalsIgnoreCase(header - .getAcceptRanges()); - - if (entity != null) { - InputStream is = entity.getContent(); - - // process gzip content encoding - Header contentEncoding = entity.getContentEncoding(); - InputStream nis = null; - byte[] buf = null; - int count = 0; - try { - if (contentEncoding != null && - contentEncoding.getValue().equals("gzip")) { - nis = new GZIPInputStream(is); - } else { - nis = is; - } - - /* accumulate enough data to make it worth pushing it - * up the stack */ - buf = mConnection.getBuf(); - int len = 0; - int lowWater = buf.length / 2; - while (len != -1) { - synchronized(this) { - while (mLoadingPaused) { - // Put this (network loading) thread to sleep if WebCore - // has asked us to. This can happen with plugins for - // example, if we are streaming data but the plugin has - // filled its internal buffers. - try { - wait(); - } catch (InterruptedException e) { - HttpLog.e("Interrupted exception whilst " - + "network thread paused at WebCore's request." - + " " + e.getMessage()); - } - } - } - - len = nis.read(buf, count, buf.length - count); - - if (len != -1) { - count += len; - if (supportPartialContent) mReceivedBytes += len; - } - if (len == -1 || count >= lowWater) { - if (HttpLog.LOGV) HttpLog.v("Request.readResponse() " + count); - mEventHandler.data(buf, count); - count = 0; - } - } - } catch (EOFException e) { - /* InflaterInputStream throws an EOFException when the - server truncates gzipped content. Handle this case - as we do truncated non-gzipped content: no error */ - if (count > 0) { - // if there is uncommited content, we should commit them - mEventHandler.data(buf, count); - } - if (HttpLog.LOGV) HttpLog.v( "readResponse() handling " + e); - } catch(IOException e) { - // don't throw if we have a non-OK status code - if (statusCode == HttpStatus.SC_OK - || statusCode == HttpStatus.SC_PARTIAL_CONTENT) { - if (supportPartialContent && count > 0) { - // if there is uncommited content, we should commit them - // as we will continue the request - mEventHandler.data(buf, count); - } - throw e; - } - } finally { - if (nis != null) { - nis.close(); - } - } - } - mConnection.setCanPersist(entity, statusLine.getProtocolVersion(), - header.getConnectionType()); - mEventHandler.endData(); - complete(); - - if (HttpLog.LOGV) HttpLog.v("Request.readResponse(): done " + - mHost.getSchemeName() + "://" + getHostPort() + mPath); - } - - /** - * Data will not be sent to or received from server after cancel() - * call. Does not close connection--use close() below for that. - * - * Called by RequestHandle from non-network thread - */ - synchronized void cancel() { - if (HttpLog.LOGV) { - HttpLog.v("Request.cancel(): " + getUri()); - } - - // Ensure that the network thread is not blocked by a hanging request from WebCore to - // pause the load. - mLoadingPaused = false; - notify(); - - mCancelled = true; - if (mConnection != null) { - mConnection.cancel(); - } - } - - String getHostPort() { - String myScheme = mHost.getSchemeName(); - int myPort = mHost.getPort(); - - // Only send port when we must... many servers can't deal with it - if (myPort != 80 && myScheme.equals("http") || - myPort != 443 && myScheme.equals("https")) { - return mHost.toHostString(); - } else { - return mHost.getHostName(); - } - } - - String getUri() { - if (mProxyHost == null || - mHost.getSchemeName().equals("https")) { - return mPath; - } - return mHost.getSchemeName() + "://" + getHostPort() + mPath; - } - - /** - * for debugging - */ - public String toString() { - return mPath; - } - - - /** - * If this request has been sent once and failed, it must be reset - * before it can be sent again. - */ - void reset() { - /* clear content-length header */ - mHttpRequest.removeHeaders(CONTENT_LENGTH_HEADER); - - if (mBodyProvider != null) { - try { - mBodyProvider.reset(); - } catch (IOException ex) { - if (HttpLog.LOGV) HttpLog.v( - "failed to reset body provider " + - getUri()); - } - setBodyProvider(mBodyProvider, mBodyLength); - } - - if (mReceivedBytes > 0) { - // reset the fail count as we continue the request - mFailCount = 0; - // set the "Range" header to indicate that the retry will continue - // instead of restarting the request - HttpLog.v("*** Request.reset() to range:" + mReceivedBytes); - mHttpRequest.setHeader("Range", "bytes=" + mReceivedBytes + "-"); - } - } - - /** - * Pause thread request completes. Used for synchronous requests, - * and testing - */ - void waitUntilComplete() { - synchronized (mClientResource) { - try { - if (HttpLog.LOGV) HttpLog.v("Request.waitUntilComplete()"); - mClientResource.wait(); - if (HttpLog.LOGV) HttpLog.v("Request.waitUntilComplete() done waiting"); - } catch (InterruptedException e) { - } - } - } - - void complete() { - synchronized (mClientResource) { - mClientResource.notifyAll(); - } - } - - /** - * Decide whether a response comes with an entity. - * The implementation in this class is based on RFC 2616. - * Unknown methods and response codes are supposed to - * indicate responses with an entity. - * <br/> - * Derived executors can override this method to handle - * methods and response codes not specified in RFC 2616. - * - * @param request the request, to obtain the executed method - * @param response the response, to obtain the status code - */ - - private static boolean canResponseHaveBody(final HttpRequest request, - final int status) { - - if ("HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) { - return false; - } - return status >= HttpStatus.SC_OK - && status != HttpStatus.SC_NO_CONTENT - && status != HttpStatus.SC_NOT_MODIFIED; - } - - /** - * Supply an InputStream that provides the body of a request. It's - * not great that the caller must also provide the length of the data - * returned by that InputStream, but the client needs to know up - * front, and I'm not sure how to get this out of the InputStream - * itself without a costly readthrough. I'm not sure skip() would - * do what we want. If you know a better way, please let me know. - */ - private void setBodyProvider(InputStream bodyProvider, int bodyLength) { - if (!bodyProvider.markSupported()) { - throw new IllegalArgumentException( - "bodyProvider must support mark()"); - } - // Mark beginning of stream - bodyProvider.mark(Integer.MAX_VALUE); - - ((BasicHttpEntityEnclosingRequest)mHttpRequest).setEntity( - new InputStreamEntity(bodyProvider, bodyLength)); - } - - - /** - * Handles SSL error(s) on the way down from the user (the user - * has already provided their feedback). - */ - public void handleSslErrorResponse(boolean proceed) { - HttpsConnection connection = (HttpsConnection)(mConnection); - if (connection != null) { - connection.restartConnection(proceed); - } - } - - /** - * Helper: calls error() on eventhandler with appropriate message - * This should not be called before the mConnection is set. - */ - void error(int errorId, int resourceId) { - mEventHandler.error( - errorId, - mConnection.mContext.getText( - resourceId).toString()); - } - -} diff --git a/core/java/android/net/http/RequestFeeder.java b/core/java/android/net/http/RequestFeeder.java deleted file mode 100644 index 34ca267..0000000 --- a/core/java/android/net/http/RequestFeeder.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2008 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. - */ - -/** - * Supplies Requests to a Connection - */ - -package android.net.http; - -import org.apache.http.HttpHost; - -/** - * {@hide} - */ -interface RequestFeeder { - - Request getRequest(); - Request getRequest(HttpHost host); - - /** - * @return true if a request for this host is available - */ - boolean haveRequest(HttpHost host); - - /** - * Put request back on head of queue - */ - void requeueRequest(Request request); -} diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java deleted file mode 100644 index f23f69c..0000000 --- a/core/java/android/net/http/RequestHandle.java +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright (C) 2006 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 android.net.http; - -import android.net.ParseException; -import android.net.WebAddress; -import junit.framework.Assert; -import android.webkit.CookieManager; - -import org.apache.commons.codec.binary.Base64; - -import java.io.InputStream; -import java.lang.Math; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.HashMap; -import java.util.Map; -import java.util.Random; - -/** - * RequestHandle: handles a request session that may include multiple - * redirects, HTTP authentication requests, etc. - * - * {@hide} - */ -public class RequestHandle { - - private String mUrl; - private WebAddress mUri; - private String mMethod; - private Map<String, String> mHeaders; - private RequestQueue mRequestQueue; - private Request mRequest; - private InputStream mBodyProvider; - private int mBodyLength; - private int mRedirectCount = 0; - // Used only with synchronous requests. - private Connection mConnection; - - private final static String AUTHORIZATION_HEADER = "Authorization"; - private final static String PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization"; - - public final static int MAX_REDIRECT_COUNT = 16; - - /** - * Creates a new request session. - */ - public RequestHandle(RequestQueue requestQueue, String url, WebAddress uri, - String method, Map<String, String> headers, - InputStream bodyProvider, int bodyLength, Request request) { - - if (headers == null) { - headers = new HashMap<String, String>(); - } - mHeaders = headers; - mBodyProvider = bodyProvider; - mBodyLength = bodyLength; - mMethod = method == null? "GET" : method; - - mUrl = url; - mUri = uri; - - mRequestQueue = requestQueue; - - mRequest = request; - } - - /** - * Creates a new request session with a given Connection. This connection - * is used during a synchronous load to handle this request. - */ - public RequestHandle(RequestQueue requestQueue, String url, WebAddress uri, - String method, Map<String, String> headers, - InputStream bodyProvider, int bodyLength, Request request, - Connection conn) { - this(requestQueue, url, uri, method, headers, bodyProvider, bodyLength, - request); - mConnection = conn; - } - - /** - * Cancels this request - */ - public void cancel() { - if (mRequest != null) { - mRequest.cancel(); - } - } - - /** - * Pauses the loading of this request. For example, called from the WebCore thread - * when the plugin can take no more data. - */ - public void pauseRequest(boolean pause) { - if (mRequest != null) { - mRequest.setLoadingPaused(pause); - } - } - - /** - * Handles SSL error(s) on the way down from the user (the user - * has already provided their feedback). - */ - public void handleSslErrorResponse(boolean proceed) { - if (mRequest != null) { - mRequest.handleSslErrorResponse(proceed); - } - } - - /** - * @return true if we've hit the max redirect count - */ - public boolean isRedirectMax() { - return mRedirectCount >= MAX_REDIRECT_COUNT; - } - - public int getRedirectCount() { - return mRedirectCount; - } - - public void setRedirectCount(int count) { - mRedirectCount = count; - } - - /** - * Create and queue a redirect request. - * - * @param redirectTo URL to redirect to - * @param statusCode HTTP status code returned from original request - * @param cacheHeaders Cache header for redirect URL - * @return true if setup succeeds, false otherwise (redirect loop - * count exceeded, body provider unable to rewind on 307 redirect) - */ - public boolean setupRedirect(String redirectTo, int statusCode, - Map<String, String> cacheHeaders) { - if (HttpLog.LOGV) { - HttpLog.v("RequestHandle.setupRedirect(): redirectCount " + - mRedirectCount); - } - - // be careful and remove authentication headers, if any - mHeaders.remove(AUTHORIZATION_HEADER); - mHeaders.remove(PROXY_AUTHORIZATION_HEADER); - - if (++mRedirectCount == MAX_REDIRECT_COUNT) { - // Way too many redirects -- fail out - if (HttpLog.LOGV) HttpLog.v( - "RequestHandle.setupRedirect(): too many redirects " + - mRequest); - mRequest.error(EventHandler.ERROR_REDIRECT_LOOP, - com.android.internal.R.string.httpErrorRedirectLoop); - return false; - } - - if (mUrl.startsWith("https:") && redirectTo.startsWith("http:")) { - // implement http://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html#sec15.1.3 - if (HttpLog.LOGV) { - HttpLog.v("blowing away the referer on an https -> http redirect"); - } - mHeaders.remove("Referer"); - } - - mUrl = redirectTo; - try { - mUri = new WebAddress(mUrl); - } catch (ParseException e) { - e.printStackTrace(); - } - - // update the "Cookie" header based on the redirected url - mHeaders.remove("Cookie"); - String cookie = CookieManager.getInstance().getCookie(mUri); - if (cookie != null && cookie.length() > 0) { - mHeaders.put("Cookie", cookie); - } - - if ((statusCode == 302 || statusCode == 303) && mMethod.equals("POST")) { - if (HttpLog.LOGV) { - HttpLog.v("replacing POST with GET on redirect to " + redirectTo); - } - mMethod = "GET"; - } - /* Only repost content on a 307. If 307, reset the body - provider so we can replay the body */ - if (statusCode == 307) { - try { - if (mBodyProvider != null) mBodyProvider.reset(); - } catch (java.io.IOException ex) { - if (HttpLog.LOGV) { - HttpLog.v("setupRedirect() failed to reset body provider"); - } - return false; - } - - } else { - mHeaders.remove("Content-Type"); - mBodyProvider = null; - } - - // Update the cache headers for this URL - mHeaders.putAll(cacheHeaders); - - createAndQueueNewRequest(); - return true; - } - - /** - * Create and queue an HTTP authentication-response (basic) request. - */ - public void setupBasicAuthResponse(boolean isProxy, String username, String password) { - String response = computeBasicAuthResponse(username, password); - if (HttpLog.LOGV) { - HttpLog.v("setupBasicAuthResponse(): response: " + response); - } - mHeaders.put(authorizationHeader(isProxy), "Basic " + response); - setupAuthResponse(); - } - - /** - * Create and queue an HTTP authentication-response (digest) request. - */ - public void setupDigestAuthResponse(boolean isProxy, - String username, - String password, - String realm, - String nonce, - String QOP, - String algorithm, - String opaque) { - - String response = computeDigestAuthResponse( - username, password, realm, nonce, QOP, algorithm, opaque); - if (HttpLog.LOGV) { - HttpLog.v("setupDigestAuthResponse(): response: " + response); - } - mHeaders.put(authorizationHeader(isProxy), "Digest " + response); - setupAuthResponse(); - } - - private void setupAuthResponse() { - try { - if (mBodyProvider != null) mBodyProvider.reset(); - } catch (java.io.IOException ex) { - if (HttpLog.LOGV) { - HttpLog.v("setupAuthResponse() failed to reset body provider"); - } - } - createAndQueueNewRequest(); - } - - /** - * @return HTTP request method (GET, PUT, etc). - */ - public String getMethod() { - return mMethod; - } - - /** - * @return Basic-scheme authentication response: BASE64(username:password). - */ - public static String computeBasicAuthResponse(String username, String password) { - Assert.assertNotNull(username); - Assert.assertNotNull(password); - - // encode username:password to base64 - return new String(Base64.encodeBase64((username + ':' + password).getBytes())); - } - - public void waitUntilComplete() { - mRequest.waitUntilComplete(); - } - - public void processRequest() { - if (mConnection != null) { - mConnection.processRequests(mRequest); - } - } - - /** - * @return Digest-scheme authentication response. - */ - private String computeDigestAuthResponse(String username, - String password, - String realm, - String nonce, - String QOP, - String algorithm, - String opaque) { - - Assert.assertNotNull(username); - Assert.assertNotNull(password); - Assert.assertNotNull(realm); - - String A1 = username + ":" + realm + ":" + password; - String A2 = mMethod + ":" + mUrl; - - // because we do not preemptively send authorization headers, nc is always 1 - String nc = "00000001"; - String cnonce = computeCnonce(); - String digest = computeDigest(A1, A2, nonce, QOP, nc, cnonce); - - String response = ""; - response += "username=" + doubleQuote(username) + ", "; - response += "realm=" + doubleQuote(realm) + ", "; - response += "nonce=" + doubleQuote(nonce) + ", "; - response += "uri=" + doubleQuote(mUrl) + ", "; - response += "response=" + doubleQuote(digest) ; - - if (opaque != null) { - response += ", opaque=" + doubleQuote(opaque); - } - - if (algorithm != null) { - response += ", algorithm=" + algorithm; - } - - if (QOP != null) { - response += ", qop=" + QOP + ", nc=" + nc + ", cnonce=" + doubleQuote(cnonce); - } - - return response; - } - - /** - * @return The right authorization header (dependeing on whether it is a proxy or not). - */ - public static String authorizationHeader(boolean isProxy) { - if (!isProxy) { - return AUTHORIZATION_HEADER; - } else { - return PROXY_AUTHORIZATION_HEADER; - } - } - - /** - * @return Double-quoted MD5 digest. - */ - private String computeDigest( - String A1, String A2, String nonce, String QOP, String nc, String cnonce) { - if (HttpLog.LOGV) { - HttpLog.v("computeDigest(): QOP: " + QOP); - } - - if (QOP == null) { - return KD(H(A1), nonce + ":" + H(A2)); - } else { - if (QOP.equalsIgnoreCase("auth")) { - return KD(H(A1), nonce + ":" + nc + ":" + cnonce + ":" + QOP + ":" + H(A2)); - } - } - - return null; - } - - /** - * @return MD5 hash of concat(secret, ":", data). - */ - private String KD(String secret, String data) { - return H(secret + ":" + data); - } - - /** - * @return MD5 hash of param. - */ - private String H(String param) { - if (param != null) { - try { - MessageDigest md5 = MessageDigest.getInstance("MD5"); - - byte[] d = md5.digest(param.getBytes()); - if (d != null) { - return bufferToHex(d); - } - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - - return null; - } - - /** - * @return HEX buffer representation. - */ - private String bufferToHex(byte[] buffer) { - final char hexChars[] = - { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; - - if (buffer != null) { - int length = buffer.length; - if (length > 0) { - StringBuilder hex = new StringBuilder(2 * length); - - for (int i = 0; i < length; ++i) { - byte l = (byte) (buffer[i] & 0x0F); - byte h = (byte)((buffer[i] & 0xF0) >> 4); - - hex.append(hexChars[h]); - hex.append(hexChars[l]); - } - - return hex.toString(); - } else { - return ""; - } - } - - return null; - } - - /** - * Computes a random cnonce value based on the current time. - */ - private String computeCnonce() { - Random rand = new Random(); - int nextInt = rand.nextInt(); - nextInt = (nextInt == Integer.MIN_VALUE) ? - Integer.MAX_VALUE : Math.abs(nextInt); - return Integer.toString(nextInt, 16); - } - - /** - * "Double-quotes" the argument. - */ - private String doubleQuote(String param) { - if (param != null) { - return "\"" + param + "\""; - } - - return null; - } - - /** - * Creates and queues new request. - */ - private void createAndQueueNewRequest() { - // mConnection is non-null if and only if the requests are synchronous. - if (mConnection != null) { - RequestHandle newHandle = mRequestQueue.queueSynchronousRequest( - mUrl, mUri, mMethod, mHeaders, mRequest.mEventHandler, - mBodyProvider, mBodyLength); - mRequest = newHandle.mRequest; - mConnection = newHandle.mConnection; - newHandle.processRequest(); - return; - } - mRequest = mRequestQueue.queueRequest( - mUrl, mUri, mMethod, mHeaders, mRequest.mEventHandler, - mBodyProvider, - mBodyLength).mRequest; - } -} diff --git a/core/java/android/net/http/RequestQueue.java b/core/java/android/net/http/RequestQueue.java deleted file mode 100644 index 7d2da1b..0000000 --- a/core/java/android/net/http/RequestQueue.java +++ /dev/null @@ -1,542 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -/** - * High level HTTP Interface - * Queues requests as necessary - */ - -package android.net.http; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.Proxy; -import android.net.WebAddress; -import android.util.Log; - -import java.io.InputStream; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.ListIterator; -import java.util.Map; - -import org.apache.http.HttpHost; - -/** - * {@hide} - */ -public class RequestQueue implements RequestFeeder { - - - /** - * Requests, indexed by HttpHost (scheme, host, port) - */ - private final LinkedHashMap<HttpHost, LinkedList<Request>> mPending; - private final Context mContext; - private final ActivePool mActivePool; - private final ConnectivityManager mConnectivityManager; - - private HttpHost mProxyHost = null; - private BroadcastReceiver mProxyChangeReceiver; - - /* default simultaneous connection count */ - private static final int CONNECTION_COUNT = 4; - - /** - * This class maintains active connection threads - */ - class ActivePool implements ConnectionManager { - /** Threads used to process requests */ - ConnectionThread[] mThreads; - - IdleCache mIdleCache; - - private int mTotalRequest; - private int mTotalConnection; - private int mConnectionCount; - - ActivePool(int connectionCount) { - mIdleCache = new IdleCache(); - mConnectionCount = connectionCount; - mThreads = new ConnectionThread[mConnectionCount]; - - for (int i = 0; i < mConnectionCount; i++) { - mThreads[i] = new ConnectionThread( - mContext, i, this, RequestQueue.this); - } - } - - void startup() { - for (int i = 0; i < mConnectionCount; i++) { - mThreads[i].start(); - } - } - - void shutdown() { - for (int i = 0; i < mConnectionCount; i++) { - mThreads[i].requestStop(); - } - } - - void startConnectionThread() { - synchronized (RequestQueue.this) { - RequestQueue.this.notify(); - } - } - - public void startTiming() { - for (int i = 0; i < mConnectionCount; i++) { - ConnectionThread rt = mThreads[i]; - rt.mCurrentThreadTime = -1; - rt.mTotalThreadTime = 0; - } - mTotalRequest = 0; - mTotalConnection = 0; - } - - public void stopTiming() { - int totalTime = 0; - for (int i = 0; i < mConnectionCount; i++) { - ConnectionThread rt = mThreads[i]; - if (rt.mCurrentThreadTime != -1) { - totalTime += rt.mTotalThreadTime; - } - rt.mCurrentThreadTime = 0; - } - Log.d("Http", "Http thread used " + totalTime + " ms " + " for " - + mTotalRequest + " requests and " + mTotalConnection - + " new connections"); - } - - void logState() { - StringBuilder dump = new StringBuilder(); - for (int i = 0; i < mConnectionCount; i++) { - dump.append(mThreads[i] + "\n"); - } - HttpLog.v(dump.toString()); - } - - - public HttpHost getProxyHost() { - return mProxyHost; - } - - /** - * Turns off persistence on all live connections - */ - void disablePersistence() { - for (int i = 0; i < mConnectionCount; i++) { - Connection connection = mThreads[i].mConnection; - if (connection != null) connection.setCanPersist(false); - } - mIdleCache.clear(); - } - - /* Linear lookup -- okay for small thread counts. Might use - private HashMap<HttpHost, LinkedList<ConnectionThread>> mActiveMap; - if this turns out to be a hotspot */ - ConnectionThread getThread(HttpHost host) { - synchronized(RequestQueue.this) { - for (int i = 0; i < mThreads.length; i++) { - ConnectionThread ct = mThreads[i]; - Connection connection = ct.mConnection; - if (connection != null && connection.mHost.equals(host)) { - return ct; - } - } - } - return null; - } - - public Connection getConnection(Context context, HttpHost host) { - host = RequestQueue.this.determineHost(host); - Connection con = mIdleCache.getConnection(host); - if (con == null) { - mTotalConnection++; - con = Connection.getConnection(mContext, host, mProxyHost, - RequestQueue.this); - } - return con; - } - public boolean recycleConnection(Connection connection) { - return mIdleCache.cacheConnection(connection.getHost(), connection); - } - - } - - /** - * A RequestQueue class instance maintains a set of queued - * requests. It orders them, makes the requests against HTTP - * servers, and makes callbacks to supplied eventHandlers as data - * is read. It supports request prioritization, connection reuse - * and pipelining. - * - * @param context application context - */ - public RequestQueue(Context context) { - this(context, CONNECTION_COUNT); - } - - /** - * A RequestQueue class instance maintains a set of queued - * requests. It orders them, makes the requests against HTTP - * servers, and makes callbacks to supplied eventHandlers as data - * is read. It supports request prioritization, connection reuse - * and pipelining. - * - * @param context application context - * @param connectionCount The number of simultaneous connections - */ - public RequestQueue(Context context, int connectionCount) { - mContext = context; - - mPending = new LinkedHashMap<HttpHost, LinkedList<Request>>(32); - - mActivePool = new ActivePool(connectionCount); - mActivePool.startup(); - - mConnectivityManager = (ConnectivityManager) - context.getSystemService(Context.CONNECTIVITY_SERVICE); - } - - /** - * Enables data state and proxy tracking - */ - public synchronized void enablePlatformNotifications() { - if (HttpLog.LOGV) HttpLog.v("RequestQueue.enablePlatformNotifications() network"); - - if (mProxyChangeReceiver == null) { - mProxyChangeReceiver = - new BroadcastReceiver() { - @Override - public void onReceive(Context ctx, Intent intent) { - setProxyConfig(); - } - }; - mContext.registerReceiver(mProxyChangeReceiver, - new IntentFilter(Proxy.PROXY_CHANGE_ACTION)); - } - // we need to resample the current proxy setup - setProxyConfig(); - } - - /** - * If platform notifications have been enabled, call this method - * to disable before destroying RequestQueue - */ - public synchronized void disablePlatformNotifications() { - if (HttpLog.LOGV) HttpLog.v("RequestQueue.disablePlatformNotifications() network"); - - if (mProxyChangeReceiver != null) { - mContext.unregisterReceiver(mProxyChangeReceiver); - mProxyChangeReceiver = null; - } - } - - /** - * Because our IntentReceiver can run within a different thread, - * synchronize setting the proxy - */ - private synchronized void setProxyConfig() { - NetworkInfo info = mConnectivityManager.getActiveNetworkInfo(); - if (info != null && info.getType() == ConnectivityManager.TYPE_WIFI) { - mProxyHost = null; - } else { - String host = Proxy.getHost(mContext); - if (HttpLog.LOGV) HttpLog.v("RequestQueue.setProxyConfig " + host); - if (host == null) { - mProxyHost = null; - } else { - mActivePool.disablePersistence(); - mProxyHost = new HttpHost(host, Proxy.getPort(mContext), "http"); - } - } - } - - /** - * used by webkit - * @return proxy host if set, null otherwise - */ - public HttpHost getProxyHost() { - return mProxyHost; - } - - /** - * Queues an HTTP request - * @param url The url to load. - * @param method "GET" or "POST." - * @param headers A hashmap of http headers. - * @param eventHandler The event handler for handling returned - * data. Callbacks will be made on the supplied instance. - * @param bodyProvider InputStream providing HTTP body, null if none - * @param bodyLength length of body, must be 0 if bodyProvider is null - */ - public RequestHandle queueRequest( - String url, String method, - Map<String, String> headers, EventHandler eventHandler, - InputStream bodyProvider, int bodyLength) { - WebAddress uri = new WebAddress(url); - return queueRequest(url, uri, method, headers, eventHandler, - bodyProvider, bodyLength); - } - - /** - * Queues an HTTP request - * @param url The url to load. - * @param uri The uri of the url to load. - * @param method "GET" or "POST." - * @param headers A hashmap of http headers. - * @param eventHandler The event handler for handling returned - * data. Callbacks will be made on the supplied instance. - * @param bodyProvider InputStream providing HTTP body, null if none - * @param bodyLength length of body, must be 0 if bodyProvider is null - */ - public RequestHandle queueRequest( - String url, WebAddress uri, String method, Map<String, String> headers, - EventHandler eventHandler, - InputStream bodyProvider, int bodyLength) { - - if (HttpLog.LOGV) HttpLog.v("RequestQueue.queueRequest " + uri); - - // Ensure there is an eventHandler set - if (eventHandler == null) { - eventHandler = new LoggingEventHandler(); - } - - /* Create and queue request */ - Request req; - HttpHost httpHost = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme()); - - // set up request - req = new Request(method, httpHost, mProxyHost, uri.getPath(), bodyProvider, - bodyLength, eventHandler, headers); - - queueRequest(req, false); - - mActivePool.mTotalRequest++; - - // dump(); - mActivePool.startConnectionThread(); - - return new RequestHandle( - this, url, uri, method, headers, bodyProvider, bodyLength, - req); - } - - private static class SyncFeeder implements RequestFeeder { - // This is used in the case where the request fails and needs to be - // requeued into the RequestFeeder. - private Request mRequest; - SyncFeeder() { - } - public Request getRequest() { - Request r = mRequest; - mRequest = null; - return r; - } - public Request getRequest(HttpHost host) { - return getRequest(); - } - public boolean haveRequest(HttpHost host) { - return mRequest != null; - } - public void requeueRequest(Request r) { - mRequest = r; - } - } - - public RequestHandle queueSynchronousRequest(String url, WebAddress uri, - String method, Map<String, String> headers, - EventHandler eventHandler, InputStream bodyProvider, - int bodyLength) { - if (HttpLog.LOGV) { - HttpLog.v("RequestQueue.dispatchSynchronousRequest " + uri); - } - - HttpHost host = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme()); - - Request req = new Request(method, host, mProxyHost, uri.getPath(), - bodyProvider, bodyLength, eventHandler, headers); - - // Open a new connection that uses our special RequestFeeder - // implementation. - host = determineHost(host); - Connection conn = Connection.getConnection(mContext, host, mProxyHost, - new SyncFeeder()); - - // TODO: I would like to process the request here but LoadListener - // needs a RequestHandle to process some messages. - return new RequestHandle(this, url, uri, method, headers, bodyProvider, - bodyLength, req, conn); - - } - - // Chooses between the proxy and the request's host. - private HttpHost determineHost(HttpHost host) { - // There used to be a comment in ConnectionThread about t-mob's proxy - // being really bad about https. But, HttpsConnection actually looks - // for a proxy and connects through it anyway. I think that this check - // is still valid because if a site is https, we will use - // HttpsConnection rather than HttpConnection if the proxy address is - // not secure. - return (mProxyHost == null || "https".equals(host.getSchemeName())) - ? host : mProxyHost; - } - - /** - * @return true iff there are any non-active requests pending - */ - synchronized boolean requestsPending() { - return !mPending.isEmpty(); - } - - - /** - * debug tool: prints request queue to log - */ - synchronized void dump() { - HttpLog.v("dump()"); - StringBuilder dump = new StringBuilder(); - int count = 0; - Iterator<Map.Entry<HttpHost, LinkedList<Request>>> iter; - - // mActivePool.log(dump); - - if (!mPending.isEmpty()) { - iter = mPending.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry<HttpHost, LinkedList<Request>> entry = iter.next(); - String hostName = entry.getKey().getHostName(); - StringBuilder line = new StringBuilder("p" + count++ + " " + hostName + " "); - - LinkedList<Request> reqList = entry.getValue(); - ListIterator reqIter = reqList.listIterator(0); - while (iter.hasNext()) { - Request request = (Request)iter.next(); - line.append(request + " "); - } - dump.append(line); - dump.append("\n"); - } - } - HttpLog.v(dump.toString()); - } - - /* - * RequestFeeder implementation - */ - public synchronized Request getRequest() { - Request ret = null; - - if (!mPending.isEmpty()) { - ret = removeFirst(mPending); - } - if (HttpLog.LOGV) HttpLog.v("RequestQueue.getRequest() => " + ret); - return ret; - } - - /** - * @return a request for given host if possible - */ - public synchronized Request getRequest(HttpHost host) { - Request ret = null; - - if (mPending.containsKey(host)) { - LinkedList<Request> reqList = mPending.get(host); - ret = reqList.removeFirst(); - if (reqList.isEmpty()) { - mPending.remove(host); - } - } - if (HttpLog.LOGV) HttpLog.v("RequestQueue.getRequest(" + host + ") => " + ret); - return ret; - } - - /** - * @return true if a request for this host is available - */ - public synchronized boolean haveRequest(HttpHost host) { - return mPending.containsKey(host); - } - - /** - * Put request back on head of queue - */ - public void requeueRequest(Request request) { - queueRequest(request, true); - } - - /** - * This must be called to cleanly shutdown RequestQueue - */ - public void shutdown() { - mActivePool.shutdown(); - } - - protected synchronized void queueRequest(Request request, boolean head) { - HttpHost host = request.mProxyHost == null ? request.mHost : request.mProxyHost; - LinkedList<Request> reqList; - if (mPending.containsKey(host)) { - reqList = mPending.get(host); - } else { - reqList = new LinkedList<Request>(); - mPending.put(host, reqList); - } - if (head) { - reqList.addFirst(request); - } else { - reqList.add(request); - } - } - - - public void startTiming() { - mActivePool.startTiming(); - } - - public void stopTiming() { - mActivePool.stopTiming(); - } - - /* helper */ - private Request removeFirst(LinkedHashMap<HttpHost, LinkedList<Request>> requestQueue) { - Request ret = null; - Iterator<Map.Entry<HttpHost, LinkedList<Request>>> iter = requestQueue.entrySet().iterator(); - if (iter.hasNext()) { - Map.Entry<HttpHost, LinkedList<Request>> entry = iter.next(); - LinkedList<Request> reqList = entry.getValue(); - ret = reqList.removeFirst(); - if (reqList.isEmpty()) { - requestQueue.remove(entry.getKey()); - } - } - return ret; - } - - /** - * This interface is exposed to each connection - */ - interface ConnectionManager { - HttpHost getProxyHost(); - Connection getConnection(Context context, HttpHost host); - boolean recycleConnection(Connection connection); - } -} diff --git a/core/java/android/webkit/LegacyErrorStrings.java b/core/java/android/webkit/LegacyErrorStrings.java index 11fc05d..60a6ee1 100644 --- a/core/java/android/webkit/LegacyErrorStrings.java +++ b/core/java/android/webkit/LegacyErrorStrings.java @@ -17,7 +17,6 @@ package android.webkit; import android.content.Context; -import android.net.http.EventHandler; import android.util.Log; /** @@ -44,52 +43,52 @@ class LegacyErrorStrings { */ private static int getResource(int errorCode) { switch(errorCode) { - case EventHandler.OK: + case 0: /* EventHandler.OK: */ return com.android.internal.R.string.httpErrorOk; - case EventHandler.ERROR: + case -1: /* EventHandler.ERROR: */ return com.android.internal.R.string.httpError; - case EventHandler.ERROR_LOOKUP: + case -2: /* EventHandler.ERROR_LOOKUP: */ return com.android.internal.R.string.httpErrorLookup; - case EventHandler.ERROR_UNSUPPORTED_AUTH_SCHEME: + case -3: /* EventHandler.ERROR_UNSUPPORTED_AUTH_SCHEME: */ return com.android.internal.R.string.httpErrorUnsupportedAuthScheme; - case EventHandler.ERROR_AUTH: + case -4: /* EventHandler.ERROR_AUTH: */ return com.android.internal.R.string.httpErrorAuth; - case EventHandler.ERROR_PROXYAUTH: + case -5: /* EventHandler.ERROR_PROXYAUTH: */ return com.android.internal.R.string.httpErrorProxyAuth; - case EventHandler.ERROR_CONNECT: + case -6: /* EventHandler.ERROR_CONNECT: */ return com.android.internal.R.string.httpErrorConnect; - case EventHandler.ERROR_IO: + case -7: /* EventHandler.ERROR_IO: */ return com.android.internal.R.string.httpErrorIO; - case EventHandler.ERROR_TIMEOUT: + case -8: /* EventHandler.ERROR_TIMEOUT: */ return com.android.internal.R.string.httpErrorTimeout; - case EventHandler.ERROR_REDIRECT_LOOP: + case -9: /* EventHandler.ERROR_REDIRECT_LOOP: */ return com.android.internal.R.string.httpErrorRedirectLoop; - case EventHandler.ERROR_UNSUPPORTED_SCHEME: + case -10: /* EventHandler.ERROR_UNSUPPORTED_SCHEME: */ return com.android.internal.R.string.httpErrorUnsupportedScheme; - case EventHandler.ERROR_FAILED_SSL_HANDSHAKE: + case -11: /* EventHandler.ERROR_FAILED_SSL_HANDSHAKE: */ return com.android.internal.R.string.httpErrorFailedSslHandshake; - case EventHandler.ERROR_BAD_URL: + case -12: /* EventHandler.ERROR_BAD_URL: */ return com.android.internal.R.string.httpErrorBadUrl; - case EventHandler.FILE_ERROR: + case -13: /* EventHandler.FILE_ERROR: */ return com.android.internal.R.string.httpErrorFile; - case EventHandler.FILE_NOT_FOUND_ERROR: + case -14: /* EventHandler.FILE_NOT_FOUND_ERROR: */ return com.android.internal.R.string.httpErrorFileNotFound; - case EventHandler.TOO_MANY_REQUESTS_ERROR: + case -15: /* EventHandler.TOO_MANY_REQUESTS_ERROR: */ return com.android.internal.R.string.httpErrorTooManyRequests; default: |