1 1 | /*
|
2 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3 3 | * SPDX-License-Identifier: Apache-2.0
|
4 4 | */
|
5 5 |
|
6 - | use aws_sdk_s3::config::{timeout::TimeoutConfig, Region};
|
6 + | use aws_sdk_s3::config::{retry::RetryConfig, timeout::TimeoutConfig, Region};
|
7 7 | use aws_sdk_s3::error::DisplayErrorContext;
|
8 8 | use aws_sdk_s3::primitives::ByteStream;
|
9 9 | use aws_sdk_s3::types::{
|
10 10 | CompressionType, CsvInput, CsvOutput, ExpressionType, FileHeaderInfo, InputSerialization,
|
11 11 | OutputSerialization,
|
12 12 | };
|
13 13 | use aws_sdk_s3::{Client, Config};
|
14 14 | use aws_smithy_async::assert_elapsed;
|
15 15 | use aws_smithy_http_client::test_util::NeverClient;
|
16 16 | use std::future::Future;
|
17 17 | use std::net::SocketAddr;
|
18 18 | use std::time::Duration;
|
19 19 | use tokio::net::TcpListener;
|
20 20 | use tokio::time::timeout;
|
21 21 |
|
22 22 | #[tokio::test(start_paused = true)]
|
23 23 | async fn test_event_stream_request_times_out_if_server_is_unresponsive() {
|
24 24 | let config = Config::builder()
|
25 25 | .with_test_defaults()
|
26 26 | .region(Region::new("us-east-2"))
|
27 27 | .http_client(NeverClient::new())
|
28 28 | .timeout_config(
|
29 29 | TimeoutConfig::builder()
|
30 30 | .operation_timeout(Duration::from_millis(500))
|
31 31 | .build(),
|
32 32 | )
|
33 33 | .build();
|
34 34 | let client = Client::from_conf(config);
|
35 35 |
|
36 36 | let now = tokio::time::Instant::now();
|
105 105 |
|
106 106 | #[tokio::test]
|
107 107 | async fn test_read_timeout() {
|
108 108 | async fn run_server(
|
109 109 | mut shutdown_receiver: tokio::sync::oneshot::Receiver<()>,
|
110 110 | ) -> (impl Future<Output = ()>, SocketAddr) {
|
111 111 | let listener = TcpListener::bind("0.0.0.0:0").await.unwrap();
|
112 112 | let listener_addr = listener.local_addr().unwrap();
|
113 113 |
|
114 114 | (
|
115 115 | async move {
|
116 116 | while shutdown_receiver.try_recv().is_err() {
|
117 117 | if let Ok(Ok((_socket, _))) =
|
118 118 | timeout(Duration::from_millis(100), listener.accept()).await
|
119 119 | {
|
120 120 | tokio::time::sleep(Duration::from_millis(1000)).await;
|
121 121 | }
|
122 122 | }
|
123 123 | },
|
124 124 | listener_addr,
|
125 125 | )
|
126 126 | }
|
127 127 | let (server_shutdown, server_shutdown_receiver) = tokio::sync::oneshot::channel();
|
128 128 | let (server_fut, server_addr) = run_server(server_shutdown_receiver).await;
|
129 129 | let server_handle = tokio::spawn(server_fut);
|
130 130 | tokio::time::sleep(Duration::from_millis(100)).await;
|
131 131 |
|
132 132 | let config = Config::builder()
|
133 133 | .with_test_defaults()
|
134 134 | .region(Region::new("us-east-1"))
|
135 + | .retry_config(RetryConfig::disabled())
|
135 136 | .timeout_config(
|
136 137 | TimeoutConfig::builder()
|
137 138 | .read_timeout(Duration::from_millis(300))
|
138 139 | .build(),
|
139 140 | )
|
140 141 | .endpoint_url(format!("http://{server_addr}"))
|
141 142 | .build();
|
142 143 | let client = Client::from_conf(config);
|
143 144 |
|
144 145 | if let Ok(result) = timeout(
|
145 146 | Duration::from_millis(1000),
|
146 147 | client.get_object().bucket("test").key("test").send(),
|
147 148 | )
|
148 149 | .await
|
149 150 | {
|
150 151 | match result {
|
151 152 | Ok(_) => panic!("should not have succeeded"),
|
152 153 | Err(err) => {
|
153 154 | let message = format!("{}", DisplayErrorContext(&err));
|
154 155 | let expected = "timeout: HTTP read timeout occurred after 300ms";
|
155 156 | assert!(
|
156 157 | message.contains(expected),
|
157 158 | "expected '{message}' to contain '{expected}'"
|
158 159 | );
|
159 160 | }
|
160 161 | }
|
161 162 | } else {
|
162 163 | panic!("the client didn't timeout");
|
163 164 | }
|
164 165 |
|
165 166 | server_shutdown.send(()).unwrap();
|
166 167 | server_handle.await.unwrap();
|
167 168 | }
|
168 169 |
|
169 170 | #[tokio::test]
|
170 171 | async fn test_connect_timeout() {
|
171 172 | let config = Config::builder()
|
172 173 | .with_test_defaults()
|
173 174 | .region(Region::new("us-east-1"))
|
175 + | .retry_config(RetryConfig::disabled())
|
174 176 | .timeout_config(
|
175 177 | TimeoutConfig::builder()
|
176 178 | .connect_timeout(Duration::from_millis(300))
|
177 179 | .build(),
|
178 180 | )
|
179 181 | .endpoint_url(
|
180 182 | // Emulate a connect timeout error by hitting an unroutable IP
|
181 183 | "http://172.255.255.0:18104",
|
182 184 | )
|
183 185 | .build();
|
184 186 | let client = Client::from_conf(config);
|
185 187 |
|
186 188 | if let Ok(result) = timeout(
|
187 189 | Duration::from_millis(1000),
|
188 190 | client.get_object().bucket("test").key("test").send(),
|
189 191 | )
|
190 192 | .await
|
191 193 | {
|
192 194 | match result {
|
193 195 | Ok(_) => panic!("should not have succeeded"),
|
194 196 | Err(err) => {
|
195 197 | let message = format!("{}", DisplayErrorContext(&err));
|
196 198 | let expected =
|
197 199 | "timeout: client error (Connect): HTTP connect timeout occurred after 300ms";
|
198 200 | assert!(
|
199 201 | message.contains(expected),
|
200 202 | "expected '{message}' to contain '{expected}'"
|
201 203 | );
|
202 204 | }
|
203 205 | }
|