s3_wire/client/object/
listing.rs1use std::collections::HashSet;
2
3use http::Method;
4
5use super::super::S3Client;
6use super::super::request::{OperationDeadline, protocol_error, request_headers};
7use super::headers::{parse_request_ids, push_optional_query};
8use crate::error::{ErrorCategory, RetryClassification, S3Error};
9use crate::operation::{ListObjectsV2Output, ListObjectsV2Request};
10use crate::protocol::parse_list_objects_v2;
11
12impl S3Client {
13 pub async fn list_objects_v2(
19 &self,
20 request: ListObjectsV2Request,
21 ) -> Result<ListObjectsV2Output, S3Error> {
22 let deadline = self.deadline();
23 self.list_objects_v2_with_deadline(&request, &deadline)
24 .await
25 }
26
27 pub async fn list_objects_v2_all(
34 &self,
35 mut request: ListObjectsV2Request,
36 maximum_pages: usize,
37 ) -> Result<Vec<ListObjectsV2Output>, S3Error> {
38 if maximum_pages == 0 {
39 return Err(S3Error::configuration(
40 "maximum listing page count must be greater than zero",
41 ));
42 }
43 let deadline = self.deadline();
44 let mut seen = HashSet::new();
45 if let Some(token) = &request.continuation_token {
46 seen.insert(token.clone());
47 }
48 let mut pages = Vec::with_capacity(maximum_pages.min(16));
49 for _ in 0..maximum_pages {
50 let page = self
51 .list_objects_v2_with_deadline(&request, &deadline)
52 .await?;
53 let is_truncated = page.is_truncated;
54 let next = page.next_continuation_token.clone();
55 pages.push(page);
56 if !is_truncated {
57 return Ok(pages);
58 }
59 let next = next.ok_or_else(|| {
60 S3Error::invalid_response(
61 "truncated listing response omitted its continuation token",
62 )
63 })?;
64 if !seen.insert(next.clone()) {
65 return Err(S3Error::invalid_response(
66 "listing response repeated a continuation token",
67 ));
68 }
69 request.continuation_token = Some(next);
70 request.start_after = None;
71 }
72 Err(S3Error::new(
73 ErrorCategory::OversizedResponse,
74 "listing exceeded the configured page limit",
75 RetryClassification::Never,
76 ))
77 }
78
79 async fn list_objects_v2_with_deadline(
80 &self,
81 request: &ListObjectsV2Request,
82 deadline: &OperationDeadline,
83 ) -> Result<ListObjectsV2Output, S3Error> {
84 let mut query = vec![
85 ("list-type".to_owned(), "2".to_owned()),
86 ("max-keys".to_owned(), request.max_keys.get().to_string()),
87 ("encoding-type".to_owned(), "url".to_owned()),
88 ];
89 push_optional_query(&mut query, "prefix", request.prefix.as_deref());
90 push_optional_query(&mut query, "delimiter", request.delimiter.as_deref());
91 push_optional_query(
92 &mut query,
93 "continuation-token",
94 request.continuation_token.as_deref(),
95 );
96 push_optional_query(
97 &mut query,
98 "start-after",
99 request
100 .start_after
101 .as_ref()
102 .map(crate::operation::ObjectKey::as_str),
103 );
104 if request.fetch_owner {
105 query.push(("fetch-owner".to_owned(), "true".to_owned()));
106 }
107 let target = self.operation_target(None)?;
108 let response = self
109 .send_signed(
110 Method::GET,
111 target,
112 &query,
113 request_headers(request.headers.clone())?,
114 None,
115 deadline,
116 )
117 .await?;
118 let response_headers = response.headers().clone();
119 let maximum = self.inner.config.max_xml_response_size();
120 let body = self.collect_response(response, maximum, deadline).await?;
121 let mut output = parse_list_objects_v2(&body, maximum).map_err(protocol_error)?;
122 output.request_ids = parse_request_ids(&response_headers)?;
123 Ok(output)
124 }
125}