43 using Result = GSsl::Protocol::Result ;
45 using Segments = std::vector<Segment> ;
48 std::size_t segment{0U} ;
49 std::size_t offset{0U} ;
50 Position( std::size_t segment_ , std::size_t offset_ ) : segment(segment_) , offset(offset_) {}
56 StreamSocket & ,
unsigned int secure_connection_timeout ) ;
60 void otherEvent( EventHandler::Reason ) ;
61 bool send(
const std::string & data , std::size_t offset ) ;
62 bool send(
const Segments & , std::size_t ) ;
64 void secureConnect() ;
67 std::string peerCertificate()
const ;
68 static void setReadBufferSize( std::size_t ) ;
77 enum class State { raw , connecting , accepting , writing , idle , shuttingdown } ;
78 static std::unique_ptr<GSsl::Protocol> newProtocol(
const std::string & ) ;
79 static void log(
int level ,
const std::string & line ) ;
82 bool rawWriteEvent() ;
83 void rawOtherEvent() ;
84 bool rawSend(
const Segments & , Position ,
bool =
false ) ;
85 bool rawSendImp(
const Segments & , Position , Position & ) ;
87 bool sslSend(
const Segments & segments , Position pos ) ;
89 bool sslSendImp(
const Segments & segments , Position pos , Position & ) ;
90 void secureConnectImp() ;
91 void secureAcceptImp() ;
93 void logSecure(
const std::string & ,
const std::string & )
const ;
94 void onSecureConnectionTimeout() ;
95 static std::size_t size(
const Segments & ) ;
96 static bool finished(
const Segments & , Position ) ;
97 static Position firstPosition(
const Segments & , std::size_t ) ;
98 static Position newPosition(
const Segments & , Position , std::size_t ) ;
100 friend std::ostream & operator<<( std::ostream & , State ) ;
108 unsigned int m_secure_connection_timeout ;
109 Segments m_one_segment ;
110 Segments m_segments ;
111 Position m_position ;
112 std::string m_data_copy ;
114 std::unique_ptr<GSsl::Protocol> m_ssl ;
116 std::vector<char> m_read_buffer ;
117 ssize_t m_read_buffer_n ;
119 std::string m_peer_certificate ;
120 static std::size_t m_read_buffer_size ;
125 std::ostream & operator<<( std::ostream & stream , SocketProtocolImp::State state )
127 return stream << static_cast<int>(state) ;
129 std::ostream & operator<<( std::ostream & stream ,
const SocketProtocolImp::Position & pos )
131 return stream <<
"(" << pos.segment <<
"," << pos.offset <<
")" ;
133 std::ostream & operator<<( std::ostream & stream ,
const SocketProtocolImp::Segments & segments )
136 const char * sep =
"" ;
137 for( std::size_t i = 0U ; i < segments.size() ; i++ , sep =
"," )
138 stream << sep <<
"(" <<
static_cast<const void*
>(segments.at(i).data())
139 <<
":" << segments.at(i).size() <<
")" ;
145std::size_t GNet::SocketProtocolImp::m_read_buffer_size = G::limits::net_buffer ;
147GNet::SocketProtocolImp::SocketProtocolImp( EventHandler & handler , ExceptionSink es ,
148 SocketProtocol::Sink & sink , StreamSocket & socket ,
unsigned int secure_connection_timeout ) :
153 m_secure_connection_timeout(secure_connection_timeout) ,
156 m_state(State::raw) ,
157 m_read_buffer(m_read_buffer_size) ,
159 m_secure_connection_timer(*this,&SocketProtocolImp::onSecureConnectionTimeout,es)
161 G_ASSERT( m_read_buffer.size() == m_read_buffer_size ) ;
164GNet::SocketProtocolImp::~SocketProtocolImp()
167void GNet::SocketProtocolImp::setReadBufferSize( std::size_t n )
169 m_read_buffer_size = std::max( std::size_t(1U) , n ) ;
172void GNet::SocketProtocolImp::onSecureConnectionTimeout()
174 G_DEBUG(
"GNet::SocketProtocolImp::onSecureConnectionTimeout: timed out" ) ;
175 throw SocketProtocol::SecureConnectionTimeout() ;
178void GNet::SocketProtocolImp::readEvent()
180 G_DEBUG(
"SocketProtocolImp::readEvent: read event: " << m_socket.
asString() <<
": "
181 <<
"state=" <<
static_cast<int>(m_state) ) ;
182 if( m_state == State::raw )
184 else if( m_state == State::connecting )
186 else if( m_state == State::accepting )
188 else if( m_state == State::writing )
190 else if( m_state == State::shuttingdown )
196bool GNet::SocketProtocolImp::writeEvent()
198 G_DEBUG(
"GNet::SocketProtocolImp::writeEvent: write event: " << m_socket.
asString() <<
": "
199 <<
"state=" <<
static_cast<int>(m_state) ) ;
201 if( m_state == State::raw )
202 rc = rawWriteEvent() ;
203 else if( m_state == State::connecting )
205 else if( m_state == State::accepting )
207 else if( m_state == State::writing )
209 else if( m_state == State::shuttingdown )
216void GNet::SocketProtocolImp::otherEvent( EventHandler::Reason reason )
218 if( m_state == State::raw && reason == EventHandler::Reason::closed
229std::size_t GNet::SocketProtocolImp::size(
const Segments & segments )
231 return std::accumulate( segments.begin() , segments.end() , std::size_t(0) ,
237 return newPosition( s , Position() , offset ) ;
243 pos.offset += offset ;
244 for( ; pos.segment < s.size() && pos.offset >= s[pos.segment].size() ; pos.segment++ )
246 pos.offset -= s[pos.segment].size() ;
251G::string_view GNet::SocketProtocolImp::chunk(
const Segments & s , Position pos )
253 G_ASSERT( pos.segment < s.size() ) ;
254 G_ASSERT( pos.offset < s[pos.segment].size() ) ;
255 return s.at(pos.segment).substr( pos.offset ) ;
258bool GNet::SocketProtocolImp::send(
const std::string & data , std::size_t offset )
260 if( data.empty() || offset >= data.length() )
264 if( m_state == State::raw )
266 G_ASSERT( m_one_segment.size() == 1U ) ;
268 rc = rawSend( m_one_segment , Position(0U,offset) ,
true ) ;
270 else if( m_state == State::connecting || m_state == State::accepting )
272 throw SocketProtocol::SendError(
"still busy negotiating" ) ;
274 else if( m_state == State::writing )
276 throw SocketProtocol::SendError(
"still busy sending the last packet" ) ;
278 else if( m_state == State::shuttingdown )
280 throw SocketProtocol::SendError(
"shuting down" ) ;
285 m_data_copy = data.substr( offset ) ;
286 G_ASSERT( m_one_segment.size() == 1U ) ;
287 m_one_segment[0] =
G::string_view( m_data_copy.data() , m_data_copy.size() ) ;
288 rc = sslSend( m_one_segment , Position() ) ;
293bool GNet::SocketProtocolImp::send(
const Segments & segments , std::size_t offset )
295 if( segments.empty() || size(segments) == 0U || offset >= size(segments) )
299 if( m_state == State::raw )
301 rc = rawSend( segments , firstPosition(segments,offset) ) ;
303 else if( m_state == State::connecting || m_state == State::accepting )
305 throw SocketProtocol::SendError(
"still busy negotiating" ) ;
307 else if( m_state == State::writing )
309 throw SocketProtocol::SendError(
"still busy sending the last packet" ) ;
311 else if( m_state == State::shuttingdown )
313 throw SocketProtocol::SendError(
"shutting down" ) ;
317 rc = sslSend( segments , firstPosition(segments,offset) ) ;
322void GNet::SocketProtocolImp::shutdown()
324 if( m_state == State::idle )
326 m_state = State::shuttingdown ;
331void GNet::SocketProtocolImp::shutdownImp()
333 G_ASSERT( m_ssl !=
nullptr ) ;
334 G_ASSERT( m_state == State::shuttingdown ) ;
335 Result rc = m_ssl->shutdown() ;
336 if( rc == Result::ok )
341 else if( rc == Result::error )
344 throw SocketProtocol::ShutdownError() ;
346 else if( rc == Result::read )
350 else if( rc == Result::write )
356bool GNet::SocketProtocolImp::secure()
const
358 return m_state == State::writing || m_state == State::idle ;
361void GNet::SocketProtocolImp::secureConnect()
363 G_DEBUG(
"SocketProtocolImp::secureConnect" ) ;
364 G_ASSERT( m_ssl ==
nullptr ) ;
366 m_ssl = newProtocol(
"client" ) ;
367 m_state = State::connecting ;
368 if( m_secure_connection_timeout != 0U )
369 m_secure_connection_timer.startTimer( m_secure_connection_timeout ) ;
373void GNet::SocketProtocolImp::secureConnectImp()
375 G_DEBUG(
"SocketProtocolImp::secureConnectImp" ) ;
376 G_ASSERT( m_ssl !=
nullptr ) ;
377 G_ASSERT( m_state == State::connecting ) ;
379 Result rc = m_ssl->connect( m_socket ) ;
381 if( rc == Result::error )
384 m_state = State::raw ;
385 throw SocketProtocol::ReadError(
"ssl connect" ) ;
387 else if( rc == Result::read )
391 else if( rc == Result::write )
398 m_state = State::idle ;
399 if( m_secure_connection_timeout != 0U )
400 m_secure_connection_timer.cancelTimer() ;
401 m_peer_certificate = m_ssl->peerCertificate() ;
402 std::string protocol = m_ssl->protocol() ;
403 std::string cipher = m_ssl->cipher() ;
404 logSecure( protocol , cipher ) ;
405 m_sink.
onSecure( m_peer_certificate , protocol , cipher ) ;
409void GNet::SocketProtocolImp::secureAccept()
411 G_DEBUG(
"SocketProtocolImp::secureAccept" ) ;
412 G_ASSERT( m_ssl ==
nullptr ) ;
414 m_ssl = newProtocol(
"server" ) ;
415 m_state = State::accepting ;
419void GNet::SocketProtocolImp::secureAcceptImp()
421 G_DEBUG(
"SocketProtocolImp::secureAcceptImp" ) ;
422 G_ASSERT( m_ssl !=
nullptr ) ;
423 G_ASSERT( m_state == State::accepting ) ;
425 Result rc = m_ssl->accept( m_socket ) ;
427 if( rc == Result::error )
430 m_state = State::raw ;
431 throw SocketProtocol::ReadError(
"ssl accept" ) ;
433 else if( rc == Result::read )
437 else if( rc == Result::write )
444 m_state = State::idle ;
445 m_peer_certificate = m_ssl->peerCertificate() ;
446 std::string protocol = m_ssl->protocol() ;
447 std::string cipher = m_ssl->cipher() ;
448 logSecure( protocol , cipher ) ;
449 m_sink.
onSecure( m_peer_certificate , protocol , cipher ) ;
453bool GNet::SocketProtocolImp::sslSend(
const Segments & segments , Position pos )
455 if( !finished(m_segments,m_position) )
456 throw SocketProtocol::SendError(
"still busy sending the last packet" ) ;
458 G_ASSERT( m_state == State::idle ) ;
459 m_state = State::writing ;
462 bool all_sent = sslSendImp( segments , pos , pos_out ) ;
463 if( !all_sent && failed() )
466 m_position = Position() ;
467 throw SocketProtocol::SendError() ;
472 m_position = Position() ;
476 m_segments = segments ;
477 m_position = pos_out ;
482bool GNet::SocketProtocolImp::sslSendImp()
484 return sslSendImp( m_segments , m_position , m_position ) ;
487bool GNet::SocketProtocolImp::sslSendImp(
const Segments & segments , Position pos , Position & pos_out )
489 while( !finished(segments,pos) )
493 GSsl::Protocol::Result result = m_ssl->write( c.data() , c.size() , nsent ) ;
494 if( result == Result::error )
497 m_state = State::idle ;
501 else if( result == Result::read )
506 else if( result == Result::write )
514 G_ASSERT( nsent >= 0 ) ;
515 pos_out = pos = newPosition( segments , pos ,
516 nsent >= 0 ?
static_cast<std::size_t
>(nsent) : std::size_t(0U) ) ;
519 m_state = State::idle ;
523void GNet::SocketProtocolImp::sslReadImp()
525 G_DEBUG(
"SocketProtocolImp::sslReadImp" ) ;
526 G_ASSERT( m_state == State::idle ) ;
527 G_ASSERT( m_ssl !=
nullptr ) ;
529 Result rc = Result::more ;
530 for(
int sanity = 0 ; rc == Result::more && sanity < 1000 ; sanity++ )
532 rc = m_ssl->read( &m_read_buffer[0] , m_read_buffer.size() , m_read_buffer_n ) ;
534 if( rc == Result::error )
537 m_state = State::idle ;
538 throw SocketProtocol::ReadError(
"ssl read" ) ;
540 else if( rc == Result::read )
544 else if( rc == Result::write )
550 G_ASSERT( rc == Result::ok || rc == Result::more ) ;
551 G_ASSERT( m_read_buffer_n >= 0 ) ;
553 m_state = State::idle ;
554 std::size_t n =
static_cast<std::size_t
>(m_read_buffer_n) ;
555 m_read_buffer_n = 0 ;
556 G_DEBUG(
"SocketProtocolImp::sslReadImp: calling onData(): " << n ) ;
560 m_sink.
onData( &m_read_buffer[0] , n ) ;
561 if( this_.deleted() ) break ;
564 if( rc == Result::more )
566 G_DEBUG(
"SocketProtocolImp::sslReadImp: more available to read" ) ;
571void GNet::SocketProtocolImp::rawOtherEvent()
575 G_DEBUG(
"GNet::SocketProtocolImp::rawOtherEvent: clearing receive queue" ) ;
578 const ssize_t rc = m_socket.
read( &m_read_buffer[0] , m_read_buffer.size() ) ;
579 G_DEBUG(
"GNet::SocketProtocolImp::rawOtherEvent: read " << m_socket.
asString() <<
": " << rc ) ;
583 throw SocketProtocol::ReadError( m_socket.
reason() ) ;
585 G_ASSERT(
static_cast<std::size_t
>(rc) <= m_read_buffer.size() ) ;
587 m_sink.
onData( &m_read_buffer[0] ,
static_cast<std::size_t
>(rc) ) ;
588 if( this_.deleted() ) break ;
592void GNet::SocketProtocolImp::rawReadEvent()
594 const ssize_t rc = m_socket.
read( &m_read_buffer[0] , m_read_buffer.size() ) ;
595 if( rc == 0 || ( rc == -1 && !m_socket.
eWouldBlock() ) )
597 throw SocketProtocol::ReadError( rc == 0 ? std::string() : m_socket.
reason() ) ;
601 G_ASSERT(
static_cast<std::size_t
>(rc) <= m_read_buffer.size() ) ;
602 m_sink.
onData( &m_read_buffer[0] ,
static_cast<std::size_t
>(rc) ) ;
606 G_DEBUG(
"GNet::SocketProtocolImp::rawReadEvent: read event read nothing" ) ;
611bool GNet::SocketProtocolImp::rawSend(
const Segments & segments , Position pos ,
bool do_copy )
613 G_ASSERT( !do_copy || segments.size() == 1U ) ;
615 if( !finished(m_segments,m_position) )
616 throw SocketProtocol::SendError(
"still busy sending the last packet" ) ;
619 bool all_sent = rawSendImp( segments , pos , pos_out ) ;
620 if( !all_sent && failed() )
623 m_position = Position() ;
624 m_data_copy.clear() ;
625 throw SocketProtocol::SendError( m_socket.
reason() ) ;
630 m_position = Position() ;
631 m_data_copy.clear() ;
636 G_ASSERT( segments.size() == 1U ) ;
637 G_ASSERT( pos_out.offset < segments[0].size() ) ;
638 m_segments = segments ;
639 m_data_copy.assign( segments[0].data()+pos_out.offset , segments[0].size()-pos_out.offset ) ;
640 m_segments[0] =
G::string_view( m_data_copy.data() , m_data_copy.size() ) ;
641 m_position = Position() ;
648 m_segments = segments ;
649 m_data_copy.clear() ;
650 m_position = pos_out ;
657bool GNet::SocketProtocolImp::rawWriteEvent()
660 bool all_sent = rawSendImp( m_segments , m_position , m_position ) ;
661 if( !all_sent && failed() )
664 m_position = Position() ;
665 m_data_copy.clear() ;
666 throw SocketProtocol::SendError() ;
671 m_position = Position() ;
672 m_data_copy.clear() ;
681bool GNet::SocketProtocolImp::rawSendImp(
const Segments & segments , Position pos , Position & pos_out )
683 while( !finished(segments,pos) )
686 ssize_t rc = m_socket.
write( c.data() , c.size() ) ;
690 pos_out = Position() ;
694 else if( rc < 0 ||
static_cast<std::size_t
>(rc) < c.size() )
697 std::size_t nsent = rc > 0 ?
static_cast<std::size_t
>(rc) : 0U ;
698 pos_out = newPosition( segments , pos , nsent ) ;
699 G_ASSERT( !finished(segments,pos_out) ) ;
704 pos = newPosition( segments , pos ,
static_cast<std::size_t
>(rc) ) ;
710std::unique_ptr<GSsl::Protocol> GNet::SocketProtocolImp::newProtocol(
const std::string & profile_name )
713 if( library ==
nullptr )
714 throw G::Exception(
"SocketProtocolImp::newProtocol: no tls library available" ) ;
716 return std::make_unique<GSsl::Protocol>( library->
profile(profile_name) ) ;
719bool GNet::SocketProtocolImp::finished(
const Segments & segments , Position pos )
721 G_ASSERT( pos.segment <= segments.size() ) ;
722 return pos.segment == segments.size() ;
725bool GNet::SocketProtocolImp::failed()
const
730std::string GNet::SocketProtocolImp::peerCertificate()
const
732 return m_peer_certificate ;
735void GNet::SocketProtocolImp::log(
int level ,
const std::string & log_line )
738 G_DEBUG(
"GNet::SocketProtocolImp::log: tls: " << log_line ) ;
739 else if( level == 2 )
740 G_LOG(
"GNet::SocketProtocolImp::log: tls: " << log_line ) ;
742 G_WARNING(
"GNet::SocketProtocolImp::log: tls: " << log_line ) ;
745void GNet::SocketProtocolImp::logSecure(
const std::string & protocol ,
const std::string & cipher )
const
747 G_LOG(
"GNet::SocketProtocolImp: tls protocol established with "
749 << (protocol.empty()?
"":
" protocol ") << protocol
756 Sink & sink ,
StreamSocket & socket ,
unsigned int secure_connection_timeout ) :
757 m_imp(std::make_unique<
SocketProtocolImp>(handler,es,sink,socket,secure_connection_timeout))
771 return m_imp->writeEvent() ;
776 m_imp->otherEvent( reason ) ;
781 return m_imp->send( data , offset ) ;
786 return m_imp->send( data , offset ) ;
801 m_imp->secureConnect() ;
811 m_imp->secureAccept() ;
816 return m_imp->peerCertificate() ;
821 SocketProtocolImp::setReadBufferSize( n ) ;
A base class for classes that handle asynchronous events from the event loop.
static std::string str(Reason)
Returns a printable description of the other-event reason.
A tuple containing an ExceptionHandler interface pointer and a bound 'exception source' pointer.
std::string reason() const
Returns the reason for the previous error.
std::string asString() const
Returns the socket handle as a string.
void addWriteHandler(EventHandler &, ExceptionSink)
Adds this socket to the event source list so that the given handler receives write events when flow c...
bool eWouldBlock() const override
Returns true if the previous socket operation failed because the socket would have blocked.
void dropWriteHandler() noexcept
Reverses addWriteHandler().
A pimple-pattern implementation class used by GNet::SocketProtocol.
An interface used by GNet::SocketProtocol to deliver data from a socket.
virtual void onSecure(const std::string &peer_certificate, const std::string &protocol, const std::string &cipher)=0
Called once the secure socket protocol has been successfully negotiated.
virtual void onData(const char *, std::size_t)=0
Called when data is read from the socket.
bool send(const std::string &data, std::size_t offset=0U)
Sends data.
bool writeEvent()
Called on receipt of a write event.
void otherEvent(EventHandler::Reason)
Called on receipt of an 'other' event.
static bool secureAcceptCapable()
Returns true if the implementation supports TLS/SSL and a "server" profile has been configured.
void secureAccept()
Waits for the TLS/SSL handshake protocol, acting as a server.
~SocketProtocol()
Destructor.
SocketProtocol(EventHandler &, ExceptionSink, Sink &, StreamSocket &, unsigned int secure_connection_timeout)
Constructor. The references are kept.
void shutdown()
Initiates a TLS-close if secure, together with a Socket::shutdown(1).
std::string peerCertificate() const
Returns the peer's TLS/SSL certificate or the empty string.
static bool secureConnectCapable()
Returns true if the implementation supports TLS/SSL and a "client" profile has been configured.
static void setReadBufferSize(std::size_t n)
Sets the read buffer size. Used in testing.
void readEvent()
Called on receipt of a read event.
void secureConnect()
Initiates the TLS/SSL handshake, acting as a client.
void shutdown(int how=1)
Modifies the local socket state so that so that new sends (1 or 2) and/or receives (0 or 2) will fail...
std::pair< bool, Address > getPeerAddress() const
Retrieves address of socket's peer.
A derivation of GNet::Socket for a stream socket.
ssize_type write(const char *buf, size_type len) override
Override from Socket::write().
ssize_type read(char *buffer, size_type buffer_length) override
Override from ReadWrite::read().
A timer class template in which the timeout is delivered to the specified method.
A singleton class for initialising the underlying TLS library.
static bool enabledAs(const std::string &profile_name)
A static convenience function that returns true if there is an enabled() Library instance() that has ...
static Library * instance()
Returns a pointer to a library object, if any.
const Profile & profile(const std::string &profile_name) const
Returns an opaque reference to the named profile.
static std::string str(Result result)
Converts a result enumeration into a printable string.
An object to represent a nested execution context.
A linked list of CallFrame pointers.
A general-purpose exception class derived from std::exception and containing an error message.
static std::string printable(const std::string &in, char escape='\\')
Returns a printable representation of the given input string, using chacter code ranges 0x20 to 0x7e ...
static bool enabled() noexcept
Returns true if test features are enabled.
A class template like c++17's std::basic_string_view.
An interface to an underlying TLS library.
A pointer into the scatter/gather payload of GNet::SocketProtocolImp::send().