Socket connect no longer synchronous and blocking

Summary:
Never really liked this code. Before this change, calls to connect were blocking.

Because of this, we had to make use of promises and a bit of really not that good-looking code.

So, this change makes connect non-blocking meaning that we make full use of our event handler.

These changes contain:
- CSR is not getting generated after each failed attempt.
- Connect is no longer blocking.
- Do not report events via the handler when explicitly disconnecting.

Reviewed By: jknoxville

Differential Revision: D46853228

fbshipit-source-id: 00e6a9c7c039a756175fe14982959e078d92bacb
This commit is contained in:
Lorenzo Blasa
2023-06-28 12:09:58 -07:00
committed by Facebook GitHub Bot
parent 65e515bdaa
commit e42db220ee
22 changed files with 286 additions and 436 deletions

View File

@@ -276,9 +276,9 @@ class JFlipperWebSocket : public facebook::flipper::FlipperSocket {
messageHandler_ = std::move(messageHandler); messageHandler_ = std::move(messageHandler);
} }
virtual bool connect(FlipperConnectionManager* manager) override { virtual void connect(FlipperConnectionManager* manager) override {
if (socket_ != nullptr) { if (socket_ != nullptr) {
return true; return;
} }
std::string connectionURL = endpoint_.secure ? "wss://" : "ws://"; std::string connectionURL = endpoint_.secure ? "wss://" : "ws://";
@@ -297,38 +297,9 @@ class JFlipperWebSocket : public facebook::flipper::FlipperSocket {
auto secure = endpoint_.secure; auto secure = endpoint_.secure;
std::promise<bool> promise;
auto connected = promise.get_future();
connecting_ = true;
socket_ = make_global(JFlipperSocketImpl::create(connectionURL)); socket_ = make_global(JFlipperSocketImpl::create(connectionURL));
socket_->setEventHandler(JFlipperSocketEventHandlerImpl::newObjectCxxArgs( socket_->setEventHandler(JFlipperSocketEventHandlerImpl::newObjectCxxArgs(
[this, &promise, eventHandler = eventHandler_](SocketEvent event) { [eventHandler = eventHandler_](SocketEvent event) {
/**
Only fulfill the promise the first time the event handler is used.
If the open event is received, then set the promise value to true.
For any other event, consider a failure and set to false.
*/
if (this->connecting_) {
this->connecting_ = false;
if (event == SocketEvent::OPEN) {
promise.set_value(true);
} else if (event == SocketEvent::SSL_ERROR) {
try {
promise.set_exception(
std::make_exception_ptr(folly::AsyncSocketException(
folly::AsyncSocketException::SSL_ERROR,
"SSL handshake failed")));
} catch (...) {
// set_exception() may throw an exception
// In that case, just set the value to false.
promise.set_value(false);
}
} else {
promise.set_value(false);
}
}
eventHandler(event); eventHandler(event);
}, },
[messageHandler = messageHandler_](const std::string& message) { [messageHandler = messageHandler_](const std::string& message) {
@@ -348,8 +319,6 @@ class JFlipperWebSocket : public facebook::flipper::FlipperSocket {
return JFlipperObject::create(std::move(object_)); return JFlipperObject::create(std::move(object_));
})); }));
socket_->connect(); socket_->connect();
return connected.get();
} }
virtual void disconnect() override { virtual void disconnect() override {
@@ -409,7 +378,6 @@ class JFlipperWebSocket : public facebook::flipper::FlipperSocket {
facebook::flipper::SocketMessageHandler messageHandler_; facebook::flipper::SocketMessageHandler messageHandler_;
jni::global_ref<JFlipperSocketImpl> socket_; jni::global_ref<JFlipperSocketImpl> socket_;
bool connecting_;
}; };
class JFlipperSocketProvider : public facebook::flipper::FlipperSocketProvider { class JFlipperSocketProvider : public facebook::flipper::FlipperSocketProvider {

View File

@@ -117,7 +117,7 @@ class FlipperSocketImpl extends WebSocketClient implements FlipperSocket {
this.connect(); this.connect();
} catch (Exception e) { } catch (Exception e) {
Log.e("Flipper", "Failed to initialize the socket before connect. " + e.getMessage()); Log.e("flipper", "Failed to initialize the socket before connect. Error: " + e.getMessage());
this.mEventHandler.onConnectionEvent(FlipperSocketEventHandler.SocketEvent.ERROR); this.mEventHandler.onConnectionEvent(FlipperSocketEventHandler.SocketEvent.ERROR);
} }
} }
@@ -139,6 +139,13 @@ class FlipperSocketImpl extends WebSocketClient implements FlipperSocket {
@Override @Override
public void onClose(int code, String reason, boolean remote) { public void onClose(int code, String reason, boolean remote) {
/**
* If the socket is not yet open, don't report the close event. Usually, onError is invoked
* instead which is the one that needs reporting.
*/
if (!this.isOpen()) {
return;
}
this.mEventHandler.onConnectionEvent(FlipperSocketEventHandler.SocketEvent.CLOSE); this.mEventHandler.onConnectionEvent(FlipperSocketEventHandler.SocketEvent.CLOSE);
} }
@@ -162,7 +169,6 @@ class FlipperSocketImpl extends WebSocketClient implements FlipperSocket {
@Override @Override
public void flipperDisconnect() { public void flipperDisconnect() {
this.mEventHandler.onConnectionEvent(FlipperSocketEventHandler.SocketEvent.CLOSE);
this.mEventHandler = this.mEventHandler =
new FlipperSocketEventHandler() { new FlipperSocketEventHandler() {
@Override @Override

View File

@@ -181,11 +181,6 @@ static constexpr int connectionKeepaliveSeconds = 10;
[_dispatchQueue cancelAllOperations]; [_dispatchQueue cancelAllOperations];
[_dispatchQueue waitUntilAllOperationsAreFinished]; [_dispatchQueue waitUntilAllOperationsAreFinished];
// Manually trigger a 'close' event as SocketRocket close method will
// not notify the delegate. SocketRocket only triggers the close event
// when the connection is closed from the server.
_eventHandler(facebook::flipper::SocketEvent::CLOSE);
} }
- (void)send:(NSString*)message - (void)send:(NSString*)message

View File

@@ -39,7 +39,7 @@ class FlipperWebSocket : public FlipperSocket {
virtual void setEventHandler(SocketEventHandler eventHandler) override; virtual void setEventHandler(SocketEventHandler eventHandler) override;
virtual void setMessageHandler(SocketMessageHandler messageHandler) override; virtual void setMessageHandler(SocketMessageHandler messageHandler) override;
virtual bool connect(FlipperConnectionManager* manager) override; virtual void connect(FlipperConnectionManager* manager) override;
virtual void disconnect() override; virtual void disconnect() override;
virtual void send(const folly::dynamic& message, SocketSendHandler completion) virtual void send(const folly::dynamic& message, SocketSendHandler completion)

View File

@@ -53,9 +53,9 @@ void FlipperWebSocket::setMessageHandler(SocketMessageHandler messageHandler) {
messageHandler_ = std::move(messageHandler); messageHandler_ = std::move(messageHandler);
} }
bool FlipperWebSocket::connect(FlipperConnectionManager* manager) { void FlipperWebSocket::connect(FlipperConnectionManager* manager) {
if (socket_ != NULL) { if (socket_ != NULL) {
return true; return;
} }
std::string connectionURL = endpoint_.secure ? "wss://" : "ws://"; std::string connectionURL = endpoint_.secure ? "wss://" : "ws://";
@@ -72,43 +72,16 @@ bool FlipperWebSocket::connect(FlipperConnectionManager* manager) {
connectionURL += payload; connectionURL += payload;
} }
__block bool fullfilled = false;
__block std::promise<bool> promise;
auto connected = promise.get_future();
NSURL* urlObjc = [NSURL NSURL* urlObjc = [NSURL
URLWithString:[NSString stringWithUTF8String:connectionURL.c_str()]]; URLWithString:[NSString stringWithUTF8String:connectionURL.c_str()]];
auto eventHandler = eventHandler_;
socket_ = [[FlipperPlatformWebSocket alloc] initWithURL:urlObjc]; socket_ = [[FlipperPlatformWebSocket alloc] initWithURL:urlObjc];
socket_.eventHandler = ^(SocketEvent event) { socket_.eventHandler = ^(SocketEvent event) {
/** eventHandler_(event);
Only fulfill the promise the first time the event handler is used. If the
open event is received, then set the promise value to true. For any other
event, consider a failure and set to false.
*/
if (!fullfilled) {
fullfilled = true;
if (event == SocketEvent::OPEN) {
promise.set_value(true);
} else if (event == SocketEvent::SSL_ERROR) {
try {
promise.set_exception(
std::make_exception_ptr(SSLException("SSL handshake failed")));
} catch (...) {
// set_exception() may throw an exception
// In that case, just set the value to false.
promise.set_value(false);
}
} else {
promise.set_value(false);
}
}
eventHandler(event);
}; };
auto messageHandler = messageHandler_;
socket_.messageHandler = ^(const std::string& message) { socket_.messageHandler = ^(const std::string& message) {
messageHandler(message); messageHandler_(message);
}; };
if (endpoint_.secure) { if (endpoint_.secure) {
@@ -124,8 +97,6 @@ bool FlipperWebSocket::connect(FlipperConnectionManager* manager) {
} }
[socket_ connect]; [socket_ connect];
return connected.get();
} }
void FlipperWebSocket::disconnect() { void FlipperWebSocket::disconnect() {
@@ -157,9 +128,9 @@ void FlipperWebSocket::send(
} }
/** /**
Only ever used for insecure connections to receive the device_id from a * Only ever used for insecure connections to receive the device_id from a
signCertificate request. If the intended usage ever changes, then a better * signCertificate request. If the intended usage ever changes, then a better
approach needs to be put in place. * approach needs to be put in place.
*/ */
void FlipperWebSocket::sendExpectResponse( void FlipperWebSocket::sendExpectResponse(
const std::string& message, const std::string& message,

View File

@@ -65,7 +65,7 @@ class FlipperReactBaseSocket {
messageHandler_ = std::move(messageHandler); messageHandler_ = std::move(messageHandler);
} }
virtual bool connect(FlipperConnectionManager* manager) = 0; virtual void connect(FlipperConnectionManager* manager) = 0;
virtual void disconnect() = 0; virtual void disconnect() = 0;
virtual void send( virtual void send(

View File

@@ -55,8 +55,8 @@ void FlipperReactSocket::setMessageHandler(
socket_->setMessageHandler(messageHandler); socket_->setMessageHandler(messageHandler);
} }
bool FlipperReactSocket::connect(FlipperConnectionManager* manager) { void FlipperReactSocket::connect(FlipperConnectionManager* manager) {
return socket_->connect(manager); socket_->connect(manager);
} }
void FlipperReactSocket::disconnect() { void FlipperReactSocket::disconnect() {

View File

@@ -37,7 +37,7 @@ class FlipperReactSocket : public FlipperSocket {
virtual void setEventHandler(SocketEventHandler eventHandler) override; virtual void setEventHandler(SocketEventHandler eventHandler) override;
virtual void setMessageHandler(SocketMessageHandler messageHandler) override; virtual void setMessageHandler(SocketMessageHandler messageHandler) override;
virtual bool connect(FlipperConnectionManager* manager) override; virtual void connect(FlipperConnectionManager* manager) override;
virtual void disconnect() override; virtual void disconnect() override;
virtual void send(const folly::dynamic& message, SocketSendHandler completion) virtual void send(const folly::dynamic& message, SocketSendHandler completion)

View File

@@ -134,9 +134,9 @@ FlipperReactSocketClient::getClientCertificate() {
return installClientCertificate(); return installClientCertificate();
} }
bool FlipperReactSocketClient::connect(FlipperConnectionManager* manager) { void FlipperReactSocketClient::connect(FlipperConnectionManager* manager) {
if (status_ != Status::Unconnected) { if (status_ != Status::Unconnected) {
return false; return;
} }
status_ = Status::Connecting; status_ = Status::Connecting;
@@ -176,30 +176,34 @@ bool FlipperReactSocketClient::connect(FlipperConnectionManager* manager) {
socket_.Closed({this, &FlipperReactSocketClient::OnWebSocketClosed}); socket_.Closed({this, &FlipperReactSocketClient::OnWebSocketClosed});
try { try {
this->socket_.ConnectAsync(winrt::Windows::Foundation::Uri(uri))
.wait_for(std::chrono::seconds(10));
status_ = Status::Initializing; status_ = Status::Initializing;
scheduler_->schedule(
[eventHandler = eventHandler_]() { eventHandler(SocketEvent::OPEN); });
return true; Windows::Foundation::IAsyncAction ^ connectAction;
connectAction =
this->socket_.ConnectAsync(winrt::Windows::Foundation::Uri(uri));
connectAction->Completed = ref new AsyncActionCompletedHandler(
[eventHandler = eventHandler_](
Windows::Foundation::IAsyncAction ^ asyncAction,
Windows::Foundation::AsyncStatus asyncStatus) {
if (asyncStatus == Windows::Foundation::AsyncStatus::Completed) {
eventHandler(SocketEvent::OPEN);
} else {
eventHandler(SocketEvent::ERROR);
}
});
} catch (winrt::hresult_error const& ex) { } catch (winrt::hresult_error const& ex) {
winrt::Windows::Web::WebErrorStatus webErrorStatus{ winrt::Windows::Web::WebErrorStatus webErrorStatus{
winrt::Windows::Networking::Sockets::WebSocketError::GetStatus( winrt::Windows::Networking::Sockets::WebSocketError::GetStatus(
ex.to_abi())}; ex.to_abi())};
socket_ = nullptr;
status_ = Status::Unconnected;
eventHandler(SocketEvent::ERROR);
} }
disconnect();
return false;
} }
void FlipperReactSocketClient::disconnect() { void FlipperReactSocketClient::disconnect() {
status_ = Status::Closed; status_ = Status::Closed;
scheduler_->schedule(
[eventHandler = eventHandler_]() { eventHandler(SocketEvent::CLOSE); });
// socket_.Close();
socket_ = nullptr; socket_ = nullptr;
} }
@@ -269,14 +273,10 @@ void FlipperReactSocketClient::OnWebSocketMessageReceived(
const std::string payload = winrt::to_string(message); const std::string payload = winrt::to_string(message);
if (overrideHandler_ != nullptr) { if (overrideHandler_ != nullptr) {
scheduler_->schedule([payload, messageHandler = *overrideHandler_]() {
messageHandler(payload, false); messageHandler(payload, false);
});
overrideHandler_ = nullptr; overrideHandler_ = nullptr;
} else if (messageHandler_) { } else if (messageHandler_) {
scheduler_->schedule([payload, messageHandler = messageHandler_]() {
messageHandler(payload); messageHandler(payload);
});
} }
} catch (winrt::hresult_error const& ex) { } catch (winrt::hresult_error const& ex) {
// winrt::Windows::Web::WebErrorStatus webErrorStatus{ // winrt::Windows::Web::WebErrorStatus webErrorStatus{
@@ -291,8 +291,7 @@ void FlipperReactSocketClient::OnWebSocketClosed(
return; return;
} }
status_ = Status::Closed; status_ = Status::Closed;
scheduler_->schedule( eventHandler(SocketEvent::CLOSE);
[eventHandler = eventHandler_]() { eventHandler(SocketEvent::CLOSE); });
} }
} // namespace flipper } // namespace flipper

View File

@@ -40,7 +40,7 @@ class FlipperReactSocketClient : public FlipperReactBaseSocket {
virtual ~FlipperReactSocketClient(); virtual ~FlipperReactSocketClient();
virtual bool connect(FlipperConnectionManager* manager) override; virtual void connect(FlipperConnectionManager* manager) override;
virtual void disconnect() override; virtual void disconnect() override;
virtual void send(const folly::dynamic& message, SocketSendHandler completion) virtual void send(const folly::dynamic& message, SocketSendHandler completion)
@@ -61,7 +61,6 @@ class FlipperReactSocketClient : public FlipperReactBaseSocket {
args); args);
private: private:
std::promise<bool> connected_;
winrt::Windows::Networking::Sockets::MessageWebSocket socket_; winrt::Windows::Networking::Sockets::MessageWebSocket socket_;
winrt::event_token messageReceivedEventToken_; winrt::event_token messageReceivedEventToken_;
winrt::event_token closedEventToken_; winrt::event_token closedEventToken_;

View File

@@ -48,7 +48,8 @@ bool ConnectionContextStore::hasRequiredFiles() {
std::string config = std::string config =
loadStringFromFile(absoluteFilePath(CONNECTION_CONFIG_FILE)); loadStringFromFile(absoluteFilePath(CONNECTION_CONFIG_FILE));
if (caCert == "" || clientCert == "" || privateKey == "" || config == "") { if (caCert.empty() || clientCert.empty() || privateKey.empty() ||
config.empty()) {
return false; return false;
} }
return true; return true;
@@ -56,14 +57,14 @@ bool ConnectionContextStore::hasRequiredFiles() {
std::string ConnectionContextStore::getCertificateSigningRequest() { std::string ConnectionContextStore::getCertificateSigningRequest() {
// Use in-memory CSR if already loaded // Use in-memory CSR if already loaded
if (csr != "") { if (!csr_.empty()) {
return csr; return csr_;
} }
// Attempt to load existing CSR from previous run of the app // Attempt to load existing CSR from previous run of the app
csr = loadStringFromFile(absoluteFilePath(CSR_FILE_NAME)); csr_ = loadStringFromFile(absoluteFilePath(CSR_FILE_NAME));
if (csr != "") { if (!csr_.empty()) {
return csr; return csr_;
} }
// Clean all state and generate a new one // Clean all state and generate a new one
@@ -75,9 +76,9 @@ std::string ConnectionContextStore::getCertificateSigningRequest() {
if (!success) { if (!success) {
throw new std::runtime_error("Failed to generate CSR"); throw new std::runtime_error("Failed to generate CSR");
} }
csr = loadStringFromFile(absoluteFilePath(CSR_FILE_NAME)); csr_ = loadStringFromFile(absoluteFilePath(CSR_FILE_NAME));
return csr; return csr_;
} }
std::string ConnectionContextStore::getDeviceId() { std::string ConnectionContextStore::getDeviceId() {
@@ -124,7 +125,8 @@ void ConnectionContextStore::storeConnectionConfig(folly::dynamic& config) {
writeStringToFile(json, absoluteFilePath(CONNECTION_CONFIG_FILE)); writeStringToFile(json, absoluteFilePath(CONNECTION_CONFIG_FILE));
} }
std::string ConnectionContextStore::absoluteFilePath(const char* filename) { std::string ConnectionContextStore::absoluteFilePath(
const char* filename) const {
#ifndef WIN32 #ifndef WIN32
return std::string(deviceData_.privateAppDirectory + "/sonar/" + filename); return std::string(deviceData_.privateAppDirectory + "/sonar/" + filename);
#else #else
@@ -159,7 +161,7 @@ std::string ConnectionContextStore::getPath(StoreItem storeItem) {
bool ConnectionContextStore::resetState() { bool ConnectionContextStore::resetState() {
// Clear in-memory state // Clear in-memory state
csr = ""; csr_ = "";
// Delete state from disk // Delete state from disk
std::string dirPath = absoluteFilePath(""); std::string dirPath = absoluteFilePath("");
@@ -208,6 +210,18 @@ std::pair<std::string, std::string> ConnectionContextStore::getCertificate() {
return std::make_pair(certificate_path, std::string(CERTIFICATE_PASSWORD)); return std::make_pair(certificate_path, std::string(CERTIFICATE_PASSWORD));
} }
bool ConnectionContextStore::hasCertificateSigningRequest() const {
std::string csr = loadStringFromFile(absoluteFilePath(CSR_FILE_NAME));
return !csr.empty();
}
bool ConnectionContextStore::hasClientCertificate() const {
std::string clientCertificate =
loadStringFromFile(absoluteFilePath(CLIENT_CERT_FILE_NAME));
return !clientCertificate.empty();
}
std::string loadStringFromFile(std::string fileName) { std::string loadStringFromFile(std::string fileName) {
if (!fileExists(fileName)) { if (!fileExists(fileName)) {
return ""; return "";

View File

@@ -57,11 +57,18 @@ class ConnectionContextStore {
*/ */
std::pair<std::string, std::string> getCertificate(); std::pair<std::string, std::string> getCertificate();
/** Is there a CSR present.
*/
bool hasCertificateSigningRequest() const;
/** Is there a client certificate present.
*/
bool hasClientCertificate() const;
private: private:
DeviceData deviceData_; DeviceData deviceData_;
std::string csr = ""; std::string csr_ = "";
std::string absoluteFilePath(const char* filename); std::string absoluteFilePath(const char* filename) const;
}; };
} // namespace flipper } // namespace flipper

View File

@@ -20,12 +20,12 @@
#define WRONG_THREAD_EXIT_MSG \ #define WRONG_THREAD_EXIT_MSG \
"ERROR: Aborting flipper initialization because it's not running in the flipper thread." "ERROR: Aborting flipper initialization because it's not running in the flipper thread."
static constexpr int reconnectIntervalSeconds = 2; static constexpr int RECONNECT_INTERVAL_SECONDS = 3;
// Not a public-facing version number. // Not a public-facing version number.
// Used for compatibility checking with desktop flipper. // Used for compatibility checking with desktop flipper.
// To be bumped for every core platform interface change. // To be bumped for every core platform interface change.
static constexpr int sdkVersion = 4; static constexpr int SDK_VERSION = 4;
using namespace folly; using namespace folly;
@@ -66,14 +66,14 @@ FlipperConnectionManagerImpl::FlipperConnectionManagerImpl(
std::shared_ptr<FlipperState> state, std::shared_ptr<FlipperState> state,
std::shared_ptr<ConnectionContextStore> contextStore) std::shared_ptr<ConnectionContextStore> contextStore)
: deviceData_(config.deviceData), : deviceData_(config.deviceData),
flipperState_(state), state_(state),
insecurePort(config.insecurePort), insecurePort(config.insecurePort),
securePort(config.securePort), securePort(config.securePort),
altInsecurePort(config.altInsecurePort), altInsecurePort(config.altInsecurePort),
altSecurePort(config.altSecurePort), altSecurePort(config.altSecurePort),
flipperScheduler_(config.callbackWorker), scheduler_(config.callbackWorker),
connectionScheduler_(config.connectionWorker), connectionScheduler_(config.connectionWorker),
contextStore_(contextStore), store_(contextStore),
implWrapper_(std::make_shared<FlipperConnectionManagerWrapper>(this)) { implWrapper_(std::make_shared<FlipperConnectionManagerWrapper>(this)) {
CHECK_THROW(config.callbackWorker, std::invalid_argument); CHECK_THROW(config.callbackWorker, std::invalid_argument);
CHECK_THROW(config.connectionWorker, std::invalid_argument); CHECK_THROW(config.connectionWorker, std::invalid_argument);
@@ -85,39 +85,52 @@ FlipperConnectionManagerImpl::~FlipperConnectionManagerImpl() {
void FlipperConnectionManagerImpl::setCertificateProvider( void FlipperConnectionManagerImpl::setCertificateProvider(
const std::shared_ptr<FlipperCertificateProvider> provider) { const std::shared_ptr<FlipperCertificateProvider> provider) {
certProvider_ = provider; certificateProvider_ = provider;
}; };
std::shared_ptr<FlipperCertificateProvider> std::shared_ptr<FlipperCertificateProvider>
FlipperConnectionManagerImpl::getCertificateProvider() { FlipperConnectionManagerImpl::getCertificateProvider() {
return certProvider_; return certificateProvider_;
} }
void FlipperConnectionManagerImpl::handleSocketEvent(SocketEvent event) { void FlipperConnectionManagerImpl::handleSocketEvent(SocketEvent event) {
// Ensure that the event is handled on the correct thread i.e. scheduler.
scheduler_->schedule([this, event]() {
switch (event) { switch (event) {
case SocketEvent::OPEN: case SocketEvent::OPEN:
isConnected_ = true; isConnected_ = true;
if (connectionIsTrusted_) { if (isConnectionTrusted_) {
failedConnectionAttempts_ = 0;
callbacks_->onConnected(); callbacks_->onConnected();
} else {
requestSignedCertificate();
} }
break; break;
case SocketEvent::SSL_ERROR: case SocketEvent::SSL_ERROR:
// SSL errors are not handled as a connection event log("[conn] handleSocketEvent(SSL_ERROR)");
// on this handler. failedConnectionAttempts_++;
reconnect();
break; break;
case SocketEvent::CLOSE: case SocketEvent::CLOSE:
case SocketEvent::ERROR: case SocketEvent::ERROR:
log("[conn] handleSocketEvent(CLOSE_ERROR)");
if (!isConnected_) { if (!isConnected_) {
reconnect();
return; return;
} }
failedConnectionAttempts_++;
isConnected_ = false; isConnected_ = false;
if (connectionIsTrusted_) {
connectionIsTrusted_ = false; if (isConnectionTrusted_) {
isConnectionTrusted_ = false;
callbacks_->onDisconnected(); callbacks_->onDisconnected();
} }
reconnect(); reconnect();
break; break;
} }
});
} }
void FlipperConnectionManagerImpl::start() { void FlipperConnectionManagerImpl::start() {
@@ -126,22 +139,22 @@ void FlipperConnectionManagerImpl::start() {
return; return;
} }
if (isStarted_) { if (started_) {
log("Already started"); log("Already started");
return; return;
} }
isStarted_ = true; started_ = true;
auto step = flipperState_->start("Start connection thread"); auto step = state_->start("Start connection thread");
flipperScheduler_->schedule([this, step]() { scheduler_->schedule([this, step]() {
step->complete(); step->complete();
startSync(); startSync();
}); });
} }
void FlipperConnectionManagerImpl::startSync() { void FlipperConnectionManagerImpl::startSync() {
if (!isStarted_) { if (!started_) {
log("Not started"); log("Not started");
return; return;
} }
@@ -153,52 +166,28 @@ void FlipperConnectionManagerImpl::startSync() {
log("Already connected"); log("Already connected");
return; return;
} }
socket_ = nullptr;
bool isClientSetupStep = isCertificateExchangeNeeded(); bool isClientSetupStep = isCertificateExchangeNeeded();
auto step = flipperState_->start( auto step = state_->start(
isClientSetupStep ? "Establish pre-setup connection" isClientSetupStep ? "Establish certificate exchange connection"
: "Establish main connection"); : "Establish main connection");
try {
if (isClientSetupStep) { if (isClientSetupStep) {
bool success = connectAndExchangeCertificate(); connectAndExchangeCertificate();
if (!success) {
reconnect();
return;
}
} else { } else {
if (!connectSecurely()) { connectSecurely();
// The expected code path when flipper desktop is not running.
// Don't count as a failed attempt, or it would invalidate the
// connection files for no reason. On iOS devices, we can always connect
// to the local port forwarding server even when it can't connect to
// flipper. In that case we get a Network error instead of a Port not
// open error, so we treat them the same.
step->fail(
"No route to flipper found. Is flipper desktop running? Retrying...");
reconnect();
}
} }
step->complete(); step->complete();
} catch (const SSLException& e) {
auto message = std::string(e.what()) +
"\nMake sure the date and time of your device is up to date.";
log(message);
step->fail(message);
failedConnectionAttempts_++;
reconnect();
} catch (const std::exception& e) {
log(e.what());
step->fail(e.what());
failedConnectionAttempts_++;
reconnect();
}
} }
bool FlipperConnectionManagerImpl::connectAndExchangeCertificate() { void FlipperConnectionManagerImpl::connectAndExchangeCertificate() {
log("[conn] connectAndExchangeCertificate()");
auto port = insecurePort; auto port = insecurePort;
auto endpoint = FlipperConnectionEndpoint(deviceData_.host, port, false); auto endpoint = FlipperConnectionEndpoint(deviceData_.host, port, false);
int medium = certProvider_ != nullptr int medium = certificateProvider_ != nullptr
? certProvider_->getCertificateExchangeMedium() ? certificateProvider_->getCertificateExchangeMedium()
: FlipperCertificateExchangeMedium::FS_ACCESS; : FlipperCertificateExchangeMedium::FS_ACCESS;
auto payload = std::make_unique<FlipperSocketBasePayload>(); auto payload = std::make_unique<FlipperSocketBasePayload>();
@@ -206,47 +195,31 @@ bool FlipperConnectionManagerImpl::connectAndExchangeCertificate() {
payload->device = deviceData_.device; payload->device = deviceData_.device;
payload->device_id = "unknown"; payload->device_id = "unknown";
payload->app = deviceData_.app; payload->app = deviceData_.app;
payload->sdk_version = sdkVersion; payload->sdk_version = SDK_VERSION;
payload->medium = medium; payload->medium = medium;
client_ = FlipperSocketProvider::socketCreate( socket_ = FlipperSocketProvider::socketCreate(
endpoint, std::move(payload), flipperScheduler_); endpoint, std::move(payload), scheduler_);
client_->setEventHandler(ConnectionEvents(implWrapper_)); socket_->setEventHandler(ConnectionEvents(implWrapper_));
connectionIsTrusted_ = false; isConnectionTrusted_ = false;
auto step =
flipperState_->start("Attempt to connect for certificate exchange");
auto step = state_->start("Attempt to connect for certificate exchange");
step->complete(); step->complete();
// NON-TLS: socket_->connect(this);
// On failure: clear the client.
// On success: proceed to request the client certificate.
// Connect is just handled here, move this elsewhere.
if (!client_->connect(this)) {
client_ = nullptr;
return false;
} }
requestSignedCertificate(); void FlipperConnectionManagerImpl::connectSecurely() {
return true;
}
bool FlipperConnectionManagerImpl::connectSecurely() {
client_ = nullptr;
auto port = securePort; auto port = securePort;
auto endpoint = FlipperConnectionEndpoint(deviceData_.host, port, true); auto endpoint = FlipperConnectionEndpoint(deviceData_.host, port, true);
int medium = certProvider_ != nullptr int medium = certificateProvider_ != nullptr
? certProvider_->getCertificateExchangeMedium() ? certificateProvider_->getCertificateExchangeMedium()
: FlipperCertificateExchangeMedium::FS_ACCESS; : FlipperCertificateExchangeMedium::FS_ACCESS;
auto loadingDeviceId = flipperState_->start("Load Device Id"); auto loadingDeviceId = state_->start("Load Device Id");
auto deviceId = contextStore_->getDeviceId(); auto deviceId = store_->getDeviceId();
if (deviceId.compare("unknown")) { if (deviceId.compare("unknown")) {
loadingDeviceId->complete(); loadingDeviceId->complete();
} }
@@ -256,15 +229,15 @@ bool FlipperConnectionManagerImpl::connectSecurely() {
payload->device = deviceData_.device; payload->device = deviceData_.device;
payload->device_id = deviceId; payload->device_id = deviceId;
payload->app = deviceData_.app; payload->app = deviceData_.app;
payload->sdk_version = sdkVersion; payload->sdk_version = SDK_VERSION;
payload->medium = medium; payload->medium = medium;
payload->csr = contextStore_->getCertificateSigningRequest().c_str(); payload->csr = store_->getCertificateSigningRequest().c_str();
payload->csr_path = contextStore_->getCertificateDirectoryPath().c_str(); payload->csr_path = store_->getCertificateDirectoryPath().c_str();
client_ = FlipperSocketProvider::socketCreate( socket_ = FlipperSocketProvider::socketCreate(
endpoint, std::move(payload), connectionScheduler_, contextStore_.get()); endpoint, std::move(payload), connectionScheduler_, store_.get());
client_->setEventHandler(ConnectionEvents(implWrapper_)); socket_->setEventHandler(ConnectionEvents(implWrapper_));
client_->setMessageHandler([this](const std::string& msg) { socket_->setMessageHandler([this](const std::string& msg) {
std::unique_ptr<FireAndForgetBasedFlipperResponder> responder; std::unique_ptr<FireAndForgetBasedFlipperResponder> responder;
auto message = folly::parseJson(msg); auto message = folly::parseJson(msg);
auto idItr = message.find("id"); auto idItr = message.find("id");
@@ -278,54 +251,41 @@ bool FlipperConnectionManagerImpl::connectSecurely() {
this->onMessageReceived(folly::parseJson(msg), std::move(responder)); this->onMessageReceived(folly::parseJson(msg), std::move(responder));
}); });
connectionIsTrusted_ = true; isConnectionTrusted_ = true;
auto step = flipperState_->start(
"Attempt to connect with existing client certificate");
auto step =
state_->start("Attempt to connect with existing client certificate");
step->complete(); step->complete();
// TLS: socket_->connect(this);
// On failure: clear the client.
// On success: clear number of failed attempts.
// Connect is just handled here, move this elsewhere.
if (!client_->connect(this)) {
client_ = nullptr;
return false;
}
failedConnectionAttempts_ = 0;
return true;
} }
void FlipperConnectionManagerImpl::reconnect() { void FlipperConnectionManagerImpl::reconnect() {
if (!isStarted_) { if (!started_) {
log("Not started"); log("Not started");
return; return;
} }
flipperScheduler_->scheduleAfter( log("[conn] reconnect()");
[this]() { startSync(); }, reconnectIntervalSeconds * 1000.0f); scheduler_->scheduleAfter(
[this]() { startSync(); }, RECONNECT_INTERVAL_SECONDS * 1000.0f);
} }
void FlipperConnectionManagerImpl::stop() { void FlipperConnectionManagerImpl::stop() {
if (certProvider_ && certProvider_->shouldResetCertificateFolder()) { if (certificateProvider_ &&
contextStore_->resetState(); certificateProvider_->shouldResetCertificateFolder()) {
store_->resetState();
} }
if (!isStarted_) { if (!started_) {
log("Not started"); log("Not started");
return; return;
} }
isStarted_ = false; started_ = false;
std::shared_ptr<std::promise<void>> joinPromise = std::shared_ptr<std::promise<void>> joinPromise =
std::make_shared<std::promise<void>>(); std::make_shared<std::promise<void>>();
std::future<void> join = joinPromise->get_future(); std::future<void> join = joinPromise->get_future();
flipperScheduler_->schedule([this, joinPromise]() { scheduler_->schedule([this, joinPromise]() {
if (client_) { socket_ = nullptr;
client_->disconnect();
}
client_ = nullptr;
joinPromise->set_value(); joinPromise->set_value();
}); });
@@ -333,7 +293,7 @@ void FlipperConnectionManagerImpl::stop() {
} }
bool FlipperConnectionManagerImpl::isConnected() const { bool FlipperConnectionManagerImpl::isConnected() const {
return isConnected_ && connectionIsTrusted_; return isConnected_ && isConnectionTrusted_;
} }
void FlipperConnectionManagerImpl::setCallbacks(Callbacks* callbacks) { void FlipperConnectionManagerImpl::setCallbacks(Callbacks* callbacks) {
@@ -341,10 +301,10 @@ void FlipperConnectionManagerImpl::setCallbacks(Callbacks* callbacks) {
} }
void FlipperConnectionManagerImpl::sendMessage(const folly::dynamic& message) { void FlipperConnectionManagerImpl::sendMessage(const folly::dynamic& message) {
flipperScheduler_->schedule([this, message]() { scheduler_->schedule([this, message]() {
try { try {
if (client_) { if (socket_) {
client_->send(message, []() {}); socket_->send(message, []() {});
} }
} catch (std::length_error& e) { } catch (std::length_error& e) {
// Skip sending messages that are too large. // Skip sending messages that are too large.
@@ -355,10 +315,10 @@ void FlipperConnectionManagerImpl::sendMessage(const folly::dynamic& message) {
} }
void FlipperConnectionManagerImpl::sendMessageRaw(const std::string& message) { void FlipperConnectionManagerImpl::sendMessageRaw(const std::string& message) {
flipperScheduler_->schedule([this, message]() { scheduler_->schedule([this, message]() {
try { try {
if (client_) { if (socket_) {
client_->send(message, []() {}); socket_->send(message, []() {});
} }
} catch (std::length_error& e) { } catch (std::length_error& e) {
// Skip sending messages that are too large. // Skip sending messages that are too large.
@@ -379,7 +339,7 @@ bool FlipperConnectionManagerImpl::isCertificateExchangeNeeded() {
return true; return true;
} }
auto last_known_medium = contextStore_->getLastKnownMedium(); auto last_known_medium = store_->getLastKnownMedium();
if (!last_known_medium) { if (!last_known_medium) {
return true; return true;
} }
@@ -387,15 +347,15 @@ bool FlipperConnectionManagerImpl::isCertificateExchangeNeeded() {
// When we exchange certs over WWW, we use a fake generated serial number and // When we exchange certs over WWW, we use a fake generated serial number and
// a virtual device. If medium changes to FS_ACCESS at some point, we should // a virtual device. If medium changes to FS_ACCESS at some point, we should
// restart the exchange process to get the device ID of the real device. // restart the exchange process to get the device ID of the real device.
int medium = certProvider_ != nullptr int medium = certificateProvider_ != nullptr
? certProvider_->getCertificateExchangeMedium() ? certificateProvider_->getCertificateExchangeMedium()
: FlipperCertificateExchangeMedium::FS_ACCESS; : FlipperCertificateExchangeMedium::FS_ACCESS;
if (last_known_medium != medium) { if (last_known_medium != medium) {
return true; return true;
} }
auto step = flipperState_->start("Check required certificates are present"); auto step = state_->start("Check required certificates are present");
bool hasRequiredFiles = contextStore_->hasRequiredFiles(); bool hasRequiredFiles = store_->hasRequiredFiles();
if (hasRequiredFiles) { if (hasRequiredFiles) {
step->complete(); step->complete();
} }
@@ -406,40 +366,27 @@ void FlipperConnectionManagerImpl::processSignedCertificateResponse(
std::shared_ptr<FlipperStep> gettingCert, std::shared_ptr<FlipperStep> gettingCert,
std::string response, std::string response,
bool isError) { bool isError) {
/**
Need to keep track of whether the response has been
handled. On success, the completion handler deallocates the socket which in
turn triggers a disconnect. A disconnect is called within
the context of a subscription handler. This means that the completion
handler can be called again to notify that the stream has
been interrupted because we are effectively still handing the response
read. So, if already handled, ignore and return;
*/
if (certificateExchangeCompleted_)
return;
certificateExchangeCompleted_ = true;
if (isError) { if (isError) {
auto error = auto error =
"Desktop failed to provide certificates. Error from flipper desktop:\n" + "Flipper failed to provide certificates. Error from Flipper Desktop:\n" +
response; response;
log(error); log(error);
gettingCert->fail(error); gettingCert->fail(error);
client_ = nullptr;
return; } else {
} int medium = certificateProvider_ != nullptr
int medium = certProvider_ != nullptr ? certificateProvider_->getCertificateExchangeMedium()
? certProvider_->getCertificateExchangeMedium()
: FlipperCertificateExchangeMedium::FS_ACCESS; : FlipperCertificateExchangeMedium::FS_ACCESS;
if (!response.empty()) { if (!response.empty()) {
folly::dynamic config = folly::parseJson(response); folly::dynamic config = folly::parseJson(response);
config["medium"] = medium; config["medium"] = medium;
contextStore_->storeConnectionConfig(config); store_->storeConnectionConfig(config);
} }
if (certProvider_) { if (certificateProvider_) {
certProvider_->setFlipperState(flipperState_); certificateProvider_->setFlipperState(state_);
auto gettingCertFromProvider = auto gettingCertFromProvider =
flipperState_->start("Getting cert from Cert Provider"); state_->start("Getting client certificate from Certificate Provider");
try { try {
// Certificates should be present in app's sandbox after it is // Certificates should be present in app's sandbox after it is
@@ -449,66 +396,58 @@ void FlipperConnectionManagerImpl::processSignedCertificateResponse(
// and recreates the app.csr. By the time completion block is // and recreates the app.csr. By the time completion block is
// called the DeviceCA cert doesn't match app's csr and it // called the DeviceCA cert doesn't match app's csr and it
// throws an SSL error. // throws an SSL error.
certProvider_->getCertificates( certificateProvider_->getCertificates(
contextStore_->getCertificateDirectoryPath(), store_->getCertificateDirectoryPath(), store_->getDeviceId());
contextStore_->getDeviceId());
gettingCertFromProvider->complete(); gettingCertFromProvider->complete();
} catch (std::exception& e) { } catch (std::exception& e) {
gettingCertFromProvider->fail(e.what()); gettingCertFromProvider->fail(e.what());
gettingCert->fail(e.what()); gettingCert->fail(e.what());
} catch (...) { } catch (...) {
gettingCertFromProvider->fail("Exception from certProvider"); gettingCertFromProvider->fail(
gettingCert->fail("Exception from certProvider"); "Exception thrown from Certificate Provider");
gettingCert->fail("Exception thrown from Certificate Provider");
} }
} }
log("Certificate exchange complete."); log("Certificate exchange complete.");
gettingCert->complete(); gettingCert->complete();
}
// Disconnect after message sending is complete. socket_ = nullptr;
// The client destructor will send a disconnected event reconnect();
// which will be handled by Flipper which will initiate
// a reconnect sequence.
client_ = nullptr;
} }
void FlipperConnectionManagerImpl::requestSignedCertificate() { void FlipperConnectionManagerImpl::requestSignedCertificate() {
auto resettingState = flipperState_->start("Reset connection store state"); auto resettingState = state_->start("Reset connection store state");
contextStore_->resetState(); store_->resetState();
resettingState->complete(); resettingState->complete();
auto generatingCSR = flipperState_->start("Generate CSR"); auto generatingCSR = state_->start("Generate CSR");
std::string csr = contextStore_->getCertificateSigningRequest(); std::string csr = store_->getCertificateSigningRequest();
generatingCSR->complete(); generatingCSR->complete();
int medium = certProvider_ != nullptr int medium = certificateProvider_ != nullptr
? certProvider_->getCertificateExchangeMedium() ? certificateProvider_->getCertificateExchangeMedium()
: FlipperCertificateExchangeMedium::FS_ACCESS; : FlipperCertificateExchangeMedium::FS_ACCESS;
folly::dynamic message = folly::dynamic::object("method", "signCertificate")( folly::dynamic message =
"csr", csr.c_str())( folly::dynamic::object("method", "signCertificate")("csr", csr.c_str())(
"destination", "destination",
contextStore_->getCertificateDirectoryPath().c_str())("medium", medium); store_->getCertificateDirectoryPath().c_str())("medium", medium);
auto gettingCert = flipperState_->start("Getting cert from desktop"); auto gettingCert = state_->start("Getting cert from desktop");
certificateExchangeCompleted_ = false; socket_->sendExpectResponse(
flipperScheduler_->schedule([this, message, gettingCert]() {
if (!client_) {
return;
}
client_->sendExpectResponse(
folly::toJson(message), folly::toJson(message),
[this, gettingCert](const std::string& response, bool isError) { [this, gettingCert](const std::string& response, bool isError) {
flipperScheduler_->schedule([this, gettingCert, response, isError]() { scheduler_->schedule([this, gettingCert, response, isError]() {
this->processSignedCertificateResponse( this->processSignedCertificateResponse(
gettingCert, response, isError); gettingCert, response, isError);
}); });
}); });
});
failedConnectionAttempts_ = 0; failedConnectionAttempts_ = 0;
} }
bool FlipperConnectionManagerImpl::isRunningInOwnThread() { bool FlipperConnectionManagerImpl::isRunningInOwnThread() {
return flipperScheduler_->isRunningInOwnThread(); return scheduler_->isRunningInOwnThread();
} }
} // namespace flipper } // namespace flipper

View File

@@ -55,33 +55,35 @@ class FlipperConnectionManagerImpl : public FlipperConnectionManager {
private: private:
bool isConnected_ = false; bool isConnected_ = false;
bool isStarted_ = false; bool started_ = false;
std::shared_ptr<FlipperCertificateProvider> certProvider_ = nullptr; bool isConnectionTrusted_ = false;
std::shared_ptr<FlipperCertificateProvider> certificateProvider_ = nullptr;
Callbacks* callbacks_; Callbacks* callbacks_;
DeviceData deviceData_; DeviceData deviceData_;
std::shared_ptr<FlipperState> flipperState_;
std::shared_ptr<FlipperState> state_;
int insecurePort; int insecurePort;
int securePort; int securePort;
int altInsecurePort; int altInsecurePort;
int altSecurePort; int altSecurePort;
Scheduler* flipperScheduler_; Scheduler* scheduler_;
Scheduler* connectionScheduler_; Scheduler* connectionScheduler_;
std::unique_ptr<FlipperSocket> client_; std::unique_ptr<FlipperSocket> socket_;
bool connectionIsTrusted_;
bool certificateExchangeCompleted_ = false;
int failedConnectionAttempts_ = 0; int failedConnectionAttempts_ = 0;
int failedSocketConnectionAttempts = 0;
std::shared_ptr<ConnectionContextStore> contextStore_; std::shared_ptr<ConnectionContextStore> store_;
std::shared_ptr<FlipperConnectionManagerWrapper> implWrapper_; std::shared_ptr<FlipperConnectionManagerWrapper> implWrapper_;
void startSync(); void startSync();
bool connectAndExchangeCertificate(); void connectAndExchangeCertificate();
bool connectSecurely(); void connectSecurely();
void handleSocketEvent(const SocketEvent event); void handleSocketEvent(const SocketEvent event);
bool isCertificateExchangeNeeded(); bool isCertificateExchangeNeeded();
void requestSignedCertificate(); void requestSignedCertificate();

View File

@@ -37,7 +37,7 @@ class FlipperSocket {
used or error. used or error.
@param manager An instance of FlipperConnectionManager. @param manager An instance of FlipperConnectionManager.
*/ */
virtual bool connect(FlipperConnectionManager* manager) = 0; virtual void connect(FlipperConnectionManager* manager) = 0;
/** /**
Disconnect from the endpoint. Disconnect from the endpoint.
*/ */

View File

@@ -68,7 +68,7 @@ class BaseClient {
messageHandler_ = std::move(messageHandler); messageHandler_ = std::move(messageHandler);
} }
virtual bool connect(FlipperConnectionManager* manager) = 0; virtual void connect(FlipperConnectionManager* manager) = 0;
virtual void disconnect() = 0; virtual void disconnect() = 0;
virtual void send( virtual void send(

View File

@@ -60,8 +60,8 @@ void FlipperWebSocket::setMessageHandler(SocketMessageHandler messageHandler) {
socket_->setMessageHandler(messageHandler); socket_->setMessageHandler(messageHandler);
} }
bool FlipperWebSocket::connect(FlipperConnectionManager* manager) { void FlipperWebSocket::connect(FlipperConnectionManager* manager) {
return socket_->connect(manager); socket_->connect(manager);
} }
void FlipperWebSocket::disconnect() { void FlipperWebSocket::disconnect() {

View File

@@ -41,7 +41,7 @@ class FlipperWebSocket : public FlipperSocket {
virtual void setEventHandler(SocketEventHandler eventHandler) override; virtual void setEventHandler(SocketEventHandler eventHandler) override;
virtual void setMessageHandler(SocketMessageHandler messageHandler) override; virtual void setMessageHandler(SocketMessageHandler messageHandler) override;
virtual bool connect(FlipperConnectionManager* manager) override; virtual void connect(FlipperConnectionManager* manager) override;
virtual void disconnect() override; virtual void disconnect() override;
virtual void send(const folly::dynamic& message, SocketSendHandler completion) virtual void send(const folly::dynamic& message, SocketSendHandler completion)

View File

@@ -62,9 +62,9 @@ WebSocketClient::~WebSocketClient() {
disconnect(); disconnect();
} }
bool WebSocketClient::connect(FlipperConnectionManager* manager) { void WebSocketClient::connect(FlipperConnectionManager*) {
if (status_ != Status::Unconnected) { if (status_ != Status::Unconnected) {
return false; return;
} }
status_ = Status::Connecting; status_ = Status::Connecting;
@@ -89,7 +89,8 @@ bool WebSocketClient::connect(FlipperConnectionManager* manager) {
if (ec) { if (ec) {
status_ = Status::Failed; status_ = Status::Failed;
return false; eventHandler_(SocketEvent::ERROR);
return;
} }
handle_ = connection_->get_handle(); handle_ = connection_->get_handle();
@@ -119,18 +120,7 @@ bool WebSocketClient::connect(FlipperConnectionManager* manager) {
&socket_, &socket_,
websocketpp::lib::placeholders::_1)); websocketpp::lib::placeholders::_1));
auto connected = connected_.get_future();
socket_.connect(connection_); socket_.connect(connection_);
auto state = connected.wait_for(std::chrono::seconds(10));
if (state == std::future_status::ready) {
return connected.get();
}
disconnect();
return false;
} }
void WebSocketClient::disconnect() { void WebSocketClient::disconnect() {
@@ -147,8 +137,6 @@ void WebSocketClient::disconnect() {
thread_->join(); thread_->join();
} }
thread_ = nullptr; thread_ = nullptr;
scheduler_->schedule(
[eventHandler = eventHandler_]() { eventHandler(SocketEvent::CLOSE); });
} }
void WebSocketClient::send( void WebSocketClient::send(
@@ -204,8 +192,7 @@ void WebSocketClient::onOpen(SocketClient* c, websocketpp::connection_hdl hdl) {
} }
status_ = Status::Initializing; status_ = Status::Initializing;
scheduler_->schedule( eventHandler_(SocketEvent::OPEN);
[eventHandler = eventHandler_]() { eventHandler(SocketEvent::OPEN); });
} }
void WebSocketClient::onMessage( void WebSocketClient::onMessage(
@@ -229,16 +216,14 @@ void WebSocketClient::onFail(SocketClient* c, websocketpp::connection_hdl hdl) {
connected_.set_value(false); connected_.set_value(false);
} }
status_ = Status::Failed; status_ = Status::Failed;
scheduler_->schedule( eventHandler_(SocketEvent::ERROR);
[eventHandler = eventHandler_]() { eventHandler(SocketEvent::ERROR); });
} }
void WebSocketClient::onClose( void WebSocketClient::onClose(
SocketClient* c, SocketClient* c,
websocketpp::connection_hdl hdl) { websocketpp::connection_hdl hdl) {
status_ = Status::Closed; status_ = Status::Closed;
scheduler_->schedule( eventHandler_(SocketEvent::CLOSE);
[eventHandler = eventHandler_]() { eventHandler(SocketEvent::CLOSE); });
} }
} // namespace flipper } // namespace flipper

View File

@@ -47,7 +47,7 @@ class WebSocketClient : public BaseClient {
virtual ~WebSocketClient(); virtual ~WebSocketClient();
virtual bool connect(FlipperConnectionManager* manager) override; virtual void connect(FlipperConnectionManager* manager) override;
virtual void disconnect() override; virtual void disconnect() override;
virtual void send(const folly::dynamic& message, SocketSendHandler completion) virtual void send(const folly::dynamic& message, SocketSendHandler completion)

View File

@@ -65,9 +65,9 @@ WebSocketTLSClient::~WebSocketTLSClient() {
disconnect(); disconnect();
} }
bool WebSocketTLSClient::connect(FlipperConnectionManager* manager) { void WebSocketTLSClient::connect(FlipperConnectionManager*) {
if (status_ != Status::Unconnected) { if (status_ != Status::Unconnected) {
return false; return;
} }
status_ = Status::Connecting; status_ = Status::Connecting;
@@ -98,7 +98,8 @@ bool WebSocketTLSClient::connect(FlipperConnectionManager* manager) {
if (ec) { if (ec) {
status_ = Status::Failed; status_ = Status::Failed;
return false; eventHandler_(SocketEvent::ERROR);
return;
} }
handle_ = connection_->get_handle(); handle_ = connection_->get_handle();
@@ -128,18 +129,7 @@ bool WebSocketTLSClient::connect(FlipperConnectionManager* manager) {
&socket_, &socket_,
websocketpp::lib::placeholders::_1)); websocketpp::lib::placeholders::_1));
auto connected = connected_.get_future();
socket_.connect(connection_); socket_.connect(connection_);
auto state = connected.wait_for(std::chrono::seconds(10));
if (state == std::future_status::ready) {
return connected.get();
}
disconnect();
return false;
} }
void WebSocketTLSClient::disconnect() { void WebSocketTLSClient::disconnect() {
@@ -157,8 +147,6 @@ void WebSocketTLSClient::disconnect() {
} }
thread_ = nullptr; thread_ = nullptr;
scheduler_->schedule(
[eventHandler = eventHandler_]() { eventHandler(SocketEvent::CLOSE); });
} }
void WebSocketTLSClient::send( void WebSocketTLSClient::send(
@@ -211,13 +199,8 @@ void WebSocketTLSClient::sendExpectResponse(
void WebSocketTLSClient::onOpen( void WebSocketTLSClient::onOpen(
SocketTLSClient* c, SocketTLSClient* c,
websocketpp::connection_hdl hdl) { websocketpp::connection_hdl hdl) {
if (status_ == Status::Connecting) {
connected_.set_value(true);
}
status_ = Status::Initializing; status_ = Status::Initializing;
scheduler_->schedule( eventHandler_(SocketEvent::OPEN);
[eventHandler = eventHandler_]() { eventHandler(SocketEvent::OPEN); });
} }
void WebSocketTLSClient::onMessage( void WebSocketTLSClient::onMessage(
@@ -242,36 +225,19 @@ void WebSocketTLSClient::onFail(
(reason.find("TLS handshake failed") != std::string::npos || (reason.find("TLS handshake failed") != std::string::npos ||
reason.find("Generic TLS related error") != std::string::npos); reason.find("Generic TLS related error") != std::string::npos);
if (status_ == Status::Connecting) {
if (sslError) {
try {
connected_.set_exception(
std::make_exception_ptr(SSLException("SSL handshake failed")));
} catch (...) {
// set_exception() may throw an exception
// In that case, just set the value to false.
connected_.set_value(false);
}
} else {
connected_.set_value(false);
}
}
status_ = Status::Failed; status_ = Status::Failed;
scheduler_->schedule([eventHandler = eventHandler_, sslError]() {
if (sslError) { if (sslError) {
eventHandler(SocketEvent::SSL_ERROR); eventHandler_(SocketEvent::SSL_ERROR);
} else { } else {
eventHandler(SocketEvent::ERROR); eventHandler_(SocketEvent::ERROR);
} }
});
} }
void WebSocketTLSClient::onClose( void WebSocketTLSClient::onClose(
SocketTLSClient* c, SocketTLSClient* c,
websocketpp::connection_hdl hdl) { websocketpp::connection_hdl hdl) {
status_ = Status::Closed; status_ = Status::Closed;
scheduler_->schedule( eventHandler_(SocketEvent::CLOSE);
[eventHandler = eventHandler_]() { eventHandler(SocketEvent::CLOSE); });
} }
SocketTLSContext WebSocketTLSClient::onTLSInit( SocketTLSContext WebSocketTLSClient::onTLSInit(

View File

@@ -50,7 +50,7 @@ class WebSocketTLSClient : public BaseClient {
virtual ~WebSocketTLSClient(); virtual ~WebSocketTLSClient();
virtual bool connect(FlipperConnectionManager* manager) override; virtual void connect(FlipperConnectionManager* manager) override;
virtual void disconnect() override; virtual void disconnect() override;
virtual void send(const folly::dynamic& message, SocketSendHandler completion) virtual void send(const folly::dynamic& message, SocketSendHandler completion)
@@ -76,7 +76,6 @@ class WebSocketTLSClient : public BaseClient {
SocketTLSThread thread_; SocketTLSThread thread_;
websocketpp::connection_hdl handle_; websocketpp::connection_hdl handle_;
std::promise<bool> connected_;
}; };
} // namespace flipper } // namespace flipper