@@ -31,6 +31,18 @@ auto Unexpected(std::errc code) {
31
31
return nonstd::make_unexpected (make_error_code (code));
32
32
}
33
33
34
+ #define RETURN_UNEXPECTED (x ) do { \
35
+ auto ec = (x); \
36
+ if (ec) \
37
+ return nonstd::make_unexpected (ec); \
38
+ } while (false )
39
+
40
+ #define RETURN_ERROR (x ) do { \
41
+ auto ec = (x); \
42
+ if (ec) \
43
+ return ec; \
44
+ } while (false )
45
+
34
46
string AuthHeader (string_view access_token) {
35
47
return absl::StrCat (" Bearer " , access_token);
36
48
}
@@ -162,35 +174,34 @@ io::Result<TokenTtl> ParseTokenResponse(std::string&& response) {
162
174
return result;
163
175
}
164
176
165
- template <typename RespBody>
166
- error_code SendWithToken (GCPCredsProvider* provider, http::Client* client, EmptyRequest* req, h2::response<RespBody>* resp) {
177
+ using EmptyParserPtr = std::unique_ptr<h2::response_parser<h2::empty_body>>;
178
+ io::Result<EmptyParserPtr> SendWithToken (GCPCredsProvider* provider, http::Client* client,
179
+ EmptyRequest* req) {
180
+ error_code ec;
167
181
for (unsigned i = 0 ; i < 2 ; ++i) { // Iterate for possible token refresh.
168
182
VLOG (1 ) << " HttpReq" << i << " : " << *req << " , socket " << client->native_handle ();
169
183
170
- error_code ec = client->Send (*req, resp);
171
- if (ec) {
172
- return ec;
173
- }
174
- VLOG (1 ) << " HttpResp" << i << " : " << *resp;
184
+ RETURN_UNEXPECTED (client->Send (*req));
185
+ EmptyParserPtr parser (new h2::response_parser<h2::empty_body>());
186
+ RETURN_UNEXPECTED (client->ReadHeader (parser.get ()));
175
187
176
- if (resp->result () == h2::status::ok) {
177
- break ;
178
- };
188
+ VLOG (1 ) << " RespHeader" << i << " : " << parser.get ();
179
189
180
- if (IsUnauthorized (*resp)) {
181
- ec = provider->RefreshToken (client->proactor ());
182
- if (ec) {
183
- return ec;
184
- }
190
+ if (parser->get ().result () == h2::status::ok) {
191
+ return parser;
192
+ };
185
193
186
- *resp = {};
194
+ if (IsUnauthorized (parser->get ())) {
195
+ RETURN_UNEXPECTED (provider->RefreshToken (client->proactor ()));
187
196
req->set (h2::field::authorization, AuthHeader (provider->access_token ()));
188
197
189
198
continue ;
190
199
}
191
- LOG (FATAL) << " Unexpected response " << *resp;
200
+ ec = make_error_code (errc::bad_message);
201
+ LOG (DFATAL) << " Unexpected response " << parser.get ();
192
202
}
193
- return {};
203
+
204
+ return nonstd::make_unexpected (ec);
194
205
}
195
206
196
207
} // namespace
@@ -218,18 +229,16 @@ error_code GCPCredsProvider::Init(unsigned connect_ms, fb2::ProactorBase* pb) {
218
229
use_instance_metadata_ = true ;
219
230
LOG (FATAL) << " TBD: do not support reading from instance metadata" ;
220
231
} else {
221
- error_code ec = LoadGCPConfig (&account_id_, &project_id_);
222
- if (ec)
223
- return ec;
232
+ RETURN_ERROR (LoadGCPConfig (&account_id_, &project_id_));
233
+
224
234
if (account_id_.empty () || project_id_.empty ()) {
225
235
LOG (WARNING) << " gcloud config file is not valid" ;
226
236
return make_error_code (errc::not_supported);
227
237
}
228
238
string adc_file = absl::StrCat (*root_path, " /legacy_credentials/" , account_id_, " /adc.json" );
229
239
VLOG (1 ) << " ADC file: " << adc_file;
230
- ec = ParseADC (adc_file, &client_id_, &client_secret_, &refresh_token_);
231
- if (ec)
232
- return ec;
240
+ RETURN_ERROR (ParseADC (adc_file, &client_id_, &client_secret_, &refresh_token_));
241
+
233
242
if (client_id_.empty () || client_secret_.empty () || refresh_token_.empty ()) {
234
243
LOG (WARNING) << " Bad ADC file " << adc_file;
235
244
return make_error_code (errc::bad_message);
@@ -264,9 +273,8 @@ error_code GCPCredsProvider::RefreshToken(fb2::ProactorBase* pb) {
264
273
VLOG (1 ) << " Req: " << req;
265
274
266
275
h2::response<h2::string_body> resp;
267
- ec = https_client.Send (req, &resp);
268
- if (ec)
269
- return ec;
276
+ RETURN_ERROR (https_client.Send (req, &resp));
277
+
270
278
if (resp.result () != h2::status::ok) {
271
279
LOG (WARNING) << " Http error: " << string (resp.reason ()) << " , Body: " , resp.body ();
272
280
return make_error_code (errc::permission_denied);
@@ -297,18 +305,57 @@ std::error_code GCS::Connect(unsigned msec) {
297
305
return client_->Connect (kDomain , " 443" , ssl_ctx_);
298
306
}
299
307
300
- auto GCS::ListBuckets () -> ListBucketResult {
308
+ error_code GCS::ListBuckets (ListBucketCb cb) {
301
309
string url = absl::StrCat (" /storage/v1/b?project=" , creds_provider_.project_id ());
302
- absl::StrAppend (&url, " &fields=items,nextPageToken" );
310
+ absl::StrAppend (&url, " &maxResults=50& fields=items,nextPageToken" );
303
311
304
312
auto http_req = PrepareRequest (h2::verb::get, url, creds_provider_.access_token ());
305
313
306
314
rj::Document doc;
307
- h2::response<h2::string_body> resp_msg;
308
- error_code ec = SendWithToken (&creds_provider_, client_.get (), &http_req, &resp_msg);
309
- if (ec)
310
- return nonstd::make_unexpected (ec);
311
- VLOG (2 ) << " ListResponse: " << resp_msg.body ();
315
+
316
+ while (true ) {
317
+ io::Result<EmptyParserPtr> parse_res =
318
+ SendWithToken (&creds_provider_, client_.get (), &http_req);
319
+ if (!parse_res)
320
+ return parse_res.error ();
321
+ EmptyParserPtr empty_parser = std::move (*parse_res);
322
+ h2::response_parser<h2::string_body> resp (std::move (*empty_parser));
323
+ RETURN_ERROR (client_->Recv (&resp));
324
+
325
+ auto msg = resp.release ();
326
+
327
+ VLOG (2 ) << " ListResponse: " << msg.body ();
328
+
329
+ doc.ParseInsitu (&msg.body ().front ());
330
+ if (doc.HasParseError ()) {
331
+ return make_error_code (errc::bad_message);
332
+ }
333
+
334
+ auto it = doc.FindMember (" items" );
335
+ if (it == doc.MemberEnd ())
336
+ break ;
337
+
338
+ const auto & val = it->value ;
339
+ if (!val.IsArray ()) {
340
+ return make_error_code (errc::bad_message);
341
+ }
342
+ auto array = val.GetArray ();
343
+
344
+ for (size_t i = 0 ; i < array.Size (); ++i) {
345
+ const auto & item = array[i];
346
+ auto it = item.FindMember (" name" );
347
+ if (it != item.MemberEnd ()) {
348
+ cb (string_view{it->value .GetString (), it->value .GetStringLength ()});
349
+ }
350
+ }
351
+
352
+ it = doc.FindMember (" nextPageToken" );
353
+ if (it == doc.MemberEnd ()) {
354
+ break ;
355
+ }
356
+ absl::string_view page_token{it->value .GetString (), it->value .GetStringLength ()};
357
+ http_req.target (absl::StrCat (url, " &pageToken=" , page_token));
358
+ }
312
359
return {};
313
360
}
314
361
0 commit comments