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 com.android.volley.AuthFailureError;
20d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.Request;
21e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queruimport com.android.volley.Request.Method;
22d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
23d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.HttpEntity;
24d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.HttpResponse;
25d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.NameValuePair;
26d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.client.HttpClient;
27e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queruimport org.apache.http.client.methods.HttpDelete;
28e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queruimport org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
29d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.client.methods.HttpGet;
30d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.client.methods.HttpPost;
31e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queruimport org.apache.http.client.methods.HttpPut;
32d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.client.methods.HttpUriRequest;
33d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.entity.ByteArrayEntity;
34d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.message.BasicNameValuePair;
35d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.params.HttpConnectionParams;
36d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport org.apache.http.params.HttpParams;
37d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
38d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.io.IOException;
39d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.util.ArrayList;
40d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.util.List;
41d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.util.Map;
42d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
43d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru/**
44d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * An HttpStack that performs request over an {@link HttpClient}.
45d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */
46d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Querupublic class HttpClientStack implements HttpStack {
47d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    protected final HttpClient mClient;
48d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
49e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru    private final static String HEADER_CONTENT_TYPE = "Content-Type";
50e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru
51d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    public HttpClientStack(HttpClient client) {
52d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        mClient = client;
53d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
54d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
55d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private static void addHeaders(HttpUriRequest httpRequest, Map<String, String> headers) {
56d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        for (String key : headers.keySet()) {
57d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            httpRequest.setHeader(key, headers.get(key));
58d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
59d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
60d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
61d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    @SuppressWarnings("unused")
62d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private static List<NameValuePair> getPostParameterPairs(Map<String, String> postParams) {
63d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        List<NameValuePair> result = new ArrayList<NameValuePair>(postParams.size());
64d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        for (String key : postParams.keySet()) {
65d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            result.add(new BasicNameValuePair(key, postParams.get(key)));
66d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        }
67d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        return result;
68d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
69d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
70d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    @Override
71d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
72d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru            throws IOException, AuthFailureError {
73e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru        HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
74d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        addHeaders(httpRequest, additionalHeaders);
75d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        addHeaders(httpRequest, request.getHeaders());
76d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        onPrepareRequest(httpRequest);
77d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        HttpParams httpParams = httpRequest.getParams();
78d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        int timeoutMs = request.getTimeoutMs();
79d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // TODO: Reevaluate this connection timeout based on more wide-scale
80d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // data collection and possibly different for wifi vs. 3G.
81d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
82d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
83d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        return mClient.execute(httpRequest);
84d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
85d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
86d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    /**
87e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru     * Creates the appropriate subclass of HttpUriRequest for passed in request.
88e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru     */
89e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru    @SuppressWarnings("deprecation")
90e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru    /* protected */ static HttpUriRequest createHttpRequest(Request<?> request,
91e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru            Map<String, String> additionalHeaders) throws AuthFailureError {
92e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru        switch (request.getMethod()) {
93e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru            case Method.DEPRECATED_GET_OR_POST: {
94e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                // This is the deprecated way that needs to be handled for backwards compatibility.
95e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                // If the request's post body is null, then the assumption is that the request is
96e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                // GET.  Otherwise, it is assumed that the request is a POST.
97e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                byte[] postBody = request.getPostBody();
98e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                if (postBody != null) {
99e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                    HttpPost postRequest = new HttpPost(request.getUrl());
100e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                    postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType());
101e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                    HttpEntity entity;
102e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                    entity = new ByteArrayEntity(postBody);
103e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                    postRequest.setEntity(entity);
104e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                    return postRequest;
105e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                } else {
106e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                    return new HttpGet(request.getUrl());
107e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                }
108e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru            }
109e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru            case Method.GET:
110e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                return new HttpGet(request.getUrl());
111e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru            case Method.DELETE:
112e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                return new HttpDelete(request.getUrl());
113e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru            case Method.POST: {
114e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                HttpPost postRequest = new HttpPost(request.getUrl());
115e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
116e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                setEntityIfNonEmptyBody(postRequest, request);
117e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                return postRequest;
118e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru            }
119e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru            case Method.PUT: {
120e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                HttpPut putRequest = new HttpPut(request.getUrl());
121e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
122e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                setEntityIfNonEmptyBody(putRequest, request);
123e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                return putRequest;
124e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru            }
125e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru            default:
126e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru                throw new IllegalStateException("Unknown request method.");
127e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru        }
128e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru    }
129e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru
130e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru    private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest,
131e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru            Request<?> request) throws AuthFailureError {
132e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru        byte[] body = request.getBody();
133e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru        if (body != null) {
134e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru            HttpEntity entity = new ByteArrayEntity(body);
135e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru            httpRequest.setEntity(entity);
136e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru        }
137e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru    }
138e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru
139e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru    /**
140d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * Called before the request is executed using the underlying HttpClient.
141d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     *
142d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     * <p>Overwrite in subclasses to augment the request.</p>
143d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru     */
144d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    protected void onPrepareRequest(HttpUriRequest request) throws IOException {
145d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // Nothing.
146d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
147d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru}
148