Merge "Move apache specific portions of android.net.http to external/apache-http."

This commit is contained in:
Narayan Kamath
2015-02-13 09:51:41 +00:00
committed by Android (Google) Code Review
56 changed files with 1980 additions and 13888 deletions

View File

@@ -614,7 +614,10 @@ $(gen): $(aidl_files) | $(AIDL)
# TODO: deal with com/google/android/googleapps
packages_to_document := \
android \
javax/microedition/khronos
javax/microedition/khronos \
org/apache/http/conn \
org/apache/http/params
# Search through the base framework dirs for these packages.
# The result will be relative to frameworks/base.
@@ -635,7 +638,6 @@ include libcore/Docs.mk
include external/junit/Common.mk
non_base_dirs := \
../../external/apache-http/src/org/apache/http \
../opt/telephony/src/java/android/provider \
../opt/telephony/src/java/android/telephony \
../opt/telephony/src/java/android/telephony/gsm \
@@ -1024,13 +1026,8 @@ include $(BUILD_DROIDDOC)
# Build ext.jar
# ============================================================
# NOTICE notes for non-obvious sections
# apache-http - covered by the Apache Commons section.
ext_dirs := \
../../external/nist-sip/java \
../../external/apache-http/src \
../../external/tagsoup/src \
ext_src_files := $(call all-java-files-under,$(ext_dirs))

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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;

View File

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

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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";
}
}

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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:

View File

@@ -1,86 +0,0 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/ByteArrayPartSource.java,v 1.7 2004/04/18 23:51:37 jsdever Exp $
* $Revision: 480424 $
* $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
*
* ====================================================================
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package com.android.internal.http.multipart;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
* A PartSource that reads from a byte array. This class should be used when
* the data to post is already loaded into memory.
*
* @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
*
* @since 2.0
*/
public class ByteArrayPartSource implements PartSource {
/** Name of the source file. */
private String fileName;
/** Byte array of the source file. */
private byte[] bytes;
/**
* Constructor for ByteArrayPartSource.
*
* @param fileName the name of the file these bytes represent
* @param bytes the content of this part
*/
public ByteArrayPartSource(String fileName, byte[] bytes) {
this.fileName = fileName;
this.bytes = bytes;
}
/**
* @see PartSource#getLength()
*/
public long getLength() {
return bytes.length;
}
/**
* @see PartSource#getFileName()
*/
public String getFileName() {
return fileName;
}
/**
* @see PartSource#createInputStream()
*/
public InputStream createInputStream() {
return new ByteArrayInputStream(bytes);
}
}

View File

@@ -1,259 +0,0 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/FilePart.java,v 1.19 2004/04/18 23:51:37 jsdever Exp $
* $Revision: 480424 $
* $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
*
* ====================================================================
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package com.android.internal.http.multipart;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.http.util.EncodingUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* This class implements a part of a Multipart post object that
* consists of a file.
*
* @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a>
* @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
* @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
* @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
* @author <a href="mailto:mdiggory@latte.harvard.edu">Mark Diggory</a>
* @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
* @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
*
* @since 2.0
*
* @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 class FilePart extends PartBase {
/** Default content encoding of file attachments. */
public static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";
/** Default charset of file attachments. */
public static final String DEFAULT_CHARSET = "ISO-8859-1";
/** Default transfer encoding of file attachments. */
public static final String DEFAULT_TRANSFER_ENCODING = "binary";
/** Log object for this class. */
private static final Log LOG = LogFactory.getLog(FilePart.class);
/** Attachment's file name */
protected static final String FILE_NAME = "; filename=";
/** Attachment's file name as a byte array */
private static final byte[] FILE_NAME_BYTES =
EncodingUtils.getAsciiBytes(FILE_NAME);
/** Source of the file part. */
private PartSource source;
/**
* FilePart Constructor.
*
* @param name the name for this part
* @param partSource the source for this part
* @param contentType the content type for this part, if <code>null</code> the
* {@link #DEFAULT_CONTENT_TYPE default} is used
* @param charset the charset encoding for this part, if <code>null</code> the
* {@link #DEFAULT_CHARSET default} is used
*/
public FilePart(String name, PartSource partSource, String contentType, String charset) {
super(
name,
contentType == null ? DEFAULT_CONTENT_TYPE : contentType,
charset == null ? "ISO-8859-1" : charset,
DEFAULT_TRANSFER_ENCODING
);
if (partSource == null) {
throw new IllegalArgumentException("Source may not be null");
}
this.source = partSource;
}
/**
* FilePart Constructor.
*
* @param name the name for this part
* @param partSource the source for this part
*/
public FilePart(String name, PartSource partSource) {
this(name, partSource, null, null);
}
/**
* FilePart Constructor.
*
* @param name the name of the file part
* @param file the file to post
*
* @throws FileNotFoundException if the <i>file</i> is not a normal
* file or if it is not readable.
*/
public FilePart(String name, File file)
throws FileNotFoundException {
this(name, new FilePartSource(file), null, null);
}
/**
* FilePart Constructor.
*
* @param name the name of the file part
* @param file the file to post
* @param contentType the content type for this part, if <code>null</code> the
* {@link #DEFAULT_CONTENT_TYPE default} is used
* @param charset the charset encoding for this part, if <code>null</code> the
* {@link #DEFAULT_CHARSET default} is used
*
* @throws FileNotFoundException if the <i>file</i> is not a normal
* file or if it is not readable.
*/
public FilePart(String name, File file, String contentType, String charset)
throws FileNotFoundException {
this(name, new FilePartSource(file), contentType, charset);
}
/**
* FilePart Constructor.
*
* @param name the name of the file part
* @param fileName the file name
* @param file the file to post
*
* @throws FileNotFoundException if the <i>file</i> is not a normal
* file or if it is not readable.
*/
public FilePart(String name, String fileName, File file)
throws FileNotFoundException {
this(name, new FilePartSource(fileName, file), null, null);
}
/**
* FilePart Constructor.
*
* @param name the name of the file part
* @param fileName the file name
* @param file the file to post
* @param contentType the content type for this part, if <code>null</code> the
* {@link #DEFAULT_CONTENT_TYPE default} is used
* @param charset the charset encoding for this part, if <code>null</code> the
* {@link #DEFAULT_CHARSET default} is used
*
* @throws FileNotFoundException if the <i>file</i> is not a normal
* file or if it is not readable.
*/
public FilePart(String name, String fileName, File file, String contentType, String charset)
throws FileNotFoundException {
this(name, new FilePartSource(fileName, file), contentType, charset);
}
/**
* Write the disposition header to the output stream
* @param out The output stream
* @throws IOException If an IO problem occurs
* @see Part#sendDispositionHeader(OutputStream)
*/
@Override
protected void sendDispositionHeader(OutputStream out)
throws IOException {
LOG.trace("enter sendDispositionHeader(OutputStream out)");
super.sendDispositionHeader(out);
String filename = this.source.getFileName();
if (filename != null) {
out.write(FILE_NAME_BYTES);
out.write(QUOTE_BYTES);
out.write(EncodingUtils.getAsciiBytes(filename));
out.write(QUOTE_BYTES);
}
}
/**
* Write the data in "source" to the specified stream.
* @param out The output stream.
* @throws IOException if an IO problem occurs.
* @see Part#sendData(OutputStream)
*/
@Override
protected void sendData(OutputStream out) throws IOException {
LOG.trace("enter sendData(OutputStream out)");
if (lengthOfData() == 0) {
// this file contains no data, so there is nothing to send.
// we don't want to create a zero length buffer as this will
// cause an infinite loop when reading.
LOG.debug("No data to send.");
return;
}
byte[] tmp = new byte[4096];
InputStream instream = source.createInputStream();
try {
int len;
while ((len = instream.read(tmp)) >= 0) {
out.write(tmp, 0, len);
}
} finally {
// we're done with the stream, close it
instream.close();
}
}
/**
* Returns the source of the file part.
*
* @return The source.
*/
protected PartSource getSource() {
LOG.trace("enter getSource()");
return this.source;
}
/**
* Return the length of the data.
* @return The length.
* @see Part#lengthOfData()
*/
@Override
protected long lengthOfData() {
LOG.trace("enter lengthOfData()");
return source.getLength();
}
}

View File

@@ -1,131 +0,0 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/FilePartSource.java,v 1.10 2004/04/18 23:51:37 jsdever Exp $
* $Revision: 480424 $
* $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
*
* ====================================================================
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package com.android.internal.http.multipart;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
/**
* A PartSource that reads from a File.
*
* @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
* @author <a href="mailto:mdiggory@latte.harvard.edu">Mark Diggory</a>
* @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
*
* @since 2.0
*/
public class FilePartSource implements PartSource {
/** File part file. */
private File file = null;
/** File part file name. */
private String fileName = null;
/**
* Constructor for FilePartSource.
*
* @param file the FilePart source File.
*
* @throws FileNotFoundException if the file does not exist or
* cannot be read
*/
public FilePartSource(File file) throws FileNotFoundException {
this.file = file;
if (file != null) {
if (!file.isFile()) {
throw new FileNotFoundException("File is not a normal file.");
}
if (!file.canRead()) {
throw new FileNotFoundException("File is not readable.");
}
this.fileName = file.getName();
}
}
/**
* Constructor for FilePartSource.
*
* @param fileName the file name of the FilePart
* @param file the source File for the FilePart
*
* @throws FileNotFoundException if the file does not exist or
* cannot be read
*/
public FilePartSource(String fileName, File file)
throws FileNotFoundException {
this(file);
if (fileName != null) {
this.fileName = fileName;
}
}
/**
* Return the length of the file
* @return the length of the file.
* @see PartSource#getLength()
*/
public long getLength() {
if (this.file != null) {
return this.file.length();
} else {
return 0;
}
}
/**
* Return the current filename
* @return the filename.
* @see PartSource#getFileName()
*/
public String getFileName() {
return (fileName == null) ? "noname" : fileName;
}
/**
* Return a new {@link FileInputStream} for the current filename.
* @return the new input stream.
* @throws IOException If an IO problem occurs.
* @see PartSource#createInputStream()
*/
public InputStream createInputStream() throws IOException {
if (this.file != null) {
return new FileInputStream(this.file);
} else {
return new ByteArrayInputStream(new byte[] {});
}
}
}

View File

@@ -1,236 +0,0 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/MultipartRequestEntity.java,v 1.1 2004/10/06 03:39:59 mbecke Exp $
* $Revision: 502647 $
* $Date: 2007-02-02 17:22:54 +0100 (Fri, 02 Feb 2007) $
*
* ====================================================================
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package com.android.internal.http.multipart;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Random;
import org.apache.http.Header;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EncodingUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Implements a request entity suitable for an HTTP multipart POST method.
* <p>
* The HTTP multipart POST method is defined in section 3.3 of
* <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC1867</a>:
* <blockquote>
* The media-type multipart/form-data follows the rules of all multipart
* MIME data streams as outlined in RFC 1521. The multipart/form-data contains
* a series of parts. Each part is expected to contain a content-disposition
* header where the value is "form-data" and a name attribute specifies
* the field name within the form, e.g., 'content-disposition: form-data;
* name="xxxxx"', where xxxxx is the field name corresponding to that field.
* Field names originally in non-ASCII character sets may be encoded using
* the method outlined in RFC 1522.
* </blockquote>
* </p>
* <p>This entity is designed to be used in conjunction with the
* {@link org.apache.http.HttpRequest} to provide
* multipart posts. Example usage:</p>
* <pre>
* File f = new File("/path/fileToUpload.txt");
* HttpRequest request = new HttpRequest("http://host/some_path");
* Part[] parts = {
* new StringPart("param_name", "value"),
* new FilePart(f.getName(), f)
* };
* filePost.setEntity(
* new MultipartRequestEntity(parts, filePost.getParams())
* );
* HttpClient client = new HttpClient();
* int status = client.executeMethod(filePost);
* </pre>
*
* @since 3.0
*
* @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 class MultipartEntity extends AbstractHttpEntity {
private static final Log log = LogFactory.getLog(MultipartEntity.class);
/** The Content-Type for multipart/form-data. */
private static final String MULTIPART_FORM_CONTENT_TYPE = "multipart/form-data";
/**
* Sets the value to use as the multipart boundary.
* <p>
* This parameter expects a value if type {@link String}.
* </p>
*/
public static final String MULTIPART_BOUNDARY = "http.method.multipart.boundary";
/**
* The pool of ASCII chars to be used for generating a multipart boundary.
*/
private static byte[] MULTIPART_CHARS = EncodingUtils.getAsciiBytes(
"-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
/**
* Generates a random multipart boundary string.
*/
private static byte[] generateMultipartBoundary() {
Random rand = new Random();
byte[] bytes = new byte[rand.nextInt(11) + 30]; // a random size from 30 to 40
for (int i = 0; i < bytes.length; i++) {
bytes[i] = MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)];
}
return bytes;
}
/** The MIME parts as set by the constructor */
protected Part[] parts;
private byte[] multipartBoundary;
private HttpParams params;
private boolean contentConsumed = false;
/**
* Creates a new multipart entity containing the given parts.
* @param parts The parts to include.
* @param params The params of the HttpMethod using this entity.
*/
public MultipartEntity(Part[] parts, HttpParams params) {
if (parts == null) {
throw new IllegalArgumentException("parts cannot be null");
}
if (params == null) {
throw new IllegalArgumentException("params cannot be null");
}
this.parts = parts;
this.params = params;
}
public MultipartEntity(Part[] parts) {
setContentType(MULTIPART_FORM_CONTENT_TYPE);
if (parts == null) {
throw new IllegalArgumentException("parts cannot be null");
}
this.parts = parts;
this.params = null;
}
/**
* Returns the MIME boundary string that is used to demarcate boundaries of
* this part. The first call to this method will implicitly create a new
* boundary string. To create a boundary string first the
* HttpMethodParams.MULTIPART_BOUNDARY parameter is considered. Otherwise
* a random one is generated.
*
* @return The boundary string of this entity in ASCII encoding.
*/
protected byte[] getMultipartBoundary() {
if (multipartBoundary == null) {
String temp = null;
if (params != null) {
temp = (String) params.getParameter(MULTIPART_BOUNDARY);
}
if (temp != null) {
multipartBoundary = EncodingUtils.getAsciiBytes(temp);
} else {
multipartBoundary = generateMultipartBoundary();
}
}
return multipartBoundary;
}
/**
* Returns <code>true</code> if all parts are repeatable, <code>false</code> otherwise.
*/
public boolean isRepeatable() {
for (int i = 0; i < parts.length; i++) {
if (!parts[i].isRepeatable()) {
return false;
}
}
return true;
}
/* (non-Javadoc)
*/
public void writeTo(OutputStream out) throws IOException {
Part.sendParts(out, parts, getMultipartBoundary());
}
/* (non-Javadoc)
* @see org.apache.commons.http.AbstractHttpEntity.#getContentType()
*/
@Override
public Header getContentType() {
StringBuffer buffer = new StringBuffer(MULTIPART_FORM_CONTENT_TYPE);
buffer.append("; boundary=");
buffer.append(EncodingUtils.getAsciiString(getMultipartBoundary()));
return new BasicHeader(HTTP.CONTENT_TYPE, buffer.toString());
}
/* (non-Javadoc)
*/
public long getContentLength() {
try {
return Part.getLengthOfParts(parts, getMultipartBoundary());
} catch (Exception e) {
log.error("An exception occurred while getting the length of the parts", e);
return 0;
}
}
public InputStream getContent() throws IOException, IllegalStateException {
if(!isRepeatable() && this.contentConsumed ) {
throw new IllegalStateException("Content has been consumed");
}
this.contentConsumed = true;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Part.sendParts(baos, this.parts, this.multipartBoundary);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
return bais;
}
public boolean isStreaming() {
return false;
}
}

View File

@@ -1,445 +0,0 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/Part.java,v 1.16 2005/01/14 21:16:40 olegk Exp $
* $Revision: 480424 $
* $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
*
* ====================================================================
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package com.android.internal.http.multipart;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.http.util.EncodingUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Abstract class for one Part of a multipart post object.
*
* @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a>
* @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
* @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
* @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
* @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
*
* @since 2.0
*
* @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 abstract class Part {
/** Log object for this class. */
private static final Log LOG = LogFactory.getLog(Part.class);
/**
* The boundary
* @deprecated use {@link org.apache.http.client.methods.multipart#MULTIPART_BOUNDARY}
*/
protected static final String BOUNDARY = "----------------314159265358979323846";
/**
* The boundary as a byte array.
* @deprecated
*/
protected static final byte[] BOUNDARY_BYTES = EncodingUtils.getAsciiBytes(BOUNDARY);
/**
* The default boundary to be used if {@link #setPartBoundary(byte[])} has not
* been called.
*/
private static final byte[] DEFAULT_BOUNDARY_BYTES = BOUNDARY_BYTES;
/** Carriage return/linefeed */
protected static final String CRLF = "\r\n";
/** Carriage return/linefeed as a byte array */
protected static final byte[] CRLF_BYTES = EncodingUtils.getAsciiBytes(CRLF);
/** Content dispostion characters */
protected static final String QUOTE = "\"";
/** Content dispostion as a byte array */
protected static final byte[] QUOTE_BYTES =
EncodingUtils.getAsciiBytes(QUOTE);
/** Extra characters */
protected static final String EXTRA = "--";
/** Extra characters as a byte array */
protected static final byte[] EXTRA_BYTES =
EncodingUtils.getAsciiBytes(EXTRA);
/** Content dispostion characters */
protected static final String CONTENT_DISPOSITION = "Content-Disposition: form-data; name=";
/** Content dispostion as a byte array */
protected static final byte[] CONTENT_DISPOSITION_BYTES =
EncodingUtils.getAsciiBytes(CONTENT_DISPOSITION);
/** Content type header */
protected static final String CONTENT_TYPE = "Content-Type: ";
/** Content type header as a byte array */
protected static final byte[] CONTENT_TYPE_BYTES =
EncodingUtils.getAsciiBytes(CONTENT_TYPE);
/** Content charset */
protected static final String CHARSET = "; charset=";
/** Content charset as a byte array */
protected static final byte[] CHARSET_BYTES =
EncodingUtils.getAsciiBytes(CHARSET);
/** Content type header */
protected static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding: ";
/** Content type header as a byte array */
protected static final byte[] CONTENT_TRANSFER_ENCODING_BYTES =
EncodingUtils.getAsciiBytes(CONTENT_TRANSFER_ENCODING);
/**
* Return the boundary string.
* @return the boundary string
* @deprecated uses a constant string. Rather use {@link #getPartBoundary}
*/
public static String getBoundary() {
return BOUNDARY;
}
/**
* The ASCII bytes to use as the multipart boundary.
*/
private byte[] boundaryBytes;
/**
* Return the name of this part.
* @return The name.
*/
public abstract String getName();
/**
* Returns the content type of this part.
* @return the content type, or <code>null</code> to exclude the content type header
*/
public abstract String getContentType();
/**
* Return the character encoding of this part.
* @return the character encoding, or <code>null</code> to exclude the character
* encoding header
*/
public abstract String getCharSet();
/**
* Return the transfer encoding of this part.
* @return the transfer encoding, or <code>null</code> to exclude the transfer encoding header
*/
public abstract String getTransferEncoding();
/**
* Gets the part boundary to be used.
* @return the part boundary as an array of bytes.
*
* @since 3.0
*/
protected byte[] getPartBoundary() {
if (boundaryBytes == null) {
// custom boundary bytes have not been set, use the default.
return DEFAULT_BOUNDARY_BYTES;
} else {
return boundaryBytes;
}
}
/**
* Sets the part boundary. Only meant to be used by
* {@link Part#sendParts(OutputStream, Part[], byte[])}
* and {@link Part#getLengthOfParts(Part[], byte[])}
* @param boundaryBytes An array of ASCII bytes.
* @since 3.0
*/
void setPartBoundary(byte[] boundaryBytes) {
this.boundaryBytes = boundaryBytes;
}
/**
* Tests if this part can be sent more than once.
* @return <code>true</code> if {@link #sendData(OutputStream)} can be successfully called
* more than once.
* @since 3.0
*/
public boolean isRepeatable() {
return true;
}
/**
* Write the start to the specified output stream
* @param out The output stream
* @throws IOException If an IO problem occurs.
*/
protected void sendStart(OutputStream out) throws IOException {
LOG.trace("enter sendStart(OutputStream out)");
out.write(EXTRA_BYTES);
out.write(getPartBoundary());
out.write(CRLF_BYTES);
}
/**
* Write the content disposition header to the specified output stream
*
* @param out The output stream
* @throws IOException If an IO problem occurs.
*/
protected void sendDispositionHeader(OutputStream out) throws IOException {
LOG.trace("enter sendDispositionHeader(OutputStream out)");
out.write(CONTENT_DISPOSITION_BYTES);
out.write(QUOTE_BYTES);
out.write(EncodingUtils.getAsciiBytes(getName()));
out.write(QUOTE_BYTES);
}
/**
* Write the content type header to the specified output stream
* @param out The output stream
* @throws IOException If an IO problem occurs.
*/
protected void sendContentTypeHeader(OutputStream out) throws IOException {
LOG.trace("enter sendContentTypeHeader(OutputStream out)");
String contentType = getContentType();
if (contentType != null) {
out.write(CRLF_BYTES);
out.write(CONTENT_TYPE_BYTES);
out.write(EncodingUtils.getAsciiBytes(contentType));
String charSet = getCharSet();
if (charSet != null) {
out.write(CHARSET_BYTES);
out.write(EncodingUtils.getAsciiBytes(charSet));
}
}
}
/**
* Write the content transfer encoding header to the specified
* output stream
*
* @param out The output stream
* @throws IOException If an IO problem occurs.
*/
protected void sendTransferEncodingHeader(OutputStream out) throws IOException {
LOG.trace("enter sendTransferEncodingHeader(OutputStream out)");
String transferEncoding = getTransferEncoding();
if (transferEncoding != null) {
out.write(CRLF_BYTES);
out.write(CONTENT_TRANSFER_ENCODING_BYTES);
out.write(EncodingUtils.getAsciiBytes(transferEncoding));
}
}
/**
* Write the end of the header to the output stream
* @param out The output stream
* @throws IOException If an IO problem occurs.
*/
protected void sendEndOfHeader(OutputStream out) throws IOException {
LOG.trace("enter sendEndOfHeader(OutputStream out)");
out.write(CRLF_BYTES);
out.write(CRLF_BYTES);
}
/**
* Write the data to the specified output stream
* @param out The output stream
* @throws IOException If an IO problem occurs.
*/
protected abstract void sendData(OutputStream out) throws IOException;
/**
* Return the length of the main content
*
* @return long The length.
* @throws IOException If an IO problem occurs
*/
protected abstract long lengthOfData() throws IOException;
/**
* Write the end data to the output stream.
* @param out The output stream
* @throws IOException If an IO problem occurs.
*/
protected void sendEnd(OutputStream out) throws IOException {
LOG.trace("enter sendEnd(OutputStream out)");
out.write(CRLF_BYTES);
}
/**
* Write all the data to the output stream.
* If you override this method make sure to override
* #length() as well
*
* @param out The output stream
* @throws IOException If an IO problem occurs.
*/
public void send(OutputStream out) throws IOException {
LOG.trace("enter send(OutputStream out)");
sendStart(out);
sendDispositionHeader(out);
sendContentTypeHeader(out);
sendTransferEncodingHeader(out);
sendEndOfHeader(out);
sendData(out);
sendEnd(out);
}
/**
* Return the full length of all the data.
* If you override this method make sure to override
* #send(OutputStream) as well
*
* @return long The length.
* @throws IOException If an IO problem occurs
*/
public long length() throws IOException {
LOG.trace("enter length()");
if (lengthOfData() < 0) {
return -1;
}
ByteArrayOutputStream overhead = new ByteArrayOutputStream();
sendStart(overhead);
sendDispositionHeader(overhead);
sendContentTypeHeader(overhead);
sendTransferEncodingHeader(overhead);
sendEndOfHeader(overhead);
sendEnd(overhead);
return overhead.size() + lengthOfData();
}
/**
* Return a string representation of this object.
* @return A string representation of this object.
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return this.getName();
}
/**
* Write all parts and the last boundary to the specified output stream.
*
* @param out The stream to write to.
* @param parts The parts to write.
*
* @throws IOException If an I/O error occurs while writing the parts.
*/
public static void sendParts(OutputStream out, final Part[] parts)
throws IOException {
sendParts(out, parts, DEFAULT_BOUNDARY_BYTES);
}
/**
* Write all parts and the last boundary to the specified output stream.
*
* @param out The stream to write to.
* @param parts The parts to write.
* @param partBoundary The ASCII bytes to use as the part boundary.
*
* @throws IOException If an I/O error occurs while writing the parts.
*
* @since 3.0
*/
public static void sendParts(OutputStream out, Part[] parts, byte[] partBoundary)
throws IOException {
if (parts == null) {
throw new IllegalArgumentException("Parts may not be null");
}
if (partBoundary == null || partBoundary.length == 0) {
throw new IllegalArgumentException("partBoundary may not be empty");
}
for (int i = 0; i < parts.length; i++) {
// set the part boundary before the part is sent
parts[i].setPartBoundary(partBoundary);
parts[i].send(out);
}
out.write(EXTRA_BYTES);
out.write(partBoundary);
out.write(EXTRA_BYTES);
out.write(CRLF_BYTES);
}
/**
* Return the total sum of all parts and that of the last boundary
*
* @param parts The parts.
* @return The total length
*
* @throws IOException If an I/O error occurs while writing the parts.
*/
public static long getLengthOfParts(Part[] parts)
throws IOException {
return getLengthOfParts(parts, DEFAULT_BOUNDARY_BYTES);
}
/**
* Gets the length of the multipart message including the given parts.
*
* @param parts The parts.
* @param partBoundary The ASCII bytes to use as the part boundary.
* @return The total length
*
* @throws IOException If an I/O error occurs while writing the parts.
*
* @since 3.0
*/
public static long getLengthOfParts(Part[] parts, byte[] partBoundary) throws IOException {
LOG.trace("getLengthOfParts(Parts[])");
if (parts == null) {
throw new IllegalArgumentException("Parts may not be null");
}
long total = 0;
for (int i = 0; i < parts.length; i++) {
// set the part boundary before we calculate the part's length
parts[i].setPartBoundary(partBoundary);
long l = parts[i].length();
if (l < 0) {
return -1;
}
total += l;
}
total += EXTRA_BYTES.length;
total += partBoundary.length;
total += EXTRA_BYTES.length;
total += CRLF_BYTES.length;
return total;
}
}

View File

@@ -1,150 +0,0 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/PartBase.java,v 1.5 2004/04/18 23:51:37 jsdever Exp $
* $Revision: 480424 $
* $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
*
* ====================================================================
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package com.android.internal.http.multipart;
/**
* Provides setters and getters for the basic Part properties.
*
* @author Michael Becke
*/
public abstract class PartBase extends Part {
/** Name of the file part. */
private String name;
/** Content type of the file part. */
private String contentType;
/** Content encoding of the file part. */
private String charSet;
/** The transfer encoding. */
private String transferEncoding;
/**
* Constructor.
*
* @param name The name of the part
* @param contentType The content type, or <code>null</code>
* @param charSet The character encoding, or <code>null</code>
* @param transferEncoding The transfer encoding, or <code>null</code>
*/
public PartBase(String name, String contentType, String charSet, String transferEncoding) {
if (name == null) {
throw new IllegalArgumentException("Name must not be null");
}
this.name = name;
this.contentType = contentType;
this.charSet = charSet;
this.transferEncoding = transferEncoding;
}
/**
* Returns the name.
* @return The name.
* @see Part#getName()
*/
@Override
public String getName() {
return this.name;
}
/**
* Returns the content type of this part.
* @return String The name.
*/
@Override
public String getContentType() {
return this.contentType;
}
/**
* Return the character encoding of this part.
* @return String The name.
*/
@Override
public String getCharSet() {
return this.charSet;
}
/**
* Returns the transfer encoding of this part.
* @return String The name.
*/
@Override
public String getTransferEncoding() {
return transferEncoding;
}
/**
* Sets the character encoding.
*
* @param charSet the character encoding, or <code>null</code> to exclude the character
* encoding header
*/
public void setCharSet(String charSet) {
this.charSet = charSet;
}
/**
* Sets the content type.
*
* @param contentType the content type, or <code>null</code> to exclude the content type header
*/
public void setContentType(String contentType) {
this.contentType = contentType;
}
/**
* Sets the part name.
*
* @param name
*/
public void setName(String name) {
if (name == null) {
throw new IllegalArgumentException("Name must not be null");
}
this.name = name;
}
/**
* Sets the transfer encoding.
*
* @param transferEncoding the transfer encoding, or <code>null</code> to exclude the
* transfer encoding header
*/
public void setTransferEncoding(String transferEncoding) {
this.transferEncoding = transferEncoding;
}
}

View File

@@ -1,72 +0,0 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/PartSource.java,v 1.6 2004/04/18 23:51:37 jsdever Exp $
* $Revision: 480424 $
* $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
*
* ====================================================================
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package com.android.internal.http.multipart;
import java.io.IOException;
import java.io.InputStream;
/**
* An interface for providing access to data when posting MultiPart messages.
*
* @see FilePart
*
* @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
*
* @since 2.0
*/
public interface PartSource {
/**
* Gets the number of bytes contained in this source.
*
* @return a value >= 0
*/
long getLength();
/**
* Gets the name of the file this source represents.
*
* @return the fileName used for posting a MultiPart file part
*/
String getFileName();
/**
* Gets a new InputStream for reading this source. This method can be
* called more than once and should therefore return a new stream every
* time.
*
* @return a new InputStream
*
* @throws IOException if an error occurs when creating the InputStream
*/
InputStream createInputStream() throws IOException;
}

View File

@@ -1,156 +0,0 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/StringPart.java,v 1.11 2004/04/18 23:51:37 jsdever Exp $
* $Revision: 480424 $
* $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
*
* ====================================================================
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package com.android.internal.http.multipart;
import java.io.OutputStream;
import java.io.IOException;
import org.apache.http.util.EncodingUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Simple string parameter for a multipart post
*
* @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a>
* @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
* @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
* @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
*
* @since 2.0
*
* @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 class StringPart extends PartBase {
/** Log object for this class. */
private static final Log LOG = LogFactory.getLog(StringPart.class);
/** Default content encoding of string parameters. */
public static final String DEFAULT_CONTENT_TYPE = "text/plain";
/** Default charset of string parameters*/
public static final String DEFAULT_CHARSET = "US-ASCII";
/** Default transfer encoding of string parameters*/
public static final String DEFAULT_TRANSFER_ENCODING = "8bit";
/** Contents of this StringPart. */
private byte[] content;
/** The String value of this part. */
private String value;
/**
* Constructor.
*
* @param name The name of the part
* @param value the string to post
* @param charset the charset to be used to encode the string, if <code>null</code>
* the {@link #DEFAULT_CHARSET default} is used
*/
public StringPart(String name, String value, String charset) {
super(
name,
DEFAULT_CONTENT_TYPE,
charset == null ? DEFAULT_CHARSET : charset,
DEFAULT_TRANSFER_ENCODING
);
if (value == null) {
throw new IllegalArgumentException("Value may not be null");
}
if (value.indexOf(0) != -1) {
// See RFC 2048, 2.8. "8bit Data"
throw new IllegalArgumentException("NULs may not be present in string parts");
}
this.value = value;
}
/**
* Constructor.
*
* @param name The name of the part
* @param value the string to post
*/
public StringPart(String name, String value) {
this(name, value, null);
}
/**
* Gets the content in bytes. Bytes are lazily created to allow the charset to be changed
* after the part is created.
*
* @return the content in bytes
*/
private byte[] getContent() {
if (content == null) {
content = EncodingUtils.getBytes(value, getCharSet());
}
return content;
}
/**
* Writes the data to the given OutputStream.
* @param out the OutputStream to write to
* @throws IOException if there is a write error
*/
@Override
protected void sendData(OutputStream out) throws IOException {
LOG.trace("enter sendData(OutputStream)");
out.write(getContent());
}
/**
* Return the length of the data.
* @return The length of the data.
* @see Part#lengthOfData()
*/
@Override
protected long lengthOfData() {
LOG.trace("enter lengthOfData()");
return getContent().length;
}
/* (non-Javadoc)
* @see org.apache.commons.httpclient.methods.multipart.BasePart#setCharSet(java.lang.String)
*/
@Override
public void setCharSet(String charSet) {
super.setCharSet(charSet);
this.content = null;
}
}

View File

@@ -0,0 +1,69 @@
/*
* $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ConnectTimeoutException.java $
* $Revision: 617645 $
* $Date: 2008-02-01 13:05:31 -0800 (Fri, 01 Feb 2008) $
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.conn;
import java.io.InterruptedIOException;
/**
* A timeout while connecting to an HTTP server or waiting for an
* available connection from an HttpConnectionManager.
*
* @author <a href="mailto:laura@lwerner.org">Laura Werner</a>
*
* @since 4.0
*
* @deprecated Please use {@link java.net.URL#openConnection} instead.
* Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
* for further details.
*/
@Deprecated
public class ConnectTimeoutException extends InterruptedIOException {
private static final long serialVersionUID = -4816682903149535989L;
/**
* Creates a ConnectTimeoutException with a <tt>null</tt> detail message.
*/
public ConnectTimeoutException() {
super();
}
/**
* Creates a ConnectTimeoutException with the specified detail message.
*
* @param message The exception detail message
*/
public ConnectTimeoutException(final String message) {
super(message);
}
}

View File

@@ -0,0 +1,47 @@
/*
* $HeadURL:$
* $Revision:$
* $Date:$
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.conn.scheme;
import java.io.IOException;
import java.net.InetAddress;
/**
* @deprecated Please use {@link java.net.URL#openConnection} instead.
* Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
* for further details.
*/
@Deprecated
public interface HostNameResolver {
InetAddress resolve (String hostname) throws IOException;
}

View File

@@ -0,0 +1,77 @@
/*
* $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/scheme/LayeredSocketFactory.java $
* $Revision: 645850 $
* $Date: 2008-04-08 04:08:52 -0700 (Tue, 08 Apr 2008) $
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.conn.scheme;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* A {@link SocketFactory SocketFactory} for layered sockets (SSL/TLS).
* See there for things to consider when implementing a socket factory.
*
* @author Michael Becke
* @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
* @since 4.0
*
* @deprecated Please use {@link java.net.URL#openConnection} instead.
* Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
* for further details.
*/
@Deprecated
public interface LayeredSocketFactory extends SocketFactory {
/**
* Returns a socket connected to the given host that is layered over an
* existing socket. Used primarily for creating secure sockets through
* proxies.
*
* @param socket the existing socket
* @param host the host name/IP
* @param port the port on the host
* @param autoClose a flag for closing the underling socket when the created
* socket is closed
*
* @return Socket a new socket
*
* @throws IOException if an I/O error occurs while creating the socket
* @throws UnknownHostException if the IP address of the host cannot be
* determined
*/
Socket createSocket(
Socket socket,
String host,
int port,
boolean autoClose
) throws IOException, UnknownHostException;
}

View File

@@ -0,0 +1,143 @@
/*
* $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/scheme/SocketFactory.java $
* $Revision: 645850 $
* $Date: 2008-04-08 04:08:52 -0700 (Tue, 08 Apr 2008) $
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.conn.scheme;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.params.HttpParams;
/**
* A factory for creating and connecting sockets.
* The factory encapsulates the logic for establishing a socket connection.
* <br/>
* Both {@link java.lang.Object#equals(java.lang.Object) Object.equals()}
* and {@link java.lang.Object#hashCode() Object.hashCode()}
* must be overridden for the correct operation of some connection managers.
*
* @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
* @author Michael Becke
* @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
*
* @deprecated Please use {@link java.net.URL#openConnection} instead.
* Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
* for further details.
*/
@Deprecated
public interface SocketFactory {
/**
* Creates a new, unconnected socket.
* The socket should subsequently be passed to
* {@link #connectSocket connectSocket}.
*
* @return a new socket
*
* @throws IOException if an I/O error occurs while creating the socket
*/
Socket createSocket()
throws IOException
;
/**
* Connects a socket to the given host.
*
* @param sock the socket to connect, as obtained from
* {@link #createSocket createSocket}.
* <code>null</code> indicates that a new socket
* should be created and connected.
* @param host the host to connect to
* @param port the port to connect to on the host
* @param localAddress the local address to bind the socket to, or
* <code>null</code> for any
* @param localPort the port on the local machine,
* 0 or a negative number for any
* @param params additional {@link HttpParams parameters} for connecting
*
* @return the connected socket. The returned object may be different
* from the <code>sock</code> argument if this factory supports
* a layered protocol.
*
* @throws IOException if an I/O error occurs
* @throws UnknownHostException if the IP address of the target host
* can not be determined
* @throws ConnectTimeoutException if the socket cannot be connected
* within the time limit defined in the <code>params</code>
*/
Socket connectSocket(
Socket sock,
String host,
int port,
InetAddress localAddress,
int localPort,
HttpParams params
) throws IOException, UnknownHostException, ConnectTimeoutException;
/**
* Checks whether a socket provides a secure connection.
* The socket must be {@link #connectSocket connected}
* by this factory.
* The factory will <i>not</i> perform I/O operations
* in this method.
* <br/>
* As a rule of thumb, plain sockets are not secure and
* TLS/SSL sockets are secure. However, there may be
* application specific deviations. For example, a plain
* socket to a host in the same intranet ("trusted zone")
* could be considered secure. On the other hand, a
* TLS/SSL socket could be considered insecure based on
* the cypher suite chosen for the connection.
*
* @param sock the connected socket to check
*
* @return <code>true</code> if the connection of the socket
* should be considered secure, or
* <code>false</code> if it should not
*
* @throws IllegalArgumentException
* if the argument is invalid, for example because it is
* not a connected socket or was created by a different
* socket factory.
* Note that socket factories are <i>not</i> required to
* check these conditions, they may simply return a default
* value when called with an invalid socket argument.
*/
boolean isSecure(Socket sock)
throws IllegalArgumentException
;
}

View File

@@ -0,0 +1,288 @@
/*
* $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java $
* $Revision: 653041 $
* $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.conn.ssl;
import java.util.regex.Pattern;
import java.io.IOException;
import java.security.cert.Certificate;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.logging.Logger;
import java.util.logging.Level;
import javax.net.ssl.DistinguishedNameParser;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
/**
* Abstract base class for all standard {@link X509HostnameVerifier}
* implementations.
*
* @author Julius Davies
*
* @deprecated Please use {@link java.net.URL#openConnection} instead.
* Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
* for further details.
*/
@Deprecated
public abstract class AbstractVerifier implements X509HostnameVerifier {
private static final Pattern IPV4_PATTERN = Pattern.compile(
"^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$");
/**
* This contains a list of 2nd-level domains that aren't allowed to
* have wildcards when combined with country-codes.
* For example: [*.co.uk].
* <p/>
* The [*.co.uk] problem is an interesting one. Should we just hope
* that CA's would never foolishly allow such a certificate to happen?
* Looks like we're the only implementation guarding against this.
* Firefox, Curl, Sun Java 1.4, 5, 6 don't bother with this check.
*/
private final static String[] BAD_COUNTRY_2LDS =
{ "ac", "co", "com", "ed", "edu", "go", "gouv", "gov", "info",
"lg", "ne", "net", "or", "org" };
static {
// Just in case developer forgot to manually sort the array. :-)
Arrays.sort(BAD_COUNTRY_2LDS);
}
public AbstractVerifier() {
super();
}
public final void verify(String host, SSLSocket ssl)
throws IOException {
if(host == null) {
throw new NullPointerException("host to verify is null");
}
SSLSession session = ssl.getSession();
Certificate[] certs = session.getPeerCertificates();
X509Certificate x509 = (X509Certificate) certs[0];
verify(host, x509);
}
public final boolean verify(String host, SSLSession session) {
try {
Certificate[] certs = session.getPeerCertificates();
X509Certificate x509 = (X509Certificate) certs[0];
verify(host, x509);
return true;
}
catch(SSLException e) {
return false;
}
}
public final void verify(String host, X509Certificate cert)
throws SSLException {
String[] cns = getCNs(cert);
String[] subjectAlts = getDNSSubjectAlts(cert);
verify(host, cns, subjectAlts);
}
public final void verify(final String host, final String[] cns,
final String[] subjectAlts,
final boolean strictWithSubDomains)
throws SSLException {
// Build the list of names we're going to check. Our DEFAULT and
// STRICT implementations of the HostnameVerifier only use the
// first CN provided. All other CNs are ignored.
// (Firefox, wget, curl, Sun Java 1.4, 5, 6 all work this way).
LinkedList<String> names = new LinkedList<String>();
if(cns != null && cns.length > 0 && cns[0] != null) {
names.add(cns[0]);
}
if(subjectAlts != null) {
for (String subjectAlt : subjectAlts) {
if (subjectAlt != null) {
names.add(subjectAlt);
}
}
}
if(names.isEmpty()) {
String msg = "Certificate for <" + host + "> doesn't contain CN or DNS subjectAlt";
throw new SSLException(msg);
}
// StringBuffer for building the error message.
StringBuffer buf = new StringBuffer();
// We're can be case-insensitive when comparing the host we used to
// establish the socket to the hostname in the certificate.
String hostName = host.trim().toLowerCase(Locale.ENGLISH);
boolean match = false;
for(Iterator<String> it = names.iterator(); it.hasNext();) {
// Don't trim the CN, though!
String cn = it.next();
cn = cn.toLowerCase(Locale.ENGLISH);
// Store CN in StringBuffer in case we need to report an error.
buf.append(" <");
buf.append(cn);
buf.append('>');
if(it.hasNext()) {
buf.append(" OR");
}
// The CN better have at least two dots if it wants wildcard
// action. It also can't be [*.co.uk] or [*.co.jp] or
// [*.org.uk], etc...
boolean doWildcard = cn.startsWith("*.") &&
cn.indexOf('.', 2) != -1 &&
acceptableCountryWildcard(cn) &&
!isIPv4Address(host);
if(doWildcard) {
match = hostName.endsWith(cn.substring(1));
if(match && strictWithSubDomains) {
// If we're in strict mode, then [*.foo.com] is not
// allowed to match [a.b.foo.com]
match = countDots(hostName) == countDots(cn);
}
} else {
match = hostName.equals(cn);
}
if(match) {
break;
}
}
if(!match) {
throw new SSLException("hostname in certificate didn't match: <" + host + "> !=" + buf);
}
}
public static boolean acceptableCountryWildcard(String cn) {
int cnLen = cn.length();
if(cnLen >= 7 && cnLen <= 9) {
// Look for the '.' in the 3rd-last position:
if(cn.charAt(cnLen - 3) == '.') {
// Trim off the [*.] and the [.XX].
String s = cn.substring(2, cnLen - 3);
// And test against the sorted array of bad 2lds:
int x = Arrays.binarySearch(BAD_COUNTRY_2LDS, s);
return x < 0;
}
}
return true;
}
public static String[] getCNs(X509Certificate cert) {
DistinguishedNameParser dnParser =
new DistinguishedNameParser(cert.getSubjectX500Principal());
List<String> cnList = dnParser.getAllMostSpecificFirst("cn");
if(!cnList.isEmpty()) {
String[] cns = new String[cnList.size()];
cnList.toArray(cns);
return cns;
} else {
return null;
}
}
/**
* Extracts the array of SubjectAlt DNS names from an X509Certificate.
* Returns null if there aren't any.
* <p/>
* Note: Java doesn't appear able to extract international characters
* from the SubjectAlts. It can only extract international characters
* from the CN field.
* <p/>
* (Or maybe the version of OpenSSL I'm using to test isn't storing the
* international characters correctly in the SubjectAlts?).
*
* @param cert X509Certificate
* @return Array of SubjectALT DNS names stored in the certificate.
*/
public static String[] getDNSSubjectAlts(X509Certificate cert) {
LinkedList<String> subjectAltList = new LinkedList<String>();
Collection<List<?>> c = null;
try {
c = cert.getSubjectAlternativeNames();
}
catch(CertificateParsingException cpe) {
Logger.getLogger(AbstractVerifier.class.getName())
.log(Level.FINE, "Error parsing certificate.", cpe);
}
if(c != null) {
for (List<?> aC : c) {
List<?> list = aC;
int type = ((Integer) list.get(0)).intValue();
// If type is 2, then we've got a dNSName
if (type == 2) {
String s = (String) list.get(1);
subjectAltList.add(s);
}
}
}
if(!subjectAltList.isEmpty()) {
String[] subjectAlts = new String[subjectAltList.size()];
subjectAltList.toArray(subjectAlts);
return subjectAlts;
} else {
return null;
}
}
/**
* Counts the number of dots "." in a string.
* @param s string to count dots from
* @return number of dots
*/
public static int countDots(final String s) {
int count = 0;
for(int i = 0; i < s.length(); i++) {
if(s.charAt(i) == '.') {
count++;
}
}
return count;
}
private static boolean isIPv4Address(final String input) {
return IPV4_PATTERN.matcher(input).matches();
}
}

View File

@@ -0,0 +1,59 @@
/*
* $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java $
* $Revision: 617642 $
* $Date: 2008-02-01 12:54:07 -0800 (Fri, 01 Feb 2008) $
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.conn.ssl;
/**
* The ALLOW_ALL HostnameVerifier essentially turns hostname verification
* off. This implementation is a no-op, and never throws the SSLException.
*
* @author Julius Davies
*
* @deprecated Please use {@link java.net.URL#openConnection} instead.
* Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
* for further details.
*/
@Deprecated
public class AllowAllHostnameVerifier extends AbstractVerifier {
public final void verify(
final String host,
final String[] cns,
final String[] subjectAlts) {
// Allow everything - so never blowup.
}
@Override
public final String toString() {
return "ALLOW_ALL";
}
}

View File

@@ -0,0 +1,67 @@
/*
* $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java $
* $Revision: 617642 $
* $Date: 2008-02-01 12:54:07 -0800 (Fri, 01 Feb 2008) $
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.conn.ssl;
import javax.net.ssl.SSLException;
/**
* The HostnameVerifier that works the same way as Curl and Firefox.
* <p/>
* The hostname must match either the first CN, or any of the subject-alts.
* A wildcard can occur in the CN, and in any of the subject-alts.
* <p/>
* The only difference between BROWSER_COMPATIBLE and STRICT is that a wildcard
* (such as "*.foo.com") with BROWSER_COMPATIBLE matches all subdomains,
* including "a.b.foo.com".
*
* @author Julius Davies
*
* @deprecated Please use {@link java.net.URL#openConnection} instead.
* Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
* for further details.
*/
@Deprecated
public class BrowserCompatHostnameVerifier extends AbstractVerifier {
public final void verify(
final String host,
final String[] cns,
final String[] subjectAlts) throws SSLException {
verify(host, cns, subjectAlts, false);
}
@Override
public final String toString() {
return "BROWSER_COMPATIBLE";
}
}

View File

@@ -0,0 +1,408 @@
/*
* $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java $
* $Revision: 659194 $
* $Date: 2008-05-22 11:33:47 -0700 (Thu, 22 May 2008) $
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.conn.ssl;
import org.apache.http.conn.scheme.HostNameResolver;
import org.apache.http.conn.scheme.LayeredSocketFactory;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
/**
* Layered socket factory for TLS/SSL connections, based on JSSE.
*.
* <p>
* SSLSocketFactory can be used to validate the identity of the HTTPS
* server against a list of trusted certificates and to authenticate to
* the HTTPS server using a private key.
* </p>
*
* <p>
* SSLSocketFactory will enable server authentication when supplied with
* a {@link KeyStore truststore} file containg one or several trusted
* certificates. The client secure socket will reject the connection during
* the SSL session handshake if the target HTTPS server attempts to
* authenticate itself with a non-trusted certificate.
* </p>
*
* <p>
* Use JDK keytool utility to import a trusted certificate and generate a truststore file:
* <pre>
* keytool -import -alias "my server cert" -file server.crt -keystore my.truststore
* </pre>
* </p>
*
* <p>
* SSLSocketFactory will enable client authentication when supplied with
* a {@link KeyStore keystore} file containg a private key/public certificate
* pair. The client secure socket will use the private key to authenticate
* itself to the target HTTPS server during the SSL session handshake if
* requested to do so by the server.
* The target HTTPS server will in its turn verify the certificate presented
* by the client in order to establish client's authenticity
* </p>
*
* <p>
* Use the following sequence of actions to generate a keystore file
* </p>
* <ul>
* <li>
* <p>
* Use JDK keytool utility to generate a new key
* <pre>keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore</pre>
* For simplicity use the same password for the key as that of the keystore
* </p>
* </li>
* <li>
* <p>
* Issue a certificate signing request (CSR)
* <pre>keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore</pre>
* </p>
* </li>
* <li>
* <p>
* Send the certificate request to the trusted Certificate Authority for signature.
* One may choose to act as her own CA and sign the certificate request using a PKI
* tool, such as OpenSSL.
* </p>
* </li>
* <li>
* <p>
* Import the trusted CA root certificate
* <pre>keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore</pre>
* </p>
* </li>
* <li>
* <p>
* Import the PKCS#7 file containg the complete certificate chain
* <pre>keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore</pre>
* </p>
* </li>
* <li>
* <p>
* Verify the content the resultant keystore file
* <pre>keytool -list -v -keystore my.keystore</pre>
* </p>
* </li>
* </ul>
* @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
* @author Julius Davies
*
* @deprecated Please use {@link java.net.URL#openConnection} instead.
* Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
* for further details.
*/
@Deprecated
public class SSLSocketFactory implements LayeredSocketFactory {
public static final String TLS = "TLS";
public static final String SSL = "SSL";
public static final String SSLV2 = "SSLv2";
public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER
= new AllowAllHostnameVerifier();
public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
= new BrowserCompatHostnameVerifier();
public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER
= new StrictHostnameVerifier();
/*
* Put defaults into holder class to avoid class preloading creating an
* instance of the classes referenced.
*/
private static class NoPreloadHolder {
/**
* The factory using the default JVM settings for secure connections.
*/
private static final SSLSocketFactory DEFAULT_FACTORY = new SSLSocketFactory();
}
/**
* Gets an singleton instance of the SSLProtocolSocketFactory.
* @return a SSLProtocolSocketFactory
*/
public static SSLSocketFactory getSocketFactory() {
return NoPreloadHolder.DEFAULT_FACTORY;
}
private final SSLContext sslcontext;
private final javax.net.ssl.SSLSocketFactory socketfactory;
private final HostNameResolver nameResolver;
private X509HostnameVerifier hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
public SSLSocketFactory(
String algorithm,
final KeyStore keystore,
final String keystorePassword,
final KeyStore truststore,
final SecureRandom random,
final HostNameResolver nameResolver)
throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
{
super();
if (algorithm == null) {
algorithm = TLS;
}
KeyManager[] keymanagers = null;
if (keystore != null) {
keymanagers = createKeyManagers(keystore, keystorePassword);
}
TrustManager[] trustmanagers = null;
if (truststore != null) {
trustmanagers = createTrustManagers(truststore);
}
this.sslcontext = SSLContext.getInstance(algorithm);
this.sslcontext.init(keymanagers, trustmanagers, random);
this.socketfactory = this.sslcontext.getSocketFactory();
this.nameResolver = nameResolver;
}
public SSLSocketFactory(
final KeyStore keystore,
final String keystorePassword,
final KeyStore truststore)
throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
{
this(TLS, keystore, keystorePassword, truststore, null, null);
}
public SSLSocketFactory(final KeyStore keystore, final String keystorePassword)
throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
{
this(TLS, keystore, keystorePassword, null, null, null);
}
public SSLSocketFactory(final KeyStore truststore)
throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
{
this(TLS, null, null, truststore, null, null);
}
/**
* Constructs an HttpClient SSLSocketFactory backed by the given JSSE
* SSLSocketFactory.
*
* @hide
*/
public SSLSocketFactory(javax.net.ssl.SSLSocketFactory socketfactory) {
super();
this.sslcontext = null;
this.socketfactory = socketfactory;
this.nameResolver = null;
}
/**
* Creates the default SSL socket factory.
* This constructor is used exclusively to instantiate the factory for
* {@link #getSocketFactory getSocketFactory}.
*/
private SSLSocketFactory() {
super();
this.sslcontext = null;
this.socketfactory = HttpsURLConnection.getDefaultSSLSocketFactory();
this.nameResolver = null;
}
private static KeyManager[] createKeyManagers(final KeyStore keystore, final String password)
throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
if (keystore == null) {
throw new IllegalArgumentException("Keystore may not be null");
}
KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
kmfactory.init(keystore, password != null ? password.toCharArray(): null);
return kmfactory.getKeyManagers();
}
private static TrustManager[] createTrustManagers(final KeyStore keystore)
throws KeyStoreException, NoSuchAlgorithmException {
if (keystore == null) {
throw new IllegalArgumentException("Keystore may not be null");
}
TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
tmfactory.init(keystore);
return tmfactory.getTrustManagers();
}
// non-javadoc, see interface org.apache.http.conn.SocketFactory
public Socket createSocket()
throws IOException {
// the cast makes sure that the factory is working as expected
return (SSLSocket) this.socketfactory.createSocket();
}
// non-javadoc, see interface org.apache.http.conn.SocketFactory
public Socket connectSocket(
final Socket sock,
final String host,
final int port,
final InetAddress localAddress,
int localPort,
final HttpParams params
) throws IOException {
if (host == null) {
throw new IllegalArgumentException("Target host may not be null.");
}
if (params == null) {
throw new IllegalArgumentException("Parameters may not be null.");
}
SSLSocket sslsock = (SSLSocket)
((sock != null) ? sock : createSocket());
if ((localAddress != null) || (localPort > 0)) {
// we need to bind explicitly
if (localPort < 0)
localPort = 0; // indicates "any"
InetSocketAddress isa =
new InetSocketAddress(localAddress, localPort);
sslsock.bind(isa);
}
int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
int soTimeout = HttpConnectionParams.getSoTimeout(params);
InetSocketAddress remoteAddress;
if (this.nameResolver != null) {
remoteAddress = new InetSocketAddress(this.nameResolver.resolve(host), port);
} else {
remoteAddress = new InetSocketAddress(host, port);
}
sslsock.connect(remoteAddress, connTimeout);
sslsock.setSoTimeout(soTimeout);
try {
hostnameVerifier.verify(host, sslsock);
// verifyHostName() didn't blowup - good!
} catch (IOException iox) {
// close the socket before re-throwing the exception
try { sslsock.close(); } catch (Exception x) { /*ignore*/ }
throw iox;
}
return sslsock;
}
/**
* Checks whether a socket connection is secure.
* This factory creates TLS/SSL socket connections
* which, by default, are considered secure.
* <br/>
* Derived classes may override this method to perform
* runtime checks, for example based on the cypher suite.
*
* @param sock the connected socket
*
* @return <code>true</code>
*
* @throws IllegalArgumentException if the argument is invalid
*/
public boolean isSecure(Socket sock)
throws IllegalArgumentException {
if (sock == null) {
throw new IllegalArgumentException("Socket may not be null.");
}
// This instanceof check is in line with createSocket() above.
if (!(sock instanceof SSLSocket)) {
throw new IllegalArgumentException
("Socket not created by this factory.");
}
// This check is performed last since it calls the argument object.
if (sock.isClosed()) {
throw new IllegalArgumentException("Socket is closed.");
}
return true;
} // isSecure
// non-javadoc, see interface LayeredSocketFactory
public Socket createSocket(
final Socket socket,
final String host,
final int port,
final boolean autoClose
) throws IOException, UnknownHostException {
SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket(
socket,
host,
port,
autoClose
);
hostnameVerifier.verify(host, sslSocket);
// verifyHostName() didn't blowup - good!
return sslSocket;
}
public void setHostnameVerifier(X509HostnameVerifier hostnameVerifier) {
if ( hostnameVerifier == null ) {
throw new IllegalArgumentException("Hostname verifier may not be null");
}
this.hostnameVerifier = hostnameVerifier;
}
public X509HostnameVerifier getHostnameVerifier() {
return hostnameVerifier;
}
}

View File

@@ -0,0 +1,74 @@
/*
* $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/StrictHostnameVerifier.java $
* $Revision: 617642 $
* $Date: 2008-02-01 12:54:07 -0800 (Fri, 01 Feb 2008) $
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.conn.ssl;
import javax.net.ssl.SSLException;
/**
* The Strict HostnameVerifier works the same way as Sun Java 1.4, Sun
* Java 5, Sun Java 6-rc. It's also pretty close to IE6. This
* implementation appears to be compliant with RFC 2818 for dealing with
* wildcards.
* <p/>
* The hostname must match either the first CN, or any of the subject-alts.
* A wildcard can occur in the CN, and in any of the subject-alts. The
* one divergence from IE6 is how we only check the first CN. IE6 allows
* a match against any of the CNs present. We decided to follow in
* Sun Java 1.4's footsteps and only check the first CN. (If you need
* to check all the CN's, feel free to write your own implementation!).
* <p/>
* A wildcard such as "*.foo.com" matches only subdomains in the same
* level, for example "a.foo.com". It does not match deeper subdomains
* such as "a.b.foo.com".
*
* @author Julius Davies
*
* @deprecated Please use {@link java.net.URL#openConnection} instead.
* Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
* for further details.
*/
@Deprecated
public class StrictHostnameVerifier extends AbstractVerifier {
public final void verify(
final String host,
final String[] cns,
final String[] subjectAlts) throws SSLException {
verify(host, cns, subjectAlts, true);
}
@Override
public final String toString() {
return "STRICT";
}
}

View File

@@ -0,0 +1,91 @@
/*
* $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/X509HostnameVerifier.java $
* $Revision: 618365 $
* $Date: 2008-02-04 10:20:08 -0800 (Mon, 04 Feb 2008) $
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.conn.ssl;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.security.cert.X509Certificate;
/**
* Interface for checking if a hostname matches the names stored inside the
* server's X.509 certificate. Implements javax.net.ssl.HostnameVerifier, but
* we don't actually use that interface. Instead we added some methods that
* take String parameters (instead of javax.net.ssl.HostnameVerifier's
* SSLSession). JUnit is a lot easier this way! :-)
* <p/>
* We provide the HostnameVerifier.DEFAULT, HostnameVerifier.STRICT, and
* HostnameVerifier.ALLOW_ALL implementations. But feel free to define
* your own implementation!
* <p/>
* Inspired by Sebastian Hauer's original StrictSSLProtocolSocketFactory in the
* HttpClient "contrib" repository.
*
* @author Julius Davies
* @author <a href="mailto:hauer@psicode.com">Sebastian Hauer</a>
*
* @since 4.0 (8-Dec-2006)
*
* @deprecated Please use {@link java.net.URL#openConnection} instead.
* Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
* for further details.
*/
@Deprecated
public interface X509HostnameVerifier extends HostnameVerifier {
boolean verify(String host, SSLSession session);
void verify(String host, SSLSocket ssl) throws IOException;
void verify(String host, X509Certificate cert) throws SSLException;
/**
* Checks to see if the supplied hostname matches any of the supplied CNs
* or "DNS" Subject-Alts. Most implementations only look at the first CN,
* and ignore any additional CNs. Most implementations do look at all of
* the "DNS" Subject-Alts. The CNs or Subject-Alts may contain wildcards
* according to RFC 2818.
*
* @param cns CN fields, in order, as extracted from the X.509
* certificate.
* @param subjectAlts Subject-Alt fields of type 2 ("DNS"), as extracted
* from the X.509 certificate.
* @param host The hostname to verify.
* @throws SSLException If verification failed.
*/
void verify(String host, String[] cns, String[] subjectAlts)
throws SSLException;
}

View File

@@ -0,0 +1,40 @@
<html>
<head>
<!--
/*
* $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/package.html $
* $Revision: 555193 $
* $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
-->
</head>
<body>
TLS/SSL specific parts of the <i>HttpConn</i> API.
</body>
</html>

View File

@@ -0,0 +1,136 @@
/*
* $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/CoreConnectionPNames.java $
* $Revision: 576077 $
* $Date: 2007-09-16 04:50:22 -0700 (Sun, 16 Sep 2007) $
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.params;
/**
* Defines parameter names for connections in HttpCore.
*
* @version $Revision: 576077 $
*
* @since 4.0
*
* @deprecated Please use {@link java.net.URL#openConnection} instead.
* Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
* for further details.
*/
@Deprecated
public interface CoreConnectionPNames {
/**
* Defines the default socket timeout (<tt>SO_TIMEOUT</tt>) in milliseconds which is the
* timeout for waiting for data. A timeout value of zero is interpreted as an infinite
* timeout. This value is used when no socket timeout is set in the
* method parameters.
* <p>
* This parameter expects a value of type {@link Integer}.
* </p>
* @see java.net.SocketOptions#SO_TIMEOUT
*/
public static final String SO_TIMEOUT = "http.socket.timeout";
/**
* Determines whether Nagle's algorithm is to be used. The Nagle's algorithm
* tries to conserve bandwidth by minimizing the number of segments that are
* sent. When applications wish to decrease network latency and increase
* performance, they can disable Nagle's algorithm (that is enable TCP_NODELAY).
* Data will be sent earlier, at the cost of an increase in bandwidth consumption.
* <p>
* This parameter expects a value of type {@link Boolean}.
* </p>
* @see java.net.SocketOptions#TCP_NODELAY
*/
public static final String TCP_NODELAY = "http.tcp.nodelay";
/**
* Determines the size of the internal socket buffer used to buffer data
* while receiving / transmitting HTTP messages.
* <p>
* This parameter expects a value of type {@link Integer}.
* </p>
*/
public static final String SOCKET_BUFFER_SIZE = "http.socket.buffer-size";
/**
* Sets SO_LINGER with the specified linger time in seconds. The maximum timeout
* value is platform specific. Value <tt>0</tt> implies that the option is disabled.
* Value <tt>-1</tt> implies that the JRE default is used. The setting only affects
* socket close.
* <p>
* This parameter expects a value of type {@link Integer}.
* </p>
* @see java.net.SocketOptions#SO_LINGER
*/
public static final String SO_LINGER = "http.socket.linger";
/**
* Determines the timeout until a connection is etablished. A value of zero
* means the timeout is not used. The default value is zero.
* <p>
* This parameter expects a value of type {@link Integer}.
* </p>
*/
public static final String CONNECTION_TIMEOUT = "http.connection.timeout";
/**
* Determines whether stale connection check is to be used. Disabling
* stale connection check may result in slight performance improvement
* at the risk of getting an I/O error when executing a request over a
* connection that has been closed at the server side.
* <p>
* This parameter expects a value of type {@link Boolean}.
* </p>
*/
public static final String STALE_CONNECTION_CHECK = "http.connection.stalecheck";
/**
* Determines the maximum line length limit. If set to a positive value, any HTTP
* line exceeding this limit will cause an IOException. A negative or zero value
* will effectively disable the check.
* <p>
* This parameter expects a value of type {@link Integer}.
* </p>
*/
public static final String MAX_LINE_LENGTH = "http.connection.max-line-length";
/**
* Determines the maximum HTTP header count allowed. If set to a positive value,
* the number of HTTP headers received from the data stream exceeding this limit
* will cause an IOException. A negative or zero value will effectively disable
* the check.
* <p>
* This parameter expects a value of type {@link Integer}.
* </p>
*/
public static final String MAX_HEADER_COUNT = "http.connection.max-header-count";
}

View File

@@ -0,0 +1,229 @@
/*
* $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpConnectionParams.java $
* $Revision: 576089 $
* $Date: 2007-09-16 05:39:56 -0700 (Sun, 16 Sep 2007) $
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.params;
/**
* An adaptor for accessing connection parameters in {@link HttpParams}.
* <br/>
* Note that the <i>implements</i> relation to {@link CoreConnectionPNames}
* is for compatibility with existing application code only. References to
* the parameter names should use the interface, not this class.
*
* @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
*
* @version $Revision: 576089 $
*
* @since 4.0
*
* @deprecated Please use {@link java.net.URL#openConnection} instead.
* 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 HttpConnectionParams implements CoreConnectionPNames {
/**
*/
private HttpConnectionParams() {
super();
}
/**
* Returns the default socket timeout (<tt>SO_TIMEOUT</tt>) in milliseconds which is the
* timeout for waiting for data. A timeout value of zero is interpreted as an infinite
* timeout. This value is used when no socket timeout is set in the
* method parameters.
*
* @return timeout in milliseconds
*/
public static int getSoTimeout(final HttpParams params) {
if (params == null) {
throw new IllegalArgumentException("HTTP parameters may not be null");
}
return params.getIntParameter(CoreConnectionPNames.SO_TIMEOUT, 0);
}
/**
* Sets the default socket timeout (<tt>SO_TIMEOUT</tt>) in milliseconds which is the
* timeout for waiting for data. A timeout value of zero is interpreted as an infinite
* timeout. This value is used when no socket timeout is set in the
* method parameters.
*
* @param timeout Timeout in milliseconds
*/
public static void setSoTimeout(final HttpParams params, int timeout) {
if (params == null) {
throw new IllegalArgumentException("HTTP parameters may not be null");
}
params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, timeout);
}
/**
* Tests if Nagle's algorithm is to be used.
*
* @return <tt>true</tt> if the Nagle's algorithm is to NOT be used
* (that is enable TCP_NODELAY), <tt>false</tt> otherwise.
*/
public static boolean getTcpNoDelay(final HttpParams params) {
if (params == null) {
throw new IllegalArgumentException("HTTP parameters may not be null");
}
return params.getBooleanParameter
(CoreConnectionPNames.TCP_NODELAY, true);
}
/**
* Determines whether Nagle's algorithm is to be used. The Nagle's algorithm
* tries to conserve bandwidth by minimizing the number of segments that are
* sent. When applications wish to decrease network latency and increase
* performance, they can disable Nagle's algorithm (that is enable TCP_NODELAY).
* Data will be sent earlier, at the cost of an increase in bandwidth consumption.
*
* @param value <tt>true</tt> if the Nagle's algorithm is to NOT be used
* (that is enable TCP_NODELAY), <tt>false</tt> otherwise.
*/
public static void setTcpNoDelay(final HttpParams params, boolean value) {
if (params == null) {
throw new IllegalArgumentException("HTTP parameters may not be null");
}
params.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, value);
}
public static int getSocketBufferSize(final HttpParams params) {
if (params == null) {
throw new IllegalArgumentException("HTTP parameters may not be null");
}
return params.getIntParameter
(CoreConnectionPNames.SOCKET_BUFFER_SIZE, -1);
}
public static void setSocketBufferSize(final HttpParams params, int size) {
if (params == null) {
throw new IllegalArgumentException("HTTP parameters may not be null");
}
params.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, size);
}
/**
* Returns linger-on-close timeout. Value <tt>0</tt> implies that the option is
* disabled. Value <tt>-1</tt> implies that the JRE default is used.
*
* @return the linger-on-close timeout
*/
public static int getLinger(final HttpParams params) {
if (params == null) {
throw new IllegalArgumentException("HTTP parameters may not be null");
}
return params.getIntParameter(CoreConnectionPNames.SO_LINGER, -1);
}
/**
* Returns linger-on-close timeout. This option disables/enables immediate return
* from a close() of a TCP Socket. Enabling this option with a non-zero Integer
* timeout means that a close() will block pending the transmission and
* acknowledgement of all data written to the peer, at which point the socket is
* closed gracefully. Value <tt>0</tt> implies that the option is
* disabled. Value <tt>-1</tt> implies that the JRE default is used.
*
* @param value the linger-on-close timeout
*/
public static void setLinger(final HttpParams params, int value) {
if (params == null) {
throw new IllegalArgumentException("HTTP parameters may not be null");
}
params.setIntParameter(CoreConnectionPNames.SO_LINGER, value);
}
/**
* Returns the timeout until a connection is etablished. A value of zero
* means the timeout is not used. The default value is zero.
*
* @return timeout in milliseconds.
*/
public static int getConnectionTimeout(final HttpParams params) {
if (params == null) {
throw new IllegalArgumentException("HTTP parameters may not be null");
}
return params.getIntParameter
(CoreConnectionPNames.CONNECTION_TIMEOUT, 0);
}
/**
* Sets the timeout until a connection is etablished. A value of zero
* means the timeout is not used. The default value is zero.
*
* @param timeout Timeout in milliseconds.
*/
public static void setConnectionTimeout(final HttpParams params, int timeout) {
if (params == null) {
throw new IllegalArgumentException("HTTP parameters may not be null");
}
params.setIntParameter
(CoreConnectionPNames.CONNECTION_TIMEOUT, timeout);
}
/**
* Tests whether stale connection check is to be used. Disabling
* stale connection check may result in slight performance improvement
* at the risk of getting an I/O error when executing a request over a
* connection that has been closed at the server side.
*
* @return <tt>true</tt> if stale connection check is to be used,
* <tt>false</tt> otherwise.
*/
public static boolean isStaleCheckingEnabled(final HttpParams params) {
if (params == null) {
throw new IllegalArgumentException("HTTP parameters may not be null");
}
return params.getBooleanParameter
(CoreConnectionPNames.STALE_CONNECTION_CHECK, true);
}
/**
* Defines whether stale connection check is to be used. Disabling
* stale connection check may result in slight performance improvement
* at the risk of getting an I/O error when executing a request over a
* connection that has been closed at the server side.
*
* @param value <tt>true</tt> if stale connection check is to be used,
* <tt>false</tt> otherwise.
*/
public static void setStaleCheckingEnabled(final HttpParams params, boolean value) {
if (params == null) {
throw new IllegalArgumentException("HTTP parameters may not be null");
}
params.setBooleanParameter
(CoreConnectionPNames.STALE_CONNECTION_CHECK, value);
}
}

View File

@@ -0,0 +1,192 @@
/*
* $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpParams.java $
* $Revision: 610763 $
* $Date: 2008-01-10 04:01:13 -0800 (Thu, 10 Jan 2008) $
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.params;
/**
* Represents a collection of HTTP protocol and framework parameters.
*
* @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
*
* @version $Revision: 610763 $
*
* @since 4.0
*
* @deprecated Please use {@link java.net.URL#openConnection} instead.
* Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
* for further details.
*/
@Deprecated
public interface HttpParams {
/**
* Obtains the value of the given parameter.
*
* @param name the parent name.
*
* @return an object that represents the value of the parameter,
* <code>null</code> if the parameter is not set or if it
* is explicitly set to <code>null</code>
*
* @see #setParameter(String, Object)
*/
Object getParameter(String name);
/**
* Assigns the value to the parameter with the given name.
*
* @param name parameter name
* @param value parameter value
*/
HttpParams setParameter(String name, Object value);
/**
* Creates a copy of these parameters.
*
* @return a new set of parameters holding the same values as this one
*/
HttpParams copy();
/**
* Removes the parameter with the specified name.
*
* @param name parameter name
*
* @return true if the parameter existed and has been removed, false else.
*/
boolean removeParameter(String name);
/**
* Returns a {@link Long} parameter value with the given name.
* If the parameter is not explicitly set, the default value is returned.
*
* @param name the parent name.
* @param defaultValue the default value.
*
* @return a {@link Long} that represents the value of the parameter.
*
* @see #setLongParameter(String, long)
*/
long getLongParameter(String name, long defaultValue);
/**
* Assigns a {@link Long} to the parameter with the given name
*
* @param name parameter name
* @param value parameter value
*/
HttpParams setLongParameter(String name, long value);
/**
* Returns an {@link Integer} parameter value with the given name.
* If the parameter is not explicitly set, the default value is returned.
*
* @param name the parent name.
* @param defaultValue the default value.
*
* @return a {@link Integer} that represents the value of the parameter.
*
* @see #setIntParameter(String, int)
*/
int getIntParameter(String name, int defaultValue);
/**
* Assigns an {@link Integer} to the parameter with the given name
*
* @param name parameter name
* @param value parameter value
*/
HttpParams setIntParameter(String name, int value);
/**
* Returns a {@link Double} parameter value with the given name.
* If the parameter is not explicitly set, the default value is returned.
*
* @param name the parent name.
* @param defaultValue the default value.
*
* @return a {@link Double} that represents the value of the parameter.
*
* @see #setDoubleParameter(String, double)
*/
double getDoubleParameter(String name, double defaultValue);
/**
* Assigns a {@link Double} to the parameter with the given name
*
* @param name parameter name
* @param value parameter value
*/
HttpParams setDoubleParameter(String name, double value);
/**
* Returns a {@link Boolean} parameter value with the given name.
* If the parameter is not explicitly set, the default value is returned.
*
* @param name the parent name.
* @param defaultValue the default value.
*
* @return a {@link Boolean} that represents the value of the parameter.
*
* @see #setBooleanParameter(String, boolean)
*/
boolean getBooleanParameter(String name, boolean defaultValue);
/**
* Assigns a {@link Boolean} to the parameter with the given name
*
* @param name parameter name
* @param value parameter value
*/
HttpParams setBooleanParameter(String name, boolean value);
/**
* Checks if a boolean parameter is set to <code>true</code>.
*
* @param name parameter name
*
* @return <tt>true</tt> if the parameter is set to value <tt>true</tt>,
* <tt>false</tt> if it is not set or set to <code>false</code>
*/
boolean isParameterTrue(String name);
/**
* Checks if a boolean parameter is not set or <code>false</code>.
*
* @param name parameter name
*
* @return <tt>true</tt> if the parameter is either not set or
* set to value <tt>false</tt>,
* <tt>false</tt> if it is set to <code>true</code>
*/
boolean isParameterFalse(String name);
}

View File

@@ -22,9 +22,9 @@ LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := \
$(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_JAVA_LIBRARIES := android.test.runner org.apache.http.legacy
LOCAL_PACKAGE_NAME := BandwidthTests
include $(BUILD_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
include $(call all-makefiles-under,$(LOCAL_PATH))

View File

@@ -19,6 +19,7 @@
<application >
<uses-library android:name="android.test.runner" />
<uses-library android:name="org.apache.http.legacy" android:required="false" />
</application>
<instrumentation

View File

@@ -24,7 +24,7 @@ LOCAL_SRC_FILES := \
LOCAL_DX_FLAGS := --core-library
LOCAL_AAPT_FLAGS = -0 dat -0 gld
LOCAL_STATIC_JAVA_LIBRARIES := core-tests-support android-common frameworks-core-util-lib mockwebserver guava littlemock mockito-target
LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt telephony-common
LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt telephony-common org.apache.http.legacy
LOCAL_PACKAGE_NAME := FrameworksCoreTests
LOCAL_CERTIFICATE := platform

View File

@@ -112,6 +112,7 @@
<application android:theme="@style/Theme">
<uses-library android:name="android.test.runner" />
<uses-library android:name="org.apache.http.legacy" android:required="false" />
<activity android:name="android.view.ViewAttachTestActivity" android:label="View Attach Test">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@@ -153,7 +153,6 @@
<library name="org.apache.http.legacy"
file="/system/framework/org.apache.http.legacy.jar" />
<!-- These are the standard packages that are white-listed to always have internet
access while in power save mode, even if they aren't in the foreground. -->
<allow-in-power-save package="com.android.providers.downloads" />

View File

@@ -6,7 +6,7 @@ LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := \
$(call all-subdir-java-files)
LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt
LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt org.apache.http.legacy
LOCAL_PACKAGE_NAME := CoreTests

View File

@@ -35,6 +35,7 @@
<application>
<uses-library android:name="android.test.runner" />
<uses-library android:name="org.apache.http.legacy" android:required="false" />
</application>
<instrumentation

View File

@@ -12,6 +12,8 @@ LOCAL_CERTIFICATE := platform
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-media-protocols
LOCAL_JAVA_LIBRARIES += org.apache.http.legacy
LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_PACKAGE)

View File

@@ -13,6 +13,8 @@
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<uses-library android:name="org.apache.http.legacy" android:required="false" />
<activity
android:name="com.android.onemedia.OnePlayerActivity"
android:label="@string/app_name" >