Skip to content

Commit b3a2043

Browse files
committed
Make libwebsockets also work with security profile 1 (so that the url is not set to wss). Extra check if websocket is already stopped. Extra check if a reconnection is already taking place (in connectivity manager).
Signed-off-by: Maaike <maaike@iolar.nl>
1 parent 0ffdeb4 commit b3a2043

File tree

9 files changed

+65
-82
lines changed

9 files changed

+65
-82
lines changed

dependencies.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ libwebsockets:
5555
- LWS_WITHOUT_TEST_SERVER_EXTPOLL ON
5656
- LWS_WITHOUT_TEST_PING ON
5757
- LWS_WITHOUT_TEST_CLIENT ON
58+
- LWS_WITH_NETWORK ON
5859
gtest:
5960
# GoogleTest now follows the Abseil Live at Head philosophy. We recommend updating to the latest commit in the main branch as often as possible.
6061
git: https://github.com/google/googletest.git

doc/build-with-fetchcontent/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ macro(find_package PACKAGE_NAME)
160160
find_nlohmann_json_schema_validator()
161161
elseif("${PACKAGE_NAME}" STREQUAL "websocketpp")
162162
find_websocketpp()
163+
elseif("${PACKAGE_NAME}" STREQUAL "libwebsockets")
164+
find_libwebsockets()
163165
elseif("${PACKAGE_NAME}" STREQUAL "fsm")
164166
find_fsm()
165167
elseif("${PACKAGE_NAME}" STREQUAL "everest-timer")

include/ocpp/common/websocket/websocket_base.hpp

-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ class WebsocketBase {
5858
std::function<void(ConnectionFailedReason)> connection_failed_callback;
5959
std::unique_ptr<Everest::SteadyTimer> ping_timer;
6060
websocketpp::connection_hdl handle;
61-
std::mutex reconnect_mutex;
6261
std::mutex connection_mutex;
6362
bool shutting_down;
6463

include/ocpp/common/websocket/websocket_libwebsockets.hpp

+5-4
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class WebsocketTlsTPM final : public WebsocketBase {
2727
explicit WebsocketTlsTPM(const WebsocketConnectionOptions& connection_options,
2828
std::shared_ptr<EvseSecurity> evse_security);
2929

30-
~WebsocketTlsTPM();
30+
virtual ~WebsocketTlsTPM() override;
3131

3232
void set_connection_options(const WebsocketConnectionOptions& connection_options) override;
3333

@@ -38,10 +38,10 @@ class WebsocketTlsTPM final : public WebsocketBase {
3838
/// \brief Reconnects the websocket using the delay, a reason for this reconnect can be provided with the
3939
/// \param reason parameter
4040
/// \param delay delay of the reconnect attempt
41-
void reconnect(std::error_code reason, long delay) override;
41+
void reconnect() override;
4242

4343
/// \brief closes the websocket
44-
void close(WebsocketCloseReason code, const std::string& reason) override;
44+
void close(WebsocketCloseReason code, const std::string& reason, const bool stop_perpetual = false) override;
4545

4646
/// \brief send a \p message over the websocket
4747
/// \returns true if the message was sent successfully
@@ -82,7 +82,6 @@ class WebsocketTlsTPM final : public WebsocketBase {
8282
std::shared_ptr<EvseSecurity> evse_security;
8383

8484
// Connection related data
85-
Everest::SteadyTimer reconnect_timer_tpm;
8685
std::unique_ptr<std::thread> websocket_thread;
8786
std::shared_ptr<ConnectionData> conn_data;
8887
std::condition_variable conn_cv;
@@ -97,6 +96,8 @@ class WebsocketTlsTPM final : public WebsocketBase {
9796
std::mutex recv_mutex;
9897
std::queue<std::string> recv_message_queue;
9998
std::condition_variable recv_message_cv;
99+
100+
std::atomic_bool stopped;
100101
};
101102

102103
} // namespace ocpp

include/ocpp/common/websocket/websocket_tls.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ class WebsocketTLS final : public WebsocketBase {
2626
tls_client wss_client;
2727
std::shared_ptr<EvseSecurity> evse_security;
2828
websocketpp::lib::shared_ptr<websocketpp::lib::thread> websocket_thread;
29+
std::atomic_bool closed;
30+
2931
/// \brief Called when a TLS websocket connection gets initialized, manages the supported TLS versions, cipher lists
3032
/// and how verification of the server certificate is handled
3133
tls_context on_tls_init(std::string hostname, websocketpp::connection_hdl hdl, int32_t security_profile);

include/ocpp/v201/connectivity_manager.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ class ConnectivityManager {
7272
int connection_attempts;
7373
/// \brief The last reconnect wait time.
7474
std::chrono::milliseconds reconnect_backoff_ms;
75+
/// \brief Something is waiting to reconnect, do not reconnect in the meantime.
76+
std::atomic_bool wait_for_reconnect;
7577

7678
/* Callbacks for networking */
7779
/// \brief The message callback.

lib/CMakeLists.txt

+7-1
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,16 @@ target_link_libraries(ocpp
124124
)
125125

126126
if(LIBOCPP_ENABLE_LIBWEBSOCKETS)
127+
set(LIBWEBSOCKETS_LIBRARY)
128+
if(LWS_WITH_STATIC)
129+
set(LIBOCPP_LIBWEBSOCKETS_LIBRARY ${LIBWEBSOCKETS_LIBRARIES_STATIC})
130+
else()
131+
set(LIBOCPP_LIBWEBSOCKETS_LIBRARY ${LIBWEBSOCKETS_LIBRARIES_SHARED})
132+
endif()
127133
find_package(libwebsockets REQUIRED)
128134
target_link_libraries(ocpp
129135
PUBLIC
130-
websockets_shared
136+
${LIBOCPP_LIBWEBSOCKETS_LIBRARY}
131137
)
132138
target_compile_definitions(ocpp
133139
PRIVATE

lib/ocpp/common/websocket/websocket_libwebsockets.cpp

+34-69
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ static bool verify_csms_cn(const std::string& hostname, bool preverified, const
206206

207207
WebsocketTlsTPM::WebsocketTlsTPM(const WebsocketConnectionOptions& connection_options,
208208
std::shared_ptr<EvseSecurity> evse_security) :
209-
WebsocketBase(), evse_security(evse_security) {
209+
WebsocketBase(), evse_security(evse_security), stopped(false) {
210210

211211
set_connection_options(connection_options);
212212

@@ -232,17 +232,18 @@ void WebsocketTlsTPM::set_connection_options(const WebsocketConnectionOptions& c
232232
switch (connection_options.security_profile) { // `switch` used to lint on missing enum-values
233233
case security::SecurityProfile::OCPP_1_6_ONLY_UNSECURED_TRANSPORT_WITHOUT_BASIC_AUTHENTICATION:
234234
case security::SecurityProfile::UNSECURED_TRANSPORT_WITH_BASIC_AUTHENTICATION:
235+
set_connection_options_base(connection_options);
236+
this->connection_options.csms_uri.set_secure(false);
237+
break;
235238
case security::SecurityProfile::TLS_WITH_BASIC_AUTHENTICATION:
236239
case security::SecurityProfile::TLS_WITH_CLIENT_SIDE_CERTIFICATES:
240+
set_connection_options_base(connection_options);
241+
this->connection_options.csms_uri.set_secure(true);
237242
break;
238243
default:
239244
throw std::invalid_argument("unknown `security_profile`, value = " +
240245
std::to_string(connection_options.security_profile));
241246
}
242-
243-
set_connection_options_base(connection_options);
244-
245-
this->connection_options.csms_uri.set_secure(true);
246247
}
247248

248249
static int callback_minimal(struct lws* wsi, enum lws_callback_reasons reason, void* user, void* in, size_t len) {
@@ -362,6 +363,10 @@ void WebsocketTlsTPM::tls_init(SSL_CTX* ctx, const std::string& path_chain, cons
362363
}
363364

364365
void WebsocketTlsTPM::recv_loop() {
366+
if (stopped) {
367+
return;
368+
}
369+
365370
std::shared_ptr<ConnectionData> local_data = conn_data;
366371

367372
if (local_data == nullptr) {
@@ -402,6 +407,10 @@ void WebsocketTlsTPM::recv_loop() {
402407
}
403408

404409
void WebsocketTlsTPM::client_loop() {
410+
if (stopped) {
411+
return;
412+
}
413+
405414
std::shared_ptr<ConnectionData> local_data = conn_data;
406415

407416
if (local_data == nullptr) {
@@ -428,6 +437,15 @@ void WebsocketTlsTPM::client_loop() {
428437

429438
info.fd_limit_per_thread = 1 + 1 + 1;
430439

440+
#ifdef LWS_WITH_NETWORK
441+
if (connection_options.iface_or_ip.has_value()) {
442+
info.iface = connection_options.iface_or_ip.value().c_str();
443+
// TODO make bind_iface work.
444+
// See ticket https://github.com/EVerest/libocpp/issues/542
445+
// info.bind_iface = 1;
446+
}
447+
#endif
448+
431449
if (this->connection_options.security_profile == 2 || this->connection_options.security_profile == 3) {
432450
// Setup context - need to know the key type first
433451
std::string path_key;
@@ -598,12 +616,6 @@ bool WebsocketTlsTPM::connect() {
598616
this->recv_message_thread->join();
599617
}
600618

601-
// Stop any pending reconnect timer
602-
{
603-
std::lock_guard<std::mutex> lk(this->reconnect_mutex);
604-
this->reconnect_timer_tpm.stop();
605-
}
606-
607619
// Clear any pending messages on a new connection
608620
{
609621
std::lock_guard<std::mutex> lock(queue_mutex);
@@ -617,20 +629,6 @@ bool WebsocketTlsTPM::connect() {
617629
empty.swap(recv_message_queue);
618630
}
619631

620-
// Bind reconnect callback
621-
this->reconnect_callback = [this](const websocketpp::lib::error_code& ec) {
622-
EVLOG_info << "Reconnecting to TLS websocket at uri: " << this->connection_options.csms_uri.string()
623-
<< " with security profile: " << this->connection_options.security_profile;
624-
625-
// close connection before reconnecting
626-
if (this->m_is_connected) {
627-
EVLOG_info << "Closing websocket connection before reconnecting";
628-
this->close(websocketpp::close::status::abnormal_close, "Reconnect");
629-
}
630-
631-
this->connect();
632-
};
633-
634632
std::unique_lock<std::mutex> lock(connection_mutex);
635633

636634
// Release other threads
@@ -666,37 +664,20 @@ bool WebsocketTlsTPM::connect() {
666664
return (connected);
667665
}
668666

669-
void WebsocketTlsTPM::reconnect(std::error_code reason, long delay) {
670-
EVLOG_info << "Attempting TLS TPM reconnect with reason: " << reason << " and delay:" << delay;
667+
// void WebsocketTlsTPM::reconnect(std::error_code reason, long delay) {
668+
void WebsocketTlsTPM::reconnect() {
671669

672670
if (this->shutting_down) {
673671
EVLOG_info << "Not reconnecting because the websocket is being shutdown.";
674672
return;
675673
}
676674

677-
if (this->m_is_connected) {
678-
EVLOG_info << "Closing websocket connection before reconnecting";
679-
this->close(websocketpp::close::status::abnormal_close, "Reconnect");
675+
this->connect();
680676
}
681677

682-
EVLOG_info << "Reconnecting in: " << delay << "ms"
683-
<< ", attempt: " << this->connection_attempts;
684-
685-
{
686-
std::lock_guard<std::mutex> lk(this->reconnect_mutex);
687-
this->reconnect_timer_tpm.timeout([this]() { this->reconnect_callback(websocketpp::lib::error_code()); },
688-
std::chrono::milliseconds(delay));
689-
}
690-
}
691-
692-
void WebsocketTlsTPM::close(websocketpp::close::status::value code, const std::string& reason) {
678+
void WebsocketTlsTPM::close(WebsocketCloseReason code, const std::string& reason, const bool /*stop_perpetual*/) {
693679
EVLOG_info << "Closing TLS TPM websocket with reason: " << reason;
694680

695-
{
696-
std::lock_guard<std::mutex> lk(this->reconnect_mutex);
697-
this->reconnect_timer_tpm.stop();
698-
}
699-
700681
std::shared_ptr<ConnectionData> local_data = conn_data;
701682
if (local_data != nullptr) {
702683
// Set the trigger from us
@@ -707,16 +688,15 @@ void WebsocketTlsTPM::close(websocketpp::close::status::value code, const std::s
707688
conn_data.reset();
708689

709690
this->m_is_connected = false;
710-
std::thread closing([this]() { this->closed_callback(websocketpp::close::status::normal); });
691+
std::thread closing([this]() { this->closed_callback(WebsocketCloseReason::Normal); });
711692
closing.detach();
693+
stopped = true;
712694
}
713695

714696
void WebsocketTlsTPM::on_conn_connected() {
715697
EVLOG_info << "OCPP client successfully connected to TLS websocket server";
716698

717-
this->connection_attempts = 1; // reset connection attempts
718699
this->m_is_connected = true;
719-
this->reconnecting = false;
720700

721701
std::thread connected([this]() { this->connected_callback(this->connection_options.security_profile); });
722702
connected.detach();
@@ -727,35 +707,20 @@ void WebsocketTlsTPM::on_conn_close() {
727707

728708
std::lock_guard<std::mutex> lk(this->connection_mutex);
729709
this->m_is_connected = false;
730-
this->disconnected_callback();
731-
this->cancel_reconnect_timer();
732710

733-
std::thread closing([this]() { this->closed_callback(websocketpp::close::status::normal); });
711+
std::thread closing([this]() { this->closed_callback(WebsocketCloseReason::Normal); });
734712
closing.detach();
735713
}
736714

737715
void WebsocketTlsTPM::on_conn_fail() {
738716
EVLOG_error << "OCPP client connection failed to TLS websocket server";
739717

740718
std::lock_guard<std::mutex> lk(this->connection_mutex);
741-
if (this->m_is_connected) {
742-
std::thread disconnect([this]() { this->disconnected_callback(); });
743-
disconnect.detach();
744-
}
745-
746-
this->m_is_connected = false;
747719

748-
// -1 indicates to always attempt to reconnect
749-
if (this->connection_options.max_connection_attempts == -1 or
750-
this->connection_attempts <= this->connection_options.max_connection_attempts) {
751-
this->reconnect(std::error_code(), this->get_reconnect_interval());
720+
std::thread disconnect([this]() { this->failed_callback(WebsocketCloseReason::Normal); });
721+
disconnect.detach();
752722

753-
// Increment reconn attempts
754-
this->connection_attempts += 1;
755-
} else {
756-
EVLOG_info << "Closed TLS websocket, reconnect attempts exhausted";
757-
this->close(websocketpp::close::status::normal, "Connection failed");
758-
}
723+
this->m_is_connected = false;
759724
}
760725

761726
void WebsocketTlsTPM::on_message(void* msg, size_t len) {
@@ -1217,4 +1182,4 @@ int WebsocketTlsTPM::process_callback(void* wsi_ptr, int callback_reason, void*
12171182
return 0;
12181183
}
12191184

1220-
} // namespace ocpp
1185+
} // namespace ocpp

lib/ocpp/v201/connectivity_manager.cpp

+12-7
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ ConnectivityManager::ConnectivityManager(DeviceModel& device_model, std::shared_
4040
current_connection_options(),
4141
connection_attempts(0), // TODO don't forget to reset when new websocket is created.
4242
reconnect_backoff_ms(0),
43+
wait_for_reconnect(false),
4344
message_callback(message_callback) {
4445
}
4546

@@ -198,18 +199,15 @@ void ConnectivityManager::set_websocket_connection_options(const WebsocketConnec
198199
}
199200
}
200201

201-
void ConnectivityManager::set_websocket_connection_options_without_reconnect()
202-
{
202+
void ConnectivityManager::set_websocket_connection_options_without_reconnect() {
203203
if (this->websocket == nullptr) {
204204
return;
205205
}
206206

207207
const int32_t active_slot = this->get_active_network_configuration_slot();
208208

209-
WebsocketConnectionOptions connection_options =
210-
this->get_ws_connection_options(active_slot);
211-
if (this->current_connection_options.iface_or_ip.has_value())
212-
{
209+
WebsocketConnectionOptions connection_options = this->get_ws_connection_options(active_slot);
210+
if (this->current_connection_options.iface_or_ip.has_value()) {
213211
// This is set later and not retrieved from the get_ws_connection_options function, so copy from the current
214212
// connection options.
215213
connection_options.iface_or_ip = this->current_connection_options.iface_or_ip.value();
@@ -377,7 +375,7 @@ bool ConnectivityManager::init_websocket(std::optional<int32_t> config_slot) {
377375
}
378376

379377
// TODO this sometimes let the application hang (a thread not closing or something???)
380-
this->websocket = nullptr;
378+
// this->websocket = nullptr;
381379

382380
const auto& active_network_profile_cv = ControllerComponentVariables::ActiveNetworkProfile;
383381
if (active_network_profile_cv.variable.has_value()) {
@@ -637,8 +635,10 @@ bool ConnectivityManager::is_higher_priority_profile(const int32_t new_configura
637635

638636
void ConnectivityManager::set_retry_connection_timer(const std::chrono::milliseconds timeout) {
639637
EVLOG_info << "Trying to reconnect in " << timeout.count() / 1000 << " seconds";
638+
this->wait_for_reconnect = true;
640639
this->reconnect_timer.timeout(
641640
[this]() {
641+
this->wait_for_reconnect = false;
642642
EVLOG_debug << "Timer timed out, reconnecting.";
643643
std::unique_lock<std::mutex> lock(this->reconnect_mutex);
644644
// Notify main thread that it should reconnect.
@@ -678,6 +678,11 @@ void ConnectivityManager::reconnect(const int32_t configuration_slot, const bool
678678
return;
679679
}
680680

681+
if (this->wait_for_reconnect) {
682+
// Already waiting for reconnection, wait for that so it is not reconnecting again.
683+
return;
684+
}
685+
681686
if (!next_profile && (this->current_connection_options.max_connection_attempts == -1 ||
682687
this->connection_attempts <= this->current_connection_options.max_connection_attempts)) {
683688
std::unique_lock<std::recursive_mutex> lock(this->config_slot_mutex);

0 commit comments

Comments
 (0)