Skip to content

Commit

Permalink
Fix FileReader Base64 padding (#12522)
Browse files Browse the repository at this point in the history
* Add test BaseFileReaderResourceUnitTest::Base64EncodesCorrectly

* Add BaseFileReaderResourceUnitTest.cpp

* Use "string" reader type

* Add '=' padding as needed

* Fix typo

* Change files

* Update vnext/Shared/Shared.vcxitems.filters

Co-authored-by: Jon Thysell <thysell@gmail.com>

---------

Co-authored-by: Jon Thysell <thysell@gmail.com>
  • Loading branch information
JunielKatarn and jonthysell committed Dec 8, 2023
1 parent de27802 commit 34ae417
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Fix FileReader Base64 padding",
"packageName": "react-native-windows",
"email": "julio.rocha@microsoft.com",
"dependentChangeType": "patch"
}
81 changes: 81 additions & 0 deletions vnext/Desktop.UnitTests/BaseFileReaderResourceUnitTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#include <CppUnitTest.h>

#include <BaseFileReaderResource.h>

// Windows Libraries
#include <winrt/Windows.Security.Cryptography.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

using std::shared_ptr;
using std::string;
using std::vector;
using winrt::array_view;

namespace Microsoft::React::Test {

TEST_CLASS (BaseFileReaderResourceUnitTest) {
TEST_METHOD(Base64EncodesCorrectly) {
class DummyBlobPersistor final : public IBlobPersistor {
std::unordered_map<string, vector<uint8_t>> m_blobs;

public:
#pragma region IBlobPersistor

array_view<uint8_t const> ResolveMessage(string &&blobId, int64_t offset, int64_t size) override {
auto dataItr = m_blobs.find(std::move(blobId));
// Not found.
if (dataItr == m_blobs.cend())
throw std::invalid_argument("Blob object not found");

auto &bytes = (*dataItr).second;
auto endBound = static_cast<size_t>(offset + size);
// Out of bounds.
if (endBound > bytes.size() || offset >= static_cast<int64_t>(bytes.size()) || offset < 0)
throw std::out_of_range("Offset or size out of range");

return array_view<uint8_t const>(bytes.data() + offset, bytes.data() + endBound);
}

void RemoveMessage(string && /*blobId*/) noexcept override {
// Not implemented
}

void StoreMessage(vector<uint8_t> &&message, string &&blobId) noexcept override {
m_blobs.insert_or_assign(std::move(blobId), std::move(message));
}

string StoreMessage(vector<uint8_t> && /*message*/) noexcept override {
return "Not implemented";
}

#pragma endregion IBlobPersistor
};

string messageStr = "abcde";
// Computed using [System.Convert]::ToBase64String('abcd'.ToCharArray())
constexpr char expected[] = "data:string;base64,YWJjZGU=";
constexpr char guid[] = "93252b5d-a419-4d98-a928-c3ef386f2445";

vector<uint8_t> message{};
message.reserve(messageStr.size());
message.insert(message.end(), messageStr.begin(), messageStr.end());

shared_ptr<IBlobPersistor> persistor = std::make_shared<DummyBlobPersistor>();
shared_ptr<IFileReaderResource> reader = std::make_shared<BaseFileReaderResource>(persistor);
string result;
auto resolver = [&result](string &&value) { result = std::move(value); };
auto rejecter = [&result](string &&) { result = "ERROR"; };

persistor->StoreMessage(std::move(message), string{guid});

reader->ReadAsDataUrl(guid, 0 /*offset*/, messageStr.size(), "string", std::move(resolver), std::move(rejecter));

Assert::AreEqual(expected, result.c_str());
}
};

} // namespace Microsoft::React::Test
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
<Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\IJSValueWriter.idl" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="BaseFileReaderResourceUnitTest.cpp" />
<ClCompile Include="BytecodeUnitTests.cpp" />
<ClCompile Include="EmptyUIManagerModule.cpp" />
<ClCompile Include="LayoutAnimationTests.cpp" />
Expand Down Expand Up @@ -128,4 +129,4 @@
<Target Name="Test">
<Exec Command="$(OutDir)$(TargetFileName)" IgnoreStandardErrorWarningFormat="true" />
</Target>
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
<ClCompile Include="RedirectHttpFilterUnitTest.cpp">
<Filter>Unit Tests</Filter>
</ClCompile>
<ClCompile Include="BaseFileReaderResourceUnitTest.cpp">
<Filter>Unit Tests</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="Source Files">
Expand Down Expand Up @@ -84,4 +87,8 @@
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\IJSValueReader.idl" />
<Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\IJSValueWriter.idl" />
</ItemGroup>
</Project>
6 changes: 6 additions & 0 deletions vnext/Shared/BaseFileReaderResource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ void BaseFileReaderResource::ReadAsDataUrl(
std::copy(encode_base64(bytes.cbegin()), encode_base64(bytes.cend()), ostream_iterator<char>(oss));
result += oss.str();

// https://unix.stackexchange.com/questions/631501
auto padLength = 4 - (oss.tellp() % 4);
for (auto i = 0; i < padLength; ++i) {
result += '=';
}

resolver(std::move(result));
}

Expand Down
1 change: 1 addition & 0 deletions vnext/Shared/Shared.vcxitems.filters
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@
<ClCompile Include="$(MSBuildThisFileDirectory)..\Microsoft.ReactNative\Fabric\platform\react\renderer\graphics\PlatformColorUtils.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)..\Microsoft.ReactNative\Fabric\AbiViewShadowNode.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)..\Microsoft.ReactNative\Fabric\AbiState.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)..\Microsoft.ReactNative\Utils\ThemeUtils.cpp" />
</ItemGroup>
<ItemGroup>
<Filter Include="Source Files">
Expand Down

0 comments on commit 34ae417

Please sign in to comment.