39 m_line_buffer(config.line_buffer_config) ,
40 m_remote_location(remote) ,
41 m_bind_local_address(config.bind_local_address) ,
42 m_local_address(config.local_address) ,
43 m_sync_dns(config.sync_dns) ,
44 m_secure_connection_timeout(config.secure_connection_timeout) ,
45 m_connection_timeout(config.connection_timeout) ,
46 m_response_timeout(config.response_timeout) ,
47 m_idle_timeout(config.idle_timeout) ,
48 m_state(State::Idle) ,
50 m_has_connected(false) ,
51 m_start_timer(*this,&
GNet::
Client::onStartTimeout,es) ,
52 m_connect_timer(*this,&
GNet::
Client::onConnectTimeout,es) ,
53 m_connected_timer(*this,&
GNet::
Client::onConnectedTimeout,es) ,
54 m_response_timer(*this,&
GNet::
Client::onResponseTimeout,es) ,
55 m_idle_timer(*this,&
GNet::
Client::onIdleTimeout,es)
57 G_DEBUG(
"Client::ctor" ) ;
58 if( config.auto_start )
59 m_start_timer.startTimer( 0U ) ;
70 G_DEBUG(
"GNet::Client::disconnect" ) ;
72 m_start_timer.cancelTimer() ;
73 m_connect_timer.cancelTimer() ;
74 m_connected_timer.cancelTimer() ;
75 m_response_timer.cancelTimer() ;
76 m_idle_timer.cancelTimer() ;
78 m_state = State::Disconnected ;
88 return m_event_signal ;
93 return m_remote_location ;
98 if( m_socket ==
nullptr )
99 throw NotConnected() ;
105 if( m_socket ==
nullptr )
106 throw NotConnected() ;
112 m_line_buffer.clear() ;
113 m_response_timer.cancelTimer() ;
116void GNet::Client::onStartTimeout()
118 G_DEBUG(
"GNet::Client::onStartTimeout: auto-start connecting" ) ;
124 G_DEBUG(
"GNet::Client::connect: [" << m_remote_location.displayString() <<
"] "
125 <<
"(" <<
static_cast<int>(m_state) <<
")" ) ;
126 if( m_state != State::Idle )
127 throw ConnectError(
"wrong state" ) ;
130 if( m_connection_timeout )
131 m_connect_timer.startTimer( m_connection_timeout ) ;
133 m_remote_location.resolveTrivially() ;
134 if( m_remote_location.resolved() )
136 setState( State::Connecting ) ;
143 throw DnsError( error ) ;
145 setState( State::Connecting ) ;
150 setState( State::Resolving ) ;
151 if( m_resolver ==
nullptr )
154 m_resolver = std::make_unique<Resolver>( resolver_callback , m_es ) ;
156 m_resolver->start( m_remote_location ) ;
157 emit(
"resolving" ) ;
161void GNet::Client::onResolved( std::string error ,
Location location )
164 throw DnsError( error ) ;
166 G_DEBUG(
"GNet::Client::onResolved: " << location.
displayString() ) ;
167 m_remote_location.update( location.
address() , location.
name() ) ;
168 setState( State::Connecting ) ;
172void GNet::Client::startConnecting()
174 G_DEBUG(
"GNet::Client::startConnecting: local: " << m_local_address.displayString() ) ;
175 G_DEBUG(
"GNet::Client::startConnecting: remote: " << m_remote_location.displayString() ) ;
177 setState( State::Testing ) ;
182 m_socket = std::make_unique<StreamSocket>( m_remote_location.address().family() ) ;
183 socket().addWriteHandler( *
this , m_es ) ;
187 EventHandler & eh = *this ;
188 SocketProtocolSink & sp_sink = *this ;
189 m_sp = std::make_unique<SocketProtocol>( eh , m_es , sp_sink , *m_socket , m_secure_connection_timeout ) ;
193 if( m_bind_local_address )
194 bindLocalAddress( m_local_address ) ;
198 bool immediate = false ;
199 if( !socket().connect( m_remote_location.address() , &immediate ) )
200 throw ConnectError(
"cannot connect to " + m_remote_location.address().displayString() ) ;
206 socket().dropWriteHandler() ;
207 m_connected_timer.startTimer( 0U ) ;
211 emit(
"connecting" ) ;
218 if( with_socket_shutdown )
220 if( m_sp !=
nullptr )
222 else if( m_socket !=
nullptr )
223 m_socket->shutdown() ;
234 return m_has_connected ;
239 onDelete( (done||m_finished) ? std::string() : reason ) ;
242void GNet::Client::emit(
const std::string & action )
244 m_event_signal.emit( std::string(action) , m_remote_location.displayString() , std::string() ) ;
247void GNet::Client::onConnectTimeout()
249 std::ostringstream ss ;
250 ss <<
"cannot connect to " << m_remote_location <<
": timed out out after " << m_connection_timeout <<
"s" ;
251 G_DEBUG(
"GNet::Client::onConnectTimeout: " << ss.str() ) ;
252 throw ConnectError( ss.str() ) ;
255void GNet::Client::onResponseTimeout()
257 std::ostringstream ss ;
258 ss <<
"no response after " << m_response_timeout <<
"s while connected to " << m_remote_location ;
259 G_DEBUG(
"GNet::Client::onResponseTimeout: response timeout: " << ss.str() ) ;
260 throw ResponseTimeout( ss.str() ) ;
263void GNet::Client::onIdleTimeout()
265 std::ostringstream ss ;
266 ss <<
"no activity after " << m_idle_timeout <<
"s while connected to " << m_remote_location ;
267 throw IdleTimeout( ss.str() ) ;
270void GNet::Client::onConnectedTimeout()
272 G_DEBUG(
"GNet::Client::onConnectedTimeout: immediate connection" ) ;
276void GNet::Client::writeEvent()
278 G_DEBUG(
"GNet::Client::writeEvent" ) ;
282void GNet::Client::onWriteable()
284 bool has_peer = m_state == State::Connecting && socket().getPeerAddress().first ;
285 if( m_state == State::Connected )
287 if( m_sp->writeEvent() )
290 else if( m_state == State::Testing )
292 socket().dropWriteHandler() ;
293 setState( State::Connecting ) ;
294 m_connected_timer.startTimer( 2U , 100000U ) ;
296 else if( m_state == State::Connecting && has_peer && m_remote_location.socks() )
298 setState( State::Socksing ) ;
299 m_socks = std::make_unique<Socks>( m_remote_location ) ;
300 if( m_socks->send( socket() ) )
302 socket().addOtherHandler( *
this , m_es ) ;
303 socket().dropWriteHandler() ;
304 socket().addReadHandler( *
this , m_es ) ;
308 socket().addOtherHandler( *
this , m_es ) ;
309 socket().addWriteHandler( *
this , m_es ) ;
310 socket().dropReadHandler() ;
313 else if( m_state == State::Connecting && has_peer )
315 socket().dropWriteHandler() ;
316 socket().addReadHandler( *
this , m_es ) ;
317 socket().addOtherHandler( *
this , m_es ) ;
319 setState( State::Connected ) ;
322 else if( m_state == State::Connecting )
324 socket().dropWriteHandler() ;
325 throw ConnectError(
"cannot connect to " + m_remote_location.address().displayString() ) ;
327 else if( m_state == State::Socksing )
329 G_ASSERT( m_socks !=
nullptr ) ;
330 if( m_socks->send( socket() ) )
332 socket().dropWriteHandler() ;
333 socket().addReadHandler( *
this , m_es ) ;
335 setState( State::Connected ) ;
339 else if( m_state == State::Disconnected )
345void GNet::Client::doOnConnect()
349 if( this_.deleted() ) return ;
350 emit(
"connected" ) ;
353void GNet::Client::otherEvent( EventHandler::Reason reason )
355 if( m_state == State::Socksing || m_sp ==
nullptr )
358 m_sp->otherEvent( reason ) ;
361void GNet::Client::readEvent()
363 G_ASSERT( m_sp !=
nullptr ) ;
364 if( m_state == State::Socksing )
366 G_ASSERT( m_socks !=
nullptr ) ;
367 bool complete = m_socks->read( socket() ) ;
370 setState( State::Connected ) ;
376 if( m_sp !=
nullptr )
381void GNet::Client::onData(
const char * data , std::size_t size )
383 if( m_response_timeout && m_line_buffer.transparent() )
384 m_response_timer.cancelTimer() ;
387 m_idle_timer.startTimer( m_idle_timeout ) ;
389 bool fragments = m_line_buffer.transparent() ;
390 m_line_buffer.apply(
this , &Client::onDataImp , data , size , fragments ) ;
393bool GNet::Client::onDataImp(
const char * data , std::size_t size , std::size_t eolsize ,
394 std::size_t linesize ,
char c0 )
396 if( m_response_timeout && eolsize )
397 m_response_timer.cancelTimer() ;
399 return onReceive( data , size , eolsize , linesize , c0 ) ;
404 return m_state == State::Connected ;
407void GNet::Client::bindLocalAddress(
const Address & local_address )
411 socket().bind( local_address ) ;
414 if( local_address.
isLoopback() && !m_remote_location.address().isLoopback() )
415 G_WARNING_ONCE(
"GNet::Client::bindLocalAddress: binding the loopback address for "
416 "outgoing connections may result in connection failures" ) ;
419void GNet::Client::setState( State new_state )
421 if( new_state != State::Connecting && new_state != State::Resolving )
422 m_connect_timer.cancelTimer() ;
424 if( new_state == State::Connected )
425 m_has_connected = true ;
427 if( new_state == State::Connected && m_idle_timeout )
428 m_idle_timer.startTimer( m_idle_timeout ) ;
430 m_state = new_state ;
436 m_socket !=
nullptr ?
437 std::make_pair(
true,socket().getLocalAddress()) :
444 m_socket !=
nullptr ?
445 socket().getPeerAddress() :
451 std::pair<bool,Address> pair =
452 m_socket !=
nullptr ?
453 socket().getPeerAddress() :
458 pair.second.displayString() :
459 (
"("+m_remote_location.displayString()+
")") ;
464 return m_sp->peerCertificate() ;
469 if( m_sp ==
nullptr )
470 throw NotConnected(
"for secure-connect" ) ;
471 m_sp->secureConnect() ;
476 if( m_response_timeout && data.size() > offset )
477 m_response_timer.startTimer( m_response_timeout ) ;
478 return m_sp->send( data , offset ) ;
483 std::size_t total_size = std::accumulate( data.begin() , data.end() , std::size_t(0U) ,
485 if( m_response_timeout && offset < total_size )
486 m_response_timer.startTimer( m_response_timeout ) ;
487 return m_sp->send( data , offset ) ;
492 return m_line_buffer.state() ;
510GNet::Client::Config::Config() :
511 local_address(Address::defaultAddress()) ,
512 line_buffer_config(LineBufferConfig::transparent()) ,
513 sync_dns(ClientImp::sync_default())
517GNet::Client::Config::Config(
const LineBufferConfig & lbc ) :
518 local_address(Address::defaultAddress()) ,
519 line_buffer_config(lbc) ,
520 sync_dns(ClientImp::sync_default())
524GNet::Client::Config::Config(
const LineBufferConfig & lbc ,
unsigned int connection_timeout_in ,
525 unsigned int secure_connection_timeout_in ,
unsigned int response_timeout_in ,
526 unsigned int idle_timeout_in ) :
527 local_address(Address::defaultAddress()) ,
528 line_buffer_config(lbc) ,
529 sync_dns(ClientImp::sync_default()) ,
530 connection_timeout(connection_timeout_in) ,
531 secure_connection_timeout(secure_connection_timeout_in) ,
532 response_timeout(response_timeout_in) ,
533 idle_timeout(idle_timeout_in)
537GNet::Client::Config::Config(
const LineBufferConfig & lbc ,
unsigned int all_timeouts ) :
538 local_address(Address::defaultAddress()) ,
539 line_buffer_config(lbc) ,
540 sync_dns(ClientImp::sync_default()) ,
541 connection_timeout(all_timeouts) ,
542 secure_connection_timeout(all_timeouts) ,
543 response_timeout(all_timeouts) ,
544 idle_timeout(all_timeouts*2U)
550 connection_timeout = all_timeouts ;
551 secure_connection_timeout = all_timeouts ;
552 response_timeout = all_timeouts ;
553 idle_timeout = all_timeouts * 2U ;
The GNet::Address class encapsulates a TCP/UDP transport address.
static Address defaultAddress()
Returns a default address, being the IPv4 wildcard address with a zero port number.
bool isLoopback() const
Returns true if this is a loopback address.
A class for making an outgoing connection to a remote server, with support for socket-level protocols...
void disconnect()
Aborts the connection and destroys the object's internal state, resulting in a zombie object.
std::string peerCertificate() const override
Returns the peer's TLS certificate.
void secureConnect()
Starts TLS/SSL client-side negotiation.
~Client() override
Destructor.
bool send(const std::string &data, std::size_t offset=0)
Sends data to the peer and starts the response timer (if configured).
void finish(bool with_socket_shutdown)
Indicates that the last data has been sent and the client is expecting a peer disconnect.
G::Slot::Signal< const std::string &, const std::string &, const std::string & > & eventSignal() noexcept
Returns a signal that indicates that something interesting has happened.
StreamSocket & socket()
Returns a reference to the socket. Throws if not connected.
std::string connectionState() const override
Returns the connection state display string.
bool finished() const
Returns true if finish()ed or disconnect()ed.
LineBufferState lineBuffer() const
Returns information about the state of the internal line-buffer.
bool hasConnected() const
Returns true if ever connected().
bool connected() const
Returns true if connected to the peer.
Location remoteLocation() const
Returns a Location structure, including the result of name lookup if available.
Client(ExceptionSink, const Location &remote_location, const Config &)
Constructor.
void connect()
Initiates a connection to the remote server.
std::pair< bool, Address > peerAddress() const override
Override from Connection.
void doOnDelete(const std::string &reason, bool done)
Called by ClientPtr (or equivalent) to call onDelete(), just before this client object is deleted.
std::pair< bool, Address > localAddress() const override
Override from Connection.
void clearInput()
Clears the input LineBuffer and cancels the response timer if running.
virtual void otherEvent(Reason)
Called for a socket-exception event, or a socket-close event on windows.
A tuple containing an ExceptionHandler interface pointer and a bound 'exception source' pointer.
Provides information abount the state of a line buffer.
A class that represents the remote target for out-going client connections.
std::string name() const
Returns the remote canonical name.
std::string displayString() const
Returns a string representation for logging and debug.
Address address() const
Returns the remote address.
static void removeClient(const Connection &client) noexcept
Removes a client connection.
static void addClient(const Connection &client)
Adds a client connection.
static std::string resolve(Location &)
Does synchronous name resolution.
static bool async()
Returns true if the resolver supports asynchronous operation.
A derivation of GNet::Socket for a stream socket.
An object to represent a nested execution context.
A class which acquires the process's special privileges on construction and releases them on destruct...
static bool enabled() noexcept
Returns true if test features are enabled.
A class template like c++17's std::basic_string_view.
A structure containing GNet::Client configuration parameters.
An interface used for GNet::Resolver callbacks.