/* * 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 android.util.Pair; import com.android.volley.Cache; import com.android.volley.NetworkResponse; import org.apache.http.impl.cookie.DateParseException; import org.apache.http.impl.cookie.DateUtils; import org.apache.http.protocol.HTTP; import java.util.List; import java.util.Map; /** * Utility methods for parsing HTTP headers. */ public class HttpHeaderParser { /** * Extracts a {@link 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. */ public static Cache.Entry parseCacheHeaders(NetworkResponse response) { long now = System.currentTimeMillis(); List<Pair<String, String>> headers = response.headers; long serverDate = 0; long serverExpires = 0; long softExpire = 0; long maxAge = 0; boolean hasCacheControl = false; String serverEtag = null; String headerValue; // headerValue = headers.get("Date"); headerValue = null; for(Pair<String, String> p : headers){ if(p.first.equals("Date")){ headerValue = p.second; break; } } if (headerValue != null) { serverDate = parseDateAsEpoch(headerValue); } // headerValue = headers.get("Cache-Control"); headerValue = null; for(Pair<String, String> p : headers){ if(p.first.equals("Cache-Control")){ headerValue = p.second; break; } } if (headerValue != null) { hasCacheControl = true; String[] tokens = headerValue.split(","); 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.equals("must-revalidate") || token.equals("proxy-revalidate")) { maxAge = 0; } } } // headerValue = headers.get("Expires"); headerValue = null; for(Pair<String, String> p : headers){ if(p.first.equals("Expires")){ headerValue = p.second; break; } } if (headerValue != null) { serverExpires = parseDateAsEpoch(headerValue); } // serverEtag = headers.get("ETag"); serverEtag = null; for(Pair<String, String> p : headers){ if(p.first.equals("ETag")){ serverEtag = p.second; break; } } // Cache-Control takes precedence over an Expires header, even if both exist and Expires // is more restrictive. if (hasCacheControl) { softExpire = now + maxAge * 1000; } else if (serverDate > 0 && serverExpires >= serverDate) { // Default semantic for Expire header in HTTP specification is softExpire. softExpire = now + (serverExpires - serverDate); } Cache.Entry entry = new Cache.Entry(); entry.data = response.data; entry.etag = serverEtag; entry.softTtl = softExpire; entry.ttl = entry.softTtl; entry.serverDate = serverDate; entry.responseHeaders = headers; 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 DateUtils.parseDate(dateStr).getTime(); } catch (DateParseException e) { // Date in invalid format, fallback to 0 return 0; } } /** * 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(Map<String, String> headers) { String contentType = headers.get(HTTP.CONTENT_TYPE); if (contentType != null) { String[] params = contentType.split(";"); for (int i = 1; i < params.length; i++) { String[] pair = params[i].trim().split("="); if (pair.length == 2) { if (pair[0].equals("charset")) { return pair[1]; } } } } return HTTP.DEFAULT_CONTENT_CHARSET; } public static String parseCharset(List<Pair<String, String>> headers) { // String contentType = headers.get(HTTP.CONTENT_TYPE); String contentType = null; for(Pair<String, String> pair : headers){ if(pair.first.equals("HTTP.CONTENT_TYPE")){ contentType = pair.second; break; } } if (contentType != null) { String[] params = contentType.split(";"); for (int i = 1; i < params.length; i++) { String[] pair = params[i].trim().split("="); if (pair.length == 2) { if (pair[0].equals("charset")) { return pair[1]; } } } } return HTTP.DEFAULT_CONTENT_CHARSET; } }