diff options
Diffstat (limited to 'src/main/java/com/android/volley/toolbox/HttpHeaderParser.java')
-rw-r--r-- | src/main/java/com/android/volley/toolbox/HttpHeaderParser.java | 301 |
1 files changed, 0 insertions, 301 deletions
diff --git a/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java b/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java deleted file mode 100644 index 0b29e80..0000000 --- a/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java +++ /dev/null @@ -1,301 +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.Nullable; -import androidx.annotation.RestrictTo; -import androidx.annotation.RestrictTo.Scope; -import com.android.volley.Cache; -import com.android.volley.Header; -import com.android.volley.NetworkResponse; -import com.android.volley.VolleyLog; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; -import java.util.TreeMap; -import java.util.TreeSet; - -/** Utility methods for parsing HTTP headers. */ -public class HttpHeaderParser { - - @RestrictTo({Scope.LIBRARY_GROUP}) - public static final String HEADER_CONTENT_TYPE = "Content-Type"; - - private static final String DEFAULT_CONTENT_CHARSET = "ISO-8859-1"; - - private static final String RFC1123_PARSE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz"; - - // Hardcode 'GMT' rather than using 'zzz' since some platforms append an extraneous +00:00. - // See #287. - private static final String RFC1123_OUTPUT_FORMAT = "EEE, dd MMM yyyy HH:mm:ss 'GMT'"; - - /** - * Extracts a {@link com.android.volley.Cache.Entry} from a {@link NetworkResponse}. - * - * @param response The network response to parse headers from - * @return a cache entry for the given response, or null if the response is not cacheable. - */ - @Nullable - public static Cache.Entry parseCacheHeaders(NetworkResponse response) { - long now = System.currentTimeMillis(); - - Map<String, String> headers = response.headers; - if (headers == null) { - return null; - } - - long serverDate = 0; - long lastModified = 0; - long serverExpires = 0; - long softExpire = 0; - long finalExpire = 0; - long maxAge = 0; - long staleWhileRevalidate = 0; - boolean hasCacheControl = false; - boolean mustRevalidate = false; - - String serverEtag = null; - String headerValue; - - headerValue = headers.get("Date"); - if (headerValue != null) { - serverDate = parseDateAsEpoch(headerValue); - } - - headerValue = headers.get("Cache-Control"); - if (headerValue != null) { - hasCacheControl = true; - String[] tokens = headerValue.split(",", 0); - for (int i = 0; i < tokens.length; i++) { - String token = tokens[i].trim(); - if (token.equals("no-cache") || token.equals("no-store")) { - return null; - } else if (token.startsWith("max-age=")) { - try { - maxAge = Long.parseLong(token.substring(8)); - } catch (Exception e) { - } - } else if (token.startsWith("stale-while-revalidate=")) { - try { - staleWhileRevalidate = Long.parseLong(token.substring(23)); - } catch (Exception e) { - } - } else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) { - mustRevalidate = true; - } - } - } - - headerValue = headers.get("Expires"); - if (headerValue != null) { - serverExpires = parseDateAsEpoch(headerValue); - } - - headerValue = headers.get("Last-Modified"); - if (headerValue != null) { - lastModified = parseDateAsEpoch(headerValue); - } - - serverEtag = headers.get("ETag"); - - // Cache-Control takes precedence over an Expires header, even if both exist and Expires - // is more restrictive. - if (hasCacheControl) { - softExpire = now + maxAge * 1000; - finalExpire = mustRevalidate ? softExpire : softExpire + staleWhileRevalidate * 1000; - } else if (serverDate > 0 && serverExpires >= serverDate) { - // Default semantic for Expire header in HTTP specification is softExpire. - softExpire = now + (serverExpires - serverDate); - finalExpire = softExpire; - } - - Cache.Entry entry = new Cache.Entry(); - entry.data = response.data; - entry.etag = serverEtag; - entry.softTtl = softExpire; - entry.ttl = finalExpire; - entry.serverDate = serverDate; - entry.lastModified = lastModified; - entry.responseHeaders = headers; - entry.allResponseHeaders = response.allHeaders; - - return entry; - } - - /** Parse date in RFC1123 format, and return its value as epoch */ - public static long parseDateAsEpoch(String dateStr) { - try { - // Parse date in RFC1123 format if this header contains one - return newUsGmtFormatter(RFC1123_PARSE_FORMAT).parse(dateStr).getTime(); - } catch (ParseException e) { - // Date in invalid format, fallback to 0 - // If the value is either "0" or "-1" we only log to verbose, - // these values are pretty common and cause log spam. - String message = "Unable to parse dateStr: %s, falling back to 0"; - if ("0".equals(dateStr) || "-1".equals(dateStr)) { - VolleyLog.v(message, dateStr); - } else { - VolleyLog.e(e, message, dateStr); - } - - return 0; - } - } - - /** Format an epoch date in RFC1123 format. */ - static String formatEpochAsRfc1123(long epoch) { - return newUsGmtFormatter(RFC1123_OUTPUT_FORMAT).format(new Date(epoch)); - } - - private static SimpleDateFormat newUsGmtFormatter(String format) { - SimpleDateFormat formatter = new SimpleDateFormat(format, Locale.US); - formatter.setTimeZone(TimeZone.getTimeZone("GMT")); - return formatter; - } - - /** - * Retrieve a charset from headers - * - * @param headers An {@link java.util.Map} of headers - * @param defaultCharset Charset to return if none can be found - * @return Returns the charset specified in the Content-Type of this header, or the - * defaultCharset if none can be found. - */ - public static String parseCharset( - @Nullable Map<String, String> headers, String defaultCharset) { - if (headers == null) { - return defaultCharset; - } - String contentType = headers.get(HEADER_CONTENT_TYPE); - if (contentType != null) { - String[] params = contentType.split(";", 0); - for (int i = 1; i < params.length; i++) { - String[] pair = params[i].trim().split("=", 0); - if (pair.length == 2) { - if (pair[0].equals("charset")) { - return pair[1]; - } - } - } - } - - return defaultCharset; - } - - /** - * Returns the charset specified in the Content-Type of this header, or the HTTP default - * (ISO-8859-1) if none can be found. - */ - public static String parseCharset(@Nullable Map<String, String> headers) { - return parseCharset(headers, DEFAULT_CONTENT_CHARSET); - } - - // Note - these are copied from NetworkResponse to avoid making them public (as needed to access - // them from the .toolbox package), which would mean they'd become part of the Volley API. - // TODO: Consider obfuscating official releases so we can share utility methods between Volley - // and Toolbox without making them public APIs. - - static Map<String, String> toHeaderMap(List<Header> allHeaders) { - Map<String, String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - // Later elements in the list take precedence. - for (Header header : allHeaders) { - headers.put(header.getName(), header.getValue()); - } - return headers; - } - - static List<Header> toAllHeaderList(Map<String, String> headers) { - List<Header> allHeaders = new ArrayList<>(headers.size()); - for (Map.Entry<String, String> header : headers.entrySet()) { - allHeaders.add(new Header(header.getKey(), header.getValue())); - } - return allHeaders; - } - - /** - * Combine cache headers with network response headers for an HTTP 304 response. - * - * <p>An HTTP 304 response does not have all header fields. We have to use the header fields - * from the cache entry plus the new ones from the response. See also: - * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5 - * - * @param responseHeaders Headers from the network response. - * @param entry The cached response. - * @return The combined list of headers. - */ - static List<Header> combineHeaders(List<Header> responseHeaders, Cache.Entry entry) { - // First, create a case-insensitive set of header names from the network - // response. - Set<String> headerNamesFromNetworkResponse = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); - if (!responseHeaders.isEmpty()) { - for (Header header : responseHeaders) { - headerNamesFromNetworkResponse.add(header.getName()); - } - } - - // Second, add headers from the cache entry to the network response as long as - // they didn't appear in the network response, which should take precedence. - List<Header> combinedHeaders = new ArrayList<>(responseHeaders); - if (entry.allResponseHeaders != null) { - if (!entry.allResponseHeaders.isEmpty()) { - for (Header header : entry.allResponseHeaders) { - if (!headerNamesFromNetworkResponse.contains(header.getName())) { - combinedHeaders.add(header); - } - } - } - } else { - // Legacy caches only have entry.responseHeaders. - if (!entry.responseHeaders.isEmpty()) { - for (Map.Entry<String, String> header : entry.responseHeaders.entrySet()) { - if (!headerNamesFromNetworkResponse.contains(header.getKey())) { - combinedHeaders.add(new Header(header.getKey(), header.getValue())); - } - } - } - } - return combinedHeaders; - } - - static Map<String, String> getCacheHeaders(Cache.Entry entry) { - // If there's no cache entry, we're done. - if (entry == null) { - return Collections.emptyMap(); - } - - Map<String, String> headers = new HashMap<>(); - - if (entry.etag != null) { - headers.put("If-None-Match", entry.etag); - } - - if (entry.lastModified > 0) { - headers.put( - "If-Modified-Since", HttpHeaderParser.formatEpochAsRfc1123(entry.lastModified)); - } - - return headers; - } -} |