join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
http_client.hpp
Go to the documentation of this file.
1
25#ifndef JOIN_SERVICES_HTTP_CLIENT_HPP
26#define JOIN_SERVICES_HTTP_CLIENT_HPP
27
28// libjoin.
31#include <join/http_message.hpp>
32#include <join/chunk_stream.hpp>
33#include <join/tls_stream.hpp>
34#include <join/resolver.hpp>
35#include <join/zstream.hpp>
36#include <join/version.hpp>
37
38// C++.
39#include <chrono>
40
41namespace join
42{
46 template <class Protocol>
47 class BasicHttpClient : public Protocol::Stream
48 {
49 public:
50 using Endpoint = typename Protocol::Endpoint;
51
58 BasicHttpClient (const char* host, uint16_t port = Protocol::defaultPort, bool keepAlive = true)
59 : _host (host)
60 , _port (port)
62 , _keepTimeout (std::chrono::seconds::zero ())
63 {
64 }
65
72 BasicHttpClient (const std::string& host, uint16_t port = Protocol::defaultPort, bool keepAlive = true)
73 : BasicHttpClient (host.c_str (), port, keepAlive)
74 {
75 }
76
81 BasicHttpClient (const BasicHttpClient& other) = delete;
82
89
95 : Protocol::Stream (std::move (other))
96 , _host (std::move (other._host))
97 , _port (other._port)
98 , _keep (other._keep)
100 , _keepMax (other._keepMax)
101 {
102 }
103
110 {
111 Protocol::Stream::operator= (std::move (other));
112 this->_host = std::move (other._host);
113 this->_port = other._port;
114 this->_keep = other._keep;
115 this->_keepTimeout = other._keepTimeout;
116 this->_keepMax = other._keepMax;
117 return *this;
118 }
119
124 {
125 clearEncoding ();
126 }
127
132 void close () override
133 {
134 Protocol::Stream::close ();
135 this->_keepTimeout = std::chrono::seconds::zero ();
136 this->_keepMax = -1;
137 }
138
143 virtual std::string scheme () const
144 {
145 return "http";
146 }
147
152 const std::string& host () const
153 {
154 return this->_host;
155 }
156
161 uint16_t port () const
162 {
163 return this->_port;
164 }
165
170 std::string authority () const
171 {
172 std::string auth;
173
174 if (IpAddress::isIpv6Address (this->host ()))
175 {
176 auth += "[" + this->host () + "]";
177 }
178 else
179 {
180 auth += this->host ();
181 }
182
183 if (this->port () != Dns::Resolver::resolveService (this->scheme ()))
184 {
185 auth += ":" + std::to_string (this->port ());
186 }
187
188 return auth;
189 }
190
195 std::string url () const
196 {
197 return this->scheme () + "://" + this->authority () + "/";
198 }
199
204 bool keepAlive () const
205 {
206 return this->_keep;
207 }
208
213 void keepAlive (bool keep)
214 {
215 this->_keep = keep;
216 }
217
222 std::chrono::seconds keepAliveTimeout () const
223 {
224 return this->_keepTimeout;
225 }
226
231 int keepAliveMax () const
232 {
233 return this->_keepMax;
234 }
235
241 int send (HttpRequest& request)
242 {
243 // restore concrete stream.
244 clearEncoding ();
245
246 // check if reconnection is required.
247 if (this->needReconnection ())
248 {
249 Endpoint endpoint{Dns::Resolver::lookupAddress (this->host ()), this->port ()};
250 endpoint.hostname (this->host ());
251
252 this->reconnect (endpoint);
253 if (this->fail ())
254 {
255 return -1;
256 }
257 }
258
259 // set missing request headers.
260 if (!request.hasHeader ("Accept"))
261 {
262 request.header ("Accept", "*/*");
263 }
264 if (!request.hasHeader ("Connection"))
265 {
266 request.header ("Connection", this->_keep ? "keep-alive" : "close");
267 }
268 if (!request.hasHeader ("Host"))
269 {
270 request.header ("Host", this->authority ());
271 }
272 if (!request.hasHeader ("User-Agent"))
273 {
274 request.header ("User-Agent", "join/" JOIN_VERSION);
275 }
276
277 // write request headers.
278 if (request.writeHeaders (*this) == -1)
279 {
280 return -1;
281 }
282
283 // flush request headers.
284 this->flush ();
285
286 // set encoding.
287 if (request.hasHeader ("Transfer-Encoding"))
288 {
289 this->setEncoding (join::rsplit (request.header ("Transfer-Encoding"), ","));
290 }
291 if (request.hasHeader ("Content-Encoding"))
292 {
293 this->setEncoding (join::rsplit (request.header ("Content-Encoding"), ","));
294 }
295
296 return 0;
297 }
298
304 int receive (HttpResponse& response)
305 {
306 // restore concrete stream.
307 this->clearEncoding ();
308
309 // read response headers.
310 if (response.readHeaders (*this) == -1)
311 {
312 return -1;
313 }
314
315 // get connection.
316 std::string connection = response.header ("Connection");
317 std::string alive = response.header ("Keep-Alive");
318
319 // check connection.
320 if (join::compareNoCase (connection, "keep-alive"))
321 {
322 size_t pos = alive.find ("timeout=");
323 if (pos != std::string::npos)
324 {
325 this->_keepTimeout =
326 std::chrono::seconds (::atoi (alive.substr (pos + 8, alive.find (",", pos + 8)).c_str ()));
327 }
328
329 pos = alive.find ("max=");
330 if (pos != std::string::npos)
331 {
332 this->_keepMax = ::atoi (alive.substr (pos + 4, alive.find (",", pos + 4)).c_str ());
333 }
334 }
335 else if (join::compareNoCase (connection, "close"))
336 {
337 this->_keepTimeout = std::chrono::seconds::zero ();
338 this->_keepMax = 0;
339 }
340
341 // set encoding.
342 if (response.hasHeader ("Transfer-Encoding"))
343 {
344 this->setEncoding (join::rsplit (response.header ("Transfer-Encoding"), ","));
345 }
346 if (response.hasHeader ("Content-Encoding"))
347 {
348 this->setEncoding (join::rsplit (response.header ("Content-Encoding"), ","));
349 }
350
351 // get timestamp.
352 this->_timestamp = std::chrono::steady_clock::now ();
353
354 return 0;
355 }
356
357 protected:
362 void setEncoding (const std::vector<std::string>& encodings)
363 {
364 for (auto const& encoding : encodings)
365 {
366 if (encoding.find ("gzip") != std::string::npos)
367 {
368 this->_streambuf = new Zstreambuf (this->_streambuf, Zstream::Gzip, this->_wrapped);
369 this->_wrapped = true;
370 }
371 else if (encoding.find ("deflate") != std::string::npos)
372 {
373 this->_streambuf = new Zstreambuf (this->_streambuf, Zstream::Deflate, this->_wrapped);
374 this->_wrapped = true;
375 }
376 else if (encoding.find ("chunked") != std::string::npos)
377 {
378 this->_streambuf = new Chunkstreambuf (this->_streambuf, this->_wrapped);
379 this->_wrapped = true;
380 }
381 }
382
383 this->set_rdbuf (this->_streambuf);
384 }
385
390 {
391 if (this->_wrapped && this->_streambuf)
392 {
393 delete this->_streambuf;
394 this->_streambuf = nullptr;
395 }
396
397 this->_streambuf = &this->_sockbuf;
398 this->_wrapped = false;
399
400 this->set_rdbuf (this->_streambuf);
401 }
402
407 bool expired () const
408 {
409 auto duration = std::chrono::steady_clock::now () - this->_timestamp;
410 return (this->_keepTimeout < std::chrono::duration_cast<std::chrono::seconds> (duration)) ||
411 (this->_keepMax == 0);
412 }
413
419 {
420 return !this->connected () || this->expired ();
421 }
422
427 virtual void reconnect (const Endpoint& endpoint)
428 {
429 this->disconnect ();
430 this->close ();
431 this->connect (endpoint);
432 }
433
435 std::streambuf* _streambuf = nullptr;
436
438 bool _wrapped = false;
439
441 std::chrono::time_point<std::chrono::steady_clock> _timestamp;
442
444 std::string _host;
445
447 uint16_t _port;
448
450 bool _keep;
451
453 std::chrono::seconds _keepTimeout;
454
456 int _keepMax = -1;
457 };
458
465 template <class Protocol>
467 {
468 out.send (request);
469 return out;
470 }
471
478 template <class Protocol>
480 {
481 in.receive (response);
482 return in;
483 }
484
488 template <class Protocol>
489 class BasicHttpSecureClient : public BasicHttpClient<Protocol>
490 {
491 public:
492 using Endpoint = typename Protocol::Endpoint;
493 using Stream = typename Protocol::Stream;
494
502 BasicHttpSecureClient (TlsContext ctx, const char* host, uint16_t port = Protocol::defaultPort,
503 bool keepAlive = true)
504 : BasicHttpClient<Protocol> (host, port, keepAlive)
505 {
506 static_cast<Stream&> (*this) = Stream (std::move (ctx));
507 }
508
516 BasicHttpSecureClient (TlsContext ctx, const std::string& host, uint16_t port = Protocol::defaultPort,
517 bool keepAlive = true)
518 : BasicHttpSecureClient (std::move (ctx), host.c_str (), port, keepAlive)
519 {
520 }
521
527
534
540 : BasicHttpClient<Protocol> (std::move (other))
541 {
542 }
543
550 {
551 BasicHttpClient<Protocol>::operator= (std::move (other));
552 return *this;
553 }
554
558 virtual ~BasicHttpSecureClient () = default;
559
564 std::string scheme () const override
565 {
566 return "https";
567 }
568
569 protected:
574 void reconnect (const Endpoint& endpoint) override
575 {
576 this->disconnect ();
577 this->close ();
578 this->connect (endpoint);
579 if (!this->fail ())
580 {
581 this->handshake ();
582 }
583 }
584 };
585
592 template <class Protocol>
594 {
595 out.send (request);
596 return out;
597 }
598
605 template <class Protocol>
607 {
608 in.receive (response);
609 return in;
610 }
611}
612
613#endif
static IpAddress lookupAddress(const std::string &host, int family)
resolve host name using system name servers.
Definition resolver.hpp:260
static uint16_t resolveService(const std::string &service) noexcept
resolve service name.
Definition resolver.hpp:673
basic HTTP client.
Definition http_protocol.hpp:34
bool _keep
HTTP keep alive.
Definition http_client.hpp:450
bool _wrapped
HTTP stream status.
Definition http_client.hpp:438
std::string url() const
get URL.
Definition http_client.hpp:195
std::chrono::time_point< std::chrono::steady_clock > _timestamp
HTTP timestamp.
Definition http_client.hpp:441
std::string _host
HTTP host.
Definition http_client.hpp:444
BasicHttpClient(const char *host, uint16_t port=Protocol::defaultPort, bool keepAlive=true)
create the basic HTTP client instance.
Definition http_client.hpp:58
BasicHttpClient(const std::string &host, uint16_t port=Protocol::defaultPort, bool keepAlive=true)
create the basic HTTP client instance.
Definition http_client.hpp:72
uint16_t _port
HTTP port.
Definition http_client.hpp:447
uint16_t port() const
get port.
Definition http_client.hpp:161
void close() override
close the connection.
Definition http_client.hpp:132
int _keepMax
HTTP keep alive max.
Definition http_client.hpp:456
void setEncoding(const std::vector< std::string > &encodings)
set stream encoding.
Definition http_client.hpp:362
const std::string & host() const
get host.
Definition http_client.hpp:152
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 http_client.hpp:123
virtual void reconnect(const Endpoint &endpoint)
perform reconnection to the given endpoint.
Definition http_client.hpp:427
int keepAliveMax() const
get HTTP keep alive max.
Definition http_client.hpp:231
int send(HttpRequest &request)
send HTTP request.
Definition http_client.hpp:241
typename Protocol::Endpoint Endpoint
Definition http_client.hpp:50
std::chrono::seconds _keepTimeout
HTTP keep alive timeout.
Definition http_client.hpp:453
void keepAlive(bool keep)
enable/disable HTTP keep alive support.
Definition http_client.hpp:213
void clearEncoding()
clear stream encoding.
Definition http_client.hpp:389
virtual std::string scheme() const
get HTTP scheme.
Definition http_client.hpp:143
bool needReconnection()
check if client must reconnect.
Definition http_client.hpp:418
int receive(HttpResponse &response)
receive HTTP response.
Definition http_client.hpp:304
std::string authority() const
get authority.
Definition http_client.hpp:170
std::chrono::seconds keepAliveTimeout() const
get HTTP keep alive timeout.
Definition http_client.hpp:222
bool expired() const
check if HTTP keep alive is expired.
Definition http_client.hpp:407
std::streambuf * _streambuf
HTTP stream buffer.
Definition http_client.hpp:435
bool keepAlive() const
checks if HTTP keep alive is supported.
Definition http_client.hpp:204
BasicHttpClient(BasicHttpClient &&other)
create the basic HTTP client instance by move.
Definition http_client.hpp:94
basic HTTPS client.
Definition http_protocol.hpp:37
std::string scheme() const override
get HTTP scheme.
Definition http_client.hpp:564
typename Protocol::Stream Stream
Definition http_client.hpp:493
void reconnect(const Endpoint &endpoint) override
perform reconnection to the given endpoint.
Definition http_client.hpp:574
BasicHttpSecureClient(BasicHttpSecureClient &&other)
create the basic HTTPS client instance by move.
Definition http_client.hpp:539
virtual ~BasicHttpSecureClient()=default
destroy the basic HTTPS client instance.
BasicHttpSecureClient(TlsContext ctx, const std::string &host, uint16_t port=Protocol::defaultPort, bool keepAlive=true)
create the basic HTTPS client instance using the given context.
Definition http_client.hpp:516
BasicHttpSecureClient(TlsContext ctx, const char *host, uint16_t port=Protocol::defaultPort, bool keepAlive=true)
create the basic HTTPS client instance using the given context.
Definition http_client.hpp:502
BasicHttpSecureClient(const BasicHttpSecureClient &other)=delete
create the basic HTTPS client instance by copy.
typename Protocol::Endpoint Endpoint
Definition http_client.hpp:492
BasicHttpSecureClient & operator=(const BasicHttpSecureClient &other)=delete
assign the basic HTTPS client instance by copy.
chunk stream buffer.
Definition chunk_stream.hpp:41
virtual int readHeaders(std::istream &in)
read HTTP header from the given input stream.
Definition http_message.cpp:304
bool hasHeader(const std::string &name) const
checks if there is a header with the specified name.
Definition http_message.cpp:203
std::string header(const std::string &name) const
get header by name.
Definition http_message.cpp:212
HTTP request.
Definition http_message.hpp:352
virtual int writeHeaders(std::ostream &out) const override
write HTTP headers to the given output stream.
Definition http_message.cpp:666
HTTP response.
Definition http_message.hpp:558
bool isIpv6Address() const
check if IP address is an IPv6 address.
Definition ip_address.cpp:1394
TLS/DTLS context.
Definition tls_context.hpp:42
@ Deflate
Definition zstream.hpp:132
@ Gzip
Definition zstream.hpp:134
zlib stream buffer.
Definition zstream.hpp:44
Definition acceptor.hpp:32
constexpr BasicHttpClient< Protocol > & operator>>(BasicHttpClient< Protocol > &in, HttpResponse &response)
read HTTP response from the HTTP stream.
Definition http_client.hpp:479
std::ostream & operator<<(std::ostream &os, const BasicLinkLayerEndpoint< Protocol > &endpoint)
push endpoint representation into a stream.
Definition endpoint.hpp:258
bool compareNoCase(const std::string &a, const std::string &b)
case insensitive string comparison.
Definition utils.hpp:195
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:282
Definition error.hpp:144