1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "google/cloud/storage/internal/object_requests.h"
16 #include "google/cloud/storage/internal/binary_data_as_debug_string.h"
17 #include "google/cloud/storage/internal/metadata_parser.h"
18 #include "google/cloud/storage/internal/object_acl_requests.h"
19 #include "google/cloud/storage/object_metadata.h"
20 #include "google/cloud/internal/format_time_point.h"
21 #include <nlohmann/json.hpp>
22 #include <cinttypes>
23 #include <sstream>
24
25 namespace google {
26 namespace cloud {
27 namespace storage {
28 inline namespace STORAGE_CLIENT_NS {
29 namespace internal {
30 namespace {
31 /**
32 * Sets a string field in @p json when @p value is not empty.
33 *
34 * This simplifies the implementation of ToJsonString() because we repeat this
35 * check for many attributes.
36 */
SetIfNotEmpty(nlohmann::json & json,char const * key,std::string const & value)37 void SetIfNotEmpty(nlohmann::json& json, char const* key,
38 std::string const& value) {
39 if (value.empty()) {
40 return;
41 }
42 json[key] = value;
43 }
44 } // namespace
45
FromJson(nlohmann::json const & json)46 StatusOr<ObjectMetadata> ObjectMetadataParser::FromJson(
47 nlohmann::json const& json) {
48 if (!json.is_object()) {
49 return Status(StatusCode::kInvalidArgument, __func__);
50 }
51 ObjectMetadata result{};
52 auto status = CommonMetadata<ObjectMetadata>::ParseFromJson(result, json);
53 if (!status.ok()) {
54 return status;
55 }
56
57 if (json.count("acl") != 0) {
58 for (auto const& kv : json["acl"].items()) {
59 auto parsed = ObjectAccessControlParser::FromJson(kv.value());
60 if (!parsed.ok()) {
61 return std::move(parsed).status();
62 }
63 result.acl_.emplace_back(std::move(*parsed));
64 }
65 }
66
67 result.bucket_ = json.value("bucket", "");
68 result.cache_control_ = json.value("cacheControl", "");
69 result.component_count_ = internal::ParseIntField(json, "componentCount");
70 result.content_disposition_ = json.value("contentDisposition", "");
71 result.content_encoding_ = json.value("contentEncoding", "");
72 result.content_language_ = json.value("contentLanguage", "");
73 result.content_type_ = json.value("contentType", "");
74 result.crc32c_ = json.value("crc32c", "");
75 if (json.count("customerEncryption") != 0) {
76 auto field = json["customerEncryption"];
77 CustomerEncryption e;
78 e.encryption_algorithm = field.value("encryptionAlgorithm", "");
79 e.key_sha256 = field.value("keySha256", "");
80 result.customer_encryption_ = std::move(e);
81 }
82 result.event_based_hold_ = internal::ParseBoolField(json, "eventBasedHold");
83 result.generation_ = internal::ParseLongField(json, "generation");
84 result.kms_key_name_ = json.value("kmsKeyName", "");
85 result.md5_hash_ = json.value("md5Hash", "");
86 result.media_link_ = json.value("mediaLink", "");
87 if (json.count("metadata") > 0) {
88 for (auto const& kv : json["metadata"].items()) {
89 result.metadata_.emplace(kv.key(), kv.value().get<std::string>());
90 }
91 }
92 result.retention_expiration_time_ =
93 internal::ParseTimestampField(json, "retentionExpirationTime");
94 result.size_ = internal::ParseUnsignedLongField(json, "size");
95 result.temporary_hold_ = internal::ParseBoolField(json, "temporaryHold");
96 result.time_deleted_ = internal::ParseTimestampField(json, "timeDeleted");
97 result.time_storage_class_updated_ =
98 internal::ParseTimestampField(json, "timeStorageClassUpdated");
99 if (json.count("customTime") == 0) {
100 result.custom_time_.reset();
101 } else {
102 result.custom_time_ = internal::ParseTimestampField(json, "customTime");
103 }
104 return result;
105 }
106
FromString(std::string const & payload)107 StatusOr<ObjectMetadata> ObjectMetadataParser::FromString(
108 std::string const& payload) {
109 auto json = nlohmann::json::parse(payload, nullptr, false);
110 return FromJson(json);
111 }
112
ObjectMetadataJsonForCompose(ObjectMetadata const & meta)113 nlohmann::json ObjectMetadataJsonForCompose(ObjectMetadata const& meta) {
114 nlohmann::json metadata_as_json({});
115 if (!meta.acl().empty()) {
116 for (ObjectAccessControl const& a : meta.acl()) {
117 nlohmann::json entry;
118 SetIfNotEmpty(entry, "entity", a.entity());
119 SetIfNotEmpty(entry, "role", a.role());
120 metadata_as_json["acl"].emplace_back(std::move(entry));
121 }
122 }
123
124 SetIfNotEmpty(metadata_as_json, "cacheControl", meta.cache_control());
125 SetIfNotEmpty(metadata_as_json, "contentDisposition",
126 meta.content_disposition());
127 SetIfNotEmpty(metadata_as_json, "contentEncoding", meta.content_encoding());
128 SetIfNotEmpty(metadata_as_json, "contentLanguage", meta.content_language());
129 SetIfNotEmpty(metadata_as_json, "contentType", meta.content_type());
130
131 if (meta.event_based_hold()) {
132 metadata_as_json["eventBasedHold"] = true;
133 }
134
135 SetIfNotEmpty(metadata_as_json, "name", meta.name());
136 SetIfNotEmpty(metadata_as_json, "storageClass", meta.storage_class());
137
138 if (!meta.metadata().empty()) {
139 nlohmann::json meta_as_json;
140 for (auto const& kv : meta.metadata()) {
141 meta_as_json[kv.first] = kv.second;
142 }
143 metadata_as_json["metadata"] = std::move(meta_as_json);
144 }
145
146 return metadata_as_json;
147 }
148
ObjectMetadataJsonForCopy(ObjectMetadata const & meta)149 nlohmann::json ObjectMetadataJsonForCopy(ObjectMetadata const& meta) {
150 return ObjectMetadataJsonForCompose(meta);
151 }
152
ObjectMetadataJsonForInsert(ObjectMetadata const & meta)153 nlohmann::json ObjectMetadataJsonForInsert(ObjectMetadata const& meta) {
154 auto json = ObjectMetadataJsonForCompose(meta);
155 SetIfNotEmpty(json, "crc32c", meta.crc32c());
156 SetIfNotEmpty(json, "md5Hash", meta.md5_hash());
157 return json;
158 }
159
ObjectMetadataJsonForRewrite(ObjectMetadata const & meta)160 nlohmann::json ObjectMetadataJsonForRewrite(ObjectMetadata const& meta) {
161 return ObjectMetadataJsonForCompose(meta);
162 }
163
ObjectMetadataJsonForUpdate(ObjectMetadata const & meta)164 nlohmann::json ObjectMetadataJsonForUpdate(ObjectMetadata const& meta) {
165 nlohmann::json metadata_as_json({});
166 if (!meta.acl().empty()) {
167 for (ObjectAccessControl const& a : meta.acl()) {
168 nlohmann::json entry;
169 SetIfNotEmpty(entry, "entity", a.entity());
170 SetIfNotEmpty(entry, "role", a.role());
171 metadata_as_json["acl"].emplace_back(std::move(entry));
172 }
173 }
174
175 SetIfNotEmpty(metadata_as_json, "cacheControl", meta.cache_control());
176 SetIfNotEmpty(metadata_as_json, "contentDisposition",
177 meta.content_disposition());
178 SetIfNotEmpty(metadata_as_json, "contentEncoding", meta.content_encoding());
179 SetIfNotEmpty(metadata_as_json, "contentLanguage", meta.content_language());
180 SetIfNotEmpty(metadata_as_json, "contentType", meta.content_type());
181
182 metadata_as_json["eventBasedHold"] = meta.event_based_hold();
183
184 if (!meta.metadata().empty()) {
185 nlohmann::json meta_as_json;
186 for (auto const& kv : meta.metadata()) {
187 meta_as_json[kv.first] = kv.second;
188 }
189 metadata_as_json["metadata"] = std::move(meta_as_json);
190 }
191
192 if (meta.has_custom_time()) {
193 metadata_as_json["customTime"] =
194 google::cloud::internal::FormatRfc3339(meta.custom_time());
195 }
196
197 return metadata_as_json;
198 }
199
operator <<(std::ostream & os,ListObjectsRequest const & r)200 std::ostream& operator<<(std::ostream& os, ListObjectsRequest const& r) {
201 os << "ListObjectsRequest={bucket_name=" << r.bucket_name();
202 r.DumpOptions(os, ", ");
203 return os << "}";
204 }
205
FromHttpResponse(std::string const & payload)206 StatusOr<ListObjectsResponse> ListObjectsResponse::FromHttpResponse(
207 std::string const& payload) {
208 auto json = nlohmann::json::parse(payload, nullptr, false);
209 if (!json.is_object()) {
210 return Status(StatusCode::kInvalidArgument, __func__);
211 }
212
213 ListObjectsResponse result;
214 result.next_page_token = json.value("nextPageToken", "");
215
216 for (auto const& kv : json["items"].items()) {
217 auto parsed = internal::ObjectMetadataParser::FromJson(kv.value());
218 if (!parsed.ok()) {
219 return std::move(parsed).status();
220 }
221 result.items.emplace_back(std::move(*parsed));
222 }
223
224 for (auto const& prefix_iterator : json["prefixes"].items()) {
225 auto const& prefix = prefix_iterator.value();
226 if (!prefix.is_string()) {
227 return Status(StatusCode::kInternal,
228 "List Objects Response's 'prefix' is not a string.");
229 }
230 result.prefixes.emplace_back(prefix.get<std::string>());
231 }
232
233 return result;
234 }
235
operator <<(std::ostream & os,ListObjectsResponse const & r)236 std::ostream& operator<<(std::ostream& os, ListObjectsResponse const& r) {
237 os << "ListObjectsResponse={next_page_token=" << r.next_page_token
238 << ", items={";
239 std::copy(r.items.begin(), r.items.end(),
240 std::ostream_iterator<ObjectMetadata>(os, "\n "));
241 os << "}, prefixes={";
242 std::copy(r.prefixes.begin(), r.prefixes.end(),
243 std::ostream_iterator<std::string>(os, "\n "));
244 return os << "}}";
245 }
246
operator <<(std::ostream & os,GetObjectMetadataRequest const & r)247 std::ostream& operator<<(std::ostream& os, GetObjectMetadataRequest const& r) {
248 os << "GetObjectMetadataRequest={bucket_name=" << r.bucket_name()
249 << ", object_name=" << r.object_name();
250 r.DumpOptions(os, ", ");
251 return os << "}";
252 }
253
operator <<(std::ostream & os,InsertObjectMediaRequest const & r)254 std::ostream& operator<<(std::ostream& os, InsertObjectMediaRequest const& r) {
255 os << "InsertObjectMediaRequest={bucket_name=" << r.bucket_name()
256 << ", object_name=" << r.object_name();
257 r.DumpOptions(os, ", ");
258 std::size_t constexpr kMaxDumpSize = 1024;
259 if (r.contents().size() > kMaxDumpSize) {
260 os << ", contents[0..1024]=\n"
261 << BinaryDataAsDebugString(r.contents().data(), kMaxDumpSize);
262 } else {
263 os << ", contents=\n"
264 << BinaryDataAsDebugString(r.contents().data(), r.contents().size());
265 }
266 return os << "}";
267 }
268
operator <<(std::ostream & os,CopyObjectRequest const & r)269 std::ostream& operator<<(std::ostream& os, CopyObjectRequest const& r) {
270 os << "CopyObjectRequest={destination_bucket=" << r.destination_bucket()
271 << ", destination_object=" << r.destination_object()
272 << ", source_bucket=" << r.source_bucket()
273 << ", source_object=" << r.source_object();
274 r.DumpOptions(os, ", ");
275 return os << "}";
276 }
277
RequiresNoCache() const278 bool ReadObjectRangeRequest::RequiresNoCache() const {
279 if (HasOption<ReadRange>()) {
280 return true;
281 }
282 if (HasOption<ReadFromOffset>() && GetOption<ReadFromOffset>().value() != 0) {
283 return true;
284 }
285 return HasOption<ReadLast>();
286 }
287
RequiresRangeHeader() const288 bool ReadObjectRangeRequest::RequiresRangeHeader() const {
289 return RequiresNoCache();
290 }
291
RangeHeader() const292 std::string ReadObjectRangeRequest::RangeHeader() const {
293 if (HasOption<ReadRange>() && HasOption<ReadFromOffset>()) {
294 auto range = GetOption<ReadRange>().value();
295 auto offset = GetOption<ReadFromOffset>().value();
296 auto begin = (std::max)(range.begin, offset);
297 return "Range: bytes=" + std::to_string(begin) + "-" +
298 std::to_string(range.end - 1);
299 }
300 if (HasOption<ReadRange>()) {
301 auto range = GetOption<ReadRange>().value();
302 return "Range: bytes=" + std::to_string(range.begin) + "-" +
303 std::to_string(range.end - 1);
304 }
305 if (HasOption<ReadFromOffset>()) {
306 auto offset = GetOption<ReadFromOffset>().value();
307 if (offset != 0) {
308 return "Range: bytes=" + std::to_string(offset) + "-";
309 }
310 }
311 if (HasOption<ReadLast>()) {
312 auto last = GetOption<ReadLast>().value();
313 return "Range: bytes=-" + std::to_string(last);
314 }
315 return "";
316 }
317
StartingByte() const318 std::int64_t ReadObjectRangeRequest::StartingByte() const {
319 std::int64_t result = 0;
320 if (HasOption<ReadRange>()) {
321 result = (std::max)(result, GetOption<ReadRange>().value().begin);
322 }
323 if (HasOption<ReadFromOffset>()) {
324 result = (std::max)(result, GetOption<ReadFromOffset>().value());
325 }
326 if (HasOption<ReadLast>()) {
327 // The value of `StartingByte()` is unknown if `ReadLast` is set
328 result = -1;
329 }
330 return result;
331 }
332
operator <<(std::ostream & os,ReadObjectRangeRequest const & r)333 std::ostream& operator<<(std::ostream& os, ReadObjectRangeRequest const& r) {
334 os << "ReadObjectRangeRequest={bucket_name=" << r.bucket_name()
335 << ", object_name=" << r.object_name();
336 r.DumpOptions(os, ", ");
337 return os << "}";
338 }
339
340 #include "google/cloud/internal/disable_msvc_crt_secure_warnings.inc"
FromHttpResponse(HttpResponse && response)341 ReadObjectRangeResponse ReadObjectRangeResponse::FromHttpResponse(
342 HttpResponse&& response) {
343 auto loc = response.headers.find(std::string("content-range"));
344 if (response.headers.end() == loc) {
345 google::cloud::internal::ThrowInvalidArgument(
346 "invalid http response for ReadObjectRange");
347 }
348
349 std::string const& content_range_value = loc->second;
350 auto function = __func__; // capture this function name, not the lambda's
351 auto raise_error = [&content_range_value, &function]() {
352 std::ostringstream os;
353 os << static_cast<char const*>(function)
354 << " invalid format for content-range header <" << content_range_value
355 << ">";
356 google::cloud::internal::ThrowInvalidArgument(os.str());
357 };
358 char const unit_descriptor[] = "bytes";
359 if (content_range_value.rfind(unit_descriptor, 0) != 0) {
360 raise_error();
361 }
362 char const* buffer = content_range_value.data();
363 auto size = content_range_value.size();
364 // skip the initial "bytes " string.
365 buffer += sizeof(unit_descriptor);
366
367 if (size < 2) {
368 raise_error();
369 }
370
371 if (buffer[0] == '*' && buffer[1] == '/') {
372 // The header is just the indication of size ('bytes */<size>'), parse that.
373 buffer += 2;
374 std::int64_t object_size;
375 auto count = std::sscanf(buffer, "%" PRId64, &object_size);
376 if (count != 1) {
377 raise_error();
378 }
379 return ReadObjectRangeResponse{std::move(response.payload), 0, 0,
380 object_size};
381 }
382
383 std::int64_t first_byte;
384 std::int64_t last_byte;
385 std::int64_t object_size;
386 auto count = std::sscanf(buffer, "%" PRId64 "-%" PRId64 "/%" PRId64,
387 &first_byte, &last_byte, &object_size);
388 if (count != 3) {
389 raise_error();
390 }
391
392 return ReadObjectRangeResponse{std::move(response.payload), first_byte,
393 last_byte, object_size};
394 }
395 #include "google/cloud/internal/diagnostics_pop.inc"
396
operator <<(std::ostream & os,ReadObjectRangeResponse const & r)397 std::ostream& operator<<(std::ostream& os, ReadObjectRangeResponse const& r) {
398 return os << "ReadObjectRangeResponse={range=" << r.first_byte << "-"
399 << r.last_byte << "/" << r.object_size << ", contents=\n"
400 << BinaryDataAsDebugString(r.contents.data(), r.contents.size())
401 << "}";
402 }
403
operator <<(std::ostream & os,DeleteObjectRequest const & r)404 std::ostream& operator<<(std::ostream& os, DeleteObjectRequest const& r) {
405 os << "DeleteObjectRequest={bucket_name=" << r.bucket_name()
406 << ", object_name=" << r.object_name();
407 r.DumpOptions(os, ", ");
408 return os << "}";
409 }
410
operator <<(std::ostream & os,UpdateObjectRequest const & r)411 std::ostream& operator<<(std::ostream& os, UpdateObjectRequest const& r) {
412 os << "UpdateObjectRequest={bucket_name=" << r.bucket_name()
413 << ", object_name=" << r.object_name() << ", metadata=" << r.metadata();
414 r.DumpOptions(os, ", ");
415 return os << "}";
416 }
417
ComposeObjectRequest(std::string bucket_name,std::vector<ComposeSourceObject> source_objects,std::string destination_object_name)418 ComposeObjectRequest::ComposeObjectRequest(
419 std::string bucket_name, std::vector<ComposeSourceObject> source_objects,
420 std::string destination_object_name)
421 : GenericObjectRequest(std::move(bucket_name),
422 std::move(destination_object_name)),
423 source_objects_(std::move(source_objects)) {}
424
JsonPayload() const425 std::string ComposeObjectRequest::JsonPayload() const {
426 nlohmann::json compose_object_payload_json;
427 compose_object_payload_json["kind"] = "storage#composeRequest";
428 nlohmann::json destination_metadata_payload;
429 if (HasOption<WithObjectMetadata>()) {
430 destination_metadata_payload =
431 ObjectMetadataJsonForCompose(GetOption<WithObjectMetadata>().value());
432 }
433 if (!destination_metadata_payload.is_null()) {
434 compose_object_payload_json["destination"] = destination_metadata_payload;
435 }
436 nlohmann::json source_object_list;
437 for (auto const& source_object : source_objects_) {
438 nlohmann::json source_object_json;
439 source_object_json["name"] = source_object.object_name;
440 if (source_object.generation.has_value()) {
441 source_object_json["generation"] = source_object.generation.value();
442 }
443 if (source_object.if_generation_match.has_value()) {
444 source_object_json["ifGenerationMatch"] =
445 source_object.if_generation_match.value();
446 }
447 source_object_list.emplace_back(std::move(source_object_json));
448 }
449 compose_object_payload_json["sourceObjects"] = source_object_list;
450
451 return compose_object_payload_json.dump();
452 }
453
operator <<(std::ostream & os,ComposeObjectRequest const & r)454 std::ostream& operator<<(std::ostream& os, ComposeObjectRequest const& r) {
455 os << "ComposeObjectRequest={bucket_name=" << r.bucket_name()
456 << ", destination_object_name=" << r.object_name();
457 r.DumpOptions(os, ", ");
458 return os << ", payload=" << r.JsonPayload() << "}";
459 }
460
PatchObjectRequest(std::string bucket_name,std::string object_name,ObjectMetadata const & original,ObjectMetadata const & updated)461 PatchObjectRequest::PatchObjectRequest(std::string bucket_name,
462 std::string object_name,
463 ObjectMetadata const& original,
464 ObjectMetadata const& updated)
465 : GenericObjectRequest(std::move(bucket_name), std::move(object_name)) {
466 // Compare each writeable field to build the patch.
467 ObjectMetadataPatchBuilder builder;
468
469 if (original.acl() != updated.acl()) {
470 builder.SetAcl(updated.acl());
471 }
472 if (original.cache_control() != updated.cache_control()) {
473 builder.SetCacheControl(updated.cache_control());
474 }
475 if (original.content_disposition() != updated.content_disposition()) {
476 builder.SetContentDisposition(updated.content_disposition());
477 }
478 if (original.content_encoding() != updated.content_encoding()) {
479 builder.SetContentEncoding(updated.content_encoding());
480 }
481 if (original.content_language() != updated.content_language()) {
482 builder.SetContentLanguage(updated.content_language());
483 }
484 if (original.content_type() != updated.content_type()) {
485 builder.SetContentType(updated.content_type());
486 }
487 if (original.event_based_hold() != updated.event_based_hold()) {
488 builder.SetEventBasedHold(updated.event_based_hold());
489 }
490
491 if (original.metadata() != updated.metadata()) {
492 if (updated.metadata().empty()) {
493 builder.ResetMetadata();
494 } else {
495 std::map<std::string, std::string> difference;
496 // Find the keys in the original map that are not in the new map. Using
497 // `std::set_difference()` works because, unlike `std::unordered_map` the
498 // `std::map` iterators return elements ordered by key:
499 std::set_difference(original.metadata().begin(),
500 original.metadata().end(), updated.metadata().begin(),
501 updated.metadata().end(),
502 std::inserter(difference, difference.end()),
503 // We want to compare just keys and ignore values, the
504 // map class provides such a function, so use it:
505 original.metadata().value_comp());
506 for (auto&& d : difference) {
507 builder.ResetMetadata(d.first);
508 }
509
510 // Find the elements (comparing key and value) in the updated map that
511 // are not in the original map:
512 difference.clear();
513 std::set_difference(updated.metadata().begin(), updated.metadata().end(),
514 original.metadata().begin(),
515 original.metadata().end(),
516 std::inserter(difference, difference.end()));
517 for (auto&& d : difference) {
518 builder.SetMetadata(d.first, d.second);
519 }
520 }
521 }
522
523 if (original.temporary_hold() != updated.temporary_hold()) {
524 builder.SetTemporaryHold(updated.temporary_hold());
525 }
526
527 payload_ = builder.BuildPatch();
528 }
529
PatchObjectRequest(std::string bucket_name,std::string object_name,ObjectMetadataPatchBuilder const & patch)530 PatchObjectRequest::PatchObjectRequest(std::string bucket_name,
531 std::string object_name,
532 ObjectMetadataPatchBuilder const& patch)
533 : GenericObjectRequest(std::move(bucket_name), std::move(object_name)),
534 payload_(patch.BuildPatch()) {}
535
operator <<(std::ostream & os,PatchObjectRequest const & r)536 std::ostream& operator<<(std::ostream& os, PatchObjectRequest const& r) {
537 os << "PatchObjectRequest={bucket_name=" << r.bucket_name()
538 << ", object_name=" << r.object_name();
539 r.DumpOptions(os, ", ");
540 return os << ", payload=" << r.payload() << "}";
541 }
542
operator <<(std::ostream & os,RewriteObjectRequest const & r)543 std::ostream& operator<<(std::ostream& os, RewriteObjectRequest const& r) {
544 os << "RewriteObjectRequest={destination_bucket=" << r.destination_bucket()
545 << ", destination_object=" << r.destination_object()
546 << ", source_bucket=" << r.source_bucket()
547 << ", source_object=" << r.source_object()
548 << ", rewrite_token=" << r.rewrite_token();
549 r.DumpOptions(os, ", ");
550 return os << "}";
551 }
552
FromHttpResponse(std::string const & payload)553 StatusOr<RewriteObjectResponse> RewriteObjectResponse::FromHttpResponse(
554 std::string const& payload) {
555 auto object = nlohmann::json::parse(payload, nullptr, false);
556 if (!object.is_object()) {
557 return Status(StatusCode::kInvalidArgument, __func__);
558 }
559
560 RewriteObjectResponse result;
561 result.total_bytes_rewritten =
562 ParseUnsignedLongField(object, "totalBytesRewritten");
563 result.object_size = ParseUnsignedLongField(object, "objectSize");
564 result.done = object.value("done", false);
565 result.rewrite_token = object.value("rewriteToken", "");
566 if (object.count("resource") != 0) {
567 auto parsed = internal::ObjectMetadataParser::FromJson(object["resource"]);
568 if (!parsed.ok()) {
569 return std::move(parsed).status();
570 }
571 result.resource = std::move(*parsed);
572 }
573 return result;
574 }
575
operator <<(std::ostream & os,RewriteObjectResponse const & r)576 std::ostream& operator<<(std::ostream& os, RewriteObjectResponse const& r) {
577 return os << "RewriteObjectResponse={total_bytes_rewritten="
578 << r.total_bytes_rewritten << ", object_size=" << r.object_size
579 << ", done=" << std::boolalpha << r.done
580 << ", rewrite_token=" << r.rewrite_token
581 << ", resource=" << r.resource << "}";
582 }
583
operator <<(std::ostream & os,ResumableUploadRequest const & r)584 std::ostream& operator<<(std::ostream& os, ResumableUploadRequest const& r) {
585 os << "ResumableUploadRequest={bucket_name=" << r.bucket_name()
586 << ", object_name=" << r.object_name();
587 r.DumpOptions(os, ", ");
588 return os << "}";
589 }
590
operator <<(std::ostream & os,DeleteResumableUploadRequest const & r)591 std::ostream& operator<<(std::ostream& os,
592 DeleteResumableUploadRequest const& r) {
593 os << "DeleteResumableUploadRequest={upload_session_url="
594 << r.upload_session_url();
595 r.DumpOptions(os, ", ");
596 return os << "}";
597 }
598
RangeHeader() const599 std::string UploadChunkRequest::RangeHeader() const {
600 std::ostringstream os;
601 os << "Content-Range: bytes ";
602 auto const size = payload_size();
603 if (size == 0) {
604 // This typically happens when the sender realizes too late that the
605 // previous chunk was really the last chunk (e.g. the file is exactly a
606 // multiple of the quantum, reading the last chunk from a file, or sending
607 // it as part of a stream that does not detect the EOF), the formatting of
608 // the range is special in this case.
609 os << "*";
610 } else {
611 os << range_begin() << "-" << range_begin() + size - 1;
612 }
613 if (!last_chunk_) {
614 os << "/*";
615 } else {
616 os << "/" << source_size();
617 }
618 return std::move(os).str();
619 }
620
operator <<(std::ostream & os,UploadChunkRequest const & r)621 std::ostream& operator<<(std::ostream& os, UploadChunkRequest const& r) {
622 os << "UploadChunkRequest={upload_session_url=" << r.upload_session_url()
623 << ", range=<" << r.RangeHeader() << ">";
624 r.DumpOptions(os, ", ");
625 os << ", payload={";
626 auto constexpr kMaxOutputBytes = 128;
627 char const* sep = "";
628 for (auto const& b : r.payload()) {
629 os << sep << "{"
630 << BinaryDataAsDebugString(b.data(), b.size(), kMaxOutputBytes) << "}";
631 sep = ", ";
632 }
633 return os << "}}";
634 }
635
operator <<(std::ostream & os,QueryResumableUploadRequest const & r)636 std::ostream& operator<<(std::ostream& os,
637 QueryResumableUploadRequest const& r) {
638 os << "QueryResumableUploadRequest={upload_session_url="
639 << r.upload_session_url();
640 r.DumpOptions(os, ", ");
641 return os << "}";
642 }
643
644 } // namespace internal
645 } // namespace STORAGE_CLIENT_NS
646 } // namespace storage
647 } // namespace cloud
648 } // namespace google
649