Move apache specific portions of android.net.http to external/apache-http.
We continue to compile external/apache-http into ext.jar. This contains
a few changes apart fom the classes moving around :
- Makefile changes to build docs and api-stubs for now. A future change
will revert these changes and remove these classes from stubs and
docs.
- Hardcode event IDs in legacyerrorstrings to avoid a dependency between
the frameworks and apache. These strings are on their way out and will
never change anyway.
- Remove imports due to {@link} tags and use {@code} instead.
- Remove an accidental(?) dependency on apache commons code that's a
part of apache-http.
bug: 18027885
Change-Id: I51cd038d846ec7d02c283a4541b10a6a9cf62ecf
This commit is contained in:
11
Android.mk
11
Android.mk
@@ -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))
|
||||
|
||||
3345
api/current.txt
3345
api/current.txt
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
@@ -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}.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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[] {});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
69
core/java/org/apache/http/conn/ConnectTimeoutException.java
Normal file
69
core/java/org/apache/http/conn/ConnectTimeoutException.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
47
core/java/org/apache/http/conn/scheme/HostNameResolver.java
Normal file
47
core/java/org/apache/http/conn/scheme/HostNameResolver.java
Normal 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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
143
core/java/org/apache/http/conn/scheme/SocketFactory.java
Normal file
143
core/java/org/apache/http/conn/scheme/SocketFactory.java
Normal 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
|
||||
;
|
||||
|
||||
}
|
||||
288
core/java/org/apache/http/conn/ssl/AbstractVerifier.java
Normal file
288
core/java/org/apache/http/conn/ssl/AbstractVerifier.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
408
core/java/org/apache/http/conn/ssl/SSLSocketFactory.java
Normal file
408
core/java/org/apache/http/conn/ssl/SSLSocketFactory.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
91
core/java/org/apache/http/conn/ssl/X509HostnameVerifier.java
Normal file
91
core/java/org/apache/http/conn/ssl/X509HostnameVerifier.java
Normal 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;
|
||||
|
||||
|
||||
}
|
||||
40
core/java/org/apache/http/conn/ssl/package.html
Normal file
40
core/java/org/apache/http/conn/ssl/package.html
Normal 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>
|
||||
136
core/java/org/apache/http/params/CoreConnectionPNames.java
Normal file
136
core/java/org/apache/http/params/CoreConnectionPNames.java
Normal 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";
|
||||
|
||||
}
|
||||
229
core/java/org/apache/http/params/HttpConnectionParams.java
Normal file
229
core/java/org/apache/http/params/HttpConnectionParams.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
192
core/java/org/apache/http/params/HttpParams.java
Normal file
192
core/java/org/apache/http/params/HttpParams.java
Normal 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);
|
||||
|
||||
}
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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" >
|
||||
|
||||
Reference in New Issue
Block a user