-
Notifications
You must be signed in to change notification settings - Fork 548
/
HttpRequestFactory.java
167 lines (147 loc) · 6.91 KB
/
HttpRequestFactory.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/*
* Copyright 2011-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file 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.amazonaws.http;
import com.amazonaws.AmazonClientException;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.Request;
import com.amazonaws.util.HttpUtils;
import com.amazonaws.util.StringUtils;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* Responsible for creating Http request objects from AWS {@link Request}s.
*/
class HttpRequestFactory {
private static final String DEFAULT_ENCODING = "UTF-8";
/**
* Creates an HttpClient method object based on the specified request and
* populates any parameters, headers, etc. from the original request.
*
* @param request The request to convert to an HttpClient method object.
* @param context The execution context of the HTTP method to be executed
* @return The converted HttpClient method object with any parameters,
* headers, etc. from the original request set.
*/
HttpRequest createHttpRequest(Request<?> request, ClientConfiguration clientConfiguration,
ExecutionContext context) {
URI endpoint = request.getEndpoint();
/*
* HttpClient cannot handle url in pattern of "http://host//path", so we
* have to escape the double-slash between endpoint and resource-path
* into "/%2F"
*/
String uri = HttpUtils.appendUri(endpoint.toString(), request.getResourcePath(), true);
String encodedParams = HttpUtils.encodeParameters(request);
/*
* For all non-POST requests, and any POST requests that already have a
* payload, we put the encoded params directly in the URI, otherwise,
* we'll put them in the POST request's payload.
*/
boolean requestHasNoPayload = request.getContent() != null;
boolean requestIsPost = request.getHttpMethod() == HttpMethodName.POST;
boolean putParamsInUri = !requestIsPost || requestHasNoPayload;
if (encodedParams != null && putParamsInUri) {
uri += "?" + encodedParams;
}
InputStream is = request.getContent();
String method = request.getHttpMethod().toString();
if (method.equals("POST")) {
/*
* If there isn't any payload content to include in this request,
* then try to include the POST parameters in the query body,
* otherwise, just use the query string. For all AWS Query services,
* the best behavior is putting the params in the request body for
* POST requests, but we can't do that for S3.
*/
if (request.getContent() == null && encodedParams != null) {
is = new ByteArrayInputStream(encodedParams.getBytes(StringUtils.UTF8));
request.addHeader("Content-Length", String.valueOf(encodedParams.length()));
}
}
String len = request.getHeaders().get("Content-Length");
if (len == null || len.isEmpty()) {
if (is != null) {
throw new AmazonClientException("Unknown content-length");
} else {
request.addHeader("Content-Length", "0");
}
}
/*
* Amazon DynamoDB sets CRC32 checksum in the header 'x-amz-crc32'. If
* compression is turned on, then the checksum is calculated on the
* compressed data. On the client side, compression is handled by the
* HTTP client. In most cases, the client doesn't have access to
* compressed data, so there is no way to compute the checksum of
* compressed data unless the client compress it. To get around it,
* compression is turned off explicitly.
*/
if (request.getHeaders().get("Accept-Encoding") == null) {
request.addHeader("Accept-Encoding", "identity");
}
Map<String, String> headers = new HashMap<String, String>();
configureHeaders(headers, request, context, clientConfiguration);
return new HttpRequest(method, URI.create(uri), headers, is);
}
/** Configures the headers in the specified Apache HTTP request. */
private void configureHeaders(Map<String, String> headers, Request<?> request,
ExecutionContext context, ClientConfiguration clientConfiguration) {
/*
* Apache HttpClient omits the port number in the Host header (even if
* we explicitly specify it) if it's the default port for the protocol
* in use. To ensure that we use the same Host header in the request and
* in the calculated string to sign (even if Apache HttpClient changed
* and started honoring our explicit host with endpoint), we follow this
* same behavior here and in the QueryString signer.
*/
URI endpoint = request.getEndpoint();
String hostHeader = endpoint.getHost();
if (HttpUtils.isUsingNonDefaultPort(endpoint)) {
hostHeader += ":" + endpoint.getPort();
}
headers.put("Host", hostHeader);
// Copy over any other headers already in our request
for (Entry<String, String> entry : request.getHeaders().entrySet()) {
headers.put(entry.getKey(), entry.getValue());
}
/* Set content type and encoding */
if (headers.get("Content-Type") == null || headers.get("Content-Type").isEmpty()) {
headers.put("Content-Type",
"application/x-www-form-urlencoded; " +
"charset=" + DEFAULT_ENCODING.toLowerCase());
}
// Override the user agent string specified in the client params if the
// context requires it
if (context != null && context.getContextUserAgent() != null) {
headers.put("User-Agent",
createUserAgentString(clientConfiguration, context.getContextUserAgent()));
}
}
/**
* Appends the given user-agent string to the client's existing one and
* returns it.
*/
private String createUserAgentString(ClientConfiguration clientConfiguration,
String contextUserAgent) {
if (clientConfiguration.getUserAgent().contains(contextUserAgent)) {
return clientConfiguration.getUserAgent();
} else {
return clientConfiguration.getUserAgent() + " " + contextUserAgent;
}
}
}