14
14
#include " io/file.h"
15
15
#include " io/file_util.h"
16
16
#include " io/line_reader.h"
17
+ #include " strings/escaping.h"
17
18
18
19
using namespace std ;
19
20
namespace h2 = boost::beast::http;
@@ -31,17 +32,19 @@ auto Unexpected(std::errc code) {
31
32
return nonstd::make_unexpected (make_error_code (code));
32
33
}
33
34
34
- #define RETURN_UNEXPECTED (x ) do { \
35
- auto ec = (x); \
36
- if (ec) \
37
- return nonstd::make_unexpected (ec); \
38
- } while (false )
35
+ #define RETURN_UNEXPECTED (x ) \
36
+ do { \
37
+ auto ec = (x); \
38
+ if (ec) \
39
+ return nonstd::make_unexpected (ec); \
40
+ } while (false )
39
41
40
- #define RETURN_ERROR (x ) do { \
41
- auto ec = (x); \
42
- if (ec) \
43
- return ec; \
44
- } while (false )
42
+ #define RETURN_ERROR (x ) \
43
+ do { \
44
+ auto ec = (x); \
45
+ if (ec) \
46
+ return ec; \
47
+ } while (false )
45
48
46
49
string AuthHeader (string_view access_token) {
47
50
return absl::StrCat (" Bearer " , access_token);
@@ -185,7 +188,7 @@ io::Result<EmptyParserPtr> SendWithToken(GCPCredsProvider* provider, http::Clien
185
188
EmptyParserPtr parser (new h2::response_parser<h2::empty_body>());
186
189
RETURN_UNEXPECTED (client->ReadHeader (parser.get ()));
187
190
188
- VLOG (1 ) << " RespHeader" << i << " : " << parser. get ();
191
+ VLOG (1 ) << " RespHeader" << i << " : " << parser-> get ();
189
192
190
193
if (parser->get ().result () == h2::status::ok) {
191
194
return parser;
@@ -204,6 +207,11 @@ io::Result<EmptyParserPtr> SendWithToken(GCPCredsProvider* provider, http::Clien
204
207
return nonstd::make_unexpected (ec);
205
208
}
206
209
210
+ #define FETCH_ARRAY_MEMBER (val ) \
211
+ if (!(val).IsArray()) \
212
+ return make_error_code(errc::bad_message); \
213
+ auto array = val.GetArray()
214
+
207
215
} // namespace
208
216
209
217
error_code GCPCredsProvider::Init (unsigned connect_ms, fb2::ProactorBase* pb) {
@@ -335,11 +343,7 @@ error_code GCS::ListBuckets(ListBucketCb cb) {
335
343
if (it == doc.MemberEnd ())
336
344
break ;
337
345
338
- const auto & val = it->value ;
339
- if (!val.IsArray ()) {
340
- return make_error_code (errc::bad_message);
341
- }
342
- auto array = val.GetArray ();
346
+ FETCH_ARRAY_MEMBER (it->value );
343
347
344
348
for (size_t i = 0 ; i < array.Size (); ++i) {
345
349
const auto & item = array[i];
@@ -359,5 +363,71 @@ error_code GCS::ListBuckets(ListBucketCb cb) {
359
363
return {};
360
364
}
361
365
366
+ error_code GCS::List (string_view bucket, string_view prefix, bool recursive,
367
+ ListObjectCb cb) {
368
+ CHECK (!bucket.empty ());
369
+
370
+ string url = " /storage/v1/b/" ;
371
+ absl::StrAppend (&url, bucket, " /o?maxResults=200&prefix=" );
372
+ strings::AppendUrlEncoded (prefix, &url);
373
+ if (!recursive) {
374
+ absl::StrAppend (&url, " &delimiter=%2f" );
375
+ }
376
+ auto http_req = PrepareRequest (h2::verb::get, url, creds_provider_.access_token ());
377
+
378
+ rj::Document doc;
379
+ while (true ) {
380
+ io::Result<EmptyParserPtr> parse_res =
381
+ SendWithToken (&creds_provider_, client_.get (), &http_req);
382
+ if (!parse_res)
383
+ return parse_res.error ();
384
+ EmptyParserPtr empty_parser = std::move (*parse_res);
385
+ h2::response_parser<h2::string_body> resp (std::move (*empty_parser));
386
+ RETURN_ERROR (client_->Recv (&resp));
387
+
388
+ auto msg = resp.release ();
389
+
390
+ doc.ParseInsitu (&msg.body ().front ());
391
+ if (doc.HasParseError ()) {
392
+ return make_error_code (errc::bad_message);
393
+ }
394
+
395
+ auto it = doc.FindMember (" items" );
396
+ if (it != doc.MemberEnd ()) {
397
+ FETCH_ARRAY_MEMBER (it->value );
398
+
399
+ for (size_t i = 0 ; i < array.Size (); ++i) {
400
+ const auto & item = array[i];
401
+ auto it = item.FindMember (" name" );
402
+ CHECK (it != item.MemberEnd ());
403
+ absl::string_view key_name (it->value .GetString (), it->value .GetStringLength ());
404
+ it = item.FindMember (" size" );
405
+ CHECK (it != item.MemberEnd ());
406
+ absl::string_view sz_str (it->value .GetString (), it->value .GetStringLength ());
407
+ size_t item_size = 0 ;
408
+ CHECK (absl::SimpleAtoi (sz_str, &item_size));
409
+ cb (ObjectItem{item_size, key_name, false });
410
+ }
411
+ }
412
+ it = doc.FindMember (" prefixes" );
413
+ if (it != doc.MemberEnd ()) {
414
+ FETCH_ARRAY_MEMBER (it->value );
415
+ for (size_t i = 0 ; i < array.Size (); ++i) {
416
+ const auto & item = array[i];
417
+ absl::string_view str (item.GetString (), item.GetStringLength ());
418
+ cb (ObjectItem{0 , str, true });
419
+ }
420
+ }
421
+
422
+ it = doc.FindMember (" nextPageToken" );
423
+ if (it == doc.MemberEnd ()) {
424
+ break ;
425
+ }
426
+ absl::string_view page_token{it->value .GetString (), it->value .GetStringLength ()};
427
+ http_req.target (absl::StrCat (url, " &pageToken=" , page_token));
428
+ }
429
+ return {};
430
+ }
431
+
362
432
} // namespace cloud
363
433
} // namespace util
0 commit comments