join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
httpclient.hpp
Go to the documentation of this file.
1
25#ifndef __JOIN_HTTP_CLIENT_HPP__
26#define __JOIN_HTTP_CLIENT_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 = std::chrono::seconds (::atoi (alive.substr (pos + 8, alive.find (",", pos + 8)).c_str ()));
324 }
325
326 pos = alive.find ("max=");
327 if (pos != std::string::npos)
328 {
329 this->_keepMax = ::atoi (alive.substr (pos + 4, alive.find (",", pos + 4)).c_str ());
330 }
331 }
332 else if (join::compareNoCase (connection, "close"))
333 {
334 this->_keepTimeout = std::chrono::seconds::zero ();
335 this->_keepMax = 0;
336 }
337
338 // set encoding.
339 if (response.hasHeader ("Transfer-Encoding"))
340 {
341 this->setEncoding (join::rsplit (response.header ("Transfer-Encoding"), ","));
342 }
343 if (response.hasHeader ("Content-Encoding"))
344 {
345 this->setEncoding (join::rsplit (response.header ("Content-Encoding"), ","));
346 }
347
348 // get timestamp.
349 this->_timestamp = std::chrono::steady_clock::now ();
350
351 return 0;
352 }
353
354 protected:
359 void setEncoding (const std::vector <std::string>& encodings)
360 {
361 for (auto const& encoding : encodings)
362 {
363 if (encoding.find ("gzip") != std::string::npos)
364 {
365 this->_streambuf = new Zstreambuf (this->_streambuf, Zstream::Gzip, this->_wrapped);
366 this->_wrapped = true;
367 }
368 else if (encoding.find ("deflate") != std::string::npos)
369 {
370 this->_streambuf = new Zstreambuf (this->_streambuf, Zstream::Deflate, this->_wrapped);
371 this->_wrapped = true;
372 }
373 else if (encoding.find ("chunked") != std::string::npos)
374 {
375 this->_streambuf = new Chunkstreambuf (this->_streambuf, this->_wrapped);
376 this->_wrapped = true;
377 }
378 }
379
380 this->set_rdbuf (this->_streambuf);
381 }
382
387 {
388 if (this->_wrapped && this->_streambuf)
389 {
390 delete this->_streambuf;
391 this->_streambuf = nullptr;
392 }
393
394 this->_streambuf = &this->_sockbuf;
395 this->_wrapped = false;
396
397 this->set_rdbuf (this->_streambuf);
398 }
399
404 bool expired () const
405 {
406 auto duration = std::chrono::steady_clock::now () - this->_timestamp;
407 return (this->_keepTimeout < std::chrono::duration_cast <std::chrono::seconds> (duration)) || (this->_keepMax == 0);
408 }
409
415 {
416 return !this->connected () || this->expired ();
417 }
418
423 virtual void reconnect (const Endpoint& endpoint)
424 {
425 this->disconnect ();
426 this->close ();
427 this->connect (endpoint);
428 }
429
431 std::streambuf* _streambuf = nullptr;
432
434 bool _wrapped = false;
435
437 std::chrono::time_point <std::chrono::steady_clock> _timestamp;
438
440 std::string _host;
441
443 uint16_t _port;
444
446 bool _keep;
447
449 std::chrono::seconds _keepTimeout;
450
452 int _keepMax = -1;
453 };
454
461 template <class Protocol>
462 constexpr BasicHttpClient <Protocol>& operator<< (BasicHttpClient <Protocol>& out, HttpRequest& request)
463 {
464 out.send (request);
465 return out;
466 }
467
474 template <class Protocol>
475 constexpr BasicHttpClient <Protocol>& operator>> (BasicHttpClient <Protocol>& in, HttpResponse& response)
476 {
477 in.receive (response);
478 return in;
479 }
480
484 template <class Protocol>
485 class BasicHttpSecureClient : public BasicHttpClient <Protocol>
486 {
487 public:
488 using Endpoint = typename Protocol::Endpoint;
489
496 BasicHttpSecureClient (const char* host, uint16_t port = 443, bool keepAlive = true)
497 : BasicHttpClient <Protocol> (host, port, keepAlive)
498 {
499 }
500
507 BasicHttpSecureClient (const std::string& host, uint16_t port = 443, bool keepAlive = true)
509 {
510 }
511
517
524
530 : BasicHttpClient <Protocol> (std::move (other))
531 {
532 }
533
540 {
541 BasicHttpClient <Protocol>::operator= (std::move (other));
542 return *this;
543 }
544
548 virtual ~BasicHttpSecureClient () = default;
549
554 std::string scheme () const override
555 {
556 return "https";
557 }
558
559 protected:
564 void reconnect (const Endpoint& endpoint) override
565 {
566 this->disconnect ();
567 this->close ();
568 this->connectEncrypted (endpoint);
569 }
570 };
571
578 template <class Protocol>
579 constexpr BasicHttpSecureClient <Protocol>& operator<< (BasicHttpSecureClient <Protocol>& out, HttpRequest& request)
580 {
581 out.send (request);
582 return out;
583 }
584
591 template <class Protocol>
592 constexpr BasicHttpSecureClient <Protocol>& operator>> (BasicHttpSecureClient <Protocol>& in, HttpResponse& response)
593 {
594 in.receive (response);
595 return in;
596 }
597}
598
599#endif
basic HTTP client.
Definition httpclient.hpp:46
bool _keep
HTTP keep alive.
Definition httpclient.hpp:446
bool _wrapped
HTTP stream status.
Definition httpclient.hpp:434
std::string url() const
get URL.
Definition httpclient.hpp:193
std::chrono::time_point< std::chrono::steady_clock > _timestamp
HTTP timestamp.
Definition httpclient.hpp:437
std::string _host
HTTP host.
Definition httpclient.hpp:440
uint16_t _port
HTTP port.
Definition httpclient.hpp:443
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:452
void setEncoding(const std::vector< std::string > &encodings)
set stream encoding.
Definition httpclient.hpp:359
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:423
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:449
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:386
virtual std::string scheme() const
get HTTP scheme.
Definition httpclient.hpp:141
bool needReconnection()
check if client must reconnect.
Definition httpclient.hpp:414
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:404
std::streambuf * _streambuf
HTTP stream buffer.
Definition httpclient.hpp:431
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:486
std::string scheme() const override
get HTTP scheme.
Definition httpclient.hpp:554
void reconnect(const Endpoint &endpoint) override
perform reconnection to the given endpoint.
Definition httpclient.hpp:564
BasicHttpSecureClient(const char *host, uint16_t port=443, bool keepAlive=true)
create the basic HTTPS client instance.
Definition httpclient.hpp:496
BasicHttpSecureClient(BasicHttpSecureClient &&other)
create the basic HTTPS client instance by move.
Definition httpclient.hpp:529
BasicHttpSecureClient(const std::string &host, uint16_t port=443, bool keepAlive=true)
create the basic HTTPS client instance.
Definition httpclient.hpp:507
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:488
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:338
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:544
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:546
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:194
@ 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:475
std::ostream & operator<<(std::ostream &os, const BasicUnixEndpoint< Protocol > &endpoint)
push endpoint representation into a stream.
Definition endpoint.hpp:255
__inline__ 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:274
__inline__ bool compareNoCase(const std::string &a, const std::string &b)
case insensitive string comparison.
Definition utils.hpp:188
Definition error.hpp:106