Skip to content

Commit

Permalink
Reduce some exceptions being thrown in some normal use cases (#11240)
Browse files Browse the repository at this point in the history
* Reduce some exceptions being thrown in some normal use cases

* Change files

* Diagnosing PR issues

* build fix
  • Loading branch information
acoates-ms committed Feb 24, 2023
1 parent a0c6f2f commit 3bd8471
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Reduce some exceptions being thrown in some normal use cases",
"packageName": "react-native-windows",
"email": "30809111+acoates-ms@users.noreply.github.com",
"dependentChangeType": "patch"
}
75 changes: 69 additions & 6 deletions vnext/Microsoft.ReactNative/Utils/ImageUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "ImageUtils.h"

#include <Shared/cdebug.h>
#include <Utils/CppWinrtLessExceptions.h>
#include <windows.Web.Http.h>
#include <winrt/Windows.Security.Cryptography.h>
#include <winrt/Windows.Web.Http.Headers.h>
#include <winrt/Windows.Web.Http.h>
Expand All @@ -24,7 +26,10 @@ winrt::IAsyncOperation<winrt::IRandomAccessStream> GetImageStreamAsync(ReactImag
auto httpMethod{
source.method.empty() ? winrt::HttpMethod::Get() : winrt::HttpMethod{winrt::to_hstring(source.method)}};

winrt::Uri uri{winrt::to_hstring(source.uri)};
winrt::Uri uri = UriTryCreate(winrt::to_hstring(source.uri));
if (!uri) {
co_return nullptr;
}
winrt::HttpRequestMessage request{httpMethod, uri};

if (!source.headers.empty()) {
Expand All @@ -39,14 +44,56 @@ winrt::IAsyncOperation<winrt::IRandomAccessStream> GetImageStreamAsync(ReactImag
}

winrt::HttpClient httpClient;
winrt::HttpResponseMessage response{co_await httpClient.SendRequestAsync(request)};
auto httpClientAbi = reinterpret_cast<ABI::Windows::Web::Http::IHttpClient *>(winrt::get_abi(httpClient));

winrt::Windows::Foundation::IAsyncOperationWithProgress<
winrt::Windows::Web::Http::HttpResponseMessage,
winrt::Windows::Web::Http::HttpProgress>
asyncRequest{nullptr};

if (FAILED(httpClientAbi->SendRequestAsync(
reinterpret_cast<ABI::Windows::Web::Http::IHttpRequestMessage *>(winrt::get_abi(request)),
reinterpret_cast<
ABI::Windows::Foundation::
__FIAsyncOperationWithProgress_2_Windows__CWeb__CHttp__CHttpResponseMessage_Windows__CWeb__CHttp__CHttpProgress_t
**>(winrt::put_abi(asyncRequest))))) {
co_return nullptr;
}

if (!asyncRequest) {
co_return nullptr;
}

co_await lessthrow_await_adapter<winrt::Windows::Foundation::IAsyncOperationWithProgress<
winrt::Windows::Web::Http::HttpResponseMessage,
winrt::Windows::Web::Http::HttpProgress>>{asyncRequest};

if (FAILED(asyncRequest.ErrorCode())) {
co_return nullptr;
}

winrt::HttpResponseMessage response{asyncRequest.GetResults()};

if (response && response.StatusCode() == winrt::HttpStatusCode::Ok) {
winrt::IInputStream inputStream{co_await response.Content().ReadAsInputStreamAsync()};
auto asyncRead = response.Content().ReadAsInputStreamAsync();
co_await lessthrow_await_adapter<winrt::Windows::Foundation::IAsyncOperationWithProgress<
winrt::Windows::Storage::Streams::IInputStream,
uint64_t>>{asyncRead};
if (FAILED(asyncRead.ErrorCode())) {
co_return nullptr;
}

winrt::IInputStream inputStream{asyncRead.GetResults()};
winrt::InMemoryRandomAccessStream memoryStream;
co_await winrt::RandomAccessStream::CopyAsync(inputStream, memoryStream);
memoryStream.Seek(0);

auto asyncCopy = winrt::RandomAccessStream::CopyAsync(inputStream, memoryStream);
co_await lessthrow_await_adapter<winrt::Windows::Foundation::IAsyncOperationWithProgress<uint64_t, uint64_t>>{
asyncCopy};
if (FAILED(asyncCopy.ErrorCode())) {
co_return nullptr;
}

memoryStream.Seek(0);
co_return memoryStream;
}
} catch (winrt::hresult_error const &e) {
Expand Down Expand Up @@ -91,4 +138,20 @@ winrt::IAsyncOperation<winrt::IRandomAccessStream> GetImageMemoryStreamAsync(Rea
}
}

} // namespace Microsoft::ReactNative
// C# provides System.Uri.TryCreate, but no native equivalent seems to exist
winrt::Uri UriTryCreate(winrt::param::hstring const &uri) {
auto factory = winrt::
get_activation_factory<winrt::Windows::Foundation::Uri, winrt::Windows::Foundation::IUriRuntimeClassFactory>();
auto abiFactory = static_cast<ABI::Windows::Foundation::IUriRuntimeClassFactory *>(winrt::get_abi(factory));

const winrt::hstring &localUri = uri;
winrt::Windows::Foundation::Uri returnValue{nullptr};
if (FAILED(abiFactory->CreateUri(
static_cast<HSTRING>(winrt::get_abi(localUri)),
reinterpret_cast<ABI::Windows::Foundation::IUriRuntimeClass **>(winrt::put_abi(returnValue))))) {
return winrt::Windows::Foundation::Uri{nullptr};
}
return returnValue;
}

} // namespace Microsoft::ReactNative
4 changes: 3 additions & 1 deletion vnext/Microsoft.ReactNative/Utils/ImageUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,6 @@ winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::Streams::IR
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::Streams::IRandomAccessStream>
GetImageInlineDataAsync(ReactImageSource source);

} // namespace Microsoft::ReactNative
winrt::Windows::Foundation::Uri UriTryCreate(winrt::param::hstring const &uri);

} // namespace Microsoft::ReactNative
10 changes: 1 addition & 9 deletions vnext/Microsoft.ReactNative/Utils/ValueUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <UI.Xaml.Markup.h>
#include <UI.Xaml.Media.h>
#include <Utils/ValueUtils.h>
#include <windows.foundation.h>
#include <winrt/Windows.UI.ViewManagement.h>
#include "Unicode.h"
#include "XamlUtils.h"
Expand Down Expand Up @@ -313,15 +314,6 @@ REACTWINDOWS_API_(winrt::TimeSpan) TimeSpanFromMs(double ms) {
return dur;
}

// C# provides System.Uri.TryCreate, but no native equivalent seems to exist
winrt::Uri UriTryCreate(winrt::param::hstring const &uri) {
try {
return winrt::Uri(uri);
} catch (...) {
return winrt::Uri(nullptr);
}
}

bool IsValidOptionalColorValue(const winrt::Microsoft::ReactNative::JSValue &v) {
return v.Type() == winrt::Microsoft::ReactNative::JSValueType::Null || IsValidColorValue(v);
}
Expand Down
2 changes: 0 additions & 2 deletions vnext/Microsoft.ReactNative/Utils/ValueUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,6 @@ IsValidColorValue(const winrt::Microsoft::ReactNative::JSValue &v);
REACTWINDOWS_API_(winrt::Windows::Foundation::TimeSpan)
TimeSpanFromMs(double ms);

winrt::Uri UriTryCreate(winrt::param::hstring const &uri);

winrt::Windows::UI::Color ColorFromNumber(DWORD argb) noexcept;

bool IsValidOptionalColorValue(const winrt::Microsoft::ReactNative::JSValue &v);
Expand Down
1 change: 0 additions & 1 deletion vnext/Microsoft.ReactNative/Views/Image/ReactImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
#include <winrt/Windows.Web.Http.Headers.h>
#include <winrt/Windows.Web.Http.h>

#include <Utils/ValueUtils.h>
#include <Views/DynamicAutomationPeer.h>
#include "Unicode.h"
#include "XamlView.h"
Expand Down
104 changes: 59 additions & 45 deletions vnext/Shared/Networking/WinRTWebSocketResource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include <dispatchQueue/dispatchQueue.h>

// Windows API
#include <windows.Networking.Sockets.h>
#include <windows.Storage.Streams.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Security.Cryptography.h>

Expand Down Expand Up @@ -203,7 +205,7 @@ fire_and_forget WinRTWebSocketResource::PerformWrite(string &&message, bool isBi
}

try {
size_t length;
size_t length = 0;
string messageLocal;
bool isBinaryLocal;
{
Expand All @@ -216,8 +218,10 @@ fire_and_forget WinRTWebSocketResource::PerformWrite(string &&message, bool isBi
self->m_socket.Control().MessageType(SocketMessageType::Binary);

auto buffer = CryptographicBuffer::DecodeFromBase64String(winrt::to_hstring(messageLocal));
length = buffer.Length();
self->m_writer.WriteBuffer(buffer);
if (buffer) {
length = buffer.Length();
self->m_writer.WriteBuffer(buffer);
}
} else {
self->m_socket.Control().MessageType(SocketMessageType::Utf8);

Expand Down Expand Up @@ -292,49 +296,59 @@ void WinRTWebSocketResource::Synchronize() noexcept {
#pragma region IWebSocketResource

void WinRTWebSocketResource::Connect(string &&url, const Protocols &protocols, const Options &options) noexcept {
m_socket.MessageReceived(
[self = shared_from_this()](IWebSocket const &sender, IMessageWebSocketMessageReceivedEventArgs const &args) {
try {
string response;
IDataReader reader = args.GetDataReader();
auto len = reader.UnconsumedBufferLength();
if (args.MessageType() == SocketMessageType::Utf8) {
reader.UnicodeEncoding(UnicodeEncoding::Utf8);
vector<uint8_t> data(len);
reader.ReadBytes(data);

response = string(CheckedReinterpretCast<char *>(data.data()), data.size());
} else {
auto buffer = reader.ReadBuffer(len);
winrt::hstring data = CryptographicBuffer::EncodeToBase64String(buffer);

response = winrt::to_string(std::wstring_view(data));
}

if (self->m_readHandler) {
self->m_readHandler(response.length(), response, args.MessageType() == SocketMessageType::Binary);
}
} catch (hresult_error const &e) {
if (self->m_errorHandler) {
string errorMessage;
ErrorType errorType;
// See
// https://docs.microsoft.com/uwp/api/windows.networking.sockets.messagewebsocketmessagereceivedeventargs.getdatareader?view=winrt-19041#remarks
if (e.code() == WININET_E_CONNECTION_ABORTED) {
errorMessage = "[0x80072EFE] Underlying TCP connection suddenly terminated";
errorType = ErrorType::Connection;
self->m_errorHandler({errorMessage, errorType});

// Note: We are not clear whether all read-related errors should close the socket.
self->Close(CloseCode::BadPayload, std::move(errorMessage));
} else {
errorMessage = Utilities::HResultToString(e);
errorType = ErrorType::Receive;
self->m_errorHandler({errorMessage, errorType});
}
}
m_socket.MessageReceived([self = shared_from_this()](
IWebSocket const &sender, IMessageWebSocketMessageReceivedEventArgs const &args) {
try {
string response;

IDataReader reader{nullptr};
// Use ABI version to avoid throwing exceptions on expected code paths
HRESULT hr =
reinterpret_cast<ABI::Windows::Networking::Sockets::IMessageWebSocketMessageReceivedEventArgs *>(
winrt::get_abi(args))
->GetDataReader(reinterpret_cast<ABI::Windows::Storage::Streams::IDataReader **>(winrt::put_abi(reader)));

if (FAILED(hr)) {
// See
// https://docs.microsoft.com/uwp/api/windows.networking.sockets.messagewebsocketmessagereceivedeventargs.getdatareader?view=winrt-19041#remarks
if (hr == WININET_E_CONNECTION_ABORTED) {
string errorMessage{"[0x80072EFE] Underlying TCP connection suddenly terminated"};
self->m_errorHandler({errorMessage, ErrorType::Connection});
// Note: We are not clear whether all read-related errors should close the socket.
self->Close(CloseCode::BadPayload, std::move(errorMessage));
} else {
self->m_errorHandler({Utilities::HResultToString(hr), ErrorType::Receive});
}
});
return;
}

auto len = reader.UnconsumedBufferLength();
if (args.MessageType() == SocketMessageType::Utf8) {
reader.UnicodeEncoding(UnicodeEncoding::Utf8);
vector<uint8_t> data(len);
reader.ReadBytes(data);

response = string(CheckedReinterpretCast<char *>(data.data()), data.size());
} else {
auto buffer = reader.ReadBuffer(len);
winrt::hstring data = CryptographicBuffer::EncodeToBase64String(buffer);

response = winrt::to_string(std::wstring_view(data));
}

if (self->m_readHandler) {
self->m_readHandler(response.length(), response, args.MessageType() == SocketMessageType::Binary);
}
} catch (hresult_error const &e) {
if (self->m_errorHandler) {
string errorMessage;
ErrorType errorType;
errorMessage = Utilities::HResultToString(e);
errorType = ErrorType::Receive;
self->m_errorHandler({errorMessage, errorType});
}
}
});

m_readyState = ReadyState::Connecting;

Expand Down
7 changes: 6 additions & 1 deletion vnext/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@

// This file provides a stable entry-point to call the generator for this
// version of react-native-windows.
module.exports = require('@react-native-windows/cli').generateWindows;

try {
module.exports = require('@react-native-windows/cli').generateWindows;
} catch (e) {
console.error('Failure during generate ', e);
}

0 comments on commit 3bd8471

Please sign in to comment.