OpenVPN 3 Core Library
Loading...
Searching...
No Matches
httpcliset.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12#pragma once
13
14#include <string>
15#include <sstream>
16#include <ostream>
17#include <vector>
18#include <memory>
19#include <utility>
20#include <algorithm>
21#include <limits>
22#include <map>
23
37
38#ifndef OPENVPN_HTTP_CLISET_RC
39#define OPENVPN_HTTP_CLISET_RC RC<thread_unsafe_refcount>
40#endif
41
42namespace openvpn::WS {
43
44class ClientSet : public RC<thread_unsafe_refcount>
45{
46 class Client;
47
48 public:
50
52
54 {
55 std::unique_ptr<openvpn_io::io_context> io_context;
56 };
57
59 {
60 public:
62 {
63 if (!c)
64 c.reset(new Container);
65 }
66
67 void stop(const bool shutdown)
68 {
69 if (c && c->http)
70 c->http->stop(shutdown);
71 }
72
73 void reset()
74 {
75 if (c)
76 c->http.reset();
77 }
78
79 void abort(const std::string &message)
80 {
81 if (c && c->http)
82 c->http->abort(message);
83 }
84
85 bool alive() const
86 {
87 return c && c->http && c->http->is_alive();
88 }
89
90 bool alive(const std::string &host) const
91 {
92 return alive() && c->http->host_match(host);
93 }
94
95 // used for synchronous io_context
96
97 std::unique_ptr<openvpn_io::io_context> acquire_io_context()
98 {
99 if (c)
100 return std::move(c->sps.io_context);
101 else
102 return std::unique_ptr<openvpn_io::io_context>();
103 }
104
105 void persist_io_context(std::unique_ptr<openvpn_io::io_context> &&io_context)
106 {
107 if (c)
108 c->sps.io_context = std::move(io_context);
109 }
110
111#ifdef ASIO_HAS_LOCAL_SOCKETS
112 int unix_fd()
113 {
114 if (!c || !c->http)
115 return -1;
116 AsioPolySock::Unix *us = dynamic_cast<AsioPolySock::Unix *>(c->http->get_socket());
117 if (!us)
118 return -1;
119 return us->socket.native_handle();
120 }
121#endif
122
123 private:
124 friend Client;
125
126 struct Container : public RC<thread_unsafe_refcount>
127 {
131 };
132
133 void attach(Client *parent)
134 {
135 c->http->attach(parent);
136 }
137
138 void close(const bool keepalive, const bool shutdown)
139 {
140 if (c && c->http)
141 {
142 c->http->detach(keepalive, shutdown);
143 if (!keepalive)
144 stop(shutdown);
145 }
146 }
147
148 void construct(openvpn_io::io_context &io_context,
150 {
152 close(false, false);
153 c->http.reset(new HTTPDelegate(io_context, std::move(config), nullptr));
154 }
155
157 {
158 c->http->start_request();
159 }
160
162 };
163
164 // like HTTPStateContainer, but destructor automatically
165 // calls stop() method
167 {
168 public:
169 HTTPStateContainerAutoStop(const bool shutdown)
170 : shutdown_(shutdown)
171 {
172 }
173
178
179 private:
180 const bool shutdown_;
181 };
182
183 class TransactionSet;
184 struct Transaction;
185
186 struct ErrorRecovery : public RC<thread_unsafe_refcount>
187 {
189 virtual void retry(TransactionSet &ts, Transaction &t) = 0;
190 };
191
193 {
194 static constexpr int UNDEF = -1;
195
196 // input
200 bool accept_gzip_in = false;
203
204 // output
206 std::string description;
209
210 std::string url(const TransactionSet &ts) const
211 {
212 URL::Parse u = URL::Parse::from_components(bool(ts.http_config->ssl_factory),
213 ts.host.host,
214 ts.host.port,
215 req.uri);
216 return u.to_string();
217 }
218
219 std::string title(const TransactionSet &ts) const
220 {
221 return req.method + ' ' + url(ts);
222 }
223
224 void compress_content_out(const unsigned int min_size = 64,
225 const bool verbose = false)
226 {
227#ifdef HAVE_ZLIB
228 if (content_out.join_size() >= min_size)
229 {
231 content_out.clear();
232 const size_t orig_size = co->size();
233 co = ZLib::compress_gzip(co, 0, 0, 1);
234 if (verbose)
235 log_compress("HTTPClientSet: GZIP COMPRESS", orig_size, co->size());
236 ci.length = co->size();
237 content_out.push_back(std::move(co));
238 ci.content_encoding = "gzip";
239 }
240#endif
241 }
242
243 // Return true if and only if HTTP transaction
244 // succeeded AND HTTP status code was in the
245 // successful range of 2xx.
247 {
249 }
250
251 // Return true if communication succeeded
253 {
255 }
256
258 {
260 }
261
262 // Return true if request succeeded, i.e. HTTP status
263 // code was in the successful range of 2xx.
265 {
266 return reply.status_code >= 200 && reply.status_code < 300;
267 }
268
269 bool is_redirect() const
270 {
271 return reply.status_code >= 300 && reply.status_code < 400 && reply.headers.get("location");
272 }
273
274 std::string get_redirect_location() const
275 {
276 return reply.headers.get_value_trim("location");
277 }
278
279 void dump(std::ostream &os, const TransactionSet &ts) const
280 {
281 os << "----- " << format_status(ts) << " -----\n";
283 const std::string s = buf_to_string(*in);
284 os << s;
285 if (!s.empty() && !string::ends_with_newline(s))
286 os << '\n';
287 }
288
289 std::string content_in_string() const
290 {
292 return buf_to_string(*in);
293 }
294
296 {
297 return content_in.join();
298 }
299
300 std::string format_status(const TransactionSet &ts) const
301 {
302 std::string ret;
303 ret.reserve(256);
304 ret += title(ts);
305 ret += " : ";
306 ret += format_status();
307 return ret;
308 }
309
310 std::string format_status() const
311 {
312 std::string ret;
313 ret.reserve(64);
315 {
317 ret += ' ';
319 }
320 else
321 {
323 ret += ' ';
324 ret += description;
325 }
326 return ret;
327 }
328 };
329
330 class TransactionSet : public RC<thread_unsafe_refcount>
331 {
332 public:
334 typedef std::vector<std::unique_ptr<Transaction>> Vector;
335
336 // Enable preserve_http_state to reuse HTTP session
337 // across multiple completions.
338 // hsc.stop() can be called to explicitly
339 // close persistent state.
342
343 // configuration
346 unsigned int max_retries = 1;
347 bool retry_on_http_4xx = false;
348 int debug_level = 2;
349 Time::Duration delayed_start;
350 Time::Duration retry_duration = Time::Duration::seconds(5);
351
352 // request/response vector
354
355 // true if all requests were successful
356 bool status = false;
357
358 // completion method
360
361 // post-connect method, useful to validate server
362 // on local sockets
364
365 // error recovery method, called before we retry a request
366 // after an error to possibly modify connection parameters
367 // such as the hostname.
369
371 {
372 http_state.create_container();
373 hsc = http_state;
374 preserve_http_state = true;
375 }
376
377 bool alive() const
378 {
379 return hsc.alive(host.host);
380 }
381
383 {
384 if (transactions.empty())
385 throw Exception("TransactionSet::first_transaction: transaction list is empty");
386 return *transactions[0];
387 }
388
389 // Return true if and only if all HTTP transactions
390 // succeeded AND each HTTP status code was in the
391 // successful range of 2xx.
393 {
394 if (!status)
395 return false;
396 if (transactions.empty())
397 return false;
398 for (auto &t : transactions)
399 {
400 if (!t->http_status_success())
401 return false;
402 }
403 return true;
404 }
405
407 {
408 completion.reset();
409 post_connect.reset();
410 }
411
412 void stop(const bool shutdown)
413 {
415 hsc.stop(shutdown);
416 }
417
418 void dump(std::ostream &os, const bool content_only = false) const
419 {
420 for (auto &t : transactions)
421 {
422 if (content_only)
423 os << t->content_in_string();
424 else
425 t->dump(os, *this);
426 }
427 }
428 };
429
430 class HostRetry : public std::vector<std::string>,
431 public ErrorRecovery
432 {
433 public:
435
437 {
438 }
439
440 template <typename T, typename... Args>
441 HostRetry(T first, Args... args)
442 {
443 reserve(1 + sizeof...(args));
444 from_list(first, args...);
445 }
446
448 {
449 std::shuffle(begin(), end(), prng);
450 index = 0;
451 }
452
453 std::string next_host()
454 {
455 if (empty())
456 throw Exception("HostRetry: empty host list");
457 if (index >= size())
458 index = 0;
459 return (*this)[index++];
460 }
461
462 virtual void retry(TransactionSet &ts, Transaction &t) override
463 {
464 ts.host.host = next_host();
465 }
466
467 private:
468 void from_list(std::string arg)
469 {
470 push_back(std::move(arg));
471 }
472
473 void from_list(const char *arg)
474 {
475 push_back(std::string(arg));
476 }
477
478 template <typename T, typename... Args>
479 void from_list(T first, Args... args)
480 {
481 from_list(first);
482 from_list(args...);
483 }
484
485 size_t index = 0;
486 };
487
488 ClientSet(openvpn_io::io_context &io_context_arg)
489 : io_context(io_context_arg),
490 halt(false),
491 next_id(0)
492 {
493 }
494
496 {
497 prng = std::move(prng_arg);
498 }
499
501 {
502 const client_t id = new_client_id();
503 Client::Ptr cli = new Client(this, std::move(ts), id);
504 clients[id] = cli;
505 cli->start();
506 }
507
509 Stop *stop = nullptr,
510 RandomAPI *prng = nullptr,
511 const bool sps = false)
512 {
513 std::unique_ptr<openvpn_io::io_context> io_context;
514 auto clean = Cleanup([&]()
515 {
516 // ensure that TransactionSet reference to socket
517 // is reset before method returns (unless sps is true
518 // in which case we should retain it).
519 if (!sps)
520 ts->hsc.reset(); });
521 ts->preserve_http_state = sps;
522 if (sps)
523 {
525 if (io_context)
526 {
527 if (io_context->stopped())
528 {
529 // OPENVPN_LOG("RESTART IO_CONTEXT");
530 io_context->restart();
531 }
532 else
533 {
534 // OPENVPN_LOG("GET IO_CONTEXT");
535 }
536 }
537 }
538 if (!io_context)
539 {
540 if (sps)
541 {
542 // OPENVPN_LOG("NEW IO_CONTEXT");
543 }
544 io_context.reset(new openvpn_io::io_context(1));
545 }
547 try
548 {
549 AsioStopScope scope(*io_context, stop, [&]()
550 {
551 if (cs)
552 cs->abort("stop message received"); });
553 cs.reset(new ClientSet(*io_context));
554 if (prng)
555 cs->set_random(RandomAPI::Ptr(prng));
556 cs->new_request(ts);
557 if (sps)
558 {
559 while (cs->clients.size() && !io_context->stopped())
560 io_context->run_one();
561 }
562 else
563 io_context->run();
564 }
565 catch (...)
566 {
567 if (cs)
568 cs->stop(); // on exception, stop ClientSet
569 io_context->poll(); // execute completion handlers
570 throw;
571 }
572 if (sps)
573 {
574 // OPENVPN_LOG("PUT IO_CONTEXT");
575 ts->hsc.persist_io_context(std::move(io_context));
576 }
577 }
578
580 Stop *stop = nullptr,
581 RandomAPI *prng = nullptr)
582 {
583 std::unique_ptr<openvpn_io::io_context> io_context(new openvpn_io::io_context(1));
585 try
586 {
587 AsioStopScope scope(*io_context, stop, [&]()
588 {
589 if (cs)
590 cs->abort("stop message received"); });
591 cs.reset(new ClientSet(*io_context));
592 cs->set_random(prng);
593 job(cs);
594 io_context->run();
595 }
596 catch (...)
597 {
598 if (cs)
599 cs->stop(); // on exception, stop ClientSet
600 io_context->poll(); // execute completion handlers
601 throw;
602 }
603 }
604
605 void stop()
606 {
607 if (halt)
608 return;
609 halt = true;
610 for (auto &c : clients)
611 {
612 c.second->stop(false, false);
613 c.second->reset_callbacks();
614 }
615 }
616
617 void abort(const std::string &message)
618 {
619 for (auto &c : clients)
620 c.second->abort(message);
621 }
622
623 private:
624 typedef unsigned int client_t;
625
627 {
628 public:
631
632 Client(ClientSet *parent_arg,
633 const TransactionSet::Ptr ts_arg,
634 client_t client_id_arg)
635 : parent(parent_arg),
636 ts(std::move(ts_arg)),
637 n_retries(0),
638 buf_tailroom((*ts->http_config->frame)[Frame::READ_HTTP].tailroom()),
639 reconnect_timer(parent_arg->io_context),
640 client_id(client_id_arg),
641 halt(false),
642 started(false)
643 {
644 }
645
646 bool start()
647 {
648 if (started || halt)
649 return false;
650 started = true;
651 ts->status = false;
652 ts_iter = ts->transactions.begin();
653 if (ts->delayed_start.defined())
654 {
655 retry_duration = ts->delayed_start;
656 reconnect_schedule(false);
657 }
658 else
659 {
660 next_request(false);
661 }
662 return true;
663 }
664
665 void stop(const bool keepalive, const bool shutdown)
666 {
667 if (halt)
668 return;
669 halt = true;
671 close_http(keepalive, shutdown);
672 }
673
675 {
676 if (ts)
677 ts->reset_callbacks(); // break refcount cycles in callback closures
678 }
679
680 void abort(const std::string &message)
681 {
682 if (ts)
683 ts->hsc.abort(message);
684 }
685
686 private:
687 void close_http(const bool keepalive, const bool shutdown)
688 {
689 ts->hsc.close(keepalive, shutdown);
690 }
691
693 {
694 openvpn_io::post(parent->io_context,
696 { parent->remove_client_id(id); });
697 }
698
700 {
701 if (ts_iter == ts->transactions.end())
702 {
703 done(true, true);
704 return true;
705 }
706 else
707 return false;
708 }
709
710 void done(const bool status, const bool shutdown)
711 {
712 {
713 auto clean = Cleanup([this, shutdown]()
714 {
715 if (!ts->preserve_http_state)
716 ts->hsc.stop(shutdown); });
717 stop(status, shutdown);
719 ts->status = status;
720 }
721 if (ts->completion)
722 ts->completion(*ts);
723 }
724
726 {
727 return **ts_iter;
728 }
729
730 const Transaction &trans() const
731 {
732 return **ts_iter;
733 }
734
735 std::string title() const
736 {
737 return trans().title(*ts);
738 }
739
740 void next_request(const bool error_retry)
741 {
742 if (check_if_done())
743 return;
744
745 retry_duration = ts->retry_duration;
746
747 // get current transaction
748 Transaction &t = trans();
749
750 // set up content out iterator
751 out_iter = t.content_out.begin();
752
753 // init buffer to receive content in
754 t.content_in.clear();
755
756 // if this is an error retry, allow user-defined recovery
757 if (error_retry && ts->error_recovery)
758 ts->error_recovery->retry(*ts, t);
759
760 // init and attach HTTPStateContainer
761 if (ts->debug_level >= 3)
762 OPENVPN_LOG("HTTPStateContainer alive=" << ts->alive() << " error_retry=" << error_retry << " n_clients=" << parent->clients.size());
763 if (!ts->alive())
764 ts->hsc.construct(parent->io_context, ts->http_config);
765 ts->hsc.attach(this);
766
767 ts->hsc.start_request();
768 }
769
770 void reconnect_schedule(const bool error_retry)
771 {
772 if (check_if_done())
773 return;
775 reconnect_timer.async_wait([self = Ptr(this), error_retry](const openvpn_io::error_code &error)
776 {
777 if (!error && !self->halt)
778 self->next_request(error_retry); });
779 }
780
782 {
783 return ts->host;
784 }
785
787 {
788 return trans().req;
789 }
790
792 {
793 const Transaction &t = trans();
795 if (!ci.length)
797#ifdef HAVE_ZLIB
798 if (t.accept_gzip_in)
799 ci.extra_headers.emplace_back("Accept-Encoding: gzip");
800#endif
801 return ci;
802 }
803
805 {
806 if (ts->debug_level >= 2)
807 {
808 std::ostringstream os;
809 os << "----- HEADERS RECEIVED -----\n";
810 os << " " << title() << '\n';
811 os << " ENDPOINT: " << hd.remote_endpoint_str() << '\n';
812 os << " HANDSHAKE_DETAILS: " << hd.ssl_handshake_details() << '\n';
813 os << " CONTENT-LENGTH: " << hd.content_length() << '\n';
814 os << " HEADERS: " << string::indent(HTTP::headers_redact(hd.reply().to_string()), 0, 13) << '\n';
815 OPENVPN_LOG_STRING(os.str());
816 }
817
818 Transaction &t = trans();
819
820 // save reply
821 t.reply = hd.reply();
822 }
823
825 {
826 if (out_iter != trans().content_out.end())
827 {
829 ++out_iter;
830 return ret;
831 }
832 else
833 return BufferPtr();
834 }
835
839
841 {
842 if (ts->debug_level >= 2)
843 {
844 std::ostringstream os;
845 os << "----- HEADERS SENT -----\n";
846 os << " " << title() << '\n';
847 os << " ENDPOINT: " << hd.remote_endpoint_str() << '\n';
848 os << " HEADERS: " << string::indent(HTTP::headers_redact(buf_to_string(buf)), 0, 13) << '\n';
849 OPENVPN_LOG_STRING(os.str());
850 }
851 }
852
853 void http_mutate_resolver_results(HTTPDelegate &hd, openvpn_io::ip::tcp::resolver::results_type &results)
854 {
855 // filter results by IP version
856 if (trans().ip_version_preference != IP::Addr::UNSPEC)
857 filter_by_ip_version(results, trans().ip_version_preference);
858
859 // randomize results
860 if (parent->prng && trans().randomize_resolver_results)
861 randomize_results(results, *parent->prng);
862 }
863
868
869 void http_done(HTTPDelegate &hd, const int status, const std::string &description)
870 {
871 Transaction &t = trans();
872 try
873 {
874 // save status
875 t.status = status;
876 t.description = description;
877
878 // status value should reflect HTTP status
879 const int http_status = hd.reply().status_code;
881 {
882 switch (http_status)
883 {
884 case 400:
886 break;
887 default:
889 break;
890 }
891 t.description = std::to_string(http_status) + ' ' + WS::Client::Status::error_str(t.status);
892 }
893
894 // debug output
895 if (ts->debug_level >= 2)
896 {
897 std::ostringstream os;
898 os << "----- DONE -----\n";
899 os << " " << title() << '\n';
900 os << " STATUS: " << WS::Client::Status::error_str(t.status) << '\n';
901 os << " DESCRIPTION: " << t.description << '\n';
902 OPENVPN_LOG_STRING(os.str());
903 }
904
906 {
907 // uncompress if server sent gzip-compressed data
908 if (hd.reply().headers.get_value_trim("content-encoding") == "gzip")
909 {
910#ifdef HAVE_ZLIB
911 BufferPtr bp = t.content_in.join();
912 t.content_in.clear();
913 bp = ZLib::decompress_gzip(std::move(bp), 0, 0, hd.http_config().max_content_bytes);
914 t.content_in.push_back(std::move(bp));
915#else
916 throw Exception("gzip-compressed data returned from server but app not linked with zlib");
917#endif
918 }
919
920 // do next request
921 ++ts_iter;
922
923 // Post a call to next_request() under a fresh stack.
924 // Currently we may actually be under tcp_read_handler() and
925 // next_request() can trigger destructors.
927 }
928 else
929 {
930 // failed
931 ++n_retries;
932 if (ts->max_retries && n_retries >= ts->max_retries)
933 {
934 // fail -- no more retries
935 done(false, false);
936 }
937 else
938 {
939 // fail -- retry
940 close_http(false, false);
941
942 // special case -- no delay after TCP EOF on first retry
945 else
946 reconnect_schedule(true);
947 }
948 }
949 }
950 catch (const std::exception &e)
951 {
953 t.description = std::string("http_done: ") + e.what();
954 if (!halt)
955 done(false, false);
956 }
957 }
958
960 {
961 openvpn_io::post(parent->io_context,
962 [self = Ptr(this)]()
963 { self->next_request(false); });
964 }
965
966 void http_keepalive_close(HTTPDelegate &hd, const int status, const std::string &description)
967 {
968 // this may be a no-op because ts->hsc.alive() is always tested before construction
969 // OPENVPN_LOG("http_keepalive_close " << WS::Client::Status::error_str(status) << " description=" << description << " http_status=" << std::to_string(hd.reply().status_code) << " http_text=" << hd.reply().status_text);
970 }
971
973 {
974 if (ts->post_connect)
975 ts->post_connect(*ts, sock);
976 }
977
978 bool http_status_should_retry(const int status) const
979 {
980 return status >= (ts->retry_on_http_4xx ? 400 : 500) && status < 600;
981 }
982
985 TransactionSet::Vector::const_iterator ts_iter;
987 BufferList::const_iterator out_iter;
988 unsigned int n_retries;
990 Time::Duration retry_duration;
993 bool halt;
995 };
996
997 void remove_client_id(const client_t client_id)
998 {
999 auto e = clients.find(client_id);
1000 if (e != clients.end())
1001 clients.erase(e);
1002 }
1003
1005 {
1006 while (true)
1007 {
1008 // find an ID that's not already in use
1009 const client_t id = next_id++;
1010 if (clients.find(id) == clients.end())
1011 return id;
1012 }
1013 }
1014
1015 openvpn_io::io_context &io_context;
1016 bool halt;
1019 std::map<client_t, Client::Ptr> clients;
1020};
1021
1022} // namespace openvpn::WS
std::size_t expires_after(const Time::Duration &d)
void async_wait(F &&func)
The smart pointer class.
Definition rc.hpp:119
void reset() noexcept
Points this RCPtr<T> to nullptr safely.
Definition rc.hpp:290
Reference count base class for objects tracked by RCPtr. Disallows copying and assignment.
Definition rc.hpp:912
Abstract base class for random number generators.
Definition randapi.hpp:39
static Ptr Create(ArgsT &&...args)
Creates a new instance of RcEnable with the given arguments.
Definition make_rc.hpp:43
std::string to_string() const
Definition urlparse.hpp:200
static Parse from_components(const bool https, const std::string &host, const std::string &port, const std::string &uri)
Definition urlparse.hpp:143
void http_headers_sent(HTTPDelegate &hd, const Buffer &buf)
void http_mutate_resolver_results(HTTPDelegate &hd, openvpn_io::ip::tcp::resolver::results_type &results)
void next_request(const bool error_retry)
void http_post_connect(HTTPDelegate &hd, AsioPolySock::Base &sock)
void http_content_in(HTTPDelegate &hd, BufferAllocated &buf)
BufferPtr http_content_out(HTTPDelegate &hd)
WS::Client::Request http_request(HTTPDelegate &hd) const
BufferList::const_iterator out_iter
void http_content_out_needed(HTTPDelegate &hd)
void http_keepalive_close(HTTPDelegate &hd, const int status, const std::string &description)
WS::Client::Host http_host(HTTPDelegate &hd) const
Client(ClientSet *parent_arg, const TransactionSet::Ptr ts_arg, client_t client_id_arg)
void abort(const std::string &message)
WS::Client::ContentInfo http_content_info(HTTPDelegate &hd) const
void http_done(HTTPDelegate &hd, const int status, const std::string &description)
void http_headers_received(HTTPDelegate &hd)
void reconnect_schedule(const bool error_retry)
const Transaction & trans() const
bool http_status_should_retry(const int status) const
TransactionSet::Vector::const_iterator ts_iter
void done(const bool status, const bool shutdown)
void close_http(const bool keepalive, const bool shutdown)
void stop(const bool keepalive, const bool shutdown)
std::unique_ptr< openvpn_io::io_context > acquire_io_context()
bool alive(const std::string &host) const
void abort(const std::string &message)
void construct(openvpn_io::io_context &io_context, const WS::Client::Config::Ptr config)
void close(const bool keepalive, const bool shutdown)
void persist_io_context(std::unique_ptr< openvpn_io::io_context > &&io_context)
void from_list(T first, Args... args)
void from_list(const char *arg)
void from_list(std::string arg)
HostRetry(T first, Args... args)
virtual void retry(TransactionSet &ts, Transaction &t) override
void shuffle(RandomAPI &prng)
WS::ClientSet::Transaction & first_transaction()
std::vector< std::unique_ptr< Transaction > > Vector
void assign_http_state(HTTPStateContainer &http_state)
void dump(std::ostream &os, const bool content_only=false) const
Function< void(TransactionSet &ts, AsioPolySock::Base &sock)> post_connect
WS::Client::Config::Ptr http_config
Function< void(TransactionSet &ts)> completion
static void run_synchronous(Function< void(ClientSet::Ptr)> job, Stop *stop=nullptr, RandomAPI *prng=nullptr)
WS::Client::HTTPDelegate< Client > HTTPDelegate
ClientSet(openvpn_io::io_context &io_context_arg)
std::map< client_t, Client::Ptr > clients
openvpn_io::io_context & io_context
RCPtr< ClientSet > Ptr
void new_request(const TransactionSet::Ptr ts)
void set_random(RandomAPI::Ptr prng_arg)
static void new_request_synchronous(const TransactionSet::Ptr ts, Stop *stop=nullptr, RandomAPI *prng=nullptr, const bool sps=false)
void abort(const std::string &message)
void remove_client_id(const client_t client_id)
std::string remote_endpoint_str() const
Definition httpcli.hpp:492
const HTTP::Reply & reply() const
Definition httpcli.hpp:487
olong content_length() const
std::string ssl_handshake_details() const
const CONFIG & http_config() const
#define OPENVPN_HTTP_CLISET_RC
#define OPENVPN_LOG(args)
#define OPENVPN_LOG_STRING(str)
std::string headers_redact(const std::string &headers)
void randomize_results(RESULTS &results, RandomAPI &prng)
void filter_by_ip_version(RESULTS &results, const IP::Addr::Version ip_ver)
bool ends_with_newline(const STRING &str)
Definition string.hpp:141
std::string indent(const std::string &str, const int first, const int remaining)
Definition string.hpp:421
std::string to_string(T value)
Definition to_string.hpp:33
CleanupType< F > Cleanup(F method) noexcept
Definition cleanup.hpp:43
void log_compress(const std::string prefix, const size_t orig_size, const size_t new_size)
Definition complog.hpp:15
RCPtr< BufferAllocatedRc > BufferPtr
Definition buffer.hpp:1859
std::string buf_to_string(const Buffer &buf)
Definition bufstr.hpp:22
Implementation of the base classes for random number generators.
size_t join_size() const
Definition buflist.hpp:64
BufferPtr join(const size_t headroom, const size_t tailroom, const bool size_1_optim) const
Definition buflist.hpp:32
void put_consume(BufferAllocated &buf, const size_t tailroom=0)
Definition buflist.hpp:86
const Header * get(const std::string &key) const
Definition header.hpp:55
std::string get_value_trim(const std::string &key) const
Definition header.hpp:84
std::string to_string() const
Definition reply.hpp:44
std::string status_text
Definition reply.hpp:58
HeaderList headers
Definition reply.hpp:59
virtual void retry(TransactionSet &ts, Transaction &t)=0
std::unique_ptr< openvpn_io::io_context > io_context
void dump(std::ostream &os, const TransactionSet &ts) const
std::string format_status(const TransactionSet &ts) const
std::string url(const TransactionSet &ts) const
std::string title(const TransactionSet &ts) const
void compress_content_out(const unsigned int min_size=64, const bool verbose=false)
std::string get_redirect_location() const
std::string content_in_string() const
std::vector< std::string > extra_headers
Definition httpcli.hpp:332
static std::string error_str(const int status)
Definition httpcli.hpp:124
proxy_autoconfig_url url
proxy_host_port host
std::string ret
static bool verbose
Definition test_ip.cpp:31
static const char config[]
const char message[]