Skip to content

Commit 0a0c4fc

Browse files
authored
Fix storage content-type on desktop, and add test. (#911)
* Fix storage content-type on desktop, and add test. * Fix data thread access. * Set content_type in a consistent way.
1 parent 6c2f067 commit 0a0c4fc

File tree

4 files changed

+118
-39
lines changed

4 files changed

+118
-39
lines changed

release_build_files/readme.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,11 @@ workflow use only during the development of your app, not for publicly shipping
567567
code.
568568

569569
## Release Notes
570+
### Upcoming release
571+
- Changes
572+
- Storage (Desktop): Set Content-Type HTTP header when uploading with
573+
custom metadata.
574+
570575
### 8.11.0
571576
- Changes
572577
- Firestore/Database (Desktop): Upgrade LevelDb dependency to 1.23

storage/integration_test/src/integration_test.cc

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,66 @@ TEST_F(FirebaseStorageTest, TestWriteAndReadFileWithCustomMetadata) {
509509
}
510510
}
511511

512+
// 1x1 transparent PNG file
513+
static const unsigned char kEmptyPngFileBytes[] = {
514+
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
515+
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
516+
0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0x15, 0xc4, 0x89, 0x00, 0x00, 0x00,
517+
0x0d, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xfc, 0xcf, 0xc0, 0x50,
518+
0x0f, 0x00, 0x04, 0x85, 0x01, 0x80, 0x84, 0xa9, 0x8c, 0x21, 0x00, 0x00,
519+
0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82};
520+
521+
TEST_F(FirebaseStorageTest, TestWriteAndReadCustomContentType) {
522+
SignIn();
523+
524+
firebase::storage::StorageReference ref =
525+
CreateFolder().Child("TestFile-CustomContentType.png");
526+
LogDebug("Storage URL: gs://%s%s", ref.bucket().c_str(),
527+
ref.full_path().c_str());
528+
cleanup_files_.push_back(ref);
529+
std::string content_type = "image/png";
530+
// Write to a simple file.
531+
{
532+
LogDebug("Write a sample file with custom content-type from byte buffer.");
533+
firebase::storage::Metadata metadata;
534+
metadata.set_content_type(content_type.c_str());
535+
firebase::Future<firebase::storage::Metadata> future =
536+
ref.PutBytes(kEmptyPngFileBytes, sizeof(kEmptyPngFileBytes), metadata);
537+
WaitForCompletion(future, "PutBytes");
538+
const firebase::storage::Metadata* metadata_written = future.result();
539+
ASSERT_NE(metadata_written, nullptr);
540+
EXPECT_EQ(metadata_written->content_type(), content_type);
541+
}
542+
// Now read back the file.
543+
{
544+
LogDebug("Download sample file with custom content-type to memory.");
545+
const size_t kBufferSize = 1024;
546+
char buffer[kBufferSize];
547+
memset(buffer, 0, sizeof(buffer));
548+
549+
firebase::Future<size_t> future = RunWithRetry<size_t>(
550+
[&]() { return ref.GetBytes(buffer, kBufferSize); });
551+
WaitForCompletion(future, "GetBytes");
552+
ASSERT_NE(future.result(), nullptr);
553+
size_t file_size = *future.result();
554+
EXPECT_EQ(file_size, sizeof(kEmptyPngFileBytes));
555+
EXPECT_THAT(kEmptyPngFileBytes, ElementsAreArray(buffer, file_size))
556+
<< "Download failed, file contents did not match.";
557+
}
558+
// And read the custom content type
559+
{
560+
LogDebug("Read custom content-type.");
561+
firebase::Future<firebase::storage::Metadata> future =
562+
RunWithRetry<firebase::storage::Metadata>(
563+
[&]() { return ref.GetMetadata(); });
564+
WaitForCompletion(future, "GetFileMetadata");
565+
const firebase::storage::Metadata* metadata = future.result();
566+
ASSERT_NE(metadata, nullptr);
567+
568+
EXPECT_EQ(metadata->content_type(), content_type);
569+
}
570+
}
571+
512572
const char kPutFileTestFile[] = "PutFileTest.txt";
513573
const char kGetFileTestFile[] = "GetFileTest.txt";
514574
const char kFileUriScheme[] = "file://";

storage/src/desktop/storage_reference_desktop.cc

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@ Future<void> StorageReferenceInternal::DeleteLastResult() {
230230
// Handy utility function, since REST calls have similar setup and teardown.
231231
void StorageReferenceInternal::PrepareRequest(rest::Request* request,
232232
const char* url,
233-
const char* method) {
233+
const char* method,
234+
const char* content_type) {
234235
request->set_url(url);
235236
request->set_method(method);
236237

@@ -240,6 +241,10 @@ void StorageReferenceInternal::PrepareRequest(rest::Request* request,
240241
std::string auth_header = "Bearer " + token;
241242
request->add_header("Authorization", auth_header.c_str());
242243
}
244+
// if content_type was specified, add a header.
245+
if (content_type != nullptr && *content_type != '\0') {
246+
request->add_header("Content-Type", content_type);
247+
}
243248
// Unfortunately the storage backend rejects requests with the complete
244249
// user agent specified by the x-goog-api-client header so we only use
245250
// the X-Firebase-Storage-Version header to attribute the client.
@@ -404,10 +409,12 @@ Future<Metadata> StorageReferenceInternal::PutBytes(
404409

405410
Future<Metadata> StorageReferenceInternal::PutBytesInternal(
406411
const void* buffer, size_t buffer_size, Listener* listener,
407-
Controller* controller_out) {
412+
Controller* controller_out, const char* content_type) {
408413
auto* future_api = future();
409414
auto handle = future_api->SafeAlloc<Metadata>(kStorageReferenceFnPutBytes);
410-
auto send_request_funct{[&, buffer, buffer_size, listener,
415+
416+
std::string content_type_str = content_type ? content_type : "";
417+
auto send_request_funct{[&, content_type_str, buffer, buffer_size, listener,
411418
controller_out]() -> BlockingResponse* {
412419
auto* future_api = future();
413420
auto handle =
@@ -416,7 +423,8 @@ Future<Metadata> StorageReferenceInternal::PutBytesInternal(
416423
storage::internal::RequestBinary* request =
417424
new storage::internal::RequestBinary(static_cast<const char*>(buffer),
418425
buffer_size);
419-
PrepareRequest(request, storageUri_.AsHttpUrl().c_str(), rest::util::kPost);
426+
PrepareRequest(request, storageUri_.AsHttpUrl().c_str(), rest::util::kPost,
427+
content_type_str.c_str());
420428
ReturnedMetadataResponse* response =
421429
new ReturnedMetadataResponse(handle, future_api, AsStorageReference());
422430
RestCall(request, request->notifier(), response, handle.get(), listener,
@@ -442,8 +450,9 @@ Future<Metadata> StorageReferenceInternal::PutBytes(
442450
// different storage reference than the original, so the caller of this
443451
// function can't access it via PutFileLastResult.
444452
Future<Metadata> putbytes_internal =
445-
data->storage_ref.internal_->PutBytesInternal(buffer, buffer_size,
446-
listener, controller_out);
453+
data->storage_ref.internal_->PutBytesInternal(
454+
buffer, buffer_size, listener, controller_out,
455+
metadata ? metadata->content_type() : nullptr);
447456

448457
SetupMetadataChain(putbytes_internal, data);
449458

@@ -466,36 +475,38 @@ Future<Metadata> StorageReferenceInternal::PutFile(const char* path,
466475
}
467476

468477
Future<Metadata> StorageReferenceInternal::PutFileInternal(
469-
const char* path, Listener* listener, Controller* controller_out) {
478+
const char* path, Listener* listener, Controller* controller_out,
479+
const char* content_type) {
470480
auto* future_api = future();
471481
auto handle = future_api->SafeAlloc<Metadata>(kStorageReferenceFnPutFile);
472482

473483
std::string final_path = StripProtocol(path);
474-
auto send_request_funct{
475-
[&, final_path, listener, controller_out]() -> BlockingResponse* {
476-
auto* future_api = future();
477-
auto handle =
478-
future_api->SafeAlloc<Metadata>(kStorageReferenceFnPutFileInternal);
479-
480-
// Open the file, calculate the length.
481-
storage::internal::RequestFile* request(
482-
new storage::internal::RequestFile(final_path.c_str(), 0));
483-
if (!request->IsFileOpen()) {
484-
delete request;
485-
future_api->Complete(handle, kErrorUnknown, "Could not read file.");
486-
return nullptr;
487-
} else {
488-
// Everything is good. Fire off the request.
489-
ReturnedMetadataResponse* response = new ReturnedMetadataResponse(
490-
handle, future_api, AsStorageReference());
491-
492-
PrepareRequest(request, storageUri_.AsHttpUrl().c_str(),
493-
rest::util::kPost);
494-
RestCall(request, request->notifier(), response, handle.get(),
495-
listener, controller_out);
496-
return response;
497-
}
498-
}};
484+
std::string content_type_str = content_type ? content_type : "";
485+
auto send_request_funct{[&, final_path, content_type_str, listener,
486+
controller_out]() -> BlockingResponse* {
487+
auto* future_api = future();
488+
auto handle =
489+
future_api->SafeAlloc<Metadata>(kStorageReferenceFnPutFileInternal);
490+
491+
// Open the file, calculate the length.
492+
storage::internal::RequestFile* request(
493+
new storage::internal::RequestFile(final_path.c_str(), 0));
494+
if (!request->IsFileOpen()) {
495+
delete request;
496+
future_api->Complete(handle, kErrorUnknown, "Could not read file.");
497+
return nullptr;
498+
} else {
499+
// Everything is good. Fire off the request.
500+
ReturnedMetadataResponse* response = new ReturnedMetadataResponse(
501+
handle, future_api, AsStorageReference());
502+
503+
PrepareRequest(request, storageUri_.AsHttpUrl().c_str(),
504+
rest::util::kPost, content_type_str.c_str());
505+
RestCall(request, request->notifier(), response, handle.get(), listener,
506+
controller_out);
507+
return response;
508+
}
509+
}};
499510
SendRequestWithRetry(kStorageReferenceFnPutFileInternal, send_request_funct,
500511
handle, storage_->max_upload_retry_time());
501512
return PutFileLastResult();
@@ -516,8 +527,9 @@ Future<Metadata> StorageReferenceInternal::PutFile(const char* path,
516527
// different storage reference than the original, so the caller of this
517528
// function can't access it via PutFileLastResult.
518529
Future<Metadata> putfile_internal =
519-
data->storage_ref.internal_->PutFileInternal(path, listener,
520-
controller_out);
530+
data->storage_ref.internal_->PutFileInternal(
531+
path, listener, controller_out,
532+
metadata ? metadata->content_type() : nullptr);
521533

522534
SetupMetadataChain(putfile_internal, data);
523535

@@ -579,11 +591,11 @@ Future<Metadata> StorageReferenceInternal::UpdateMetadata(
579591
new ReturnedMetadataResponse(handle, future_api, AsStorageReference());
580592

581593
storage::internal::Request* request = new storage::internal::Request();
582-
PrepareRequest(request, storageUri_.AsHttpUrl().c_str(), "PATCH");
594+
PrepareRequest(request, storageUri_.AsHttpUrl().c_str(), "PATCH",
595+
"application/json");
583596

584597
std::string metadata_json = metadata->internal_->ExportAsJson();
585598
request->set_post_fields(metadata_json.c_str(), metadata_json.length());
586-
request->add_header("Content-Type", "application/json");
587599

588600
RestCall(request, request->notifier(), response, handle.get(), nullptr,
589601
nullptr);

storage/src/desktop/storage_reference_desktop.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,17 +176,19 @@ class StorageReferenceInternal {
176176
// Upload data without metadata.
177177
Future<Metadata> PutBytesInternal(const void* buffer, size_t buffer_size,
178178
Listener* listener,
179-
Controller* controller_out);
179+
Controller* controller_out,
180+
const char* content_type = nullptr);
180181
// Upload file without metadata.
181182
Future<Metadata> PutFileInternal(const char* path, Listener* listener,
182-
Controller* controller_out);
183+
Controller* controller_out,
184+
const char* content_type = nullptr);
183185

184186
void RestCall(rest::Request* request, internal::Notifier* request_notifier,
185187
BlockingResponse* response, FutureHandle handle,
186188
Listener* listener, Controller* controller_out);
187189

188190
void PrepareRequest(rest::Request* request, const char* url,
189-
const char* method);
191+
const char* method, const char* content_type = nullptr);
190192

191193
void SetupMetadataChain(Future<Metadata> starting_future,
192194
MetadataChainData* data);

0 commit comments

Comments
 (0)