Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
Actor Framework
Commits
8e7f7b58
Commit
8e7f7b58
authored
Feb 20, 2022
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement new OpenSSL transport
parent
f1a3b267
Changes
17
Show whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
1587 additions
and
329 deletions
+1587
-329
libcaf_net/CMakeLists.txt
libcaf_net/CMakeLists.txt
+7
-0
libcaf_net/caf/net/connection_acceptor.hpp
libcaf_net/caf/net/connection_acceptor.hpp
+22
-12
libcaf_net/caf/net/handshake_worker.hpp
libcaf_net/caf/net/handshake_worker.hpp
+141
-0
libcaf_net/caf/net/multiplexer.hpp
libcaf_net/caf/net/multiplexer.hpp
+51
-26
libcaf_net/caf/net/openssl_transport.hpp
libcaf_net/caf/net/openssl_transport.hpp
+345
-0
libcaf_net/caf/net/pollset_updater.hpp
libcaf_net/caf/net/pollset_updater.hpp
+9
-4
libcaf_net/caf/net/producer_adapter.hpp
libcaf_net/caf/net/producer_adapter.hpp
+1
-7
libcaf_net/caf/net/socket_manager.hpp
libcaf_net/caf/net/socket_manager.hpp
+70
-36
libcaf_net/caf/net/stream_transport.hpp
libcaf_net/caf/net/stream_transport.hpp
+107
-79
libcaf_net/caf/net/stream_transport_error.hpp
libcaf_net/caf/net/stream_transport_error.hpp
+45
-0
libcaf_net/src/multiplexer.cpp
libcaf_net/src/multiplexer.cpp
+251
-125
libcaf_net/src/pollset_updater.cpp
libcaf_net/src/pollset_updater.cpp
+29
-19
libcaf_net/src/socket_manager.cpp
libcaf_net/src/socket_manager.cpp
+10
-4
libcaf_net/test/multiplexer.cpp
libcaf_net/test/multiplexer.cpp
+35
-17
libcaf_net/test/net/openssl_transport.cpp
libcaf_net/test/net/openssl_transport.cpp
+304
-0
libcaf_net/test/net/openssl_transport_constants.cpp
libcaf_net/test/net/openssl_transport_constants.cpp
+159
-0
libcaf_net/test/net/producer_adapter.cpp
libcaf_net/test/net/producer_adapter.cpp
+1
-0
No files found.
libcaf_net/CMakeLists.txt
View file @
8e7f7b58
...
...
@@ -19,6 +19,7 @@ caf_incubator_add_component(
net.http.method
net.http.status
net.operation
net.stream_transport_error
net.web_socket.status
HEADERS
${
CAF_NET_HEADERS
}
...
...
@@ -79,3 +80,9 @@ caf_incubator_add_component(
stream_transport
tcp_sockets
udp_datagram_socket
)
if
(
TARGET OpenSSL::SSL AND TARGET OpenSSL::Crypto
)
caf_incubator_add_test_suites
(
caf-net-test net.openssl_transport
)
target_sources
(
caf-net-test PRIVATE test/net/openssl_transport_constants.cpp
)
target_link_libraries
(
caf-net-test PRIVATE OpenSSL::SSL OpenSSL::Crypto
)
endif
()
libcaf_net/caf/net/connection_acceptor.hpp
View file @
8e7f7b58
...
...
@@ -40,33 +40,32 @@ public:
// nop
}
// --
member functions ---
----------------------------------------------------
// --
interface functions
----------------------------------------------------
template
<
class
LowerLayerPtr
>
error
init
(
socket_manager
*
owner
,
LowerLayerPtr
parent
,
const
settings
&
config
)
{
error
init
(
socket_manager
*
owner
,
LowerLayerPtr
down
,
const
settings
&
cfg
)
{
CAF_LOG_TRACE
(
""
);
owner_
=
owner
;
cfg_
=
c
onfi
g
;
if
(
auto
err
=
factory_
.
init
(
owner
,
c
onfi
g
))
cfg_
=
c
f
g
;
if
(
auto
err
=
factory_
.
init
(
owner
,
c
f
g
))
return
err
;
parent
->
register_reading
();
down
->
register_reading
();
return
none
;
}
template
<
class
LowerLayerPtr
>
read_result
handle_read_event
(
LowerLayerPtr
parent
)
{
read_result
handle_read_event
(
LowerLayerPtr
down
)
{
CAF_LOG_TRACE
(
""
);
if
(
auto
x
=
accept
(
parent
->
handle
()))
{
if
(
auto
x
=
accept
(
down
->
handle
()))
{
socket_manager_ptr
child
=
factory_
.
make
(
*
x
,
owner_
->
mpx_ptr
());
if
(
!
child
)
{
CAF_LOG_ERROR
(
"factory failed to create a new child"
);
parent
->
abort_reason
(
sec
::
runtime_error
);
down
->
abort_reason
(
sec
::
runtime_error
);
return
read_result
::
stop
;
}
if
(
auto
err
=
child
->
init
(
cfg_
))
{
CAF_LOG_ERROR
(
"failed to initialize new child:"
<<
err
);
parent
->
abort_reason
(
std
::
move
(
err
));
down
->
abort_reason
(
std
::
move
(
err
));
return
read_result
::
stop
;
}
if
(
limit_
==
0
)
{
...
...
@@ -81,8 +80,13 @@ public:
}
template
<
class
LowerLayerPtr
>
static
void
continue_reading
(
LowerLayerPtr
)
{
// nop
static
read_result
handle_buffered_data
(
LowerLayerPtr
)
{
return
read_result
::
again
;
}
template
<
class
LowerLayerPtr
>
static
read_result
handle_continue_reading
(
LowerLayerPtr
)
{
return
read_result
::
again
;
}
template
<
class
LowerLayerPtr
>
...
...
@@ -91,6 +95,12 @@ public:
return
write_result
::
stop
;
}
template
<
class
LowerLayerPtr
>
static
write_result
handle_continue_writing
(
LowerLayerPtr
)
{
CAF_LOG_ERROR
(
"connection_acceptor received continue writing event"
);
return
write_result
::
stop
;
}
template
<
class
LowerLayerPtr
>
void
abort
(
LowerLayerPtr
,
const
error
&
reason
)
{
CAF_LOG_ERROR
(
"connection_acceptor aborts due to an error: "
<<
reason
);
...
...
libcaf_net/caf/net/handshake_worker.hpp
0 → 100644
View file @
8e7f7b58
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/net/socket_manager.hpp"
#include "caf/net/stream_transport_error.hpp"
namespace
caf
::
net
{
template
<
class
OnSuccess
,
class
OnError
>
struct
default_handshake_worker_factory
{
OnSuccess
make
;
OnError
abort
;
};
/// An connect worker calls an asynchronous `connect` callback until it succeeds.
/// On success, the worker calls a factory object to transfer ownership of
/// socket and communication policy to the create the socket manager that takes
/// care of the established connection.
template
<
bool
IsServer
,
class
Socket
,
class
Policy
,
class
Factory
>
class
handshake_worker
:
public
socket_manager
{
public:
// -- member types -----------------------------------------------------------
using
super
=
socket_manager
;
using
read_result
=
typename
super
::
read_result
;
using
write_result
=
typename
super
::
write_result
;
handshake_worker
(
Socket
handle
,
multiplexer
*
parent
,
Policy
policy
,
Factory
factory
)
:
super
(
handle
,
parent
),
policy_
(
std
::
move
(
policy
)),
factory_
(
std
::
move
(
factory
))
{
// nop
}
// -- interface functions ----------------------------------------------------
error
init
(
const
settings
&
config
)
override
{
cfg_
=
config
;
register_writing
();
return
caf
::
none
;
}
read_result
handle_read_event
()
override
{
auto
fd
=
socket_cast
<
Socket
>
(
this
->
handle
());
if
(
auto
res
=
advance_handshake
(
fd
);
res
>
0
)
{
return
read_result
::
handover
;
}
else
if
(
res
==
0
)
{
factory_
.
abort
(
make_error
(
sec
::
connection_closed
));
return
read_result
::
stop
;
}
else
{
auto
err
=
policy_
.
last_error
(
fd
,
res
);
switch
(
err
)
{
case
stream_transport_error
:
:
want_read
:
case
stream_transport_error
:
:
temporary
:
return
read_result
::
again
;
case
stream_transport_error
:
:
want_write
:
return
read_result
::
want_write
;
default:
auto
err
=
make_error
(
sec
::
cannot_connect_to_node
,
policy_
.
fetch_error_str
());
factory_
.
abort
(
std
::
move
(
err
));
return
read_result
::
stop
;
}
}
}
read_result
handle_buffered_data
()
override
{
return
read_result
::
again
;
}
read_result
handle_continue_reading
()
override
{
return
read_result
::
again
;
}
write_result
handle_write_event
()
override
{
auto
fd
=
socket_cast
<
Socket
>
(
this
->
handle
());
if
(
auto
res
=
advance_handshake
(
fd
);
res
>
0
)
{
return
write_result
::
handover
;
}
else
if
(
res
==
0
)
{
factory_
.
abort
(
make_error
(
sec
::
connection_closed
));
return
write_result
::
stop
;
}
else
{
switch
(
policy_
.
last_error
(
fd
,
res
))
{
case
stream_transport_error
:
:
want_write
:
case
stream_transport_error
:
:
temporary
:
return
write_result
::
again
;
case
stream_transport_error
:
:
want_read
:
return
write_result
::
want_read
;
default:
auto
err
=
make_error
(
sec
::
cannot_connect_to_node
,
policy_
.
fetch_error_str
());
factory_
.
abort
(
std
::
move
(
err
));
return
write_result
::
stop
;
}
}
}
write_result
handle_continue_writing
()
override
{
return
write_result
::
again
;
}
void
handle_error
(
sec
code
)
override
{
factory_
.
abort
(
make_error
(
code
));
}
socket_manager_ptr
make_next_manager
(
socket
hdl
)
override
{
auto
ptr
=
factory_
.
make
(
socket_cast
<
Socket
>
(
hdl
),
this
->
mpx_ptr
(),
std
::
move
(
policy_
));
if
(
ptr
)
{
if
(
auto
err
=
ptr
->
init
(
cfg_
))
{
factory_
.
abort
(
err
);
return
nullptr
;
}
else
{
return
ptr
;
}
}
else
{
factory_
.
abort
(
make_error
(
sec
::
runtime_error
,
"factory_.make failed"
));
return
nullptr
;
}
}
private:
ptrdiff_t
advance_handshake
(
Socket
fd
)
{
if
constexpr
(
IsServer
)
return
policy_
.
accept
(
fd
);
else
return
policy_
.
connect
(
fd
);
}
settings
cfg_
;
Policy
policy_
;
Factory
factory_
;
};
}
// namespace caf::net
libcaf_net/caf/net/multiplexer.hpp
View file @
8e7f7b58
...
...
@@ -47,6 +47,12 @@ public:
friend
class
pollset_updater
;
// Needs access to the `do_*` functions.
// -- static utility functions -----------------------------------------------
/// Blocks the PIPE signal on the current thread when running on a POSIX
/// windows. Has no effect when running on Windows.
static
void
block_sigpipe
();
// -- constructors, destructors, and assignment operators --------------------
/// @param parent Points to the owning middleman instance. May be `nullptr`
...
...
@@ -91,6 +97,14 @@ public:
/// @thread-safe
void
register_writing
(
const
socket_manager_ptr
&
mgr
);
/// Triggers a continue reading event for `mgr`.
/// @thread-safe
void
continue_reading
(
const
socket_manager_ptr
&
mgr
);
/// Triggers a continue writing event for `mgr`.
/// @thread-safe
void
continue_writing
(
const
socket_manager_ptr
&
mgr
);
/// Schedules a call to `mgr->handle_error(sec::discarded)`.
/// @thread-safe
void
discard
(
const
socket_manager_ptr
&
mgr
);
...
...
@@ -118,10 +132,9 @@ public:
/// @thread-safe
void
init
(
const
socket_manager_ptr
&
mgr
);
/// Closes the pipe for signaling updates to the multiplexer. After closing
/// the pipe, calls to `update` no longer have any effect.
/// Signals the multiplexer to initiate shutdown.
/// @thread-safe
void
close_pipe
();
void
shutdown
();
// -- control flow -----------------------------------------------------------
...
...
@@ -138,16 +151,42 @@ public:
/// Polls until no socket event handler remains.
void
run
();
/// Signals the multiplexer to initiate shutdown.
/// @thread-safe
void
shutdown
();
protected:
// -- utility functions ------------------------------------------------------
/// Handles an I/O event on given manager.
void
handle
(
const
socket_manager_ptr
&
mgr
,
short
events
,
short
revents
);
/// Transfers socket ownership from one manager to another.
void
do_handover
(
const
socket_manager_ptr
&
mgr
);
/// Returns a change entry for the socket at given index. Lazily creates a new
/// entry before returning if necessary.
poll_update
&
update_for
(
ptrdiff_t
index
);
/// Returns a change entry for the socket of the manager.
poll_update
&
update_for
(
const
socket_manager_ptr
&
mgr
);
/// Writes `opcode` and pointer to `mgr` the the pipe for handling an event
/// later via the pollset updater.
template
<
class
T
>
void
write_to_pipe
(
uint8_t
opcode
,
T
*
ptr
);
/// @copydoc write_to_pipe
template
<
class
Enum
,
class
T
>
std
::
enable_if_t
<
std
::
is_enum_v
<
Enum
>>
write_to_pipe
(
Enum
opcode
,
T
*
ptr
)
{
write_to_pipe
(
static_cast
<
uint8_t
>
(
opcode
),
ptr
);
}
/// Queries the currently active event bitmask for `mgr`.
short
active_mask_of
(
const
socket_manager_ptr
&
mgr
);
/// Queries whether `mgr` is currently registered for reading.
bool
is_reading
(
const
socket_manager_ptr
&
mgr
);
/// Queries whether `mgr` is currently registered for writing.
bool
is_writing
(
const
socket_manager_ptr
&
mgr
);
// -- member variables -------------------------------------------------------
/// Bookkeeping data for managed sockets.
...
...
@@ -178,25 +217,7 @@ protected:
bool
shutting_down_
=
false
;
private:
/// Returns a change entry for the socket at given index. Lazily creates a new
/// entry before returning if necessary.
poll_update
&
update_for
(
ptrdiff_t
index
);
/// Returns a change entry for the socket of the manager.
poll_update
&
update_for
(
const
socket_manager_ptr
&
mgr
);
/// Writes `opcode` and pointer to `mgr` the the pipe for handling an event
/// later via the pollset updater.
template
<
class
T
>
void
write_to_pipe
(
uint8_t
opcode
,
T
*
ptr
);
/// @copydoc write_to_pipe
template
<
class
Enum
,
class
T
>
std
::
enable_if_t
<
std
::
is_enum_v
<
Enum
>>
write_to_pipe
(
Enum
opcode
,
T
*
ptr
)
{
write_to_pipe
(
static_cast
<
uint8_t
>
(
opcode
),
ptr
);
}
// -- internal callback the pollset updater ----------------------------------
// -- internal callbacks the pollset updater ---------------------------------
void
do_shutdown
();
...
...
@@ -204,6 +225,10 @@ private:
void
do_register_writing
(
const
socket_manager_ptr
&
mgr
);
void
do_continue_reading
(
const
socket_manager_ptr
&
mgr
);
void
do_continue_writing
(
const
socket_manager_ptr
&
mgr
);
void
do_discard
(
const
socket_manager_ptr
&
mgr
);
void
do_shutdown_reading
(
const
socket_manager_ptr
&
mgr
);
...
...
libcaf_net/caf/net/openssl_transport.hpp
0 → 100644
View file @
8e7f7b58
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/byte_buffer.hpp"
#include "caf/defaults.hpp"
#include "caf/detail/has_after_reading.hpp"
#include "caf/fwd.hpp"
#include "caf/logger.hpp"
#include "caf/net/fwd.hpp"
#include "caf/net/handshake_worker.hpp"
#include "caf/net/multiplexer.hpp"
#include "caf/net/receive_policy.hpp"
#include "caf/net/stream_oriented_layer_ptr.hpp"
#include "caf/net/stream_socket.hpp"
#include "caf/net/stream_transport.hpp"
#include "caf/sec.hpp"
#include "caf/settings.hpp"
#include "caf/span.hpp"
#include "caf/tag/io_event_oriented.hpp"
#include "caf/tag/stream_oriented.hpp"
CAF_PUSH_WARNINGS
#include <openssl/err.h>
#include <openssl/ssl.h>
CAF_POP_WARNINGS
#if OPENSSL_VERSION_NUMBER < 0x10100000L
# error "OpenSSL version too old. CAF::net requires at least OpenSSL 1.1"
#endif
#include <string>
#include <string_view>
// -- small wrappers to help working with OpenSSL ------------------------------
namespace
caf
::
net
::
openssl
{
/// Dispatches to the proper OpenSSL `free` function for each OpenSSL type.
struct
deleter
{
void
operator
()(
SSL_CTX
*
ptr
)
const
{
SSL_CTX_free
(
ptr
);
}
void
operator
()(
SSL
*
ptr
)
const
{
SSL_free
(
ptr
);
}
};
/// A smart pointer to an `SSL_CTX` structure.
/// @note technically, SSL structures are reference counted and we could use
/// `intrusive_ptr` instead. However, we have no need for shared ownership
/// semantics here and use `unique_ptr` for simplicity.
using
ctx_ptr
=
std
::
unique_ptr
<
SSL_CTX
,
deleter
>
;
/// A smart pointer to an `SSL` structure.
/// @note technically, SSL structures are reference counted and we could use
/// `intrusive_ptr` instead. However, we have no need for shared ownership
/// semantics here and use `unique_ptr` for simplicity.
using
conn_ptr
=
std
::
unique_ptr
<
SSL
,
deleter
>
;
/// Convenience function for creating an OpenSSL context for given method.
inline
ctx_ptr
make_ctx
(
const
SSL_METHOD
*
method
)
{
if
(
auto
ptr
=
SSL_CTX_new
(
method
))
return
ctx_ptr
{
ptr
};
else
CAF_RAISE_ERROR
(
"SSL_CTX_new failed"
);
}
/// Fetches a string representation for the last OpenSSL errors.
std
::
string
fetch_error_str
()
{
auto
cb
=
[](
const
char
*
cstr
,
size_t
len
,
void
*
vptr
)
->
int
{
auto
&
str
=
*
reinterpret_cast
<
std
::
string
*>
(
vptr
);
if
(
str
.
empty
())
{
str
.
assign
(
cstr
,
len
);
}
else
{
str
+=
"; "
;
auto
view
=
std
::
string_view
{
cstr
,
len
};
str
.
insert
(
str
.
end
(),
view
.
begin
(),
view
.
end
());
}
return
1
;
};
std
::
string
result
;
ERR_print_errors_cb
(
cb
,
&
result
);
return
result
;
}
/// Loads the certificate into the SSL context.
inline
error
certificate_pem_file
(
const
ctx_ptr
&
ctx
,
const
std
::
string
&
path
)
{
auto
cstr
=
path
.
c_str
();
if
(
SSL_CTX_use_certificate_file
(
ctx
.
get
(),
cstr
,
SSL_FILETYPE_PEM
)
>
0
)
{
return
none
;
}
else
{
return
make_error
(
sec
::
invalid_argument
,
fetch_error_str
());
}
}
/// Loads the private key into the SSL context.
inline
error
private_key_pem_file
(
const
ctx_ptr
&
ctx
,
const
std
::
string
&
path
)
{
auto
cstr
=
path
.
c_str
();
if
(
SSL_CTX_use_PrivateKey_file
(
ctx
.
get
(),
cstr
,
SSL_FILETYPE_PEM
)
>
0
)
{
return
none
;
}
else
{
return
make_error
(
sec
::
invalid_argument
,
fetch_error_str
());
}
}
/// Convenience function for creating a new SSL structure from given context.
inline
conn_ptr
make_conn
(
const
ctx_ptr
&
ctx
)
{
if
(
auto
ptr
=
SSL_new
(
ctx
.
get
()))
return
conn_ptr
{
ptr
};
else
CAF_RAISE_ERROR
(
"SSL_new failed"
);
}
/// Convenience function for creating a new SSL structure from given context and
/// binding the given socket to it.
inline
conn_ptr
make_conn
(
const
ctx_ptr
&
ctx
,
stream_socket
fd
)
{
auto
ptr
=
make_conn
(
ctx
);
if
(
SSL_set_fd
(
ptr
.
get
(),
fd
.
id
))
return
ptr
;
else
CAF_RAISE_ERROR
(
"SSL_set_fd failed"
);
}
/// Manages an OpenSSL connection.
class
policy
{
public:
// -- constructors, destructors, and assignment operators --------------------
policy
()
=
delete
;
policy
(
const
policy
&
)
=
delete
;
policy
&
operator
=
(
const
policy
&
)
=
delete
;
policy
(
policy
&&
)
=
default
;
policy
&
operator
=
(
policy
&&
)
=
default
;
policy
(
ctx_ptr
ctx
,
conn_ptr
conn
)
:
ctx_
(
std
::
move
(
ctx
)),
conn_
(
std
::
move
(
conn
))
{
// nop
}
// -- factories --------------------------------------------------------------
/// Creates a policy from an SSL method and socket.
static
policy
make
(
const
SSL_METHOD
*
method
,
stream_socket
fd
)
{
auto
ctx
=
make_ctx
(
method
);
auto
conn
=
make_conn
(
ctx
,
fd
);
return
policy
{
std
::
move
(
ctx
),
std
::
move
(
conn
)};
}
/// Creates a policy from an SSL context and socket.
static
policy
make
(
ctx_ptr
ctx
,
stream_socket
fd
)
{
auto
conn
=
make_conn
(
ctx
,
fd
);
return
policy
{
std
::
move
(
ctx
),
std
::
move
(
conn
)};
}
// -- properties -------------------------------------------------------------
SSL_CTX
*
ctx
()
{
return
ctx_
.
get
();
}
SSL
*
conn
()
{
return
conn_
.
get
();
}
// -- OpenSSL settings -------------------------------------------------------
/// Loads the certificate into the SSL connection object.
error
certificate_pem_file
(
const
std
::
string
&
path
)
{
auto
cstr
=
path
.
c_str
();
if
(
SSL_use_certificate_file
(
conn
(),
cstr
,
SSL_FILETYPE_PEM
)
>
0
)
{
return
none
;
}
else
{
return
make_error
(
sec
::
invalid_argument
,
fetch_error_str
());
}
}
/// Loads the private key into the SSL connection object.
error
private_key_pem_file
(
const
std
::
string
&
path
)
{
auto
cstr
=
path
.
c_str
();
if
(
SSL_use_PrivateKey_file
(
conn
(),
cstr
,
SSL_FILETYPE_PEM
)
>
0
)
{
return
none
;
}
else
{
return
make_error
(
sec
::
invalid_argument
,
fetch_error_str
());
}
}
// -- interface functions for the stream transport ---------------------------
/// Fetches a string representation for the last error.
std
::
string
fetch_error_str
()
{
return
openssl
::
fetch_error_str
();
}
/// Reads data from the SSL connection into the buffer.
ptrdiff_t
read
(
stream_socket
,
span
<
byte
>
buf
)
{
return
SSL_read
(
conn_
.
get
(),
buf
.
data
(),
static_cast
<
int
>
(
buf
.
size
()));
}
/// Writes data from the buffer to the SSL connection.
ptrdiff_t
write
(
stream_socket
,
span
<
const
byte
>
buf
)
{
return
SSL_write
(
conn_
.
get
(),
buf
.
data
(),
static_cast
<
int
>
(
buf
.
size
()));
}
/// Performs a TLS/SSL handshake with the server.
ptrdiff_t
connect
(
stream_socket
)
{
return
SSL_connect
(
conn_
.
get
());
}
/// Waits for the client to performs a TLS/SSL handshake.
ptrdiff_t
accept
(
stream_socket
)
{
return
SSL_accept
(
conn_
.
get
());
}
/// Returns the last SSL error.
stream_transport_error
last_error
(
stream_socket
fd
,
ptrdiff_t
ret
)
{
switch
(
SSL_get_error
(
conn_
.
get
(),
static_cast
<
int
>
(
ret
)))
{
case
SSL_ERROR_NONE
:
case
SSL_ERROR_WANT_ACCEPT
:
case
SSL_ERROR_WANT_CONNECT
:
// For all of these, OpenSSL docs say to do the operation again later.
return
stream_transport_error
::
temporary
;
case
SSL_ERROR_SYSCALL
:
// Need to consult errno, which we just leave to the default policy.
return
default_stream_transport_policy
::
last_error
(
fd
,
ret
);
case
SSL_ERROR_WANT_READ
:
return
stream_transport_error
::
want_read
;
case
SSL_ERROR_WANT_WRITE
:
return
stream_transport_error
::
want_write
;
default:
// Errors like SSL_ERROR_WANT_X509_LOOKUP are technically temporary, but
// we do not configure any callbacks. So seeing this is a red flag.
return
stream_transport_error
::
permanent
;
}
}
/// Graceful shutdown.
void
notify_close
()
{
SSL_shutdown
(
conn_
.
get
());
}
/// Returns the number of bytes that are buffered internally and that are
/// available for immediate read.
size_t
buffered
()
{
return
static_cast
<
size_t
>
(
SSL_pending
(
conn_
.
get
()));
}
private:
/// Our SSL context.
openssl
::
ctx_ptr
ctx_
;
/// Our SSL connection data.
openssl
::
conn_ptr
conn_
;
};
/// Asynchronously starts the TLS/SSL client handshake.
/// @param fd A connected stream socket.
/// @param mpx Pointer to the multiplexer for managing the asynchronous events.
/// @param pol The OpenSSL policy with properly configured SSL/TSL method.
/// @param on_success The callback for creating a @ref socket_manager after
/// successfully connecting to the server.
/// @param on_error The callback for unexpected errors.
/// @pre `fd != invalid_socket`
/// @pre `mpx != nullptr`
template
<
class
Socket
,
class
OnSuccess
,
class
OnError
>
void
async_connect
(
Socket
fd
,
multiplexer
*
mpx
,
policy
pol
,
OnSuccess
on_success
,
OnError
on_error
)
{
using
res_t
=
decltype
(
on_success
(
fd
,
mpx
,
std
::
move
(
pol
)));
using
err_t
=
decltype
(
on_error
(
error
{}));
static_assert
(
std
::
is_convertible_v
<
res_t
,
socket_manager_ptr
>
,
"on_success must return a socket_manager_ptr"
);
static_assert
(
std
::
is_same_v
<
err_t
,
void
>
,
"on_error may not return anything"
);
using
factory_t
=
default_handshake_worker_factory
<
OnSuccess
,
OnError
>
;
using
worker_t
=
handshake_worker
<
false
,
Socket
,
policy
,
factory_t
>
;
auto
factory
=
factory_t
{
std
::
move
(
on_success
),
std
::
move
(
on_error
)};
auto
mgr
=
make_counted
<
worker_t
>
(
fd
,
mpx
,
std
::
move
(
pol
),
std
::
move
(
factory
));
mpx
->
init
(
mgr
);
}
/// Asynchronously starts the TLS/SSL server handshake.
/// @param fd A connected stream socket.
/// @param mpx Pointer to the multiplexer for managing the asynchronous events.
/// @param pol The OpenSSL policy with properly configured SSL/TSL method.
/// @param on_success The callback for creating a @ref socket_manager after
/// successfully connecting to the client.
/// @param on_error The callback for unexpected errors.
/// @pre `fd != invalid_socket`
/// @pre `mpx != nullptr`
template
<
class
Socket
,
class
OnSuccess
,
class
OnError
>
void
async_accept
(
Socket
fd
,
multiplexer
*
mpx
,
policy
pol
,
OnSuccess
on_success
,
OnError
on_error
)
{
using
res_t
=
decltype
(
on_success
(
fd
,
mpx
,
std
::
move
(
pol
)));
using
err_t
=
decltype
(
on_error
(
error
{}));
static_assert
(
std
::
is_convertible_v
<
res_t
,
socket_manager_ptr
>
,
"on_success must return a socket_manager_ptr"
);
static_assert
(
std
::
is_same_v
<
err_t
,
void
>
,
"on_error may not return anything"
);
using
factory_t
=
default_handshake_worker_factory
<
OnSuccess
,
OnError
>
;
using
worker_t
=
handshake_worker
<
true
,
Socket
,
policy
,
factory_t
>
;
auto
factory
=
factory_t
{
std
::
move
(
on_success
),
std
::
move
(
on_error
)};
auto
mgr
=
make_counted
<
worker_t
>
(
fd
,
mpx
,
std
::
move
(
pol
),
std
::
move
(
factory
));
mpx
->
init
(
mgr
);
}
}
// namespace caf::net::openssl
namespace
caf
::
net
{
/// Implements a stream_transport that manages a stream socket with encrypted
/// communication over OpenSSL.
template
<
class
UpperLayer
>
class
openssl_transport
:
public
stream_transport_base
<
openssl
::
policy
,
UpperLayer
>
{
public:
// -- member types -----------------------------------------------------------
using
super
=
stream_transport_base
<
openssl
::
policy
,
UpperLayer
>
;
// -- constructors, destructors, and assignment operators --------------------
template
<
class
...
Ts
>
openssl_transport
(
openssl
::
ctx_ptr
ctx
,
openssl
::
conn_ptr
conn
,
Ts
&&
...
xs
)
:
super
(
openssl
::
policy
{
std
::
move
(
ctx
),
std
::
move
(
conn
)},
std
::
forward
<
Ts
>
(
xs
)...)
{
// nop
}
template
<
class
...
Ts
>
openssl_transport
(
openssl
::
policy
policy
,
Ts
&&
...
xs
)
:
super
(
std
::
move
(
policy
),
std
::
forward
<
Ts
>
(
xs
)...)
{
// nop
}
};
}
// namespace caf::net
libcaf_net/caf/net/pollset_updater.hpp
View file @
8e7f7b58
...
...
@@ -22,11 +22,11 @@ public:
using
msg_buf
=
std
::
array
<
byte
,
sizeof
(
intptr_t
)
+
1
>
;
// -- constants --------------------------------------------------------------
enum
class
code
:
uint8_t
{
register_reading
,
continue_reading
,
register_writing
,
continue_writing
,
init_manager
,
discard_manager
,
shutdown_reading
,
...
...
@@ -34,6 +34,7 @@ public:
run_action
,
shutdown
,
};
// -- constructors, destructors, and assignment operators --------------------
pollset_updater
(
pipe_socket
read_handle
,
multiplexer
*
parent
);
...
...
@@ -53,11 +54,15 @@ public:
read_result
handle_read_event
()
override
;
read_result
handle_buffered_data
()
override
;
read_result
handle_continue_reading
()
override
;
write_result
handle_write_event
()
override
;
void
handle_error
(
sec
code
)
override
;
write_result
handle_continue_writing
(
)
override
;
void
continue_reading
(
)
override
;
void
handle_error
(
sec
code
)
override
;
private:
msg_buf
buf_
;
...
...
libcaf_net/caf/net/producer_adapter.hpp
View file @
8e7f7b58
...
...
@@ -39,8 +39,7 @@ public:
void
on_consumer_demand
(
size_t
new_demand
)
override
{
auto
prev
=
demand_
.
fetch_add
(
new_demand
);
if
(
prev
==
0
)
mgr_
->
mpx
().
schedule_fn
(
[
adapter
=
strong_this
()]
{
adapter
->
continue_reading
();
});
mgr_
->
continue_reading
();
}
void
ref_producer
()
const
noexcept
override
{
...
...
@@ -129,11 +128,6 @@ private:
// nop
}
void
continue_reading
()
{
if
(
mgr_
)
mgr_
->
continue_reading
();
}
void
on_cancel
()
{
if
(
buf_
)
{
mgr_
->
mpx
().
shutdown_reading
(
mgr_
);
...
...
libcaf_net/caf/net/socket_manager.hpp
View file @
8e7f7b58
...
...
@@ -164,11 +164,31 @@ public:
// -- event loop management --------------------------------------------------
/// Registers the manager for read operations on the @ref multiplexer.
/// @thread-safe
void
register_reading
();
/// Registers the manager for write operations on the @ref multiplexer.
/// @thread-safe
void
register_writing
();
/// Schedules a call to `handle_continue_reading` on the @ref multiplexer.
/// This mechanism allows users to signal changes in the environment to the
/// manager that allow it to make progress, e.g., new demand in asynchronous
/// buffer that allow the manager to push available data downstream. The event
/// is a no-op if the manager is already registered for reading.
/// @thread-safe
void
continue_reading
();
/// Schedules a call to `handle_continue_reading` on the @ref multiplexer.
/// This mechanism allows users to signal changes in the environment to the
/// manager that allow it to make progress, e.g., new data for writing in an
/// asynchronous buffer. The event is a no-op if the manager is already
/// registered for writing.
/// @thread-safe
void
continue_writing
();
// -- callbacks for the multiplexer ------------------------------------------
/// Performs a handover to another manager after `handle_read_event` or
/// `handle_read_event` returned `handover`.
socket_manager_ptr
do_handover
();
...
...
@@ -181,20 +201,31 @@ public:
/// Called whenever the socket received new data.
virtual
read_result
handle_read_event
()
=
0
;
/// Called after handovers to allow the manager to process any data that is
/// already buffered at the transport policy and thus would not trigger a read
/// event on the socket.
virtual
read_result
handle_buffered_data
()
=
0
;
/// Restarts a socket manager that suspended reads. Calling this member
/// function on active managers is a no-op. This function also should read any
/// data buffered outside of the socket.
virtual
read_result
handle_continue_reading
()
=
0
;
/// Called whenever the socket is allowed to send data.
virtual
write_result
handle_write_event
()
=
0
;
/// Restarts a socket manager that suspended writes. Calling this member
/// function on active managers is a no-op.
virtual
write_result
handle_continue_writing
()
=
0
;
/// Called when the remote side becomes unreachable due to an error.
/// @param code The error code as reported by the operating system.
virtual
void
handle_error
(
sec
code
)
=
0
;
/// Restarts a socket manager that suspended reads. Calling this member
/// function on active managers is a no-op.
virtual
void
continue_reading
()
=
0
;
/// Returns the new manager for the socket after `handle_read_event` or
/// `handle_read_event` returned `handover`.
/// @note When returning a non-null pointer, the new manager *must* also be
/// @note Called from `do_handover`.
/// @note When returning a non-null pointer, the new manager *must* be
/// initialized.
virtual
socket_manager_ptr
make_next_manager
(
socket
handle
);
...
...
@@ -236,17 +267,6 @@ public:
// nop
}
// -- initialization ---------------------------------------------------------
error
init
(
const
settings
&
config
)
override
{
CAF_LOG_TRACE
(
""
);
if
(
auto
err
=
nonblocking
(
handle
(),
true
))
{
CAF_LOG_ERROR
(
"failed to set nonblocking flag in socket:"
<<
err
);
return
err
;
}
return
protocol_
.
init
(
static_cast
<
socket_manager
*>
(
this
),
this
,
config
);
}
// -- properties -------------------------------------------------------------
/// Returns the managed socket.
...
...
@@ -254,42 +274,56 @@ public:
return
socket_cast
<
socket_type
>
(
this
->
handle_
);
}
// -- event callbacks --------------------------------------------------------
auto
&
protocol
()
noexcept
{
return
protocol_
;
}
read_result
handle_read_event
()
override
{
CAF_LOG_TRACE
(
""
);
return
protocol_
.
handle_read_event
(
this
);
const
auto
&
protocol
()
const
noexcept
{
return
protocol_
;
}
write_result
handle_write_event
()
override
{
auto
&
top_layer
()
noexcept
{
return
climb
(
protocol_
);
}
const
auto
&
top_layer
()
const
noexcept
{
return
climb
(
protocol_
);
}
// -- interface functions ----------------------------------------------------
error
init
(
const
settings
&
config
)
override
{
CAF_LOG_TRACE
(
""
);
return
protocol_
.
handle_write_event
(
this
);
if
(
auto
err
=
nonblocking
(
handle
(),
true
))
{
CAF_LOG_ERROR
(
"failed to set nonblocking flag in socket:"
<<
err
);
return
err
;
}
return
protocol_
.
init
(
static_cast
<
socket_manager
*>
(
this
),
this
,
config
);
}
void
handle_error
(
sec
code
)
override
{
CAF_LOG_TRACE
(
CAF_ARG
(
code
));
this
->
abort_reason
(
make_error
(
code
));
return
protocol_
.
abort
(
this
,
this
->
abort_reason
());
read_result
handle_read_event
()
override
{
return
protocol_
.
handle_read_event
(
this
);
}
void
continue_reading
()
override
{
return
protocol_
.
continue_reading
(
this
);
read_result
handle_buffered_data
()
override
{
return
protocol_
.
handle_buffered_data
(
this
);
}
auto
&
protocol
()
noexcept
{
return
protocol_
;
read_result
handle_continue_reading
()
override
{
return
protocol_
.
handle_continue_reading
(
this
)
;
}
const
auto
&
protocol
()
const
noexcept
{
return
protocol_
;
write_result
handle_write_event
()
override
{
return
protocol_
.
handle_write_event
(
this
)
;
}
auto
&
top_layer
()
noexcept
{
return
climb
(
protocol_
);
write_result
handle_continue_writing
()
override
{
return
protocol_
.
handle_continue_writing
(
this
);
}
const
auto
&
top_layer
()
const
noexcept
{
return
climb
(
protocol_
);
void
handle_error
(
sec
code
)
override
{
this
->
abort_reason
(
make_error
(
code
));
return
protocol_
.
abort
(
this
,
this
->
abort_reason
());
}
private:
...
...
libcaf_net/caf/net/stream_transport.hpp
View file @
8e7f7b58
...
...
@@ -16,6 +16,7 @@
#include "caf/net/socket_manager.hpp"
#include "caf/net/stream_oriented_layer_ptr.hpp"
#include "caf/net/stream_socket.hpp"
#include "caf/net/stream_transport_error.hpp"
#include "caf/sec.hpp"
#include "caf/settings.hpp"
#include "caf/span.hpp"
...
...
@@ -24,17 +25,6 @@
namespace
caf
::
net
{
enum
class
stream_transport_error
{
/// Indicates that the transport should try again later.
temporary
,
/// Indicates that the transport must read data before trying again.
want_read
,
/// Indicates that the transport must write data before trying again.
want_write
,
/// Indicates that the transport cannot resume this operation.
permanent
,
};
/// Configures a stream transport with default socket operations.
struct
default_stream_transport_policy
{
public:
...
...
@@ -77,6 +67,15 @@ public:
using
write_result
=
typename
socket_manager
::
write_result
;
/// Bundles various flags into a single block of memory.
struct
flags_t
{
/// Stores whether we left a read handler due to want_write.
bool
wanted_read_from_write_event
:
1
;
/// Stores whether we left a write handler due to want_read.
bool
wanted_write_from_read_event
:
1
;
};
// -- constructors, destructors, and assignment operators --------------------
template
<
class
...
Ts
>
...
...
@@ -204,18 +203,11 @@ public:
upper_layer_
.
abort
(
this_layer_ptr
,
parent
->
abort_reason
());
return
read_result
::
stop
;
};
// Convenience lambda for invoking the next layer.
auto
invoke_upper_layer
=
[
this
,
&
this_layer_ptr
](
byte
*
ptr
,
ptrdiff_t
off
,
ptrdiff_t
delta
)
{
auto
bytes
=
make_span
(
ptr
,
off
);
return
upper_layer_
.
consume
(
this_layer_ptr
,
bytes
,
bytes
.
subspan
(
delta
));
};
// Resume a write operation if the transport waited for the socket to be
// readable from the last call to handle_write_event.
if
(
flags
.
wanted_read_from_write_event
)
{
flags
.
wanted_read_from_write_event
=
false
;
switch
(
handle_write_event
(
parent
))
{
switch
(
handle_write_event
(
parent
))
{
case
write_result
:
:
want_read
:
CAF_ASSERT
(
flags
.
wanted_read_from_write_event
);
return
read_result
::
again
;
...
...
@@ -230,11 +222,9 @@ public:
}
// Before returning from the event handler, we always call after_reading for
// clients that request this callback.
auto
after_reading_guard
=
detail
::
make_scope_guard
([
this
,
&
this_layer_ptr
]
{
if
constexpr
(
detail
::
has_after_reading_v
<
UpperLayer
,
decltype
(
this_layer_ptr
)
>
)
upper_layer_
.
after_reading
(
this_layer_ptr
);
using
detail
::
make_scope_guard
;
auto
after_reading_guard
=
make_scope_guard
([
this
,
&
this_layer_ptr
]
{
//
after_reading
(
this_layer_ptr
);
});
// Loop until meeting one of our stop criteria.
for
(
size_t
read_count
=
0
;;)
{
...
...
@@ -273,55 +263,10 @@ public:
return
fail
(
sec
::
socket_disconnected
);
}
++
read_count
;
// Ask the next layer to process some data.
offset_
+=
read_res
;
auto
internal_buffer_size
=
policy_
.
buffered
();
// The offset_ may change as a result of invoking the upper layer. Hence,
// need to run this in a loop to push data up for as long as we have
// buffered data available.
while
(
offset_
>=
min_read_size_
)
{
// Here, we have yet another loop. This one makes sure that we do not
// leave this event handler if we can make progress from the data
// buffered inside the socket policy. For 'raw' policies (like the
// default policy), there is no buffer. However, any block-oriented
// transport like OpenSSL has to buffer data internally. We need to make
// sure to consume the buffer because the OS does not know about it and
// will not trigger a read event based on data available there.
do
{
ptrdiff_t
consumed
=
invoke_upper_layer
(
read_buf_
.
data
(),
offset_
,
delta_offset_
);
CAF_LOG_DEBUG
(
CAF_ARG2
(
"socket"
,
parent
->
handle
().
id
)
<<
CAF_ARG
(
consumed
));
if
(
consumed
<
0
)
{
upper_layer_
.
abort
(
this_layer_ptr
,
parent
->
abort_reason_or
(
caf
::
sec
::
runtime_error
,
"consumed < 0"
));
return
read_result
::
stop
;
}
// Shift unconsumed bytes to the beginning of the buffer.
if
(
consumed
<
offset_
)
std
::
copy
(
read_buf_
.
begin
()
+
consumed
,
read_buf_
.
begin
()
+
offset_
,
read_buf_
.
begin
());
offset_
-=
consumed
;
delta_offset_
=
offset_
;
// Stop if the application asked for it.
if
(
max_read_size_
==
0
)
return
read_result
::
stop
;
if
(
internal_buffer_size
>
0
&&
offset_
<
max_read_size_
)
{
// Fetch already buffered data to 'refill' the buffer as we go.
auto
n
=
std
::
min
(
internal_buffer_size
,
max_read_size_
-
static_cast
<
size_t
>
(
offset_
));
auto
rdb
=
make_span
(
read_buf_
.
data
()
+
offset_
,
n
);
auto
rd
=
policy_
.
read
(
parent
->
handle
(),
rdb
);
if
(
rd
<
0
)
return
fail
(
make_error
(
caf
::
sec
::
runtime_error
,
"policy error: reading buffered data "
"may not result in an error"
));
offset_
+=
rd
;
internal_buffer_size
=
policy_
.
buffered
();
}
}
while
(
internal_buffer_size
>
0
);
}
// Stop condition 3: application indicates to stop while processing data.
if
(
auto
res
=
handle_buffered_data
(
parent
);
res
!=
read_result
::
again
)
return
res
;
// Our thresholds may have changed if the upper layer called
// configure_read. Shrink/grow buffer as necessary.
if
(
read_buf_
.
size
()
!=
max_read_size_
&&
offset_
<=
max_read_size_
)
...
...
@@ -397,11 +342,92 @@ public:
}
template
<
class
ParentPtr
>
void
continue_reading
(
ParentPtr
parent
)
{
read_result
handle_
continue_reading
(
ParentPtr
parent
)
{
if
(
max_read_size_
==
0
)
{
auto
this_layer_ptr
=
make_stream_oriented_layer_ptr
(
this
,
parent
);
upper_layer_
.
continue_reading
(
this_layer_ptr
);
}
if
(
max_read_size_
>
0
)
return
handle_buffered_data
(
parent
);
else
return
read_result
::
stop
;
}
template
<
class
ParentPtr
>
read_result
handle_buffered_data
(
ParentPtr
parent
)
{
CAF_ASSERT
(
max_read_size_
>
0
);
// Pointer for passing "this layer" to the next one down the chain.
auto
this_layer_ptr
=
make_stream_oriented_layer_ptr
(
this
,
parent
);
// Convenience lambda for failing the application.
auto
fail
=
[
this
,
&
parent
,
&
this_layer_ptr
](
auto
reason
)
{
CAF_LOG_DEBUG
(
"read failed"
<<
CAF_ARG
(
reason
));
parent
->
abort_reason
(
std
::
move
(
reason
));
upper_layer_
.
abort
(
this_layer_ptr
,
parent
->
abort_reason
());
return
read_result
::
stop
;
};
// Convenience lambda for invoking the next layer.
auto
invoke_upper_layer
=
[
this
,
&
this_layer_ptr
](
byte
*
ptr
,
ptrdiff_t
off
,
ptrdiff_t
delta
)
{
auto
bytes
=
make_span
(
ptr
,
off
);
return
upper_layer_
.
consume
(
this_layer_ptr
,
bytes
,
bytes
.
subspan
(
delta
));
};
// Keep track of how many bytes of data are still pending in the policy.
auto
internal_buffer_size
=
policy_
.
buffered
();
// The offset_ may change as a result of invoking the upper layer. Hence,
// need to run this in a loop to push data up for as long as we have
// buffered data available.
while
(
offset_
>=
min_read_size_
)
{
// Here, we have yet another loop. This one makes sure that we do not
// leave this event handler if we can make progress from the data
// buffered inside the socket policy. For 'raw' policies (like the
// default policy), there is no buffer. However, any block-oriented
// transport like OpenSSL has to buffer data internally. We need to make
// sure to consume the buffer because the OS does not know about it and
// will not trigger a read event based on data available there.
do
{
ptrdiff_t
consumed
=
invoke_upper_layer
(
read_buf_
.
data
(),
offset_
,
delta_offset_
);
CAF_LOG_DEBUG
(
CAF_ARG2
(
"socket"
,
parent
->
handle
().
id
)
<<
CAF_ARG
(
consumed
));
if
(
consumed
<
0
)
{
upper_layer_
.
abort
(
this_layer_ptr
,
parent
->
abort_reason_or
(
caf
::
sec
::
runtime_error
,
"consumed < 0"
));
return
read_result
::
stop
;
}
// Shift unconsumed bytes to the beginning of the buffer.
if
(
consumed
<
offset_
)
std
::
copy
(
read_buf_
.
begin
()
+
consumed
,
read_buf_
.
begin
()
+
offset_
,
read_buf_
.
begin
());
offset_
-=
consumed
;
delta_offset_
=
offset_
;
// Stop if the application asked for it.
if
(
max_read_size_
==
0
)
return
read_result
::
stop
;
if
(
internal_buffer_size
>
0
&&
offset_
<
max_read_size_
)
{
// Fetch already buffered data to 'refill' the buffer as we go.
auto
n
=
std
::
min
(
internal_buffer_size
,
max_read_size_
-
static_cast
<
size_t
>
(
offset_
));
auto
rdb
=
make_span
(
read_buf_
.
data
()
+
offset_
,
n
);
auto
rd
=
policy_
.
read
(
parent
->
handle
(),
rdb
);
if
(
rd
<
0
)
return
fail
(
make_error
(
caf
::
sec
::
runtime_error
,
"policy error: reading buffered data "
"may not result in an error"
));
offset_
+=
rd
;
internal_buffer_size
=
policy_
.
buffered
();
}
}
while
(
internal_buffer_size
>
0
);
}
return
read_result
::
again
;
}
template
<
class
ParentPtr
>
write_result
handle_continue_writing
(
ParentPtr
)
{
// TODO: consider whether we need another callback for the upper layer.
// For now, we always return `again`, which triggers the write
// handler later.
return
write_result
::
again
;
}
template
<
class
ParentPtr
>
...
...
@@ -411,11 +437,13 @@ public:
}
private:
///
struct
flags_t
{
bool
wanted_read_from_write_event
:
1
;
bool
wanted_write_from_read_event
:
1
;
}
flags
;
template
<
class
ThisLayerPtr
>
void
after_reading
([[
maybe_unused
]]
ThisLayerPtr
&
this_layer_ptr
)
{
if
constexpr
(
detail
::
has_after_reading_v
<
UpperLayer
,
ThisLayerPtr
>
)
upper_layer_
.
after_reading
(
this_layer_ptr
);
}
flags_t
flags
;
/// Caches the config parameter for limiting max. socket operations.
uint32_t
max_consecutive_reads_
=
0
;
...
...
libcaf_net/caf/net/stream_transport_error.hpp
0 → 100644
View file @
8e7f7b58
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/default_enum_inspect.hpp"
#include "caf/detail/net_export.hpp"
#include "caf/fwd.hpp"
#include "caf/is_error_code_enum.hpp"
#include <cstdint>
#include <string>
#include <type_traits>
namespace
caf
::
net
{
enum
class
stream_transport_error
{
/// Indicates that the transport should try again later.
temporary
,
/// Indicates that the transport must read data before trying again.
want_read
,
/// Indicates that the transport must write data before trying again.
want_write
,
/// Indicates that the transport cannot resume this operation.
permanent
,
};
/// @relates stream_transport_error
CAF_NET_EXPORT
std
::
string
to_string
(
stream_transport_error
);
/// @relates stream_transport_error
CAF_NET_EXPORT
bool
from_string
(
string_view
,
stream_transport_error
&
);
/// @relates stream_transport_error
CAF_NET_EXPORT
bool
from_integer
(
std
::
underlying_type_t
<
stream_transport_error
>
,
stream_transport_error
&
);
/// @relates stream_transport_error
template
<
class
Inspector
>
bool
inspect
(
Inspector
&
f
,
stream_transport_error
&
x
)
{
return
default_enum_inspect
(
f
,
x
);
}
}
// namespace caf::net
libcaf_net/src/multiplexer.cpp
View file @
8e7f7b58
...
...
@@ -24,6 +24,7 @@
#ifndef CAF_WINDOWS
# include <poll.h>
# include <signal.h>
#else
# include "caf/detail/socket_sys_includes.hpp"
#endif // CAF_WINDOWS
...
...
@@ -75,24 +76,29 @@ operation to_operation(const socket_manager_ptr& mgr,
}
// namespace
template
<
class
T
>
void
multiplexer
::
write_to_pipe
(
uint8_t
opcode
,
T
*
ptr
)
{
pollset_updater
::
msg_buf
buf
;
if
(
ptr
)
intrusive_ptr_add_ref
(
ptr
);
buf
[
0
]
=
static_cast
<
byte
>
(
opcode
);
auto
value
=
reinterpret_cast
<
intptr_t
>
(
ptr
);
memcpy
(
buf
.
data
()
+
1
,
&
value
,
sizeof
(
intptr_t
));
ptrdiff_t
res
=
-
1
;
{
// Lifetime scope of guard.
std
::
lock_guard
<
std
::
mutex
>
guard
{
write_lock_
};
if
(
write_handle_
!=
invalid_socket
)
res
=
write
(
write_handle_
,
buf
);
// -- static utility functions -------------------------------------------------
#ifndef CAF_WINDOWS
void
multiplexer
::
block_sigpipe
()
{
sigset_t
sigpipe_mask
;
sigemptyset
(
&
sigpipe_mask
);
sigaddset
(
&
sigpipe_mask
,
SIGPIPE
);
sigset_t
saved_mask
;
if
(
pthread_sigmask
(
SIG_BLOCK
,
&
sigpipe_mask
,
&
saved_mask
)
==
-
1
)
{
perror
(
"pthread_sigmask"
);
exit
(
1
);
}
if
(
res
<=
0
&&
ptr
)
intrusive_ptr_release
(
ptr
);
}
#else
void
multiplexer
::
block_sigpipe
()
{
// nop
}
#endif
// -- constructors, destructors, and assignment operators ----------------------
multiplexer
::
multiplexer
(
middleman
*
owner
)
:
owner_
(
owner
)
{
...
...
@@ -158,7 +164,7 @@ operation multiplexer::mask_of(const socket_manager_ptr& mgr) {
return
to_operation
(
mgr
,
std
::
nullopt
);
}
// -- thread-safe signaling
and their internal callbacks
-----------------------
// -- thread-safe signaling
-----------------------------
-----------------------
void
multiplexer
::
register_reading
(
const
socket_manager_ptr
&
mgr
)
{
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
mgr
->
handle
().
id
));
...
...
@@ -169,15 +175,6 @@ void multiplexer::register_reading(const socket_manager_ptr& mgr) {
}
}
void
multiplexer
::
do_register_reading
(
const
socket_manager_ptr
&
mgr
)
{
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
mgr
->
handle
().
id
));
// When shutting down, no new reads are allowed.
if
(
shutting_down_
)
mgr
->
close_read
();
else
if
(
!
mgr
->
read_closed
())
update_for
(
mgr
).
events
|=
input_mask
;
}
void
multiplexer
::
register_writing
(
const
socket_manager_ptr
&
mgr
)
{
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
mgr
->
handle
().
id
));
CAF_ASSERT
(
mgr
!=
nullptr
);
...
...
@@ -188,29 +185,32 @@ void multiplexer::register_writing(const socket_manager_ptr& mgr) {
}
}
void
multiplexer
::
do_register_writ
ing
(
const
socket_manager_ptr
&
mgr
)
{
void
multiplexer
::
continue_read
ing
(
const
socket_manager_ptr
&
mgr
)
{
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
mgr
->
handle
().
id
));
// When shutting down, we do allow managers to write whatever is currently
// pending but we make sure that all read channels are closed.
if
(
shutting_down_
)
mgr
->
close_read
();
if
(
!
mgr
->
write_closed
())
update_for
(
mgr
).
events
|=
output_mask
;
if
(
std
::
this_thread
::
get_id
()
==
tid_
)
{
do_continue_reading
(
mgr
);
}
else
{
write_to_pipe
(
pollset_updater
::
code
::
continue_reading
,
mgr
.
get
());
}
}
void
multiplexer
::
discard
(
const
socket_manager_ptr
&
mgr
)
{
void
multiplexer
::
continue_writing
(
const
socket_manager_ptr
&
mgr
)
{
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
mgr
->
handle
().
id
));
CAF_ASSERT
(
mgr
!=
nullptr
);
if
(
std
::
this_thread
::
get_id
()
==
tid_
)
{
do_
discard
(
mgr
);
do_
continue_writing
(
mgr
);
}
else
{
write_to_pipe
(
pollset_updater
::
code
::
discard_manager
,
mgr
.
get
());
write_to_pipe
(
pollset_updater
::
code
::
continue_writing
,
mgr
.
get
());
}
}
void
multiplexer
::
d
o_d
iscard
(
const
socket_manager_ptr
&
mgr
)
{
void
multiplexer
::
discard
(
const
socket_manager_ptr
&
mgr
)
{
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
mgr
->
handle
().
id
));
mgr
->
handle_error
(
sec
::
disposed
);
update_for
(
mgr
).
events
=
0
;
if
(
std
::
this_thread
::
get_id
()
==
tid_
)
{
do_discard
(
mgr
);
}
else
{
write_to_pipe
(
pollset_updater
::
code
::
discard_manager
,
mgr
.
get
());
}
}
void
multiplexer
::
shutdown_reading
(
const
socket_manager_ptr
&
mgr
)
{
...
...
@@ -222,14 +222,6 @@ void multiplexer::shutdown_reading(const socket_manager_ptr& mgr) {
}
}
void
multiplexer
::
do_shutdown_reading
(
const
socket_manager_ptr
&
mgr
)
{
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
mgr
->
handle
().
id
));
if
(
!
shutting_down_
&&
!
mgr
->
read_closed
())
{
mgr
->
close_read
();
update_for
(
mgr
).
events
&=
~
input_mask
;
}
}
void
multiplexer
::
shutdown_writing
(
const
socket_manager_ptr
&
mgr
)
{
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
mgr
->
handle
().
id
));
if
(
std
::
this_thread
::
get_id
()
==
tid_
)
{
...
...
@@ -239,14 +231,6 @@ void multiplexer::shutdown_writing(const socket_manager_ptr& mgr) {
}
}
void
multiplexer
::
do_shutdown_writing
(
const
socket_manager_ptr
&
mgr
)
{
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
mgr
->
handle
().
id
));
if
(
!
shutting_down_
&&
!
mgr
->
write_closed
())
{
mgr
->
close_write
();
update_for
(
mgr
).
events
&=
~
output_mask
;
}
}
void
multiplexer
::
schedule
(
const
action
&
what
)
{
CAF_LOG_TRACE
(
""
);
write_to_pipe
(
pollset_updater
::
code
::
run_action
,
what
.
ptr
());
...
...
@@ -255,42 +239,20 @@ void multiplexer::schedule(const action& what) {
void
multiplexer
::
init
(
const
socket_manager_ptr
&
mgr
)
{
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
mgr
->
handle
().
id
));
if
(
std
::
this_thread
::
get_id
()
==
tid_
)
{
if
(
!
shutting_down_
)
{
if
(
auto
err
=
mgr
->
init
(
content
(
system
().
config
())))
{
CAF_LOG_DEBUG
(
"mgr->init failed: "
<<
err
);
// The socket manager should not register itself for any events if
// initialization fails. Purge any state just in case.
update_for
(
mgr
).
events
=
0
;
}
// Else: no update since the manager is supposed to call continue_reading
// and continue_writing as necessary.
}
do_init
(
mgr
);
}
else
{
write_to_pipe
(
pollset_updater
::
code
::
init_manager
,
mgr
.
get
());
}
}
void
multiplexer
::
do_init
(
const
socket_manager_ptr
&
mgr
)
{
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
mgr
->
handle
().
id
));
if
(
!
shutting_down_
)
{
if
(
auto
err
=
mgr
->
init
(
content
(
system
().
config
())))
{
CAF_LOG_DEBUG
(
"mgr->init failed: "
<<
err
);
// The socket manager should not register itself for any events if
// initialization fails. Purge any state just in case.
update_for
(
mgr
).
events
=
0
;
}
// Else: no update since the manager is supposed to call continue_reading
// and continue_writing as necessary.
}
}
void
multiplexer
::
close_pipe
()
{
void
multiplexer
::
shutdown
()
{
CAF_LOG_TRACE
(
""
);
std
::
lock_guard
<
std
::
mutex
>
guard
{
write_lock_
};
if
(
write_handle_
!=
invalid_socket
)
{
close
(
write_handle_
);
write_handle_
=
pipe_socket
{};
}
// Note: there is no 'shortcut' when calling the function in the multiplexer's
// thread, because do_shutdown calls apply_updates. This must only be called
// from the pollset_updater.
CAF_LOG_DEBUG
(
"push shutdown event to pipe"
);
write_to_pipe
(
pollset_updater
::
code
::
shutdown
,
static_cast
<
socket_manager
*>
(
nullptr
));
}
// -- control flow -------------------------------------------------------------
...
...
@@ -389,33 +351,18 @@ void multiplexer::set_thread_id() {
void
multiplexer
::
run
()
{
CAF_LOG_TRACE
(
""
);
// On systems like Linux, we cannot disable sigpipe on the socket alone. We
// need to block the signal at thread level since some APIs (such as OpenSSL)
// are unsafe to call otherwise.
block_sigpipe
();
while
(
!
shutting_down_
||
pollset_
.
size
()
>
1
)
poll_once
(
true
);
close_pipe
();
}
void
multiplexer
::
shutdown
()
{
// Note: there is no 'shortcut' when calling the function in the multiplexer's
// thread, because do_shutdown calls apply_updates. This must only be called
// from the pollset_updater.
CAF_LOG_DEBUG
(
"push shutdown event to pipe"
);
write_to_pipe
(
pollset_updater
::
code
::
shutdown
,
static_cast
<
socket_manager
*>
(
nullptr
));
}
void
multiplexer
::
do_shutdown
()
{
// Note: calling apply_updates here is only safe because we know that the
// pollset updater runs outside of the for-loop in run_once.
CAF_LOG_DEBUG
(
"initiate shutdown"
);
shutting_down_
=
true
;
apply_updates
();
// Skip the first manager (the pollset updater).
for
(
size_t
i
=
1
;
i
<
managers_
.
size
();
++
i
)
{
auto
&
mgr
=
managers_
[
i
];
mgr
->
close_read
();
update_for
(
static_cast
<
ptrdiff_t
>
(
i
)).
events
&=
~
input_mask
;
// Close the pipe to block any future event.
std
::
lock_guard
<
std
::
mutex
>
guard
{
write_lock_
};
if
(
write_handle_
!=
invalid_socket
)
{
close
(
write_handle_
);
write_handle_
=
pipe_socket
{};
}
apply_updates
();
}
// -- utility functions --------------------------------------------------------
...
...
@@ -426,18 +373,6 @@ void multiplexer::handle(const socket_manager_ptr& mgr,
<<
CAF_ARG
(
events
)
<<
CAF_ARG
(
revents
));
CAF_ASSERT
(
mgr
!=
nullptr
);
bool
checkerror
=
true
;
// Convenience function for performing a handover between managers.
auto
do_handover
=
[
this
,
&
mgr
]
{
// Make sure to override the manager pointer in the update. Updates are
// associated to sockets, so the new manager is likely to modify this update
// again. Hence, it *must not* point to the old manager.
auto
&
update
=
update_for
(
mgr
);
auto
new_mgr
=
mgr
->
do_handover
();
update
.
events
=
0
;
if
(
new_mgr
!=
nullptr
)
update
.
mgr
=
new_mgr
;
};
//
// Note: we double-check whether the manager is actually reading because a
// previous action from the pipe may have called shutdown_reading.
if
((
events
&
revents
&
input_mask
)
!=
0
)
{
...
...
@@ -453,7 +388,7 @@ void multiplexer::handle(const socket_manager_ptr& mgr,
update_for
(
mgr
).
events
=
output_mask
;
break
;
case
socket_manager
:
:
read_result
::
handover
:
{
do_handover
();
do_handover
(
mgr
);
return
;
}
}
...
...
@@ -472,7 +407,7 @@ void multiplexer::handle(const socket_manager_ptr& mgr,
update_for
(
mgr
).
events
=
input_mask
;
break
;
case
socket_manager
:
:
write_result
::
handover
:
do_handover
();
do_handover
(
mgr
);
return
;
}
}
...
...
@@ -487,6 +422,38 @@ void multiplexer::handle(const socket_manager_ptr& mgr,
}
}
void
multiplexer
::
do_handover
(
const
socket_manager_ptr
&
mgr
)
{
// Make sure to override the manager pointer in the update. Updates are
// associated to sockets, so the new manager is likely to modify this update
// again. Hence, it *must not* point to the old manager.
auto
&
update
=
update_for
(
mgr
);
update
.
events
=
0
;
auto
new_mgr
=
mgr
->
do_handover
();
// May alter the events mask.
if
(
new_mgr
!=
nullptr
)
{
update
.
mgr
=
new_mgr
;
// If the new manager registered itself for reading, make sure it processes
// whatever data is available in buffers outside of the socket that may not
// trigger read events.
if
((
update
.
events
&
input_mask
))
{
switch
(
mgr
->
handle_buffered_data
())
{
default:
// socket_manager::read_result::again
// Nothing to do, bitmask may remain unchanged.
break
;
case
socket_manager
:
:
read_result
::
stop
:
update
.
events
&=
~
input_mask
;
break
;
case
socket_manager
:
:
read_result
::
want_write
:
update
.
events
=
output_mask
;
break
;
case
socket_manager
:
:
read_result
::
handover
:
{
// Down the rabbit hole we go!
do_handover
(
new_mgr
);
}
}
}
}
}
multiplexer
::
poll_update
&
multiplexer
::
update_for
(
ptrdiff_t
index
)
{
auto
fd
=
socket
{
pollset_
[
index
].
fd
};
if
(
auto
i
=
updates_
.
find
(
fd
);
i
!=
updates_
.
end
())
{
...
...
@@ -501,10 +468,169 @@ multiplexer::poll_update& multiplexer::update_for(ptrdiff_t index) {
multiplexer
::
poll_update
&
multiplexer
::
update_for
(
const
socket_manager_ptr
&
mgr
)
{
auto
fd
=
mgr
->
handle
();
if
(
auto
index
=
index_of
(
fd
);
index
!=
-
1
)
{
return
update_for
(
index
);
if
(
auto
i
=
updates_
.
find
(
fd
);
i
!=
updates_
.
end
())
{
return
i
->
second
;
}
else
if
(
auto
index
=
index_of
(
fd
);
index
!=
-
1
)
{
updates_
.
container
().
emplace_back
(
fd
,
poll_update
{
pollset_
[
index
].
events
,
mgr
});
return
updates_
.
container
().
back
().
second
;
}
else
{
updates_
.
container
().
emplace_back
(
fd
,
poll_update
{
0
,
mgr
});
return
updates_
.
container
().
back
().
second
;
}
}
template
<
class
T
>
void
multiplexer
::
write_to_pipe
(
uint8_t
opcode
,
T
*
ptr
)
{
pollset_updater
::
msg_buf
buf
;
if
(
ptr
)
intrusive_ptr_add_ref
(
ptr
);
buf
[
0
]
=
static_cast
<
byte
>
(
opcode
);
auto
value
=
reinterpret_cast
<
intptr_t
>
(
ptr
);
memcpy
(
buf
.
data
()
+
1
,
&
value
,
sizeof
(
intptr_t
));
ptrdiff_t
res
=
-
1
;
{
// Lifetime scope of guard.
std
::
lock_guard
<
std
::
mutex
>
guard
{
write_lock_
};
if
(
write_handle_
!=
invalid_socket
)
res
=
write
(
write_handle_
,
buf
);
}
if
(
res
<=
0
&&
ptr
)
intrusive_ptr_release
(
ptr
);
}
short
multiplexer
::
active_mask_of
(
const
socket_manager_ptr
&
mgr
)
{
auto
fd
=
mgr
->
handle
();
if
(
auto
i
=
updates_
.
find
(
fd
);
i
!=
updates_
.
end
())
{
return
i
->
second
.
events
;
}
else
if
(
auto
index
=
index_of
(
fd
);
index
!=
-
1
)
{
return
pollset_
[
index
].
events
;
}
else
{
return
updates_
.
emplace
(
fd
,
poll_update
{
0
,
mgr
}).
first
->
second
;
return
0
;
}
}
bool
multiplexer
::
is_reading
(
const
socket_manager_ptr
&
mgr
)
{
return
(
active_mask_of
(
mgr
)
&
input_mask
);
}
bool
multiplexer
::
is_writing
(
const
socket_manager_ptr
&
mgr
)
{
return
(
active_mask_of
(
mgr
)
&
output_mask
);
}
// -- internal callbacks the pollset updater -----------------------------------
void
multiplexer
::
do_shutdown
()
{
// Note: calling apply_updates here is only safe because we know that the
// pollset updater runs outside of the for-loop in run_once.
CAF_LOG_DEBUG
(
"initiate shutdown"
);
shutting_down_
=
true
;
apply_updates
();
// Skip the first manager (the pollset updater).
for
(
size_t
i
=
1
;
i
<
managers_
.
size
();
++
i
)
{
auto
&
mgr
=
managers_
[
i
];
mgr
->
close_read
();
update_for
(
static_cast
<
ptrdiff_t
>
(
i
)).
events
&=
~
input_mask
;
}
apply_updates
();
}
void
multiplexer
::
do_register_reading
(
const
socket_manager_ptr
&
mgr
)
{
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
mgr
->
handle
().
id
));
// When shutting down, no new reads are allowed.
if
(
shutting_down_
)
mgr
->
close_read
();
else
if
(
!
mgr
->
read_closed
())
update_for
(
mgr
).
events
|=
input_mask
;
}
void
multiplexer
::
do_register_writing
(
const
socket_manager_ptr
&
mgr
)
{
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
mgr
->
handle
().
id
));
// When shutting down, we do allow managers to write whatever is currently
// pending but we make sure that all read channels are closed.
if
(
shutting_down_
)
mgr
->
close_read
();
if
(
!
mgr
->
write_closed
())
update_for
(
mgr
).
events
|=
output_mask
;
}
void
multiplexer
::
do_continue_reading
(
const
socket_manager_ptr
&
mgr
)
{
if
(
!
is_reading
(
mgr
))
{
switch
(
mgr
->
handle_continue_reading
())
{
default:
// socket_manager::read_result::stop
// Nothing to do, bitmask may remain unchanged (i.e., not reading).
break
;
case
socket_manager
:
:
read_result
::
again
:
update_for
(
mgr
).
events
|=
input_mask
;
break
;
case
socket_manager
:
:
read_result
::
want_write
:
update_for
(
mgr
).
events
=
output_mask
;
break
;
case
socket_manager
:
:
read_result
::
handover
:
{
do_handover
(
mgr
);
return
;
}
}
}
}
void
multiplexer
::
do_continue_writing
(
const
socket_manager_ptr
&
mgr
)
{
if
(
!
is_writing
(
mgr
))
{
switch
(
mgr
->
handle_continue_writing
())
{
default:
// socket_manager::read_result::stop
// Nothing to do, bitmask may remain unchanged (i.e., not writing).
break
;
case
socket_manager
:
:
write_result
::
again
:
update_for
(
mgr
).
events
|=
output_mask
;
break
;
case
socket_manager
:
:
write_result
::
want_read
:
update_for
(
mgr
).
events
=
input_mask
;
break
;
case
socket_manager
:
:
write_result
::
handover
:
{
do_handover
(
mgr
);
return
;
}
}
}
}
void
multiplexer
::
do_discard
(
const
socket_manager_ptr
&
mgr
)
{
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
mgr
->
handle
().
id
));
mgr
->
handle_error
(
sec
::
disposed
);
update_for
(
mgr
).
events
=
0
;
}
void
multiplexer
::
do_shutdown_reading
(
const
socket_manager_ptr
&
mgr
)
{
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
mgr
->
handle
().
id
));
if
(
!
shutting_down_
&&
!
mgr
->
read_closed
())
{
mgr
->
close_read
();
update_for
(
mgr
).
events
&=
~
input_mask
;
}
}
void
multiplexer
::
do_shutdown_writing
(
const
socket_manager_ptr
&
mgr
)
{
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
mgr
->
handle
().
id
));
if
(
!
shutting_down_
&&
!
mgr
->
write_closed
())
{
mgr
->
close_write
();
update_for
(
mgr
).
events
&=
~
output_mask
;
}
}
void
multiplexer
::
do_init
(
const
socket_manager_ptr
&
mgr
)
{
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
mgr
->
handle
().
id
));
if
(
!
shutting_down_
)
{
error
err
;
if
(
owner_
)
err
=
mgr
->
init
(
content
(
system
().
config
()));
else
err
=
mgr
->
init
(
settings
{});
if
(
err
)
{
CAF_LOG_DEBUG
(
"mgr->init failed: "
<<
err
);
// The socket manager should not register itself for any events if
// initialization fails. Purge any state just in case.
update_for
(
mgr
).
events
=
0
;
}
// Else: no update since the manager is supposed to call continue_reading
// and continue_writing as necessary.
}
}
...
...
libcaf_net/src/pollset_updater.cpp
View file @
8e7f7b58
...
...
@@ -16,6 +16,8 @@
namespace
caf
::
net
{
// -- constructors, destructors, and assignment operators ----------------------
pollset_updater
::
pollset_updater
(
pipe_socket
read_handle
,
multiplexer
*
parent
)
:
super
(
read_handle
,
parent
)
{
// nop
...
...
@@ -25,28 +27,22 @@ pollset_updater::~pollset_updater() {
// nop
}
// -- interface functions ------------------------------------------------------
error
pollset_updater
::
init
(
const
settings
&
)
{
CAF_LOG_TRACE
(
""
);
return
nonblocking
(
handle
(),
true
);
}
namespace
{
auto
as_mgr
(
intptr_t
ptr
)
{
CAF_LOG_TRACE
(
CAF_ARG
(
ptr
));
pollset_updater
::
read_result
pollset_updater
::
handle_read_event
()
{
CAF_LOG_TRACE
(
""
);
auto
as_mgr
=
[](
intptr_t
ptr
)
{
return
intrusive_ptr
{
reinterpret_cast
<
socket_manager
*>
(
ptr
),
false
};
}
void
run_action
(
intptr_t
ptr
)
{
CAF_LOG_TRACE
(
CAF_ARG
(
ptr
));
};
auto
run_action
=
[](
intptr_t
ptr
)
{
auto
f
=
action
{
intrusive_ptr
{
reinterpret_cast
<
action
::
impl
*>
(
ptr
),
false
}};
f
.
run
();
}
}
// namespace
pollset_updater
::
read_result
pollset_updater
::
handle_read_event
()
{
CAF_LOG_TRACE
(
""
);
};
for
(;;)
{
CAF_ASSERT
((
buf_
.
size
()
-
buf_size_
)
>
0
);
auto
num_bytes
=
read
(
handle
(),
make_span
(
buf_
.
data
()
+
buf_size_
,
...
...
@@ -62,9 +58,15 @@ pollset_updater::read_result pollset_updater::handle_read_event() {
case
code
:
:
register_reading
:
mpx_
->
do_register_reading
(
as_mgr
(
ptr
));
break
;
case
code
:
:
continue_reading
:
mpx_
->
do_continue_reading
(
as_mgr
(
ptr
));
break
;
case
code
:
:
register_writing
:
mpx_
->
do_register_writing
(
as_mgr
(
ptr
));
break
;
case
code
:
:
continue_writing
:
mpx_
->
do_continue_writing
(
as_mgr
(
ptr
));
break
;
case
code
:
:
init_manager
:
mpx_
->
do_init
(
as_mgr
(
ptr
));
break
;
...
...
@@ -100,16 +102,24 @@ pollset_updater::read_result pollset_updater::handle_read_event() {
}
}
pollset_updater
::
read_result
pollset_updater
::
handle_buffered_data
()
{
return
read_result
::
again
;
}
pollset_updater
::
read_result
pollset_updater
::
handle_continue_reading
()
{
return
read_result
::
again
;
}
pollset_updater
::
write_result
pollset_updater
::
handle_write_event
()
{
return
write_result
::
stop
;
}
void
pollset_updater
::
handle_error
(
sec
)
{
// nop
pollset_updater
::
write_result
pollset_updater
::
handle_continue_writing
(
)
{
return
write_result
::
stop
;
}
void
pollset_updater
::
continue_reading
(
)
{
register_reading
();
void
pollset_updater
::
handle_error
(
sec
)
{
// nop
}
}
// namespace caf::net
libcaf_net/src/socket_manager.cpp
View file @
8e7f7b58
...
...
@@ -43,15 +43,21 @@ void socket_manager::abort_reason(error reason) noexcept {
}
void
socket_manager
::
register_reading
()
{
if
(
!
read_closed
())
mpx_
->
register_reading
(
this
);
}
void
socket_manager
::
continue_reading
()
{
mpx_
->
continue_reading
(
this
);
}
void
socket_manager
::
register_writing
()
{
if
(
!
write_closed
())
mpx_
->
register_writing
(
this
);
}
void
socket_manager
::
continue_writing
()
{
mpx_
->
continue_writing
(
this
);
}
socket_manager_ptr
socket_manager
::
do_handover
()
{
flags_
.
read_closed
=
true
;
flags_
.
write_closed
=
true
;
...
...
libcaf_net/test/multiplexer.cpp
View file @
8e7f7b58
...
...
@@ -26,6 +26,8 @@ using shared_atomic_count = std::shared_ptr<std::atomic<size_t>>;
class
dummy_manager
:
public
socket_manager
{
public:
// -- constructors, destructors, and assignment operators --------------------
dummy_manager
(
stream_socket
handle
,
multiplexer
*
parent
,
std
::
string
name
,
shared_atomic_count
count
)
:
socket_manager
(
handle
,
parent
),
name
(
std
::
move
(
name
)),
count_
(
count
)
{
...
...
@@ -39,14 +41,31 @@ public:
--*
count_
;
}
error
init
(
const
settings
&
)
override
{
return
none
;
}
// -- properties -------------------------------------------------------------
stream_socket
handle
()
const
noexcept
{
return
socket_cast
<
stream_socket
>
(
handle_
);
}
// -- testing DSL ------------------------------------------------------------
void
send
(
string_view
x
)
{
auto
x_bytes
=
as_bytes
(
make_span
(
x
));
wr_buf_
.
insert
(
wr_buf_
.
end
(),
x_bytes
.
begin
(),
x_bytes
.
end
());
}
std
::
string
receive
()
{
std
::
string
result
(
reinterpret_cast
<
char
*>
(
rd_buf_
.
data
()),
rd_buf_pos_
);
rd_buf_pos_
=
0
;
return
result
;
}
// -- interface functions ----------------------------------------------------
error
init
(
const
settings
&
)
override
{
return
none
;
}
read_result
handle_read_event
()
override
{
if
(
trigger_handover
)
{
MESSAGE
(
name
<<
" triggered a handover"
);
...
...
@@ -67,6 +86,14 @@ public:
}
}
read_result
handle_buffered_data
()
override
{
return
read_result
::
again
;
}
read_result
handle_continue_reading
()
override
{
return
read_result
::
again
;
}
write_result
handle_write_event
()
override
{
if
(
trigger_handover
)
{
MESSAGE
(
name
<<
" triggered a handover"
);
...
...
@@ -84,12 +111,12 @@ public:
:
write_result
::
stop
;
}
void
handle_error
(
sec
code
)
override
{
FAIL
(
"handle_error called with code "
<<
code
)
;
write_result
handle_continue_writing
(
)
override
{
return
write_result
::
again
;
}
void
continue_reading
(
)
override
{
FAIL
(
"
continue_reading called"
);
void
handle_error
(
sec
code
)
override
{
FAIL
(
"
handle_error called with code "
<<
code
);
}
socket_manager_ptr
make_next_manager
(
socket
handle
)
override
{
...
...
@@ -102,16 +129,7 @@ public:
return
next
;
}
void
send
(
string_view
x
)
{
auto
x_bytes
=
as_bytes
(
make_span
(
x
));
wr_buf_
.
insert
(
wr_buf_
.
end
(),
x_bytes
.
begin
(),
x_bytes
.
end
());
}
std
::
string
receive
()
{
std
::
string
result
(
reinterpret_cast
<
char
*>
(
rd_buf_
.
data
()),
rd_buf_pos_
);
rd_buf_pos_
=
0
;
return
result
;
}
// --
bool
trigger_handover
=
false
;
...
...
libcaf_net/test/net/openssl_transport.cpp
0 → 100644
View file @
8e7f7b58
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE net.openssl_transport
#include "caf/net/openssl_transport.hpp"
#include "net-test.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/byte.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/make_actor.hpp"
#include "caf/net/actor_proxy_impl.hpp"
#include "caf/net/multiplexer.hpp"
#include "caf/net/socket_guard.hpp"
#include "caf/net/socket_manager.hpp"
#include "caf/net/stream_socket.hpp"
#include "caf/span.hpp"
#include <filesystem>
#include <random>
// Note: these constants are defined in openssl_transport_constants.cpp.
extern
std
::
string_view
ca_pem
;
extern
std
::
string_view
cert_1_pem
;
extern
std
::
string_view
cert_2_pem
;
extern
std
::
string_view
key_1_enc_pem
;
extern
std
::
string_view
key_1_pem
;
extern
std
::
string_view
key_2_pem
;
using
namespace
caf
;
using
namespace
caf
::
net
;
namespace
{
using
byte_buffer_ptr
=
std
::
shared_ptr
<
byte_buffer
>
;
struct
fixture
:
host_fixture
{
using
byte_buffer_ptr
=
std
::
shared_ptr
<
byte_buffer
>
;
fixture
(){
multiplexer
::
block_sigpipe
();
OPENSSL_init_ssl
(
OPENSSL_INIT_SSL_DEFAULT
,
nullptr
);
// Make a directory name with 8 random (hex) character suffix.
std
::
string
dir_name
=
"caf-net-test-"
;
std
::
random_device
rd
;
std
::
minstd_rand
rng
{
rd
()};
std
::
uniform_int_distribution
<
int
>
dist
(
0
,
255
);
for
(
int
i
=
0
;
i
<
4
;
++
i
)
detail
::
append_hex
(
dir_name
,
static_cast
<
uint8_t
>
(
dist
(
rng
)));
// Create the directory under /tmp (or its equivalent on non-POSIX).
namespace
fs
=
std
::
filesystem
;
tmp_dir
=
fs
::
temp_directory_path
()
/
dir_name
;
if
(
!
fs
::
create_directory
(
tmp_dir
))
{
std
::
cerr
<<
"*** failed to create "
<<
tmp_dir
.
string
()
<<
"
\n
"
;
abort
();
}
// Create the .pem files on disk.
write_file
(
"ca.pem"
,
ca_pem
);
write_file
(
"cert.1.pem"
,
cert_1_pem
);
write_file
(
"cert.2.pem"
,
cert_1_pem
);
write_file
(
"key.1.enc.pem"
,
key_1_enc_pem
);
write_file
(
"key.1.pem"
,
key_1_pem
);
write_file
(
"key.2.pem"
,
key_1_pem
);
}
~
fixture
()
{
// Clean up our files under /tmp.
if
(
!
tmp_dir
.
empty
())
std
::
filesystem
::
remove_all
(
tmp_dir
);
}
std
::
string
abs_path
(
std
::
string_view
fname
)
{
auto
path
=
tmp_dir
/
fname
;
return
path
.
string
();
}
void
write_file
(
std
::
string_view
fname
,
std
::
string_view
content
)
{
std
::
ofstream
out
{
abs_path
(
fname
)};
out
<<
content
;
}
std
::
filesystem
::
path
tmp_dir
;
};
class
dummy_app
{
public:
using
input_tag
=
tag
::
stream_oriented
;
explicit
dummy_app
(
std
::
shared_ptr
<
bool
>
done
,
byte_buffer_ptr
recv_buf
)
:
done_
(
std
::
move
(
done
)),
recv_buf_
(
std
::
move
(
recv_buf
))
{
// nop
}
~
dummy_app
()
{
*
done_
=
true
;
}
template
<
class
ParentPtr
>
error
init
(
socket_manager
*
,
ParentPtr
parent
,
const
settings
&
)
{
MESSAGE
(
"initialize dummy app"
);
parent
->
configure_read
(
receive_policy
::
exactly
(
4
));
parent
->
begin_output
();
auto
&
buf
=
parent
->
output_buffer
();
caf
::
binary_serializer
out
{
nullptr
,
buf
};
static_cast
<
void
>
(
out
.
apply
(
10
));
parent
->
end_output
();
return
none
;
}
template
<
class
ParentPtr
>
bool
prepare_send
(
ParentPtr
)
{
return
true
;
}
template
<
class
ParentPtr
>
bool
done_sending
(
ParentPtr
)
{
return
true
;
}
template
<
class
ParentPtr
>
void
continue_reading
(
ParentPtr
)
{
// nop
}
template
<
class
ParentPtr
>
size_t
consume
(
ParentPtr
down
,
span
<
const
byte
>
data
,
span
<
const
byte
>
)
{
MESSAGE
(
"dummy app received "
<<
data
.
size
()
<<
" bytes"
);
// Store the received bytes.
recv_buf_
->
insert
(
recv_buf_
->
begin
(),
data
.
begin
(),
data
.
end
());
// Respond with the same data and return.
down
->
begin_output
();
auto
&
out
=
down
->
output_buffer
();
out
.
insert
(
out
.
end
(),
data
.
begin
(),
data
.
end
());
down
->
end_output
();
return
static_cast
<
ptrdiff_t
>
(
data
.
size
());
}
template
<
class
ParentPtr
>
void
abort
(
ParentPtr
,
const
error
&
reason
)
{
MESSAGE
(
"dummy_app::abort called: "
<<
reason
);
*
done_
=
true
;
}
private:
std
::
shared_ptr
<
bool
>
done_
;
byte_buffer_ptr
recv_buf_
;
};
// Simulates a remote SSL server.
void
dummy_tls_server
(
stream_socket
fd
,
std
::
string
cert_file
,
std
::
string
key_file
)
{
namespace
ssl
=
caf
::
net
::
openssl
;
multiplexer
::
block_sigpipe
();
// Make sure we close our socket.
auto
guard
=
detail
::
make_scope_guard
([
fd
]
{
close
(
fd
);
});
// Get and configure our SSL context.
auto
ctx
=
ssl
::
make_ctx
(
TLS_server_method
());
if
(
auto
err
=
ssl
::
certificate_pem_file
(
ctx
,
cert_file
))
{
std
::
cerr
<<
"*** certificate_pem_file failed: "
<<
ssl
::
fetch_error_str
();
return
;
}
if
(
auto
err
=
ssl
::
private_key_pem_file
(
ctx
,
key_file
))
{
std
::
cerr
<<
"*** private_key_pem_file failed: "
<<
ssl
::
fetch_error_str
();
return
;
}
// Perform SSL handshake.
auto
f
=
net
::
openssl
::
policy
::
make
(
std
::
move
(
ctx
),
fd
);
if
(
f
.
accept
(
fd
)
<=
0
)
{
std
::
cerr
<<
"*** accept failed: "
<<
ssl
::
fetch_error_str
();
return
;
}
// Do some ping-pong messaging.
for
(
int
i
=
0
;
i
<
4
;
++
i
)
{
byte_buffer
buf
;
buf
.
resize
(
4
);
f
.
read
(
fd
,
buf
);
f
.
write
(
fd
,
buf
);
}
// Graceful shutdown.
f
.
notify_close
();
}
// Simulates a remote SSL client.
void
dummy_tls_client
(
stream_socket
fd
)
{
multiplexer
::
block_sigpipe
();
// Make sure we close our socket.
auto
guard
=
detail
::
make_scope_guard
([
fd
]
{
close
(
fd
);
});
// Perform SSL handshake.
auto
f
=
net
::
openssl
::
policy
::
make
(
TLS_client_method
(),
fd
);
if
(
f
.
connect
(
fd
)
<=
0
)
{
ERR_print_errors_fp
(
stderr
);
return
;
}
// Do some ping-pong messaging.
for
(
int
i
=
0
;
i
<
4
;
++
i
)
{
byte_buffer
buf
;
buf
.
resize
(
4
);
f
.
read
(
fd
,
buf
);
f
.
write
(
fd
,
buf
);
}
// Graceful shutdown.
f
.
notify_close
();
}
}
// namespace
BEGIN_FIXTURE_SCOPE
(
fixture
)
SCENARIO
(
"openssl::async_connect performs the client handshake"
)
{
GIVEN
(
"a connection to a TLS server"
)
{
auto
[
serv_fd
,
client_fd
]
=
unbox
(
make_stream_socket_pair
());
if
(
auto
err
=
net
::
nonblocking
(
client_fd
,
true
))
FAIL
(
"net::nonblocking failed: "
<<
err
);
std
::
thread
server
{
dummy_tls_server
,
serv_fd
,
abs_path
(
"cert.1.pem"
),
abs_path
(
"key.1.pem"
)};
WHEN
(
"connecting as a client to an OpenSSL server"
)
{
THEN
(
"openssl::async_connect transparently calls SSL_connect"
)
{
using
stack_t
=
openssl_transport
<
dummy_app
>
;
net
::
multiplexer
mpx
{
nullptr
};
mpx
.
set_thread_id
();
auto
done
=
std
::
make_shared
<
bool
>
(
false
);
auto
buf
=
std
::
make_shared
<
byte_buffer
>
();
auto
make_manager
=
[
done
,
buf
](
stream_socket
fd
,
multiplexer
*
ptr
,
net
::
openssl
::
policy
policy
)
{
return
make_socket_manager
<
stack_t
>
(
fd
,
ptr
,
std
::
move
(
policy
),
done
,
buf
);
};
auto
on_connect_error
=
[](
const
error
&
reason
)
{
FAIL
(
"connect failed: "
<<
reason
);
};
net
::
openssl
::
async_connect
(
client_fd
,
&
mpx
,
net
::
openssl
::
policy
::
make
(
SSLv23_method
(),
client_fd
),
make_manager
,
on_connect_error
);
mpx
.
apply_updates
();
while
(
!*
done
)
mpx
.
poll_once
(
true
);
if
(
CHECK_EQ
(
buf
->
size
(),
16u
))
{
// 4x 32-bit integers
caf
::
binary_deserializer
src
{
nullptr
,
*
buf
};
for
(
int
i
=
0
;
i
<
4
;
++
i
)
{
int32_t
value
=
0
;
static_cast
<
void
>
(
src
.
apply
(
value
));
CHECK_EQ
(
value
,
10
);
}
}
}
}
server
.
join
();
}
}
SCENARIO
(
"openssl::async_accept performs the server handshake"
)
{
GIVEN
(
"a socket that is connected to a client"
)
{
auto
[
serv_fd
,
client_fd
]
=
unbox
(
make_stream_socket_pair
());
if
(
auto
err
=
net
::
nonblocking
(
serv_fd
,
true
))
FAIL
(
"net::nonblocking failed: "
<<
err
);
std
::
thread
client
{
dummy_tls_client
,
client_fd
};
WHEN
(
"acting as the OpenSSL server"
)
{
THEN
(
"openssl::async_accept transparently calls SSL_accept"
)
{
using
stack_t
=
openssl_transport
<
dummy_app
>
;
net
::
multiplexer
mpx
{
nullptr
};
mpx
.
set_thread_id
();
auto
done
=
std
::
make_shared
<
bool
>
(
false
);
auto
buf
=
std
::
make_shared
<
byte_buffer
>
();
auto
make_manager
=
[
done
,
buf
](
stream_socket
fd
,
multiplexer
*
ptr
,
net
::
openssl
::
policy
policy
)
{
return
make_socket_manager
<
stack_t
>
(
fd
,
ptr
,
std
::
move
(
policy
),
done
,
buf
);
};
auto
on_accept_error
=
[](
const
error
&
reason
)
{
FAIL
(
"accept failed: "
<<
reason
);
};
auto
ssl
=
net
::
openssl
::
policy
::
make
(
TLS_server_method
(),
serv_fd
);
if
(
auto
err
=
ssl
.
certificate_pem_file
(
abs_path
(
"cert.1.pem"
)))
FAIL
(
"certificate_pem_file failed: "
<<
err
);
if
(
auto
err
=
ssl
.
private_key_pem_file
(
abs_path
(
"key.1.pem"
)))
FAIL
(
"privat_key_pem_file failed: "
<<
err
);
net
::
openssl
::
async_accept
(
serv_fd
,
&
mpx
,
std
::
move
(
ssl
),
make_manager
,
on_accept_error
);
mpx
.
apply_updates
();
while
(
!*
done
)
mpx
.
poll_once
(
true
);
if
(
CHECK_EQ
(
buf
->
size
(),
16u
))
{
// 4x 32-bit integers
caf
::
binary_deserializer
src
{
nullptr
,
*
buf
};
for
(
int
i
=
0
;
i
<
4
;
++
i
)
{
int32_t
value
=
0
;
static_cast
<
void
>
(
src
.
apply
(
value
));
CHECK_EQ
(
value
,
10
);
}
}
}
}
client
.
join
();
}
}
END_FIXTURE_SCOPE
()
libcaf_net/test/net/openssl_transport_constants.cpp
0 → 100644
View file @
8e7f7b58
#include <string_view>
std
::
string_view
ca_pem
=
"-----BEGIN CERTIFICATE-----
\n
"
"MIIDmzCCAoOgAwIBAgIJAPLZ3e3WR0LLMA0GCSqGSIb3DQEBCwUAMGQxCzAJBgNV
\n
"
"BAYTAlVTMQswCQYDVQQIDAJDQTERMA8GA1UEBwwIQmVya2VsZXkxIzAhBgNVBAoM
\n
"
"GkFDTUUgU2lnbmluZyBBdXRob3JpdHkgSW5jMRAwDgYDVQQDDAdmb28uYmFyMB4X
\n
"
"DTE3MDQyMTIzMjM0OFoXDTQyMDQyMTIzMjM0OFowZDELMAkGA1UEBhMCVVMxCzAJ
\n
"
"BgNVBAgMAkNBMREwDwYDVQQHDAhCZXJrZWxleTEjMCEGA1UECgwaQUNNRSBTaWdu
\n
"
"aW5nIEF1dGhvcml0eSBJbmMxEDAOBgNVBAMMB2Zvby5iYXIwggEiMA0GCSqGSIb3
\n
"
"DQEBAQUAA4IBDwAwggEKAoIBAQC6ah79JvrN3LtcPzc9bX5THdzfidWncSmowotG
\n
"
"SZA3gcIhlsYD3P3RCaUR9g+f2Z/l0l7ciKgWetpNtN9hRBbg5/9tFzSpCb/Y0SSG
\n
"
"mwtHHovEqN2MWV+Od/MUcYSlL6MmPjSDc8Ls5NSniTr9OBE9J1jm72AsuzHasjPQ
\n
"
"D84TlWeTSs0HW3H5VxDb15xWYFnmgBo0JylDWj0+VWI+G41Xr7Ubu9699lWSFYF9
\n
"
"FCtdjzM5e1CGZOMvqUbUBus38BhUAdQ4fE7Dwnn8seKh+7HpJ70omIgqG87e4DBo
\n
"
"HbnMAkZaekk8+LBl0Hfu8c66Utw9mNoMIlFf/AMlJyLDIpNxAgMBAAGjUDBOMB0G
\n
"
"A1UdDgQWBBRc6Cbyshtny6jFWZtd/cEUUfMQ3DAfBgNVHSMEGDAWgBRc6Cbyshtn
\n
"
"y6jFWZtd/cEUUfMQ3DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCY
\n
"
"numHau9XYH5h4R2CoMdnKPMGk6V7UZZdbidLLcE4roQrYhnBdyhT69b/ySJK2Ee4
\n
"
"mt8T+E0wcg3k8Pr3aJEJA8eYYaJTqZvvv+TwuMBPjmE2rYSIpgMZv2tRD3XWMaQu
\n
"
"duLbwkclfejQHDD26xNXsxuU+WNB5kuvtNAg0oKFyFdNKElLQEcjyYzfxmCF4YX5
\n
"
"WmElijr1Tzuzd59rWPqC/tVIsh42vQ+P6g8Y1PDmo8eTUFveZ+wcr/eEPW6IOMrg
\n
"
"OW7tATcrgzNuXZ1umiuGgAPuIVqPfr9ssZHBqi9UOK9L/8MQrnOxecNUpPohcTFR
\n
"
"vq+Zqu15QV9T4BVWKHv0
\n
"
"-----END CERTIFICATE-----
\n
"
;
std
::
string_view
cert_1_pem
=
"-----BEGIN CERTIFICATE-----
\n
"
"MIIDOjCCAiICCQDz7oMOR7Wm7jANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJV
\n
"
"UzELMAkGA1UECAwCQ0ExETAPBgNVBAcMCEJlcmtlbGV5MSMwIQYDVQQKDBpBQ01F
\n
"
"IFNpZ25pbmcgQXV0aG9yaXR5IEluYzEQMA4GA1UEAwwHZm9vLmJhcjAgFw0xNzA0
\n
"
"MjEyMzI2MzhaGA80NzU1MDMxOTIzMjYzOFowWDELMAkGA1UEBhMCVVMxCzAJBgNV
\n
"
"BAgMAkNBMREwDwYDVQQHDAhCZXJrZWxleTEVMBMGA1UECgwMQUNNRSBTZXJ2aWNl
\n
"
"MRIwEAYDVQQDDAkxLmZvby5iYXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
\n
"
"AoIBAQDHobccAQQqbZANdOdx852W/nUzGcwpurOi8zbh9yCxMwnFMogW9AsqKEnd
\n
"
"sypV6Ah/cIz45PAgCdEg+1pc2DG7+E0+QlV4ChNwCDuk+FSWB6pqMTCdZcLeIwlA
\n
"
"GPp6Ow9v40dW7IFpDetFKXEo6kqEzR5P58Q0a6KpCtpsSMqhk57Py83wB9gPA1vp
\n
"
"s77kN7D5CI3oay86TA5j5nfFMT1X/77Hs24csW6CLnW/OD4f1RK79UgPd/kpPKQ1
\n
"
"jNq+hsR7NZTcfrAF1hcfScxnKaznO7WopSt1k75NqLdnSN1GIci2GpiXYKtXZ9l5
\n
"
"TErv2Oucpw/u+a/wjKlXjrgLL9lfAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAKuW
\n
"
"yKA2uuiNc9MKU+yVbNaP8kPaMb/wMvVaFG8FFFpCTZ0MFMLsqRpeqtj7gMK/gaJC
\n
"
"CQm4EyadjzfWFYDLkHzm6b7gI8digvvhjr/C2RJ5Qxr2P0iFP1buGq0CqnF20XgQ
\n
"
"Q+ecS43CZ77CfKfS6ZLPmAZMAwgFLImVyo5mkaTECo3+9oCnjDYBapvXLJqCJRhk
\n
"
"NosoTmGCV0HecWN4l38ojnXd44aSktQIND9iCLus3S6++nFnX5DHGZiv6/SnSO/6
\n
"
"+Op7nV0A6zKVcMOYQ0SGZPD8UQs5wDJgrR9LY29Ox5QBwu/5NqyvNSrMQaTop5vb
\n
"
"wkMInaq5lLxEYQDSLBc=
\n
"
"-----END CERTIFICATE-----
\n
"
;
std
::
string_view
cert_2_pem
=
"-----BEGIN CERTIFICATE-----
\n
"
"MIIDOjCCAiICCQDz7oMOR7Wm7TANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJV
\n
"
"UzELMAkGA1UECAwCQ0ExETAPBgNVBAcMCEJlcmtlbGV5MSMwIQYDVQQKDBpBQ01F
\n
"
"IFNpZ25pbmcgQXV0aG9yaXR5IEluYzEQMA4GA1UEAwwHZm9vLmJhcjAgFw0xNzA0
\n
"
"MjEyMzI2MzNaGA80NzU1MDMxOTIzMjYzM1owWDELMAkGA1UEBhMCVVMxCzAJBgNV
\n
"
"BAgMAkNBMREwDwYDVQQHDAhCZXJrZWxleTEVMBMGA1UECgwMQUNNRSBTZXJ2aWNl
\n
"
"MRIwEAYDVQQDDAkyLmZvby5iYXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
\n
"
"AoIBAQDG9fAvW9qnhjGRmLpA++RvOHaesu7NiUQvxf2F6gF2rLJV0/+DSA/PztEv
\n
"
"1WJaGhgJSaEqUjaHk3HY2EKlbGXEPh1mxqgPZD5plGlu4ddTwutxCxxQiFIBH+3N
\n
"
"MYRjJvDN7ozJoi4uRiK0QQdDWAqWJs5hMOJqeWd6MCgmVXSP6pj5/omGROktbHzD
\n
"
"9jJhAW9fnYFg6k+7cGN5kLmjqqnGhJkNtgom6uW9j73S9OpU/9Er2aZme6/PrujI
\n
"
"qYFBV81TJK2vmonWUITxfQjk9JVJYhBdHamGTxUqVBbuRcbAqdImV9yx4LoGh55u
\n
"
"L6xnsW4i0n1o1k+bh03NgwPz12O3AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAJmN
\n
"
"yCdInFIeEwomE6m+Su82BWBzkztOfMG9iRE+1aGuC8EQ8kju5NNMmWQcuKetNh0s
\n
"
"hJVdY6LXh27O0ZUllhQ/ig9c+dYFh6AHoZU7WjiNKIyWuyl4IAOkQ4IEdsBvst+l
\n
"
"0rafcdJjUpqNOMWeyg6x1s+gUD5o+ZLCZGCdkCW3fZbKgF52L+vmsSRiJg2JkYZW
\n
"
"8BPNNsroHZw2UXnLvRqUXCMf1hnOrlx/B0a0Q46hD4NQvl+OzlKaxfR2L2USmJ8M
\n
"
"XZvT6+i8fWvkGv18iunm23Yu+8Zf08wTXnbqXvmMda5upAYLmwD0YKIVYC3ycihh
\n
"
"mkYCYI6PVeH63a2/zxw=
\n
"
"-----END CERTIFICATE-----
\n
"
;
std
::
string_view
key_1_enc_pem
=
"-----BEGIN RSA PRIVATE KEY-----
\n
"
"Proc-Type: 4,ENCRYPTED
\n
"
"DEK-Info: DES-EDE3-CBC,4FEF2D042C3D3463
\n
"
"
\n
"
"S3uHh5KH2Vsxzhw+B8zKsZNUG7CjMAvYfZwUptDnCms2dqxdfyzioxiRVNTN2MBb
\n
"
"nFxMKZayl5Xjr+04LhUpCAKdmvTfqGmWRFVSa8ol1hvqN/Vmg3a11B+3MD9pHXKu
\n
"
"ipgHMDTmE34DsDun6cXZ8W2k9ZSSJ20Y7qhcb4lTYUZuWq+94+Bup684COZytbyG
\n
"
"Eb6tOvVs/N6KeFhvd4vBL4YgTR3aY/vu1cHQQJeHvSYFtGKTKr7qpsfFfDFB7ExM
\n
"
"bqt2qbiUq1s+uZ0hlDjs+/TCZpC9Wuqpl9m05FrZhboubBRK9pw3wRVNvrpfwunH
\n
"
"2/E4Ta8/YaxypUwtk4saiPofKcJwvYx3Te38OHKKXgVKlein1p1e6eJNZt3qJaPd
\n
"
"BEBAR11iWstE+ghPS2WmrwT17cDqNA8wG5CRM9unYVWRMtO8Nl98OYars0vAXOgd
\n
"
"TI8bHDx6OHm2sJnkc56HAfu6hMdTbKzZXsWyOqLXUU79ioNmTvbMQUO0F+0VKDrv
\n
"
"3LikXewv69aOhhk+M96ng8KpWUW4z2kbDReiFuWQfjtiFNyEdS9wQIElpq5gU98r
\n
"
"EAeZoT6s8O0fkE/tt374ZJREEe9YEkMFcv06c96GPkMKnScncOFKRa1w0AblGxOM
\n
"
"vjICMLIk3CesFNWNzJk4MQu5ZVerJsz27UFocjNUvT6iM10rDCeU55W71bIPWRSb
\n
"
"SZL3JUi1gOeORrv3jAEamOd6YRlvPRYQjpDENg/qnx+bdXp5S3qHeHFSkRRHBCfy
\n
"
"UcYUba5hZJVDyBJXC4dkc2Ftcna149PZP1aKe9r//ZGa25PVZl1Mg6HhLROSJrEN
\n
"
"D2lqPMYcBDuSiZ6QK5ZHh2gsXJPktDgMhw4ZNIfIIqIpMDJ7rV4tQXXwB3d+iZte
\n
"
"/Ib/y1Mn8fZOo448nBqagpNFvU70eg7WkA4rzgiS3VzFYosP3g48PlZ07bEZ0awy
\n
"
"9lMGE0OKJv6x8EkVY0xHDxujzD5WVFEkanpGo8bWl1ofBrsnnDsFrvvRkDRsQbrG
\n
"
"WZkRqn7zLaGKOUirfKjfktajvPOAKMsvbl9KXAfp88sOtTZuYppCivZ/2qnzQ8xg
\n
"
"9/fuGfpAPXYfUu44+7dhrbvxJzrPp+GQRLUb1e3KA2MLgVak+yV1TIKdDGYek5vM
\n
"
"xDEmLLgOQqsE/OAcdXD2b/qIVYEmFPdut6N9K0m65AoqrN5nY6r0BzuC7usCgxnu
\n
"
"K6Z7Y2L6Ax4DjnKt21RRcjIVLLpea7KXnMiTogCYJ7+zmD82fE3i1xCDQs7R9b4T
\n
"
"tZhnkGfQS2RjOp3WaiV5rcmOKXDK3kYE+ezD0wbiVFDgB2jNdR7duOFqzW/WuPac
\n
"
"hsIPYn7VmjLB3W4p7PBf7OQ1fIiQ6IofeMvu8I8IOVRfC7eYDCJ0QKsHI8uA6j9T
\n
"
"vOCfjsZcpjygtN1Xy3uXIEZUVJJq/DF5t2Lxn95wDG9VfstJQR3KuDbPwYAWOYCh
\n
"
"W6gYu6HftIr3OlIYKurtSUFM4+iargMUaBVOUQ4XWLN8LzoyHNL3fsiGKn1afBV/
\n
"
"J3Qaqrn/YBGCAc2SOUCRl3F8yfWjmidXy6rQIzrjpF8aX2QSflB+fw==
\n
"
"-----END RSA PRIVATE KEY-----
\n
"
;
std
::
string_view
key_1_pem
=
"-----BEGIN RSA PRIVATE KEY-----
\n
"
"MIIEogIBAAKCAQEAx6G3HAEEKm2QDXTncfOdlv51MxnMKbqzovM24fcgsTMJxTKI
\n
"
"FvQLKihJ3bMqVegIf3CM+OTwIAnRIPtaXNgxu/hNPkJVeAoTcAg7pPhUlgeqajEw
\n
"
"nWXC3iMJQBj6ejsPb+NHVuyBaQ3rRSlxKOpKhM0eT+fENGuiqQrabEjKoZOez8vN
\n
"
"8AfYDwNb6bO+5Dew+QiN6GsvOkwOY+Z3xTE9V/++x7NuHLFugi51vzg+H9USu/VI
\n
"
"D3f5KTykNYzavobEezWU3H6wBdYXH0nMZyms5zu1qKUrdZO+Tai3Z0jdRiHIthqY
\n
"
"l2CrV2fZeUxK79jrnKcP7vmv8IypV464Cy/ZXwIDAQABAoIBAC0Y7jmoTR2clJ9F
\n
"
"modWhnI215kMqd9/atdT5EEVx8/f/MQMj0vII8GJSm6H6/duLIVFksMjTM+gCBtQ
\n
"
"TPCOcmXJSQHYkGBGvm9fnMG+y7T81FWa+SWFeIkgFxXgzqzQLMOU72fGk9F8sHp2
\n
"
"Szb3/o+TmtZoQB2rdxqC9ibiJsxrG5IBVKkzlSPv3POkPXwSb1HcETqrTwefuioj
\n
"
"WMuMrqtm5Y3HddJ5l4JEF5VA3KrsfXWl3JLHH0UViemVahiNjXQAVTKAXIL1PHAV
\n
"
"J2MCEvlpA7sIgXREbmvPvZUTkt3pIqhVjZVJ7tHiSnSecqNTbuxcocnhKhZrHNtC
\n
"
"v2zYKHkCgYEA6cAIhz0qOGDycZ1lf9RSWw0RO1hO8frATMQNVoFVuJJCVL22u96u
\n
"
"0FvJ0JGyYbjthULnlOKyRe7DUL5HRLVS4D7vvKCrgwDmsJp1VFxMdASUdaBfq6aX
\n
"
"oKLUW4q7kC2lQcmK/PVRYwp2GQSx8bodWe+DtXUY/GcN03znY8mhSB0CgYEA2qJK
\n
"
"1GSZsm6kFbDek3BiMMHfO+X819owB2FmXiH+GQckyIZu9xA3HWrkOWTqwglEvzfO
\n
"
"qzFF96E9iEEtseAxhcM8gPvfFuXiUj9t2nH/7SzMnVGikhtYi0p6jrgHmscc4NBx
\n
"
"AOUA15kYEFOGqpZfl2uuKqgHidrHdGkJzzSUBqsCgYAVCjb6TVQejQNlnKBFOExN
\n
"
"a8iwScuZVlO21TLKJYwct/WGgSkQkgO0N37b6jFfQHEIvLPxn9IiH1KvUuFBWvzh
\n
"
"uGiF1wR5HzykitKizEgJbVwbllrmLXGagO2Sa9NkL+efG1AKYt53hrqIl/aYZoM7
\n
"
"1CZL0AV2uqPw9F4zijOdNQKBgH1WmvWGMsKjQTgaLI9z1ybCjjqlj70jHXOtt+Tx
\n
"
"Md2hRcobn5PN3PrlY68vlpHkhF/nG3jzB3x+GGt7ijm2IE3h7la3jl5vLb8fE9gu
\n
"
"kJykmSz7Nurx+GHqMbaN8/Ycfga4GIB9yGzRHIWHjOVQzb5eAfv8Vk4GeV/YM8Jx
\n
"
"Dwd/AoGAILn8pVC9dIFac2BDOFU5y9ZvMmZAvwRxh9vEWewNvkzg27vdYc+rCHNm
\n
"
"I7H0S/RqfqVeo0ApE5PQ8Sll6RvxN/mbSQo9YeCDGQ1r1rNe4Vs12GAYXAbE4ipf
\n
"
"BTdqMbieumB/zL97iK5baHUFEJ4VRtLQhh/SOXgew/BF8ccpilI=
\n
"
"-----END RSA PRIVATE KEY-----
\n
"
;
std
::
string_view
key_2_pem
=
"-----BEGIN RSA PRIVATE KEY-----
\n
"
"MIIEpQIBAAKCAQEAxvXwL1vap4YxkZi6QPvkbzh2nrLuzYlEL8X9heoBdqyyVdP/
\n
"
"g0gPz87RL9ViWhoYCUmhKlI2h5Nx2NhCpWxlxD4dZsaoD2Q+aZRpbuHXU8LrcQsc
\n
"
"UIhSAR/tzTGEYybwze6MyaIuLkYitEEHQ1gKlibOYTDianlnejAoJlV0j+qY+f6J
\n
"
"hkTpLWx8w/YyYQFvX52BYOpPu3BjeZC5o6qpxoSZDbYKJurlvY+90vTqVP/RK9mm
\n
"
"Znuvz67oyKmBQVfNUyStr5qJ1lCE8X0I5PSVSWIQXR2phk8VKlQW7kXGwKnSJlfc
\n
"
"seC6Boeebi+sZ7FuItJ9aNZPm4dNzYMD89djtwIDAQABAoIBAQDDaWquGRl40GR/
\n
"
"C/JjQQPr+RkIZdYGKXu/MEcA8ATf+l5tzfp3hp+BCzCKOpqOxHI3LQoN9xF3t2lq
\n
"
"AX3z27NYO2nFN/h4pYxnRk0Hiulia1+zd6YnsrxYPnPhxXCxsd1xZYsBvzh8WoZb
\n
"
"ZEMt8Zr0PskUzF6VFQh9Ci9k9ym07ooo/KqP4wjXsm/JK1ueOCTpRtabrBI1icrV
\n
"
"iTaw1JEGqlTAQ92vg3pXqSG5yy69Krt7miZZtiOA5mJ90VrHtlNSgp31AOcVv/Ve
\n
"
"/LMIwJp9EzTN+4ipT7AKPeJAoeVqpFjQk+2cW44zJ7xyzw73pTs5ErxkEIhQOp4M
\n
"
"ak2iMg4BAoGBAOivDZSaOcTxEB3oKxYvN/jL9eU2Io9wdZwAZdYQpkgc8lkM9elW
\n
"
"2rbHIwifkDxQnZbl3rXM8xmjA4c5PSCUYdPnLvx6nsUJrWTG0RjakHRliSLthNEC
\n
"
"LpL9MR1aQblyz1D/ulWTFOCNvHU7m3XI3RVJEQWu3qQ5pCndzT56wXjnAoGBANrl
\n
"
"zKvR9o2SONU8SDIcMzXrO2647Z8yXn4Kz1WhWojhRQQ1V3VOLm8gBwv8bPtc7LmE
\n
"
"MSX5MIcxRoHu7D98d53hd+K/ZGYV2h/638qaIEgZDf2oa8QylBgvoGljoy1DH8nN
\n
"
"KKOgksqWK0AAEkP0+S4IFugTxHVanw8JUkV0gVSxAoGBANIRUGJrxmHt/M3zUArs
\n
"
"QE0G3o28DQGQ1y0rEsVrLKQINid9UvoBpt3C9PcRD2fUpCGakDFzwbnQeRv46h3i
\n
"
"uFtV6Q6aKYLcFMXZ1ObqU+Yx0NhOtUz4+lFL8q58UL/7Tf3jkjc13XBJpe31DYoN
\n
"
"+MMBvzNxR6HeRD5j96tDqi3bAoGAT57SqZS/l5MeNQGuSPvU7MHZZlbBp+xMTpBk
\n
"
"BgOgyLUXw4Ybf8GmRiliJsv0YCHWwUwCDIvtSN91hAGB0T3WzIiccM+pFzDPnF5G
\n
"
"VI1nPJJQcnl2aXD0SS/ZqzvguK/3uhFzvMDFZAbnSGo+OpW6pTGwE05NYVpLDM8Z
\n
"
"K8ZK3KECgYEApNoI5Mr5tmtjq4sbZrgQq6cMlfkIj9gUubOzFCryUb6NaB38Xqkp
\n
"
"2N3/jqdkR+5ZiKOYhsYj+Iy6U3jyqiEl9VySYTfEIfP/ky1CD0a8/EVC9HR4iG8J
\n
"
"im6G7/osaSBYAZctryLqVJXObTelgEy/EFwW9jW8HVph/G+ljmHOmuQ=
\n
"
"-----END RSA PRIVATE KEY-----
\n
"
;
libcaf_net/test/net/producer_adapter.cpp
View file @
8e7f7b58
...
...
@@ -125,6 +125,7 @@ struct fixture : test_coordinator_fixture<>, host_fixture {
}
bool
handle_io_event
()
override
{
mm
.
mpx
().
apply_updates
();
return
mm
.
mpx
().
poll_once
(
false
);
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment