1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.volley.toolbox;
18 
19 import com.android.volley.AuthFailureError;
20 import com.android.volley.Request;
21 import com.android.volley.Request.Method;
22 
23 import org.apache.http.HttpEntity;
24 import org.apache.http.HttpResponse;
25 import org.apache.http.NameValuePair;
26 import org.apache.http.client.HttpClient;
27 import org.apache.http.client.methods.HttpDelete;
28 import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
29 import org.apache.http.client.methods.HttpGet;
30 import org.apache.http.client.methods.HttpHead;
31 import org.apache.http.client.methods.HttpOptions;
32 import org.apache.http.client.methods.HttpPost;
33 import org.apache.http.client.methods.HttpPut;
34 import org.apache.http.client.methods.HttpTrace;
35 import org.apache.http.client.methods.HttpUriRequest;
36 import org.apache.http.entity.ByteArrayEntity;
37 import org.apache.http.message.BasicNameValuePair;
38 import org.apache.http.params.HttpConnectionParams;
39 import org.apache.http.params.HttpParams;
40 
41 import java.io.IOException;
42 import java.net.URI;
43 import java.util.ArrayList;
44 import java.util.List;
45 import java.util.Map;
46 
47 /**
48  * An HttpStack that performs request over an {@link HttpClient}.
49  */
50 public class HttpClientStack implements HttpStack {
51     protected final HttpClient mClient;
52 
53     private final static String HEADER_CONTENT_TYPE = "Content-Type";
54 
HttpClientStack(HttpClient client)55     public HttpClientStack(HttpClient client) {
56         mClient = client;
57     }
58 
addHeaders(HttpUriRequest httpRequest, Map<String, String> headers)59     private static void addHeaders(HttpUriRequest httpRequest, Map<String, String> headers) {
60         for (String key : headers.keySet()) {
61             httpRequest.setHeader(key, headers.get(key));
62         }
63     }
64 
65     @SuppressWarnings("unused")
getPostParameterPairs(Map<String, String> postParams)66     private static List<NameValuePair> getPostParameterPairs(Map<String, String> postParams) {
67         List<NameValuePair> result = new ArrayList<NameValuePair>(postParams.size());
68         for (String key : postParams.keySet()) {
69             result.add(new BasicNameValuePair(key, postParams.get(key)));
70         }
71         return result;
72     }
73 
74     @Override
performRequest(Request<?> request, Map<String, String> additionalHeaders)75     public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
76             throws IOException, AuthFailureError {
77         HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
78         addHeaders(httpRequest, additionalHeaders);
79         addHeaders(httpRequest, request.getHeaders());
80         onPrepareRequest(httpRequest);
81         HttpParams httpParams = httpRequest.getParams();
82         int timeoutMs = request.getTimeoutMs();
83         // TODO: Reevaluate this connection timeout based on more wide-scale
84         // data collection and possibly different for wifi vs. 3G.
85         HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
86         HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
87         return mClient.execute(httpRequest);
88     }
89 
90     /**
91      * Creates the appropriate subclass of HttpUriRequest for passed in request.
92      */
93     @SuppressWarnings("deprecation")
createHttpRequest(Request<?> request, Map<String, String> additionalHeaders)94     /* protected */ static HttpUriRequest createHttpRequest(Request<?> request,
95             Map<String, String> additionalHeaders) throws AuthFailureError {
96         switch (request.getMethod()) {
97             case Method.DEPRECATED_GET_OR_POST: {
98                 // This is the deprecated way that needs to be handled for backwards compatibility.
99                 // If the request's post body is null, then the assumption is that the request is
100                 // GET.  Otherwise, it is assumed that the request is a POST.
101                 byte[] postBody = request.getPostBody();
102                 if (postBody != null) {
103                     HttpPost postRequest = new HttpPost(request.getUrl());
104                     postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType());
105                     HttpEntity entity;
106                     entity = new ByteArrayEntity(postBody);
107                     postRequest.setEntity(entity);
108                     return postRequest;
109                 } else {
110                     return new HttpGet(request.getUrl());
111                 }
112             }
113             case Method.GET:
114                 return new HttpGet(request.getUrl());
115             case Method.DELETE:
116                 return new HttpDelete(request.getUrl());
117             case Method.POST: {
118                 HttpPost postRequest = new HttpPost(request.getUrl());
119                 postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
120                 setEntityIfNonEmptyBody(postRequest, request);
121                 return postRequest;
122             }
123             case Method.PUT: {
124                 HttpPut putRequest = new HttpPut(request.getUrl());
125                 putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
126                 setEntityIfNonEmptyBody(putRequest, request);
127                 return putRequest;
128             }
129             case Method.HEAD:
130                 return new HttpHead(request.getUrl());
131             case Method.OPTIONS:
132                 return new HttpOptions(request.getUrl());
133             case Method.TRACE:
134                 return new HttpTrace(request.getUrl());
135             case Method.PATCH: {
136                 HttpPatch patchRequest = new HttpPatch(request.getUrl());
137                 patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
138                 setEntityIfNonEmptyBody(patchRequest, request);
139                 return patchRequest;
140             }
141             default:
142                 throw new IllegalStateException("Unknown request method.");
143         }
144     }
145 
setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest, Request<?> request)146     private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest,
147             Request<?> request) throws AuthFailureError {
148         byte[] body = request.getBody();
149         if (body != null) {
150             HttpEntity entity = new ByteArrayEntity(body);
151             httpRequest.setEntity(entity);
152         }
153     }
154 
155     /**
156      * Called before the request is executed using the underlying HttpClient.
157      *
158      * <p>Overwrite in subclasses to augment the request.</p>
159      */
onPrepareRequest(HttpUriRequest request)160     protected void onPrepareRequest(HttpUriRequest request) throws IOException {
161         // Nothing.
162     }
163 
164     /**
165      * The HttpPatch class does not exist in the Android framework, so this has been defined here.
166      */
167     public static final class HttpPatch extends HttpEntityEnclosingRequestBase {
168 
169         public final static String METHOD_NAME = "PATCH";
170 
HttpPatch()171         public HttpPatch() {
172             super();
173         }
174 
HttpPatch(final URI uri)175         public HttpPatch(final URI uri) {
176             super();
177             setURI(uri);
178         }
179 
180         /**
181          * @throws IllegalArgumentException if the uri is invalid.
182          */
HttpPatch(final String uri)183         public HttpPatch(final String uri) {
184             super();
185             setURI(URI.create(uri));
186         }
187 
188         @Override
getMethod()189         public String getMethod() {
190             return METHOD_NAME;
191         }
192 
193     }
194 }
195