1
15 package com.amazonaws.http.apache.request.impl;
16
17 import com.amazonaws.ClientConfiguration;
18 import com.amazonaws.ProxyAuthenticationMethod;
19 import com.amazonaws.Request;
20 import com.amazonaws.SdkClientException;
21 import com.amazonaws.handlers.HandlerContextKey;
22 import com.amazonaws.http.HttpMethodName;
23 import com.amazonaws.http.RepeatableInputStreamRequestEntity;
24 import com.amazonaws.http.apache.utils.ApacheUtils;
25 import com.amazonaws.http.request.HttpRequestFactory;
26 import com.amazonaws.http.settings.HttpClientSettings;
27 import com.amazonaws.util.FakeIOException;
28 import com.amazonaws.util.SdkHttpUtils;
29 import java.net.URI;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.List;
33 import java.util.Map.Entry;
34 import org.apache.http.HttpEntity;
35 import org.apache.http.HttpHeaders;
36 import org.apache.http.client.config.AuthSchemes;
37 import org.apache.http.client.config.RequestConfig;
38 import org.apache.http.client.methods.HttpDelete;
39 import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
40 import org.apache.http.client.methods.HttpHead;
41 import org.apache.http.client.methods.HttpOptions;
42 import org.apache.http.client.methods.HttpPatch;
43 import org.apache.http.client.methods.HttpPost;
44 import org.apache.http.client.methods.HttpPut;
45 import org.apache.http.client.methods.HttpRequestBase;
46 import org.apache.http.entity.BufferedHttpEntity;
47
48
51 public class ApacheHttpRequestFactory implements
52 HttpRequestFactory<HttpRequestBase> {
53
54 private static final String DEFAULT_ENCODING = "UTF-8";
55
56 private static final List<String> ignoreHeaders = Arrays.asList
57 (HttpHeaders.CONTENT_LENGTH, HttpHeaders.HOST);
58
59 @Override
60 public HttpRequestBase create(final Request<?> request,
61 final HttpClientSettings settings)
62 throws
63 FakeIOException {
64 URI endpoint = request.getEndpoint();
65
66 String uri;
67
68
69 if (request.getOriginalRequest().getRequestClientOptions().isSkipAppendUriPath()) {
70 uri = endpoint.toString();
71 } else {
72
77 uri = SdkHttpUtils.appendUri(endpoint.toString(), request.getResourcePath(), true);
78 }
79
80 String encodedParams = SdkHttpUtils.encodeParameters(request);
81
82
87 boolean requestHasNoPayload = request.getContent() != null;
88 boolean requestIsPost = request.getHttpMethod() == HttpMethodName.POST;
89 boolean putParamsInUri = !requestIsPost || requestHasNoPayload;
90 if (encodedParams != null && putParamsInUri) {
91 uri += "?" + encodedParams;
92 }
93
94 final HttpRequestBase base = createApacheRequest(request, uri, encodedParams);
95 addHeadersToRequest(base, request);
96 addRequestConfig(base, request, settings);
97
98 return base;
99 }
100
101 private void addRequestConfig(final HttpRequestBase base,
102 final Request<?> request,
103 final HttpClientSettings settings) {
104 final RequestConfig.Builder requestConfigBuilder = RequestConfig
105 .custom()
106 .setConnectionRequestTimeout(settings.getConnectionPoolRequestTimeout())
107 .setConnectTimeout(settings.getConnectionTimeout())
108 .setSocketTimeout(settings.getSocketTimeout())
109 .setLocalAddress(settings.getLocalAddress());
110
111 ApacheUtils.disableNormalizeUri(requestConfigBuilder);
112
113
120 if (HttpMethodName.PUT == request.getHttpMethod() && settings.isUseExpectContinue()) {
121 requestConfigBuilder.setExpectContinueEnabled(true);
122 }
123
124 addProxyConfig(requestConfigBuilder, settings);
125
126 base.setConfig(requestConfigBuilder.build());
127 }
128
129 private HttpRequestBase createApacheRequest(Request<?> request, String uri, String encodedParams) throws FakeIOException {
130 switch (request.getHttpMethod()) {
131 case HEAD:
132 return new HttpHead(uri);
133 case GET:
134 return wrapEntity(request, new HttpGetWithBody(uri), encodedParams);
135 case DELETE:
136 return new HttpDelete(uri);
137 case OPTIONS:
138 return new HttpOptions(uri);
139 case PATCH:
140 return wrapEntity(request, new HttpPatch(uri), encodedParams);
141 case POST:
142 return wrapEntity(request, new HttpPost(uri), encodedParams);
143 case PUT:
144 return wrapEntity(request, new HttpPut(uri), encodedParams);
145 default:
146 throw new SdkClientException("Unknown HTTP method name: " + request.getHttpMethod());
147 }
148 }
149
150
151
159 private HttpRequestBase wrapEntity(Request<?> request,
160 HttpEntityEnclosingRequestBase entityEnclosingRequest,
161 String encodedParams) throws FakeIOException {
162
163 if (HttpMethodName.POST == request.getHttpMethod()) {
164
171 if (request.getContent() == null && encodedParams != null) {
172 entityEnclosingRequest.setEntity(ApacheUtils.newStringEntity(encodedParams));
173 } else {
174 createHttpEntityForPostVerb(request, entityEnclosingRequest);
175 }
176 } else {
177
187 if (request.getContent() != null) {
188 createHttpEntityForNonPostVerbs(request, entityEnclosingRequest);
189 }
190 }
191 return entityEnclosingRequest;
192 }
193
194
200 private void createHttpEntityForPostVerb(Request<?> request,
201 HttpEntityEnclosingRequestBase entityEnclosingRequest) throws FakeIOException {
202 HttpEntity entity = new RepeatableInputStreamRequestEntity(request);
203
204 if (request.getHeaders().get(HttpHeaders.CONTENT_LENGTH) == null && isRequiresLength(request)) {
205 entity = ApacheUtils.newBufferedHttpEntity(entity);
206 }
207
208 entityEnclosingRequest.setEntity(entity);
209 }
210
211
212
220 private void createHttpEntityForNonPostVerbs(Request<?> request,
221 HttpEntityEnclosingRequestBase entityEnclosingRequest) throws FakeIOException {
222
223 HttpEntity entity = new RepeatableInputStreamRequestEntity(request);
224
225 if (request.getHeaders().get(HttpHeaders.CONTENT_LENGTH) == null) {
226 if (isRequiresLength(request) || !hasStreamingInput(request)) {
227 entity = ApacheUtils.newBufferedHttpEntity(entity);
228 }
229 }
230
231 entityEnclosingRequest.setEntity(entity);
232 }
233
234 private boolean isRequiresLength(Request<?> request) {
235 return Boolean.TRUE.equals(request.getHandlerContext(HandlerContextKey.REQUIRES_LENGTH));
236 }
237
238 private boolean hasStreamingInput(Request<?> request) {
239 return Boolean.TRUE.equals(request.getHandlerContext(HandlerContextKey.HAS_STREAMING_INPUT));
240 }
241
242
245 private void addHeadersToRequest(HttpRequestBase httpRequest, Request<?> request) {
246
247 httpRequest.addHeader(HttpHeaders.HOST, getHostHeaderValue(request.getEndpoint()));
248
249
250 for (Entry<String, String> entry : request.getHeaders().entrySet()) {
251
257 if (!(ignoreHeaders.contains(entry.getKey()))) {
258 httpRequest.addHeader(entry.getKey(), entry.getValue());
259 }
260 }
261
262
263 if (httpRequest.getHeaders(HttpHeaders.CONTENT_TYPE) == null || httpRequest
264 .getHeaders
265 (HttpHeaders.CONTENT_TYPE).length == 0) {
266 httpRequest.addHeader(HttpHeaders.CONTENT_TYPE,
267 "application/x-www-form-urlencoded; " +
268 "charset=" + DEFAULT_ENCODING.toLowerCase());
269 }
270 }
271
272 private String getHostHeaderValue(final URI endpoint) {
273
281 return SdkHttpUtils.isUsingNonDefaultPort(endpoint)
282 ? endpoint.getHost() + ":" + endpoint.getPort()
283 : endpoint.getHost();
284 }
285
286
292 private void addProxyConfig(RequestConfig.Builder requestConfigBuilder, HttpClientSettings settings) {
293 if (settings.isProxyEnabled() && settings.isAuthenticatedProxy() && settings.getProxyAuthenticationMethods() != null) {
294 List<String> apacheAuthenticationSchemes = new ArrayList<String>();
295
296 for (ProxyAuthenticationMethod authenticationMethod : settings.getProxyAuthenticationMethods()) {
297 apacheAuthenticationSchemes.add(toApacheAuthenticationScheme(authenticationMethod));
298 }
299
300 requestConfigBuilder.setProxyPreferredAuthSchemes(apacheAuthenticationSchemes);
301 }
302 }
303
304
307 private String toApacheAuthenticationScheme(ProxyAuthenticationMethod authenticationMethod) {
308 if (authenticationMethod == null) {
309 throw new IllegalStateException("The configured proxy authentication methods must not be null.");
310 }
311
312 switch (authenticationMethod) {
313 case NTLM: return AuthSchemes.NTLM;
314 case BASIC: return AuthSchemes.BASIC;
315 case DIGEST: return AuthSchemes.DIGEST;
316 case SPNEGO: return AuthSchemes.SPNEGO;
317 case KERBEROS: return AuthSchemes.KERBEROS;
318 default: throw new IllegalStateException("Unknown authentication scheme: " + authenticationMethod);
319 }
320 }
321 }