1d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru/*
2d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Copyright (C) 2011 The Android Open Source Project
3d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru *
4d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Licensed under the Apache License, Version 2.0 (the "License");
5d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * you may not use this file except in compliance with the License.
6d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * You may obtain a copy of the License at
7d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru *
8d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru *      http://www.apache.org/licenses/LICENSE-2.0
9d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru *
10d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Unless required by applicable law or agreed to in writing, software
11d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * distributed under the License is distributed on an "AS IS" BASIS,
12d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * See the License for the specific language governing permissions and
14d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * limitations under the License.
15d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */
16d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
17d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Querupackage com.android.volley.toolbox;
18d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
19d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport android.os.SystemClock;
20d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
21d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.AuthFailureError;
22d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.Cache;
2319a40379b22eeb21991fb1c55cf45788e62188b2Ralph Bergmannimport com.android.volley.Cache.Entry;
24d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.Network;
25d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.NetworkError;
26d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.NetworkResponse;
27d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.NoConnectionError;
28d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.Request;
29d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.RetryPolicy;
30d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.ServerError;
31d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.TimeoutError;
32d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.VolleyError;
33d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.VolleyLog;
34d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
35d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.Header;
36d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.HttpEntity;
37d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.HttpResponse;
38d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.HttpStatus;
39d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.StatusLine;
40d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.conn.ConnectTimeoutException;
41d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.impl.cookie.DateUtils;
42d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
43d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.io.IOException;
44d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.io.InputStream;
45d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.net.MalformedURLException;
46d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.net.SocketTimeoutException;
47ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmannimport java.util.Collections;
48d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.util.Date;
49d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.util.HashMap;
50d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.util.Map;
51ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmannimport java.util.TreeMap;
52d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
53d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru/**
54d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * A network performing Volley requests over an {@link HttpStack}.
55d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */
56d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Querupublic class BasicNetwork implements Network {
57d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    protected static final boolean DEBUG = VolleyLog.DEBUG;
58d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
59d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private static int SLOW_REQUEST_THRESHOLD_MS = 3000;
60d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
61d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private static int DEFAULT_POOL_SIZE = 4096;
62d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
63d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    protected final HttpStack mHttpStack;
64d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
65d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    protected final ByteArrayPool mPool;
66d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
67d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    /**
68d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param httpStack HTTP stack to be used
69d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     */
70d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    public BasicNetwork(HttpStack httpStack) {
71d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // If a pool isn't passed in, then build a small default pool that will give us a lot of
72d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // benefit and not use too much memory.
73d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
74d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
75d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
76d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    /**
77d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param httpStack HTTP stack to be used
78d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param pool a buffer pool that improves GC performance in copy operations
79d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     */
80d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {
81d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        mHttpStack = httpStack;
82d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        mPool = pool;
83d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
84d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
85d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    @Override
86d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    public NetworkResponse performRequest(Request<?> request) throws VolleyError {
87d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        long requestStart = SystemClock.elapsedRealtime();
88d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        while (true) {
89d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            HttpResponse httpResponse = null;
90d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            byte[] responseContents = null;
91ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann            Map<String, String> responseHeaders = Collections.emptyMap();
92d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            try {
93d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                // Gather headers.
94d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                Map<String, String> headers = new HashMap<String, String>();
95d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                addCacheHeaders(headers, request.getCacheEntry());
96d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                httpResponse = mHttpStack.performRequest(request, headers);
97d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                StatusLine statusLine = httpResponse.getStatusLine();
98cd8ce543d0a51dac9f6308cd8730816f690ead2fJon Boekenoogen                int statusCode = statusLine.getStatusCode();
99d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
100d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
101d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                // Handle cache validation.
102cd8ce543d0a51dac9f6308cd8730816f690ead2fJon Boekenoogen                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
10319a40379b22eeb21991fb1c55cf45788e62188b2Ralph Bergmann
10419a40379b22eeb21991fb1c55cf45788e62188b2Ralph Bergmann                    Entry entry = request.getCacheEntry();
10519a40379b22eeb21991fb1c55cf45788e62188b2Ralph Bergmann                    if (entry == null) {
10619a40379b22eeb21991fb1c55cf45788e62188b2Ralph Bergmann                        return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
107750c5b995f4f326c7b30ab88eda20b25285dd2f7Ivan Stalev                                responseHeaders, true,
108750c5b995f4f326c7b30ab88eda20b25285dd2f7Ivan Stalev                                SystemClock.elapsedRealtime() - requestStart);
10919a40379b22eeb21991fb1c55cf45788e62188b2Ralph Bergmann                    }
11019a40379b22eeb21991fb1c55cf45788e62188b2Ralph Bergmann
11119a40379b22eeb21991fb1c55cf45788e62188b2Ralph Bergmann                    // A HTTP 304 response does not have all header fields. We
11219a40379b22eeb21991fb1c55cf45788e62188b2Ralph Bergmann                    // have to use the header fields from the cache entry plus
11319a40379b22eeb21991fb1c55cf45788e62188b2Ralph Bergmann                    // the new ones from the response.
11419a40379b22eeb21991fb1c55cf45788e62188b2Ralph Bergmann                    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
11519a40379b22eeb21991fb1c55cf45788e62188b2Ralph Bergmann                    entry.responseHeaders.putAll(responseHeaders);
11619a40379b22eeb21991fb1c55cf45788e62188b2Ralph Bergmann                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
117750c5b995f4f326c7b30ab88eda20b25285dd2f7Ivan Stalev                            entry.responseHeaders, true,
118750c5b995f4f326c7b30ab88eda20b25285dd2f7Ivan Stalev                            SystemClock.elapsedRealtime() - requestStart);
119d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                }
120d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
121e8f5353b17aea6659dc3aa13223317b484d01483Ray Colline                // Some responses such as 204s do not have content.  We must check.
122e8f5353b17aea6659dc3aa13223317b484d01483Ray Colline                if (httpResponse.getEntity() != null) {
123e8f5353b17aea6659dc3aa13223317b484d01483Ray Colline                  responseContents = entityToBytes(httpResponse.getEntity());
124e8f5353b17aea6659dc3aa13223317b484d01483Ray Colline                } else {
125e8f5353b17aea6659dc3aa13223317b484d01483Ray Colline                  // Add 0 byte response as a way of honestly representing a
126e8f5353b17aea6659dc3aa13223317b484d01483Ray Colline                  // no-content request.
127e8f5353b17aea6659dc3aa13223317b484d01483Ray Colline                  responseContents = new byte[0];
128e8f5353b17aea6659dc3aa13223317b484d01483Ray Colline                }
129e8f5353b17aea6659dc3aa13223317b484d01483Ray Colline
130d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                // if the request is slow, log it.
131d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
132d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                logSlowRequests(requestLifetime, request, responseContents, statusLine);
133d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
134f2cd36f7fa3932c2d7973eabb87fdb22a37e1bbcReed Ellsworth                if (statusCode < 200 || statusCode > 299) {
135d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                    throw new IOException();
136d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                }
137750c5b995f4f326c7b30ab88eda20b25285dd2f7Ivan Stalev                return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
138750c5b995f4f326c7b30ab88eda20b25285dd2f7Ivan Stalev                        SystemClock.elapsedRealtime() - requestStart);
139d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            } catch (SocketTimeoutException e) {
140d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                attemptRetryOnException("socket", request, new TimeoutError());
141d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            } catch (ConnectTimeoutException e) {
142d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                attemptRetryOnException("connection", request, new TimeoutError());
143d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            } catch (MalformedURLException e) {
144d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                throw new RuntimeException("Bad URL " + request.getUrl(), e);
145d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            } catch (IOException e) {
146d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                int statusCode = 0;
147d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                NetworkResponse networkResponse = null;
148d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                if (httpResponse != null) {
149d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                    statusCode = httpResponse.getStatusLine().getStatusCode();
150d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                } else {
151d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                    throw new NoConnectionError(e);
152d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                }
153d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
154d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                if (responseContents != null) {
155d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                    networkResponse = new NetworkResponse(statusCode, responseContents,
156750c5b995f4f326c7b30ab88eda20b25285dd2f7Ivan Stalev                            responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
157d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                    if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
158d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                            statusCode == HttpStatus.SC_FORBIDDEN) {
159d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                        attemptRetryOnException("auth",
160d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                                request, new AuthFailureError(networkResponse));
161d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                    } else {
162d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                        // TODO: Only throw ServerError for 5xx status codes.
163d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                        throw new ServerError(networkResponse);
164d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                    }
165d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                } else {
166d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                    throw new NetworkError(networkResponse);
167d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                }
168d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            }
169d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
170d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
171d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
172d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    /**
173d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * Logs requests that took over SLOW_REQUEST_THRESHOLD_MS to complete.
174d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     */
175d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private void logSlowRequests(long requestLifetime, Request<?> request,
176d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            byte[] responseContents, StatusLine statusLine) {
177d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        if (DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) {
178d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            VolleyLog.d("HTTP response for request=<%s> [lifetime=%d], [size=%s], " +
179d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                    "[rc=%d], [retryCount=%s]", request, requestLifetime,
180d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                    responseContents != null ? responseContents.length : "null",
181d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                    statusLine.getStatusCode(), request.getRetryPolicy().getCurrentRetryCount());
182d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
183d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
184d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
185d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    /**
186d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * Attempts to prepare the request for a retry. If there are no more attempts remaining in the
187d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * request's retry policy, a timeout exception is thrown.
188d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * @param request The request to use.
189d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     */
190d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private static void attemptRetryOnException(String logPrefix, Request<?> request,
191d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            VolleyError exception) throws VolleyError {
192d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        RetryPolicy retryPolicy = request.getRetryPolicy();
193d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        int oldTimeout = request.getTimeoutMs();
194d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
195d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        try {
196d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            retryPolicy.retry(exception);
197d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        } catch (VolleyError e) {
198d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            request.addMarker(
199d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                    String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
200d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            throw e;
201d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
202d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
203d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
204d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
205d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {
206d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // If there's no cache entry, we're done.
207d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        if (entry == null) {
208d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            return;
209d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
210d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
211d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        if (entry.etag != null) {
212d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            headers.put("If-None-Match", entry.etag);
213d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
214d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
2159324df1b8046548587ffec89ec755264f6fbb097Ralph Bergmann        if (entry.lastModified > 0) {
2169324df1b8046548587ffec89ec755264f6fbb097Ralph Bergmann            Date refTime = new Date(entry.lastModified);
217d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
218d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
219d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
220d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
221d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    protected void logError(String what, String url, long start) {
222d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        long now = SystemClock.elapsedRealtime();
223d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        VolleyLog.v("HTTP ERROR(%s) %d ms to fetch %s", what, (now - start), url);
224d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
225d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
226d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    /** Reads the contents of HttpEntity into a byte[]. */
227d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private byte[] entityToBytes(HttpEntity entity) throws IOException, ServerError {
228d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        PoolingByteArrayOutputStream bytes =
229d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                new PoolingByteArrayOutputStream(mPool, (int) entity.getContentLength());
230d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        byte[] buffer = null;
231d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        try {
232d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            InputStream in = entity.getContent();
233d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            if (in == null) {
234d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                throw new ServerError();
235d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            }
236d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            buffer = mPool.getBuf(1024);
237d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            int count;
238d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            while ((count = in.read(buffer)) != -1) {
239d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                bytes.write(buffer, 0, count);
240d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            }
241d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            return bytes.toByteArray();
242d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        } finally {
243d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            try {
244d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                // Close the InputStream and release the resources by "consuming the content".
245d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                entity.consumeContent();
246d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            } catch (IOException e) {
247d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                // This can happen if there was an exception above that left the entity in
248d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                // an invalid state.
249d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru                VolleyLog.v("Error occured when calling consumingContent");
250d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            }
251d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            mPool.returnBuf(buffer);
252d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            bytes.close();
253d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
254d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
255d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
256d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    /**
257d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * Converts Headers[] to Map<String, String>.
258d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     */
259ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann    protected static Map<String, String> convertHeaders(Header[] headers) {
260ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann        Map<String, String> result = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
261d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        for (int i = 0; i < headers.length; i++) {
262d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            result.put(headers[i].getName(), headers[i].getValue());
263d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
264d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        return result;
265d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
266d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru}
267