join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
httpclient.hpp
Go to the documentation of this file.
1
25#ifndef JOIN_SERVICES_HTTPCLIENT_HPP
26#define JOIN_SERVICES_HTTPCLIENT_HPP
27
28// libjoin.
29#include <join/version.hpp>
30#include <join/zstream.hpp>
31#include <join/resolver.hpp>
32#include <join/chunkstream.hpp>
33#include <join/httpmessage.hpp>
34#include <join/socketstream.hpp>
35
36// C++.
37#include <chrono>
38
39namespace join
40{
44 template <class Protocol>
45 class BasicHttpClient : public Protocol::Stream
46 {
47 public:
48 using Endpoint = typename Protocol::Endpoint;
49
56 BasicHttpClient (const char* host, uint16_t port = 80, bool keepAlive = true)
57 : _host (host)
58 , _port (port)
60 , _keepTimeout (std::chrono::seconds::zero ())
61 {
62 }
63
70 BasicHttpClient (const std::string& host, uint16_t port = 80, bool keepAlive = true)
71 : BasicHttpClient (host.c_str (), port, keepAlive)
72 {
73 }
74
79 BasicHttpClient (const BasicHttpClient& other) = delete;
80
87
93 : Protocol::Stream (std::move (other))
94 , _host (std::move (other._host))
95 , _port (other._port)
96 , _keep (other._keep)
98 , _keepMax (other._keepMax)
99 {
100 }
101
108 {
109 Protocol::Stream::operator= (std::move (other));
110 this->_host = std::move (other._host);
111 this->_port = other._port;
112 this->_keep = other._keep;
113 this->_keepTimeout = other._keepTimeout;
114 this->_keepMax = other._keepMax;
115 return *this;
116 }
117
122 {
123 clearEncoding ();
124 }
125
130 void close () override
131 {
132 Protocol::Stream::close ();
133 this->_keepTimeout = std::chrono::seconds::zero ();
134 this->_keepMax = -1;
135 }
136
141 virtual std::string scheme () const
142 {
143 return "http";
144 }
145
150 const std::string& host () const
151 {
152 return this->_host;
153 }
154
159 uint16_t port () const
160 {
161 return this->_port;
162 }
163
168 std::string authority () const
169 {
170 std::string auth;
171
172 if (IpAddress::isIpv6Address (this->host ()))
173 {
174 auth += "[" + this->host () + "]";
175 }
176 else
177 {
178 auth += this->host ();
179 }
180
181 if (this->port () != Resolver::resolveService (this->scheme ()))
182 {
183 auth += ":" + std::to_string (this->port ());
184 }
185
186 return auth;
187 }
188
193 std::string url () const
194 {
195 return this->scheme () + "://" + this->authority () + "/";
196 }
197
202 bool keepAlive () const
203 {
204 return this->_keep;
205 }
206
211 void keepAlive (bool keep)
212 {
213 this->_keep = keep;
214 }
215
220 std::chrono::seconds keepAliveTimeout () const
221 {
222 return this->_keepTimeout;
223 }
224
229 int keepAliveMax () const
230 {
231 return this->_keepMax;
232 }
233
239 int send (HttpRequest& request)
240 {
241 // restore concrete stream.
242 clearEncoding ();
243
244 // check if reconnection is required.
245 if (this->needReconnection ())
246 {
247 Endpoint endpoint{Resolver::resolveHost (this->host ()), this->port ()};
248 endpoint.hostname (this->host ());
249
250 this->reconnect (endpoint);
251 if (this->fail ())
252 {
253 return -1;
254 }
255 }
256
257 // set missing request headers.
258 if (!request.hasHeader ("Accept"))
259 {
260 request.header ("Accept", "*/*");
261 }
262 if (!request.hasHeader ("Connection"))
263 {
264 request.header ("Connection", this->_keep ? "keep-alive" : "close");
265 }
266 if (!request.hasHeader ("Host"))
267 {
268 request.header ("Host", this->authority ());
269 }
270 if (!request.hasHeader ("User-Agent"))
271 {
272 request.header ("User-Agent", "join/" JOIN_VERSION);
273 }
274
275 // write request headers.
276 if (request.writeHeaders (*this) == -1)
277 {
278 return -1;
279 }
280
281 // flush request headers.
282 this->flush ();
283
284 // set encoding.
285 if (request.hasHeader ("Transfer-Encoding"))
286 {
287 this->setEncoding (join::rsplit (request.header ("Transfer-Encoding"), ","));
288 }
289 if (request.hasHeader ("Content-Encoding"))
290 {
291 this->setEncoding (join::rsplit (request.header ("Content-Encoding"), ","));
292 }
293
294 return 0;
295 }
296
302 int receive (HttpResponse& response)
303 {
304 // restore concrete stream.
305 this->clearEncoding ();
306
307 // read response headers.
308 if (response.readHeaders (*this) == -1)
309 {
310 return -1;
311 }
312
313 // get connection.
314 std::string connection = response.header ("Connection");
315 std::string alive = response.header ("Keep-Alive");
316
317 // check connection.
318 if (join::compareNoCase (connection, "keep-alive"))
319 {
320 size_t pos = alive.find ("timeout=");
321 if (pos != std::string::npos)
322 {
323 this->_keepTimeout =
324 std::chrono::seconds (::atoi (alive.substr (pos + 8, alive.find (",", pos + 8)).c_str ()));
325 }
326
327 pos = alive.find ("max=");
328 if (pos != std::string::npos)
329 {
330 this->_keepMax = ::atoi (alive.substr (pos + 4, alive.find (",", pos + 4)).c_str ());
331 }
332 }
333 else if (join::compareNoCase (connection, "close"))
334 {
335 this->_keepTimeout = std::chrono::seconds::zero ();
336 this->_keepMax = 0;
337 }
338
339 // set encoding.
340 if (response.hasHeader ("Transfer-Encoding"))
341 {
342 this->setEncoding (join::rsplit (response.header ("Transfer-Encoding"), ","));
343 }
344 if (response.hasHeader ("Content-Encoding"))
345 {
346 this->setEncoding (join::rsplit (response.header ("Content-Encoding"), ","));
347 }
348
349 // get timestamp.
350 this->_timestamp = std::chrono::steady_clock::now ();
351
352 return 0;
353 }
354
355 protected:
360 void setEncoding (const std::vector<std::string>& encodings)
361 {
362 for (auto const& encoding : encodings)
363 {
364 if (encoding.find ("gzip") != std::string::npos)
365 {
366 this->_streambuf = new Zstreambuf (this->_streambuf, Zstream::Gzip, this->_wrapped);
367 this->_wrapped = true;
368 }
369 else if (encoding.find ("deflate") != std::string::npos)
370 {
371 this->_streambuf = new Zstreambuf (this->_streambuf, Zstream::Deflate, this->_wrapped);
372 this->_wrapped = true;
373 }
374 else if (encoding.find ("chunked") != std::string::npos)
375 {
376 this->_streambuf = new Chunkstreambuf (this->_streambuf, this->_wrapped);
377 this->_wrapped = true;
378 }
379 }
380
381 this->set_rdbuf (this->_streambuf);
382 }
383
388 {
389 if (this->_wrapped && this->_streambuf)
390 {
391 delete this->_streambuf;
392 this->_streambuf = nullptr;
393 }
394
395 this->_streambuf = &this->_sockbuf;
396 this->_wrapped = false;
397
398 this->set_rdbuf (this->_streambuf);
399 }
400
405 bool expired () const
406 {
407 auto duration = std::chrono::steady_clock::now () - this->_timestamp;
408 return (this->_keepTimeout < std::chrono::duration_cast<std::chrono::seconds> (duration)) ||
409 (this->_keepMax == 0);
410 }
411
417 {
418 return !this->connected () || this->expired ();
419 }
420
425 virtual void reconnect (const Endpoint& endpoint)
426 {
427 this->disconnect ();
428 this->close ();
429 this->connect (endpoint);
430 }
431
433 std::streambuf* _streambuf = nullptr;
434
436 bool _wrapped = false;
437
439 std::chrono::time_point<std::chrono::steady_clock> _timestamp;
440
442 std::string _host;
443
445 uint16_t _port;
446
448 bool _keep;
449
451 std::chrono::seconds _keepTimeout;
452
454 int _keepMax = -1;
455 };
456
463 template <class Protocol>
465 {
466 out.send (request);
467 return out;
468 }
469
476 template <class Protocol>
478 {
479 in.receive (response);
480 return in;
481 }
482
486 template <class Protocol>
487 class BasicHttpSecureClient : public BasicHttpClient<Protocol>
488 {
489 public:
490 using Endpoint = typename Protocol::Endpoint;
491
498 BasicHttpSecureClient (const char* host, uint16_t port = 443, bool keepAlive = true)
499 : BasicHttpClient<Protocol> (host, port, keepAlive)
500 {
501 }
502
509 BasicHttpSecureClient (const std::string& host, uint16_t port = 443, bool keepAlive = true)
511 {
512 }
513
519
526
532 : BasicHttpClient<Protocol> (std::move (other))
533 {
534 }
535
542 {
543 BasicHttpClient<Protocol>::operator= (std::move (other));
544 return *this;
545 }
546
550 virtual ~BasicHttpSecureClient () = default;
551
556 std::string scheme () const override
557 {
558 return "https";
559 }
560
561 protected:
566 void reconnect (const Endpoint& endpoint) override
567 {
568 this->disconnect ();
569 this->close ();
570 this->connectEncrypted (endpoint);
571 }
572 };
573
580 template <class Protocol>
582 {
583 out.send (request);
584 return out;
585 }
586
593 template <class Protocol>
595 {
596 in.receive (response);
597 return in;
598 }
599}
600
601#endif
basic HTTP client.
Definition httpclient.hpp:46
bool _keep
HTTP keep alive.
Definition httpclient.hpp:448
bool _wrapped
HTTP stream status.
Definition httpclient.hpp:436
std::string url() const
get URL.
Definition httpclient.hpp:193
std::chrono::time_point< std::chrono::steady_clock > _timestamp
HTTP timestamp.
Definition httpclient.hpp:439
std::string _host
HTTP host.
Definition httpclient.hpp:442
uint16_t _port
HTTP port.
Definition httpclient.hpp:445
uint16_t port() const
get port.
Definition httpclient.hpp:159
void close() override
close the connection.
Definition httpclient.hpp:130
int _keepMax
HTTP keep alive max.
Definition httpclient.hpp:454
void setEncoding(const std::vector< std::string > &encodings)
set stream encoding.
Definition httpclient.hpp:360
const std::string & host() const
get host.
Definition httpclient.hpp:150
BasicHttpClient & operator=(const BasicHttpClient &other)=delete
assign the basic HTTP client instance by copy.
BasicHttpClient(const BasicHttpClient &other)=delete
create the basic HTTP client instance by copy.
virtual ~BasicHttpClient()
destroy the basic HTTP client instance.
Definition httpclient.hpp:121
virtual void reconnect(const Endpoint &endpoint)
perform reconnection to the given endpoint.
Definition httpclient.hpp:425
int keepAliveMax() const
get HTTP keep alive max.
Definition httpclient.hpp:229
int send(HttpRequest &request)
send HTTP request.
Definition httpclient.hpp:239
typename Protocol::Endpoint Endpoint
Definition httpclient.hpp:48
std::chrono::seconds _keepTimeout
HTTP keep alive timeout.
Definition httpclient.hpp:451
BasicHttpClient(const char *host, uint16_t port=80, bool keepAlive=true)
create the basic HTTP client instance.
Definition httpclient.hpp:56
void keepAlive(bool keep)
enable/disable HTTP keep alive support.
Definition httpclient.hpp:211
void clearEncoding()
clear stream encoding.
Definition httpclient.hpp:387
virtual std::string scheme() const
get HTTP scheme.
Definition httpclient.hpp:141
bool needReconnection()
check if client must reconnect.
Definition httpclient.hpp:416
int receive(HttpResponse &response)
receive HTTP response.
Definition httpclient.hpp:302
std::string authority() const
get authority.
Definition httpclient.hpp:168
std::chrono::seconds keepAliveTimeout() const
get HTTP keep alive timeout.
Definition httpclient.hpp:220
bool expired() const
check if HTTP keep alive is expired.
Definition httpclient.hpp:405
std::streambuf * _streambuf
HTTP stream buffer.
Definition httpclient.hpp:433
bool keepAlive() const
checks if HTTP keep alive is supported.
Definition httpclient.hpp:202
BasicHttpClient(const std::string &host, uint16_t port=80, bool keepAlive=true)
create the basic HTTP client instance.
Definition httpclient.hpp:70
BasicHttpClient(BasicHttpClient &&other)
create the basic HTTP client instance by move.
Definition httpclient.hpp:92
basic HTTPS client.
Definition httpclient.hpp:488
std::string scheme() const override
get HTTP scheme.
Definition httpclient.hpp:556
void reconnect(const Endpoint &endpoint) override
perform reconnection to the given endpoint.
Definition httpclient.hpp:566
BasicHttpSecureClient(const char *host, uint16_t port=443, bool keepAlive=true)
create the basic HTTPS client instance.
Definition httpclient.hpp:498
BasicHttpSecureClient(BasicHttpSecureClient &&other)
create the basic HTTPS client instance by move.
Definition httpclient.hpp:531
BasicHttpSecureClient(const std::string &host, uint16_t port=443, bool keepAlive=true)
create the basic HTTPS client instance.
Definition httpclient.hpp:509
virtual ~BasicHttpSecureClient()=default
destroy the basic HTTPS client instance.
BasicHttpSecureClient(const BasicHttpSecureClient &other)=delete
create the basic HTTPS client instance by copy.
typename Protocol::Endpoint Endpoint
Definition httpclient.hpp:490
BasicHttpSecureClient & operator=(const BasicHttpSecureClient &other)=delete
assign the basic HTTPS client instance by copy.
chunk stream buffer.
Definition chunkstream.hpp:37
virtual int readHeaders(std::istream &in)
read HTTP header from the given input stream.
Definition httpmessage.cpp:304
bool hasHeader(const std::string &name) const
checks if there is a header with the specified name.
Definition httpmessage.cpp:203
std::string header(const std::string &name) const
get header by name.
Definition httpmessage.cpp:212
HTTP request.
Definition httpmessage.hpp:353
virtual int writeHeaders(std::ostream &out) const override
write HTTP headers to the given output stream.
Definition httpmessage.cpp:666
HTTP response.
Definition httpmessage.hpp:559
bool isIpv6Address() const
check if IP address is an IPv6 address.
Definition ipaddress.cpp:1394
static uint16_t resolveService(const std::string &service)
resolve service name.
Definition resolver.cpp:550
IpAddress resolveHost(const std::string &host, int family, const IpAddress &server, uint16_t port=dnsPort, int timeout=5000)
resolve host name using address family.
Definition resolver.cpp:195
@ Deflate
Definition zstream.hpp:128
@ Gzip
Definition zstream.hpp:130
zlib stream buffer.
Definition zstream.hpp:40
Definition acceptor.hpp:32
constexpr BasicHttpClient< Protocol > & operator>>(BasicHttpClient< Protocol > &in, HttpResponse &response)
read HTTP response from the HTTP stream.
Definition httpclient.hpp:477
std::ostream & operator<<(std::ostream &os, const BasicUnixEndpoint< Protocol > &endpoint)
push endpoint representation into a stream.
Definition endpoint.hpp:255
bool compareNoCase(const std::string &a, const std::string &b)
case insensitive string comparison.
Definition utils.hpp:196
std::vector< std::string > rsplit(const std::string &in, const std::string &delim)
split a string in reverse order using a delimiter.
Definition utils.hpp:283
Definition error.hpp:137