diff options
Diffstat (limited to 'src/main/java/com/android/volley/toolbox/HurlStack.java')
-rw-r--r-- | src/main/java/com/android/volley/toolbox/HurlStack.java | 321 |
1 files changed, 0 insertions, 321 deletions
diff --git a/src/main/java/com/android/volley/toolbox/HurlStack.java b/src/main/java/com/android/volley/toolbox/HurlStack.java deleted file mode 100644 index 35c6a72..0000000 --- a/src/main/java/com/android/volley/toolbox/HurlStack.java +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.volley.toolbox; - -import androidx.annotation.VisibleForTesting; -import com.android.volley.AuthFailureError; -import com.android.volley.Header; -import com.android.volley.Request; -import com.android.volley.Request.Method; -import java.io.DataOutputStream; -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLSocketFactory; - -/** A {@link BaseHttpStack} based on {@link HttpURLConnection}. */ -public class HurlStack extends BaseHttpStack { - - private static final int HTTP_CONTINUE = 100; - - /** An interface for transforming URLs before use. */ - public interface UrlRewriter extends com.android.volley.toolbox.UrlRewriter {} - - private final UrlRewriter mUrlRewriter; - private final SSLSocketFactory mSslSocketFactory; - - public HurlStack() { - this(/* urlRewriter = */ null); - } - - /** @param urlRewriter Rewriter to use for request URLs */ - public HurlStack(UrlRewriter urlRewriter) { - this(urlRewriter, /* sslSocketFactory = */ null); - } - - /** - * @param urlRewriter Rewriter to use for request URLs - * @param sslSocketFactory SSL factory to use for HTTPS connections - */ - public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) { - mUrlRewriter = urlRewriter; - mSslSocketFactory = sslSocketFactory; - } - - @Override - public HttpResponse executeRequest(Request<?> request, Map<String, String> additionalHeaders) - throws IOException, AuthFailureError { - String url = request.getUrl(); - HashMap<String, String> map = new HashMap<>(); - map.putAll(additionalHeaders); - // Request.getHeaders() takes precedence over the given additional (cache) headers). - map.putAll(request.getHeaders()); - if (mUrlRewriter != null) { - String rewritten = mUrlRewriter.rewriteUrl(url); - if (rewritten == null) { - throw new IOException("URL blocked by rewriter: " + url); - } - url = rewritten; - } - URL parsedUrl = new URL(url); - HttpURLConnection connection = openConnection(parsedUrl, request); - boolean keepConnectionOpen = false; - try { - for (String headerName : map.keySet()) { - connection.setRequestProperty(headerName, map.get(headerName)); - } - setConnectionParametersForRequest(connection, request); - // Initialize HttpResponse with data from the HttpURLConnection. - int responseCode = connection.getResponseCode(); - if (responseCode == -1) { - // -1 is returned by getResponseCode() if the response code could not be retrieved. - // Signal to the caller that something was wrong with the connection. - throw new IOException("Could not retrieve response code from HttpUrlConnection."); - } - - if (!hasResponseBody(request.getMethod(), responseCode)) { - return new HttpResponse(responseCode, convertHeaders(connection.getHeaderFields())); - } - - // Need to keep the connection open until the stream is consumed by the caller. Wrap the - // stream such that close() will disconnect the connection. - keepConnectionOpen = true; - return new HttpResponse( - responseCode, - convertHeaders(connection.getHeaderFields()), - connection.getContentLength(), - createInputStream(request, connection)); - } finally { - if (!keepConnectionOpen) { - connection.disconnect(); - } - } - } - - @VisibleForTesting - static List<Header> convertHeaders(Map<String, List<String>> responseHeaders) { - List<Header> headerList = new ArrayList<>(responseHeaders.size()); - for (Map.Entry<String, List<String>> entry : responseHeaders.entrySet()) { - // HttpUrlConnection includes the status line as a header with a null key; omit it here - // since it's not really a header and the rest of Volley assumes non-null keys. - if (entry.getKey() != null) { - for (String value : entry.getValue()) { - headerList.add(new Header(entry.getKey(), value)); - } - } - } - return headerList; - } - - /** - * Checks if a response message contains a body. - * - * @see <a href="https://tools.ietf.org/html/rfc7230#section-3.3">RFC 7230 section 3.3</a> - * @param requestMethod request method - * @param responseCode response status code - * @return whether the response has a body - */ - private static boolean hasResponseBody(int requestMethod, int responseCode) { - return requestMethod != Request.Method.HEAD - && !(HTTP_CONTINUE <= responseCode && responseCode < HttpURLConnection.HTTP_OK) - && responseCode != HttpURLConnection.HTTP_NO_CONTENT - && responseCode != HttpURLConnection.HTTP_NOT_MODIFIED; - } - - /** - * Wrapper for a {@link HttpURLConnection}'s InputStream which disconnects the connection on - * stream close. - */ - static class UrlConnectionInputStream extends FilterInputStream { - private final HttpURLConnection mConnection; - - UrlConnectionInputStream(HttpURLConnection connection) { - super(inputStreamFromConnection(connection)); - mConnection = connection; - } - - @Override - public void close() throws IOException { - super.close(); - mConnection.disconnect(); - } - } - - /** - * Create and return an InputStream from which the response will be read. - * - * <p>May be overridden by subclasses to manipulate or monitor this input stream. - * - * @param request current request. - * @param connection current connection of request. - * @return an InputStream from which the response will be read. - */ - protected InputStream createInputStream(Request<?> request, HttpURLConnection connection) { - return new UrlConnectionInputStream(connection); - } - - /** - * Initializes an {@link InputStream} from the given {@link HttpURLConnection}. - * - * @param connection - * @return an HttpEntity populated with data from <code>connection</code>. - */ - private static InputStream inputStreamFromConnection(HttpURLConnection connection) { - InputStream inputStream; - try { - inputStream = connection.getInputStream(); - } catch (IOException ioe) { - inputStream = connection.getErrorStream(); - } - return inputStream; - } - - /** Create an {@link HttpURLConnection} for the specified {@code url}. */ - protected HttpURLConnection createConnection(URL url) throws IOException { - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - - // Workaround for the M release HttpURLConnection not observing the - // HttpURLConnection.setFollowRedirects() property. - // https://code.google.com/p/android/issues/detail?id=194495 - connection.setInstanceFollowRedirects(HttpURLConnection.getFollowRedirects()); - - return connection; - } - - /** - * Opens an {@link HttpURLConnection} with parameters. - * - * @param url - * @return an open connection - * @throws IOException - */ - private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException { - HttpURLConnection connection = createConnection(url); - - int timeoutMs = request.getTimeoutMs(); - connection.setConnectTimeout(timeoutMs); - connection.setReadTimeout(timeoutMs); - connection.setUseCaches(false); - connection.setDoInput(true); - - // use caller-provided custom SslSocketFactory, if any, for HTTPS - if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) { - ((HttpsURLConnection) connection).setSSLSocketFactory(mSslSocketFactory); - } - - return connection; - } - - // NOTE: Any request headers added here (via setRequestProperty or addRequestProperty) should be - // checked against the existing properties in the connection and not overridden if already set. - @SuppressWarnings("deprecation") - /* package */ void setConnectionParametersForRequest( - HttpURLConnection connection, Request<?> request) throws IOException, AuthFailureError { - switch (request.getMethod()) { - case Method.DEPRECATED_GET_OR_POST: - // This is the deprecated way that needs to be handled for backwards compatibility. - // If the request's post body is null, then the assumption is that the request is - // GET. Otherwise, it is assumed that the request is a POST. - byte[] postBody = request.getPostBody(); - if (postBody != null) { - connection.setRequestMethod("POST"); - addBody(connection, request, postBody); - } - break; - case Method.GET: - // Not necessary to set the request method because connection defaults to GET but - // being explicit here. - connection.setRequestMethod("GET"); - break; - case Method.DELETE: - connection.setRequestMethod("DELETE"); - break; - case Method.POST: - connection.setRequestMethod("POST"); - addBodyIfExists(connection, request); - break; - case Method.PUT: - connection.setRequestMethod("PUT"); - addBodyIfExists(connection, request); - break; - case Method.HEAD: - connection.setRequestMethod("HEAD"); - break; - case Method.OPTIONS: - connection.setRequestMethod("OPTIONS"); - break; - case Method.TRACE: - connection.setRequestMethod("TRACE"); - break; - case Method.PATCH: - connection.setRequestMethod("PATCH"); - addBodyIfExists(connection, request); - break; - default: - throw new IllegalStateException("Unknown method type."); - } - } - - private void addBodyIfExists(HttpURLConnection connection, Request<?> request) - throws IOException, AuthFailureError { - byte[] body = request.getBody(); - if (body != null) { - addBody(connection, request, body); - } - } - - private void addBody(HttpURLConnection connection, Request<?> request, byte[] body) - throws IOException { - // Prepare output. There is no need to set Content-Length explicitly, - // since this is handled by HttpURLConnection using the size of the prepared - // output stream. - connection.setDoOutput(true); - // Set the content-type unless it was already set (by Request#getHeaders). - if (!connection.getRequestProperties().containsKey(HttpHeaderParser.HEADER_CONTENT_TYPE)) { - connection.setRequestProperty( - HttpHeaderParser.HEADER_CONTENT_TYPE, request.getBodyContentType()); - } - DataOutputStream out = - new DataOutputStream(createOutputStream(request, connection, body.length)); - out.write(body); - out.close(); - } - - /** - * Create and return an OutputStream to which the request body will be written. - * - * <p>May be overridden by subclasses to manipulate or monitor this output stream. - * - * @param request current request. - * @param connection current connection of request. - * @param length size of stream to write. - * @return an OutputStream to which the request body will be written. - * @throws IOException if an I/O error occurs while creating the stream. - */ - protected OutputStream createOutputStream( - Request<?> request, HttpURLConnection connection, int length) throws IOException { - return connection.getOutputStream(); - } -} |