1 /**
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0.
4 */
5
6 #include "aws/s3/private/s3_auto_ranged_get.h"
7 #include "aws/s3/private/s3_auto_ranged_put.h"
8 #include "aws/s3/private/s3_client_impl.h"
9 #include "aws/s3/private/s3_default_meta_request.h"
10 #include "aws/s3/private/s3_meta_request_impl.h"
11 #include "aws/s3/private/s3_util.h"
12
13 #include <aws/auth/credentials.h>
14 #include <aws/common/assert.h>
15 #include <aws/common/atomics.h>
16 #include <aws/common/clock.h>
17 #include <aws/common/device_random.h>
18 #include <aws/common/environment.h>
19 #include <aws/common/string.h>
20 #include <aws/common/system_info.h>
21 #include <aws/http/connection.h>
22 #include <aws/http/connection_manager.h>
23 #include <aws/http/proxy.h>
24 #include <aws/http/request_response.h>
25 #include <aws/io/channel_bootstrap.h>
26 #include <aws/io/event_loop.h>
27 #include <aws/io/host_resolver.h>
28 #include <aws/io/retry_strategy.h>
29 #include <aws/io/socket.h>
30 #include <aws/io/stream.h>
31 #include <aws/io/tls_channel_handler.h>
32 #include <aws/io/uri.h>
33
34 #include <inttypes.h>
35 #include <math.h>
36
37 static const uint32_t s_connection_timeout_ms = 3000;
38 static const uint16_t s_http_port = 80;
39 static const uint16_t s_https_port = 443;
40
41 static void s_s3_endpoint_on_host_resolver_address_resolved(
42 struct aws_host_resolver *resolver,
43 const struct aws_string *host_name,
44 int err_code,
45 const struct aws_array_list *host_addresses,
46 void *user_data);
47
48 static struct aws_http_connection_manager *s_s3_endpoint_create_http_connection_manager(
49 struct aws_s3_endpoint *endpoint,
50 const struct aws_string *host_name,
51 struct aws_client_bootstrap *client_bootstrap,
52 const struct aws_tls_connection_options *tls_connection_options,
53 uint32_t max_connections);
54
55 static void s_s3_endpoint_http_connection_manager_shutdown_callback(void *user_data);
56
57 static void s_s3_endpoint_ref_count_zero(void *user_data);
58
aws_s3_endpoint_new(struct aws_allocator * allocator,const struct aws_s3_endpoint_options * options)59 struct aws_s3_endpoint *aws_s3_endpoint_new(
60 struct aws_allocator *allocator,
61 const struct aws_s3_endpoint_options *options) {
62 AWS_PRECONDITION(allocator);
63 AWS_PRECONDITION(options);
64 AWS_PRECONDITION(options->host_name);
65
66 struct aws_s3_endpoint *endpoint = aws_mem_calloc(allocator, 1, sizeof(struct aws_s3_endpoint));
67 aws_ref_count_init(&endpoint->ref_count, endpoint, s_s3_endpoint_ref_count_zero);
68
69 endpoint->allocator = allocator;
70 endpoint->host_name = options->host_name;
71
72 struct aws_host_resolution_config host_resolver_config;
73 AWS_ZERO_STRUCT(host_resolver_config);
74 host_resolver_config.impl = aws_default_dns_resolve;
75 host_resolver_config.max_ttl = options->dns_host_address_ttl_seconds;
76 host_resolver_config.impl_data = NULL;
77
78 if (aws_host_resolver_resolve_host(
79 options->client_bootstrap->host_resolver,
80 endpoint->host_name,
81 s_s3_endpoint_on_host_resolver_address_resolved,
82 &host_resolver_config,
83 NULL)) {
84
85 AWS_LOGF_ERROR(
86 AWS_LS_S3_ENDPOINT,
87 "id=%p: Error trying to resolve host for endpoint %s",
88 (void *)endpoint,
89 (const char *)endpoint->host_name->bytes);
90
91 goto error_cleanup;
92 }
93
94 endpoint->http_connection_manager = s_s3_endpoint_create_http_connection_manager(
95 endpoint,
96 options->host_name,
97 options->client_bootstrap,
98 options->tls_connection_options,
99 options->max_connections);
100
101 if (endpoint->http_connection_manager == NULL) {
102 goto error_cleanup;
103 }
104
105 endpoint->ref_count_zero_callback = options->ref_count_zero_callback;
106 endpoint->shutdown_callback = options->shutdown_callback;
107 endpoint->user_data = options->user_data;
108
109 return endpoint;
110
111 error_cleanup:
112
113 aws_string_destroy(options->host_name);
114
115 aws_mem_release(allocator, endpoint);
116
117 return NULL;
118 }
119
s_s3_endpoint_create_http_connection_manager(struct aws_s3_endpoint * endpoint,const struct aws_string * host_name,struct aws_client_bootstrap * client_bootstrap,const struct aws_tls_connection_options * tls_connection_options,uint32_t max_connections)120 static struct aws_http_connection_manager *s_s3_endpoint_create_http_connection_manager(
121 struct aws_s3_endpoint *endpoint,
122 const struct aws_string *host_name,
123 struct aws_client_bootstrap *client_bootstrap,
124 const struct aws_tls_connection_options *tls_connection_options,
125 uint32_t max_connections) {
126 AWS_PRECONDITION(endpoint);
127 AWS_PRECONDITION(client_bootstrap);
128 AWS_PRECONDITION(host_name);
129
130 struct aws_byte_cursor host_name_cursor = aws_byte_cursor_from_string(host_name);
131
132 /* Try to set up an HTTP connection manager. */
133 struct aws_socket_options socket_options;
134 AWS_ZERO_STRUCT(socket_options);
135 socket_options.type = AWS_SOCKET_STREAM;
136 socket_options.domain = AWS_SOCKET_IPV4;
137 socket_options.connect_timeout_ms = s_connection_timeout_ms;
138 struct proxy_env_var_settings proxy_ev_settings;
139 AWS_ZERO_STRUCT(proxy_ev_settings);
140 /* Turn on envrionment variable for proxy by default */
141 proxy_ev_settings.env_var_type = AWS_HPEV_ENABLE;
142
143 struct aws_http_connection_manager_options manager_options;
144 AWS_ZERO_STRUCT(manager_options);
145 manager_options.bootstrap = client_bootstrap;
146 manager_options.initial_window_size = SIZE_MAX;
147 manager_options.socket_options = &socket_options;
148 manager_options.host = host_name_cursor;
149 manager_options.max_connections = max_connections;
150 manager_options.shutdown_complete_callback = s_s3_endpoint_http_connection_manager_shutdown_callback;
151 manager_options.shutdown_complete_user_data = endpoint;
152 manager_options.proxy_ev_settings = &proxy_ev_settings;
153
154 struct aws_tls_connection_options *manager_tls_options = NULL;
155
156 if (tls_connection_options != NULL) {
157 manager_tls_options = aws_mem_calloc(endpoint->allocator, 1, sizeof(struct aws_tls_connection_options));
158 aws_tls_connection_options_copy(manager_tls_options, tls_connection_options);
159
160 /* TODO fix this in the actual aws_tls_connection_options_set_server_name function. */
161 if (manager_tls_options->server_name != NULL) {
162 aws_string_destroy(manager_tls_options->server_name);
163 manager_tls_options->server_name = NULL;
164 }
165
166 aws_tls_connection_options_set_server_name(manager_tls_options, endpoint->allocator, &host_name_cursor);
167
168 manager_options.tls_connection_options = manager_tls_options;
169 manager_options.port = s_https_port;
170 } else {
171 manager_options.port = s_http_port;
172 }
173
174 struct aws_http_connection_manager *http_connection_manager =
175 aws_http_connection_manager_new(endpoint->allocator, &manager_options);
176
177 if (manager_tls_options != NULL) {
178 aws_tls_connection_options_clean_up(manager_tls_options);
179 aws_mem_release(endpoint->allocator, manager_tls_options);
180 manager_tls_options = NULL;
181 }
182
183 if (http_connection_manager == NULL) {
184 AWS_LOGF_ERROR(AWS_LS_S3_ENDPOINT, "id=%p: Could not create http connection manager.", (void *)endpoint);
185 return NULL;
186 }
187
188 AWS_LOGF_DEBUG(
189 AWS_LS_S3_ENDPOINT,
190 "id=%p: Created connection manager %p for endpoint",
191 (void *)endpoint,
192 (void *)endpoint->http_connection_manager);
193
194 return http_connection_manager;
195 }
196
aws_s3_endpoint_acquire(struct aws_s3_endpoint * endpoint)197 struct aws_s3_endpoint *aws_s3_endpoint_acquire(struct aws_s3_endpoint *endpoint) {
198 AWS_PRECONDITION(endpoint);
199
200 aws_ref_count_acquire(&endpoint->ref_count);
201
202 return endpoint;
203 }
204
aws_s3_endpoint_release(struct aws_s3_endpoint * endpoint)205 void aws_s3_endpoint_release(struct aws_s3_endpoint *endpoint) {
206 if (endpoint == NULL) {
207 return;
208 }
209
210 aws_ref_count_release(&endpoint->ref_count);
211 }
212
s_s3_endpoint_ref_count_zero(void * user_data)213 static void s_s3_endpoint_ref_count_zero(void *user_data) {
214 struct aws_s3_endpoint *endpoint = user_data;
215 AWS_PRECONDITION(endpoint);
216
217 if (endpoint->ref_count_zero_callback != NULL && !endpoint->ref_count_zero_callback(endpoint)) {
218 return;
219 }
220
221 if (endpoint->http_connection_manager != NULL) {
222 struct aws_http_connection_manager *http_connection_manager = endpoint->http_connection_manager;
223 endpoint->http_connection_manager = NULL;
224 aws_http_connection_manager_release(http_connection_manager);
225 } else {
226 s_s3_endpoint_http_connection_manager_shutdown_callback(endpoint->user_data);
227 }
228 }
229
s_s3_endpoint_http_connection_manager_shutdown_callback(void * user_data)230 static void s_s3_endpoint_http_connection_manager_shutdown_callback(void *user_data) {
231 struct aws_s3_endpoint *endpoint = user_data;
232 AWS_ASSERT(endpoint);
233
234 aws_s3_endpoint_shutdown_fn *shutdown_callback = endpoint->shutdown_callback;
235 void *endpoint_user_data = endpoint->user_data;
236
237 aws_mem_release(endpoint->allocator, endpoint);
238
239 if (shutdown_callback != NULL) {
240 shutdown_callback(endpoint_user_data);
241 }
242 }
243
s_s3_endpoint_on_host_resolver_address_resolved(struct aws_host_resolver * resolver,const struct aws_string * host_name,int err_code,const struct aws_array_list * host_addresses,void * user_data)244 static void s_s3_endpoint_on_host_resolver_address_resolved(
245 struct aws_host_resolver *resolver,
246 const struct aws_string *host_name,
247 int err_code,
248 const struct aws_array_list *host_addresses,
249 void *user_data) {
250 (void)resolver;
251 (void)host_name;
252 (void)err_code;
253 (void)host_addresses;
254 (void)user_data;
255 }
256