E-MailRelay

gclient.h
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file gclient.h
19///
20
21#ifndef G_NET_CLIENT_H
22#define G_NET_CLIENT_H
23
24#include "gdef.h"
25#include "gaddress.h"
26#include "gsocks.h"
27#include "gconnection.h"
28#include "gexception.h"
29#include "gexceptionsource.h"
30#include "geventhandler.h"
31#include "geventlogging.h"
32#include "gresolver.h"
33#include "glocation.h"
34#include "glinebuffer.h"
35#include "gstringview.h"
36#include "gcall.h"
37#include "gtimer.h"
38#include "gsocket.h"
39#include "gsocketprotocol.h"
40#include "gevent.h"
41#include "gslot.h"
42#include "gstr.h"
43#include <string>
44#include <memory>
45
46 namespace GNet
47{
48 class Client ;
49}
50
51//| \class GNet::Client
52/// A class for making an outgoing connection to a remote server, with support
53/// for socket-level protocols such as TLS/SSL and SOCKS 4a.
54///
55/// The class handles name-to-address resolution, deals with connection issues,
56/// reads incoming data, and manages flow-control when sending. The
57/// implementation uses the SocketProtocol class in order to do TLS/SSL;
58/// see secureConnect().
59///
60/// Name-to-address lookup is performed if the supplied Location object does
61/// not contain an address. This can be done synchronously or asynchronously.
62/// The results of the lookup can be obtained via the remoteLocation()
63/// method and possibly fed back to the next Client that connects to the
64/// same host/service in order to implement name lookup cacheing.
65///
66/// Received data is delivered through a virtual method onReceive(), with
67/// optional line-buffering.
68///
69/// Clients should normally be instantiated on the heap and managed by a
70/// ClientPtr so that the onDelete() mechanism works as advertised: the
71/// ExceptionHandler passed to the Client constructor via the EventState
72/// object should be the ClientPtr instance. Clients that decide to
73/// terminate themselves cleanly should call Client::finish() and then throw
74/// a GNet::Done exception. If finish() has not been called then a
75/// disconnection results in an exception being thrown.
76///
77 class GNet::Client : private EventHandler, public Connection, private SocketProtocolSink, private Resolver::Callback , public ExceptionSource
78{
79 public:
80 G_EXCEPTION( DnsError , tx("dns error") )
81 G_EXCEPTION( ConnectError , tx("connect failure") )
82 G_EXCEPTION( NotConnected , tx("socket not connected") )
83 G_EXCEPTION( ResponseTimeout , tx("response timeout") )
84 G_EXCEPTION( IdleTimeout , tx("idle timeout") )
85
86 struct Config /// A structure containing GNet::Client configuration parameters.
87 {
88 StreamSocket::Config stream_socket_config ;
89 LineBuffer::Config line_buffer_config {LineBuffer::Config::transparent()} ;
90 SocketProtocol::Config socket_protocol_config ; // inc. secure_connection_timeout
91 Address local_address {Address::defaultAddress()} ;
92 bool sync_dns {false} ;
93 bool auto_start {true} ;
94 bool bind_local_address {false} ;
95 unsigned int connection_timeout {0U} ;
96 unsigned int response_timeout {0U} ;
97 unsigned int idle_timeout {0U} ;
98 bool no_throw_on_peer_disconnect {false} ; // call SocketProtocolSink::onPeerDisconnect() instead
99
100 Config & set_stream_socket_config( const StreamSocket::Config & ) ;
101 Config & set_line_buffer_config( const LineBuffer::Config & ) ;
102 Config & set_socket_protocol_config( const SocketProtocol::Config & ) ;
103 Config & set_sync_dns( bool = true ) noexcept ;
104 Config & set_auto_start( bool = true ) noexcept ;
105 Config & set_bind_local_address( bool = true ) noexcept ;
106 Config & set_local_address( const Address & ) ;
107 Config & set_connection_timeout( unsigned int ) noexcept ;
108 Config & set_response_timeout( unsigned int ) noexcept ;
109 Config & set_idle_timeout( unsigned int ) noexcept ;
110 Config & set_all_timeouts( unsigned int ) noexcept ;
111 Config & set_no_throw_on_peer_disconnect( bool = true ) noexcept ;
112 } ;
113
114 Client( EventState , const Location & remote_location , const Config & ) ;
115 ///< Constructor. If not auto-starting then connect()
116 ///< is required to start connecting. The EventState exception
117 ///< handler should delete this Client object when an exception
118 ///< is delivered to it, otherwise the the underlying socket
119 ///< might continue to raise events.
120
121 ~Client() override ;
122 ///< Destructor.
123
124 void connect() ;
125 ///< Initiates a connection to the remote server. Calls back
126 ///< to onConnect() when complete (non-reentrantly). Throws
127 ///< on immediate failure.
128
129 bool connected() const ;
130 ///< Returns true if connected to the peer.
131
132 bool hasConnected() const ;
133 ///< Returns true if ever connected().
134
135 std::string peerAddressString( bool with_port = true ) const ;
136 ///< Returns the peer address display string
137 ///< or the empty string if not connected().
138
139 void disconnect() ;
140 ///< Aborts the connection and destroys the object's internal
141 ///< state, resulting in a zombie object. After disconnect()
142 ///< only calls to hasConnected(), finished() and the dtor
143 ///< are allowed.
144
145 Address localAddress() const override ;
146 ///< Returns the local address.
147 ///< Override from GNet::Connection.
148
149 Address peerAddress() const override ;
150 ///< Returns the peer address. Throws if not connected().
151 ///< Override from GNet::Connection.
152
153 std::string connectionState() const override ;
154 ///< Returns the connection state display string.
155 ///< Override from GNet::Connection.
156
157 std::string peerCertificate() const override ;
158 ///< Returns the peer's TLS certificate.
159 ///< Override from GNet::Connection.
160
161 Location remoteLocation() const ;
162 ///< Returns a Location structure, including the result of
163 ///< name lookup if available.
164
165 bool send( const std::string & data ) ;
166 ///< Sends data to the peer and starts the response
167 ///< timer (if configured). Returns true if all sent.
168 ///< Returns false if flow control was asserted, in which
169 ///< case the unsent portion is copied internally and
170 ///< onSendComplete() called when complete. Throws on error.
171
172 bool send( std::string_view data ) ;
173 ///< Overload for string_view.
174
175 bool send( const std::vector<std::string_view> & data , std::size_t offset = 0 ) ;
176 ///< Overload for scatter/gather segments.
177
178 G::Slot::Signal<const std::string&,const std::string&,const std::string&> & eventSignal() noexcept ;
179 ///< Returns a signal that indicates that something interesting
180 ///< has happened. The first signal parameter is one of
181 ///< "resolving", "connecting", or "connected", but other
182 ///< classes may inject the own events into this channel.
183
184 void doOnDelete( const std::string & reason , bool done ) ;
185 ///< This should be called by the Client owner (typically
186 ///< ClientPtr) just before this Client object is deleted
187 ///< as the result of an exception.
188 ///<
189 ///< A Client onDelete() call only ever comes from
190 ///< something external calling doOnDelete().
191 ///<
192 ///< The 'done' argument should be true if the current
193 ///< exception is GNet::Done. The 'reason' string passed
194 ///< to onDelete() will be the given 'reason' or the
195 ///< empty string if 'done||finished()'.
196 ///<
197 ///< See also GNet::ExceptionHandler::onException(),
198 ///< GNet::ServerPeer::onDelete().
199
200 bool finished() const ;
201 ///< Returns true if finish() has been called.
202
203 LineBufferState lineBuffer() const ;
204 ///< Returns information about the state of the internal
205 ///< line-buffer.
206
207 bool secureConnectCapable() const ;
208 ///< Returns true if currently connected and secureConnect()
209 ///< can be used. Typically called from within the onConnect()
210 ///< callback.
211
212protected:
213 StreamSocket & socket() ;
214 ///< Returns a reference to the socket. Throws if not connected.
215
216 const StreamSocket & socket() const ;
217 ///< Returns a const reference to the socket. Throws if not connected.
218
219 void finish() ;
220 ///< Indicates that the last data has been sent and the client
221 ///< is expecting a peer disconnect. Any subsequent onDelete()
222 ///< callback from doOnDelete() will have an empty reason
223 ///< string.
224
225 void clearInput() ;
226 ///< Clears the input LineBuffer and cancels the response
227 ///< timer if running.
228
229 virtual bool onReceive( const char * data , std::size_t size , std::size_t eolsize , std::size_t linesize , char c0 ) = 0 ;
230 ///< Called with received data. If configured with no line
231 ///< buffering then only the first two parameters are
232 ///< relevant. The implementation should return false if
233 ///< it needs to stop further onReceive() calls being
234 ///< generated from data already received and buffered.
235
236 virtual void onConnect() = 0 ;
237 ///< Called once connected.
238
239 virtual void onSendComplete() = 0 ;
240 ///< Called when all residual data from send() has been sent.
241
242 virtual void onDelete( const std::string & reason ) = 0 ;
243 ///< Called from doOnDelete(), typically just before a
244 ///< ClientPtr destroys the Client as the result of handling
245 ///< an exception. The reason parameter will be forced to
246 ///< be the empty string rather than the doOnDelete() value
247 ///< if caused by a GNet::Done exception or after finish()
248 ///< or disconnect(). Consider making the implementation
249 ///< non-throwing, in the spirit of a destructor, since
250 ///< the Client object is about to be deleted.
251
252 void secureConnect() ;
253 ///< Starts TLS/SSL client-side negotiation. Uses a profile
254 ///< called "client" by default; see GSsl::Library::addProfile().
255 ///< The callback GNet::SocketProtocolSink::onSecure() is
256 ///< triggered when the secure session is established.
257
258private: // overrides
259 void readEvent() override ; // GNet::EventHandler
260 void writeEvent() override ; // GNet::EventHandler
261 void otherEvent( EventHandler::Reason ) override ; // GNet::EventHandler
262 void onResolved( std::string , Location ) override ; // GNet::Resolver
263 void onData( const char * , std::size_t ) override ; // GNet::SocketProtocolSink
264 void onPeerDisconnect() override ; // GNet::SocketProtocolSink
265
266public:
267 Client( const Client & ) = delete ;
268 Client( Client && ) = delete ;
269 Client & operator=( const Client & ) = delete ;
270 Client & operator=( Client && ) = delete ;
271 bool send( const char * , std::size_t ) = delete ;
272 bool send( const char * ) = delete ;
273 bool send( const std::string & , std::size_t ) = delete ;
274
275private:
276 enum class State
277 {
278 Idle ,
279 Resolving ,
280 Connecting ,
281 Connected ,
282 Socksing ,
283 Disconnected ,
284 Testing
285 } ;
286 bool onDataImp( const char * , std::size_t , std::size_t , std::size_t , char ) ;
287 void emit( const std::string & ) ;
288 void startConnecting() ;
289 void bindLocalAddress( const Address & ) ;
290 void setState( State ) ;
291 void onStartTimeout() ;
292 void onConnectTimeout() ;
293 void onConnectedTimeout() ;
294 void onResponseTimeout() ;
295 void onIdleTimeout() ;
296 void onWriteable() ;
297 void doOnConnect() ;
298
299 private:
300 EventState m_es ;
301 const Config m_config ;
302 G::CallStack m_call_stack ;
303 std::unique_ptr<StreamSocket> m_socket ;
304 std::unique_ptr<SocketProtocol> m_sp ;
305 std::unique_ptr<Socks> m_socks ;
306 LineBuffer m_line_buffer ;
307 std::unique_ptr<Resolver> m_resolver ;
308 Location m_remote_location ;
309 State m_state {State::Idle} ;
310 bool m_finished {false} ;
311 bool m_has_connected {false} ;
312 Timer<Client> m_start_timer ;
313 Timer<Client> m_connect_timer ;
314 Timer<Client> m_connected_timer ;
315 Timer<Client> m_response_timer ;
316 Timer<Client> m_idle_timer ;
317 G::Slot::Signal<const std::string&,const std::string&,const std::string&> m_event_signal ;
318 std::string m_event_logging_string ;
319} ;
320
321 inline GNet::Client::Config & GNet::Client::Config::set_stream_socket_config( const StreamSocket::Config & cfg ) { stream_socket_config = cfg ; return *this ; }
322 inline GNet::Client::Config & GNet::Client::Config::set_line_buffer_config( const LineBuffer::Config & cfg ) { line_buffer_config = cfg ; return *this ; }
323 inline GNet::Client::Config & GNet::Client::Config::set_socket_protocol_config( const SocketProtocol::Config & cfg ) { socket_protocol_config = cfg ; return *this ; }
324 inline GNet::Client::Config & GNet::Client::Config::set_sync_dns( bool b ) noexcept { sync_dns = b ; return *this ; }
325 inline GNet::Client::Config & GNet::Client::Config::set_auto_start( bool b ) noexcept { auto_start = b ; return *this ; }
326 inline GNet::Client::Config & GNet::Client::Config::set_bind_local_address( bool b ) noexcept { bind_local_address = b ; return *this ; }
327 inline GNet::Client::Config & GNet::Client::Config::set_local_address( const Address & a ) { local_address = a ; return *this ; }
328 inline GNet::Client::Config & GNet::Client::Config::set_connection_timeout( unsigned int t ) noexcept { connection_timeout = t ; return *this ; }
329 inline GNet::Client::Config & GNet::Client::Config::set_response_timeout( unsigned int t ) noexcept { response_timeout = t ; return *this ; }
330 inline GNet::Client::Config & GNet::Client::Config::set_idle_timeout( unsigned int t ) noexcept { idle_timeout = t ; return *this ; }
331 inline GNet::Client::Config & GNet::Client::Config::set_no_throw_on_peer_disconnect( bool b ) noexcept { no_throw_on_peer_disconnect = b ; return *this ; }
332
333#endif
GNet::Address
The GNet::Address class encapsulates a TCP/UDP transport address.
Definition: gaddress.h:63
GNet::Address::defaultAddress
static Address defaultAddress()
Returns a default address, being the IPv4 wildcard address with a zero port number.
Definition: gaddress.cpp:203
GNet::Client
A class for making an outgoing connection to a remote server, with support for socket-level protocols...
Definition: gclient.h:78
GNet::Client::onSendComplete
virtual void onSendComplete()=0
Called when all residual data from send() has been sent.
GNet::Client::disconnect
void disconnect()
Aborts the connection and destroys the object's internal state, resulting in a zombie object.
Definition: gclient.cpp:60
GNet::Client::peerCertificate
std::string peerCertificate() const override
Returns the peer's TLS certificate.
Definition: gclient.cpp:464
GNet::Client::peerAddressString
std::string peerAddressString(bool with_port=true) const
Returns the peer address display string or the empty string if not connected().
Definition: gclient.cpp:443
GNet::Client::secureConnect
void secureConnect()
Starts TLS/SSL client-side negotiation.
Definition: gclient.cpp:476
GNet::Client::~Client
~Client() override
Destructor.
Definition: gclient.cpp:54
GNet::Client::secureConnectCapable
bool secureConnectCapable() const
Returns true if currently connected and secureConnect() can be used.
Definition: gclient.cpp:470
GNet::Client::eventSignal
G::Slot::Signal< const std::string &, const std::string &, const std::string & > & eventSignal() noexcept
Returns a signal that indicates that something interesting has happened.
Definition: gclient.cpp:79
GNet::Client::Client
Client(EventState, const Location &remote_location, const Config &)
Constructor.
Definition: gclient.cpp:37
GNet::Client::localAddress
Address localAddress() const override
Returns the local address.
Definition: gclient.cpp:426
GNet::Client::socket
StreamSocket & socket()
Returns a reference to the socket. Throws if not connected.
Definition: gclient.cpp:89
GNet::Client::connectionState
std::string connectionState() const override
Returns the connection state display string.
Definition: gclient.cpp:456
GNet::Client::finished
bool finished() const
Returns true if finish() has been called.
Definition: gclient.cpp:223
GNet::Client::finish
void finish()
Indicates that the last data has been sent and the client is expecting a peer disconnect.
Definition: gclient.cpp:209
GNet::Client::lineBuffer
LineBufferState lineBuffer() const
Returns information about the state of the internal line-buffer.
Definition: gclient.cpp:509
GNet::Client::hasConnected
bool hasConnected() const
Returns true if ever connected().
Definition: gclient.cpp:228
GNet::Client::connected
bool connected() const
Returns true if connected to the peer.
Definition: gclient.cpp:395
GNet::Client::onReceive
virtual bool onReceive(const char *data, std::size_t size, std::size_t eolsize, std::size_t linesize, char c0)=0
Called with received data.
GNet::Client::remoteLocation
Location remoteLocation() const
Returns a Location structure, including the result of name lookup if available.
Definition: gclient.cpp:84
GNet::Client::peerAddress
Address peerAddress() const override
Returns the peer address.
Definition: gclient.cpp:431
GNet::Client::send
bool send(const std::string &data)
Sends data to the peer and starts the response timer (if configured).
Definition: gclient.cpp:483
GNet::Client::onConnect
virtual void onConnect()=0
Called once connected.
GNet::Client::connect
void connect()
Initiates a connection to the remote server.
Definition: gclient.cpp:115
GNet::Client::doOnDelete
void doOnDelete(const std::string &reason, bool done)
This should be called by the Client owner (typically ClientPtr) just before this Client object is del...
Definition: gclient.cpp:233
GNet::Client::onDelete
virtual void onDelete(const std::string &reason)=0
Called from doOnDelete(), typically just before a ClientPtr destroys the Client as the result of hand...
GNet::Client::clearInput
void clearInput()
Clears the input LineBuffer and cancels the response timer if running.
Definition: gclient.cpp:103
GNet::Connection
An abstract interface which provides information about a network connection.
Definition: gconnection.h:38
GNet::EventHandler
A base class for classes that have a file descriptor and handle asynchronous events from the event lo...
Definition: geventhandler.h:48
GNet::EventState
A lightweight object containing an ExceptionHandler pointer, optional ExceptionSource pointer and opt...
Definition: geventstate.h:131
GNet::ExceptionSource
A mixin base class that identifies the source of an exception when delivered to GNet::ExceptionHandle...
Definition: gexceptionsource.h:39
GNet::LineBufferState
Provides information about the state of a line buffer.
Definition: glinebuffer.h:341
GNet::LineBuffer
A class that does line buffering, supporting auto-detection of line endings and fixed-size block extr...
Definition: glinebuffer.h:84
GNet::Location
A class that represents the remote target for out-going client connections.
Definition: glocation.h:70
GNet::SocketProtocolSink
An interface used by GNet::SocketProtocol to deliver data from a socket.
Definition: gsocketprotocol.h:177
GNet::StreamSocket
A derivation of GNet::Socket for a stream socket.
Definition: gsocket.h:356
G::CallStack
A linked list of CallFrame pointers.
Definition: gcall.h:59
gaddress.h
gcall.h
gconnection.h
gevent.h
geventhandler.h
geventlogging.h
gexception.h
gexceptionsource.h
glinebuffer.h
glocation.h
gresolver.h
gslot.h
gsocket.h
gsocketprotocol.h
gsocks.h
gstr.h
gstringview.h
gtimer.h
GNet
Network classes.
Definition: gdef.h:1243
G::tx
constexpr const char * tx(const char *p) noexcept
A briefer alternative to G::gettext_noop().
Definition: ggettext.h:84
std
STL namespace.
GNet::Client::Config
A structure containing GNet::Client configuration parameters.
Definition: gclient.h:87
GNet::LineBuffer::Config
A configuration structure for GNet::LineBuffer.
Definition: glinebuffer.h:92
GNet::Resolver::Callback
An interface used for GNet::Resolver callbacks.
Definition: gresolver.h:67
GNet::SocketProtocol::Config
A configuration structure for GNet::SocketProtocol.
Definition: gsocketprotocol.h:70
GNet::StreamSocket::Config
A configuration structure for GNet::StreamSocket.
Definition: gsocket.h:363
G::Slot::Signal< const std::string &, const std::string &, const std::string & >