1
0
mirror of https://github.com/stefanocasazza/ULib.git synced 2025-09-28 19:05:55 +08:00
ULib/include/ulib/net/client/client.h
stefanocasazza 8976b94e73 sync
2019-03-08 15:59:40 +01:00

395 lines
10 KiB
C++

// ============================================================================
//
// = LIBRARY
// ULib - c++ library
//
// = FILENAME
// client.h - manages a client connection with a server
//
// = AUTHOR
// Stefano Casazza
//
// ============================================================================
#ifndef U_CLIENT_H
#define U_CLIENT_H 1
#include <ulib/url.h>
#include <ulib/net/rpc/rpc.h>
#include <ulib/mime/header.h>
#include <ulib/utility/uhttp.h>
#ifdef USE_LIBSSL
# include <ulib/ssl/certificate.h>
# include <ulib/ssl/net/sslsocket.h>
#endif
/**
* @class UClient
*
* @brief Handles a connections with a server
*/
class UHTTP;
class USocketExt;
class USSLSocket;
class UHttpPlugIn;
class UFileConfig;
class UFCGIPlugIn;
class USCGIPlugIn;
class UProxyPlugIn;
class UNoCatPlugIn;
class UServer_Base;
class UWebSocketClient;
class UHttpClient_Base;
class UClientImage_Base;
class UREDISClient_Base;
class UElasticSearchClient;
// manage client write to log
#ifdef U_LOG_DISABLE
# define U_CLIENT_LOG_RESPONSE() {}
# define U_CLIENT_LOG_REQUEST(n) {}
# define U_CLIENT_LOG( fmt,args...) {}
# define U_CLIENT_LOG_WITH_ADDR(fmt,args...) {}
#else
# define U_CLIENT_LOG( fmt,args...) { if (UClient_Base::log) UClient_Base::log->log(U_CONSTANT_TO_PARAM(fmt), ##args); }
# define U_CLIENT_LOG_WITH_ADDR(fmt,args...) { if (UClient_Base::log) UClient_Base::log->log(U_CONSTANT_TO_PARAM(fmt " %V%R"), ##args, UClient_Base::host_port.rep, 0); }
# define U_CLIENT_LOG_REQUEST(n) { if (UClient_Base::log) UClient_Base::log->log(UClient_Base::iov, "request", n, "", 0, U_CONSTANT_TO_PARAM(" to %V"), UClient_Base::host_port.rep); }
# define U_CLIENT_LOG_RESPONSE() { if (UClient_Base::log && UClient_Base::response) \
UClient_Base::log->logResponse(UClient_Base::response, U_CONSTANT_TO_PARAM(" from %V"), UClient_Base::host_port.rep); }
#endif
class U_EXPORT UClient_Base {
public:
// Check for memory error
U_MEMORY_TEST
// Allocator e Deallocator
U_MEMORY_ALLOCATOR
U_MEMORY_DEALLOCATOR
// SERVICES
bool isOpen() const
{
U_TRACE_NO_PARAM(0, "UClient_Base::isOpen()")
if (socket->isOpen()) U_RETURN(true);
U_RETURN(false);
}
bool isClosed() const
{
U_TRACE_NO_PARAM(0, "UClient_Base::isClosed()")
if (socket->isClosed()) U_RETURN(true);
U_RETURN(false);
}
bool isConnected() const
{
U_TRACE_NO_PARAM(0, "UClient_Base::isConnected()")
if (socket->isOpen() &&
socket->isConnected())
{
U_RETURN(true);
}
U_RETURN(false);
}
void reserve(uint32_t n)
{
U_TRACE(0, "UClient_Base::reserve(%u)", n)
(void) response.reserve(n);
}
void close()
{
U_TRACE_NO_PARAM(0, "UClient_Base::close()")
U_INTERNAL_ASSERT_POINTER(socket)
if (isOpen()) socket->_close_socket();
}
bool reConnect()
{
U_TRACE_NO_PARAM(0, "UClient_Base::reConnect()")
U_INTERNAL_ASSERT_POINTER(socket)
socket->reOpen();
return connect();
}
bool shutdown(int how = SHUT_WR)
{
U_TRACE(0, "UClient_Base::shutdown(%d)", how)
U_INTERNAL_ASSERT_POINTER(socket)
if (socket->shutdown(how)) U_RETURN(true);
U_RETURN(false);
}
void setTimeOut(uint32_t t)
{
U_TRACE(0, "UClient_Base::setTimeOut()")
timeoutMS = t;
}
void adjustTimeOut()
{
U_TRACE_NO_PARAM(0, "UClient_Base::adjustTimeOut()")
if (timeoutMS < U_TIMEOUT_MS) timeoutMS = U_TIMEOUT_MS;
}
void reset()
{
U_TRACE_NO_PARAM(0, "UClient_Base::reset()")
uri.clear(); // NB: to avoid DEAD OF SOURCE STRING WITH CHILD ALIVE... (uri can be a substr of url)
url.clear();
}
void prepareRequest(const char* req, uint32_t len)
{
U_TRACE(0, "UClient_Base::prepareRequest(%.*S,%u)", len, req, len)
iovcnt = 1;
iov[0].iov_base = (caddr_t)req;
iov[0].iov_len = len;
(void) U_SYSCALL(memset, "%p,%d,%u", iov+1, 0, sizeof(struct iovec) * 5);
U_INTERNAL_ASSERT_EQUALS(iov[1].iov_len, 0)
U_INTERNAL_ASSERT_EQUALS(iov[2].iov_len, 0)
U_INTERNAL_ASSERT_EQUALS(iov[3].iov_len, 0)
U_INTERNAL_ASSERT_EQUALS(iov[4].iov_len, 0)
U_INTERNAL_ASSERT_EQUALS(iov[5].iov_len, 0)
}
void prepareRequest(const UString& req) { request = req; prepareRequest(U_STRING_TO_PARAM(req)); }
void clearData()
{
U_TRACE_NO_PARAM(0, "UClient_Base::clearData()")
buffer.setEmpty();
response.setEmpty();
}
UString getUrl() const { return url.get(); }
UString getUrlPath() { return url.getPath(); }
UString getUrlHost() { return url.getHost(); }
UString getServer() const { return server; }
UString getBuffer() const { return buffer; }
UString getResponse() const { return response; }
int getFd() const { return socket->getFd(); }
const char* getResponseData() const { return response.data(); }
unsigned int getPort() const { return port; }
bool connect();
bool readResponse(uint32_t count = U_SINGLE_READ);
bool setHostPort(const UString& host, unsigned int port);
bool remoteIPAddress(UIPAddress& addr)
{
U_TRACE(0, "UClient_Base::::remoteIPAddress(%p)", &addr)
if (socket->iRemotePort)
{
addr = socket->cRemoteAddress;
U_RETURN(true);
}
U_RETURN(false);
}
static void closeLog();
// NB: return if it has modified host or port...
bool setUrl(const char* str, uint32_t len);
bool setUrl(const UString& _url) { return setUrl(U_STRING_TO_PARAM(_url)); }
/**
* Establishes a TCP/IP socket connection with the host that will satisfy requests for the provided URL.
* This may connect to the host name contained within the URL, or to a proxy server if one has been set.
* This function does not send any information to the remote server after the connection is established
*
* @param url a fully-qualified http URL for the required resource
*/
bool connectServer(const UString& url);
// QUEUE MODE
static int queue_fd;
static const UString* queue_dir;
// -----------------------------------------------------------------------------------------------------------------------
// Very simple RPC-like layer
//
// Requests and responses are build of little packets each containing a 4-byte ascii token, an 8-byte hex value or length,
// and optionally data corresponding to the length
// -----------------------------------------------------------------------------------------------------------------------
// Transmit token name (4 characters) and value (32-bit int, as 8 hex characters)
bool sendTokenInt( const char* token, uint32_t value) { buffer.setEmpty(); return URPC::sendTokenInt( socket, token, value, buffer); }
bool sendTokenString(const char* token, const UString& data) { buffer.setEmpty(); return URPC::sendTokenString(socket, token, data, buffer); } // Write token and string data
bool sendTokenVector(const char* token, UVector<UString>& vec) { buffer.setEmpty(); return URPC::sendTokenVector(socket, token, vec, buffer); } // Transmit an vector of string
// DEBUG
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
const char* dump(bool reset) const;
#endif
USocket* socket;
protected:
UString server, // host name or ip address for server
cert_file, // locations for certificate of client
key_file, // locations for private key of client
password, // password for private key of client
ca_file, // locations of trusted CA certificates used in the verification
ca_path, // locations of trusted CA certificates used in the verification
uri,
request,
response,
buffer,
host_port;
Url url;
int iovcnt,
timeoutMS, // the time-out value in milliseconds
verify_mode; // mode of verification of connection
unsigned int port; // the port number to connect to
struct iovec iov[6];
static ULog* log;
static UFileConfig* pcfg;
static bool log_shared_with_server, bIPv6;
void loadConfigParam();
bool readHTTPResponse();
bool sendRequest(bool bread_response);
bool sendRequestAndReadResponse(const UString& header, const UString& body);
bool sendRequestAndReadResponse() { return sendRequest(true); }
bool processHeader(UMimeHeader* responseHeader) { return responseHeader->readHeader(socket, response); }
#ifdef USE_LIBSSL
void setSSLContext();
void setActive(bool _flag)
{
U_TRACE(0, "UClient_Base::setActive(%b)", _flag)
((USSLSocket*)socket)->setSSLActive(_flag);
}
#endif
UClient_Base(UFileConfig* cfg = U_NULLPTR);
~UClient_Base();
private:
// U_DISALLOW_COPY_AND_ASSIGN(UClient_Base)
static USocket* csocket;
static vPFu resize_response_buffer;
void setForResizeResponseBuffer(vPFu func) { csocket = socket; resize_response_buffer = func; }
friend class UHTTP;
friend class USocketExt;
friend class USSLSocket;
friend class UFCGIPlugIn;
friend class USCGIPlugIn;
friend class Application;
friend class UHttpPlugIn;
friend class UProxyPlugIn;
friend class UNoCatPlugIn;
friend class UServer_Base;
friend class UWebSocketClient;
friend class UHttpClient_Base;
friend class UClientImage_Base;
friend class UREDISClient_Base;
friend class UElasticSearchClient;
};
template <class Socket> class U_EXPORT UClient : public UClient_Base {
public:
UClient(UFileConfig* cfg) : UClient_Base(cfg)
{
U_TRACE_CTOR(0, UClient, "%p", cfg)
U_NEW(Socket, socket, Socket(UClient_Base::bIPv6));
}
~UClient()
{
U_TRACE_DTOR(0, UClient)
}
// DEBUG
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
const char* dump(bool _reset) const { return UClient_Base::dump(_reset); }
#endif
private:
U_DISALLOW_COPY_AND_ASSIGN(UClient)
};
#ifdef USE_LIBSSL
template <> class U_EXPORT UClient<USSLSocket> : public UClient_Base {
public:
UClient(UFileConfig* cfg) : UClient_Base(cfg)
{
U_TRACE_CTOR(0, UClient<USSLSocket>, "%p", cfg)
UClient_Base::setSSLContext();
}
~UClient()
{
U_TRACE_DTOR(0, UClient<USSLSocket>)
}
// DEBUG
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
const char* dump(bool _reset) const { return UClient_Base::dump(_reset); }
#endif
private:
U_DISALLOW_COPY_AND_ASSIGN(UClient<USSLSocket>)
};
#endif
#endif