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
436 HostRetry() = default;
437
438 template <typename T, typename... Args>
439 HostRetry(T first, Args... args)
440 {
441 reserve(1 + sizeof...(args));
442 from_list(first, args...);
443 }
444
446 {
447 std::shuffle(begin(), end(), prng);
448 index = 0;
449 }
450
451 std::string next_host()
452 {
453 if (empty())
454 throw Exception("HostRetry: empty host list");
455 if (index >= size())
456 index = 0;
457 return (*this)[index++];
458 }
459
460 void retry(TransactionSet &ts, Transaction &t) override
461 {
462 ts.host.host = next_host();
463 }
464
465 private:
466 void from_list(std::string arg)
467 {
468 push_back(std::move(arg));
469 }
470
471 void from_list(const char *arg)
472 {
473 push_back(std::string(arg));
474 }
475
476 template <typename T, typename... Args>
477 void from_list(T first, Args... args)
478 {
479 from_list(first);
480 from_list(args...);
481 }
482
483 size_t index = 0;
484 };
485
486 ClientSet(openvpn_io::io_context &io_context_arg)
487 : io_context(io_context_arg),
488 halt(false),
489 next_id(0)
490 {
491 }
492
494 {
495 prng = std::move(prng_arg);
496 }
497
499 {
500 const client_t id = new_client_id();
501 Client::Ptr cli = new Client(this, std::move(ts), id);
502 clients[id] = cli;
503 cli->start();
504 }
505
507 Stop *stop = nullptr,
508 RandomAPI *prng = nullptr,
509 const bool sps = false)
510 {
511 std::unique_ptr<openvpn_io::io_context> io_context;
512 auto clean = Cleanup([&]()
513 {
514 // ensure that TransactionSet reference to socket
515 // is reset before method returns (unless sps is true
516 // in which case we should retain it).
517 if (!sps)
518 ts->hsc.reset(); });
519 ts->preserve_http_state = sps;
520 if (sps)
521 {
523 if (io_context)
524 {
525 if (io_context->stopped())
526 {
527 // OPENVPN_LOG("RESTART IO_CONTEXT");
528 io_context->restart();
529 }
530 else
531 {
532 // OPENVPN_LOG("GET IO_CONTEXT");
533 }
534 }
535 }
536 if (!io_context)
537 {
538 if (sps)
539 {
540 // OPENVPN_LOG("NEW IO_CONTEXT");
541 }
542 io_context.reset(new openvpn_io::io_context(1));
543 }
545 try
546 {
547 AsioStopScope scope(*io_context, stop, [&]()
548 {
549 if (cs)
550 cs->abort("stop message received"); });
551 cs.reset(new ClientSet(*io_context));
552 if (prng)
553 cs->set_random(RandomAPI::Ptr(prng));
554 cs->new_request(ts);
555 if (sps)
556 {
557 while (cs->clients.size() && !io_context->stopped())
558 io_context->run_one();
559 }
560 else
561 io_context->run();
562 }
563 catch (...)
564 {
565 if (cs)
566 cs->stop(); // on exception, stop ClientSet
567 io_context->poll(); // execute completion handlers
568 throw;
569 }
570 if (sps)
571 {
572 // OPENVPN_LOG("PUT IO_CONTEXT");
573 ts->hsc.persist_io_context(std::move(io_context));
574 }
575 }
576
578 Stop *stop = nullptr,
579 RandomAPI *prng = nullptr)
580 {
581 std::unique_ptr<openvpn_io::io_context> io_context(new openvpn_io::io_context(1));
583 try
584 {
585 AsioStopScope scope(*io_context, stop, [&]()
586 {
587 if (cs)
588 cs->abort("stop message received"); });
589 cs.reset(new ClientSet(*io_context));
590 cs->set_random(prng);
591 job(cs);
592 io_context->run();
593 }
594 catch (...)
595 {
596 if (cs)
597 cs->stop(); // on exception, stop ClientSet
598 io_context->poll(); // execute completion handlers
599 throw;
600 }
601 }
602
603 void stop()
604 {
605 if (halt)
606 return;
607 halt = true;
608 for (auto &c : clients)
609 {
610 c.second->stop(false, false);
611 c.second->reset_callbacks();
612 }
613 }
614
615 void abort(const std::string &message)
616 {
617 for (auto &c : clients)
618 c.second->abort(message);
619 }
620
621 private:
622 typedef unsigned int client_t;
623
625 {
626 public:
629
630 Client(ClientSet *parent_arg,
631 const TransactionSet::Ptr ts_arg,
632 client_t client_id_arg)
633 : parent(parent_arg),
634 ts(std::move(ts_arg)),
635 n_retries(0),
636 buf_tailroom((*ts->http_config->frame)[Frame::READ_HTTP].tailroom()),
637 reconnect_timer(parent_arg->io_context),
638 client_id(client_id_arg),
639 halt(false),
640 started(false)
641 {
642 }
643
644 bool start()
645 {
646 if (started || halt)
647 return false;
648 started = true;
649 ts->status = false;
650 ts_iter = ts->transactions.begin();
651 if (ts->delayed_start.defined())
652 {
653 retry_duration = ts->delayed_start;
654 reconnect_schedule(false);
655 }
656 else
657 {
658 next_request(false);
659 }
660 return true;
661 }
662
663 void stop(const bool keepalive, const bool shutdown)
664 {
665 if (halt)
666 return;
667 halt = true;
669 close_http(keepalive, shutdown);
670 }
671
673 {
674 if (ts)
675 ts->reset_callbacks(); // break refcount cycles in callback closures
676 }
677
678 void abort(const std::string &message)
679 {
680 if (ts)
681 ts->hsc.abort(message);
682 }
683
684 private:
685 void close_http(const bool keepalive, const bool shutdown)
686 {
687 ts->hsc.close(keepalive, shutdown);
688 }
689
691 {
692 openvpn_io::post(parent->io_context,
694 { parent->remove_client_id(id); });
695 }
696
698 {
699 if (ts_iter == ts->transactions.end())
700 {
701 done(true, true);
702 return true;
703 }
704 else
705 return false;
706 }
707
708 void done(const bool status, const bool shutdown)
709 {
710 {
711 auto clean = Cleanup([this, shutdown]()
712 {
713 if (!ts->preserve_http_state)
714 ts->hsc.stop(shutdown); });
715 stop(status, shutdown);
717 ts->status = status;
718 }
719 if (ts->completion)
720 ts->completion(*ts);
721 }
722
724 {
725 return **ts_iter;
726 }
727
728 const Transaction &trans() const
729 {
730 return **ts_iter;
731 }
732
733 std::string title() const
734 {
735 return trans().title(*ts);
736 }
737
738 void next_request(const bool error_retry)
739 {
740 if (check_if_done())
741 return;
742
743 retry_duration = ts->retry_duration;
744
745 // get current transaction
746 Transaction &t = trans();
747
748 // set up content out iterator
749 out_iter = t.content_out.begin();
750
751 // init buffer to receive content in
752 t.content_in.clear();
753
754 // if this is an error retry, allow user-defined recovery
755 if (error_retry && ts->error_recovery)
756 ts->error_recovery->retry(*ts, t);
757
758 // init and attach HTTPStateContainer
759 if (ts->debug_level >= 3)
760 OPENVPN_LOG("HTTPStateContainer alive=" << ts->alive() << " error_retry=" << error_retry << " n_clients=" << parent->clients.size());
761 if (!ts->alive())
762 ts->hsc.construct(parent->io_context, ts->http_config);
763 ts->hsc.attach(this);
764
765 ts->hsc.start_request();
766 }
767
768 void reconnect_schedule(const bool error_retry)
769 {
770 if (check_if_done())
771 return;
773 reconnect_timer.async_wait([self = Ptr(this), error_retry](const openvpn_io::error_code &error)
774 {
775 if (!error && !self->halt)
776 self->next_request(error_retry); });
777 }
778
780 {
781 return ts->host;
782 }
783
785 {
786 return trans().req;
787 }
788
790 {
791 const Transaction &t = trans();
793 if (!ci.length)
795#ifdef HAVE_ZLIB
796 if (t.accept_gzip_in)
797 ci.extra_headers.emplace_back("Accept-Encoding: gzip");
798#endif
799 return ci;
800 }
801
803 {
804 if (ts->debug_level >= 2)
805 {
806 std::ostringstream os;
807 os << "----- HEADERS RECEIVED -----\n";
808 os << " " << title() << '\n';
809 os << " ENDPOINT: " << hd.remote_endpoint_str() << '\n';
810 os << " HANDSHAKE_DETAILS: " << hd.ssl_handshake_details() << '\n';
811 os << " CONTENT-LENGTH: " << hd.content_length() << '\n';
812 os << " HEADERS: " << string::indent(HTTP::headers_redact(hd.reply().to_string()), 0, 13) << '\n';
813 OPENVPN_LOG_STRING(os.str());
814 }
815
816 Transaction &t = trans();
817
818 // save reply
819 t.reply = hd.reply();
820 }
821
823 {
824 if (out_iter != trans().content_out.end())
825 {
827 ++out_iter;
828 return ret;
829 }
830 else
831 return BufferPtr();
832 }
833
837
839 {
840 if (ts->debug_level >= 2)
841 {
842 std::ostringstream os;
843 os << "----- HEADERS SENT -----\n";
844 os << " " << title() << '\n';
845 os << " ENDPOINT: " << hd.remote_endpoint_str() << '\n';
846 os << " HEADERS: " << string::indent(HTTP::headers_redact(buf_to_string(buf)), 0, 13) << '\n';
847 OPENVPN_LOG_STRING(os.str());
848 }
849 }
850
851 void http_mutate_resolver_results(HTTPDelegate &hd, openvpn_io::ip::tcp::resolver::results_type &results)
852 {
853 // filter results by IP version
854 if (trans().ip_version_preference != IP::Addr::UNSPEC)
855 filter_by_ip_version(results, trans().ip_version_preference);
856
857 // randomize results
858 if (parent->prng && trans().randomize_resolver_results)
859 randomize_results(results, *parent->prng);
860 }
861
866
867 void http_done(HTTPDelegate &hd, const int status, const std::string &description)
868 {
869 Transaction &t = trans();
870 try
871 {
872 // save status
873 t.status = status;
874 t.description = description;
875
876 // status value should reflect HTTP status
877 const int http_status = hd.reply().status_code;
879 {
880 switch (http_status)
881 {
882 case 400:
884 break;
885 default:
887 break;
888 }
889 t.description = std::to_string(http_status) + ' ' + WS::Client::Status::error_str(t.status);
890 }
891
892 // debug output
893 if (ts->debug_level >= 2)
894 {
895 std::ostringstream os;
896 os << "----- DONE -----\n";
897 os << " " << title() << '\n';
898 os << " STATUS: " << WS::Client::Status::error_str(t.status) << '\n';
899 os << " DESCRIPTION: " << t.description << '\n';
900 OPENVPN_LOG_STRING(os.str());
901 }
902
904 {
905 // uncompress if server sent gzip-compressed data
906 if (hd.reply().headers.get_value_trim("content-encoding") == "gzip")
907 {
908#ifdef HAVE_ZLIB
909 BufferPtr bp = t.content_in.join();
910 t.content_in.clear();
911 bp = ZLib::decompress_gzip(std::move(bp), 0, 0, hd.http_config().max_content_bytes);
912 t.content_in.push_back(std::move(bp));
913#else
914 throw Exception("gzip-compressed data returned from server but app not linked with zlib");
915#endif
916 }
917
918 // do next request
919 ++ts_iter;
920
921 // Post a call to next_request() under a fresh stack.
922 // Currently we may actually be under tcp_read_handler() and
923 // next_request() can trigger destructors.
925 }
926 else
927 {
928 // failed
929 ++n_retries;
930 if (ts->max_retries && n_retries >= ts->max_retries)
931 {
932 // fail -- no more retries
933 done(false, false);
934 }
935 else
936 {
937 // fail -- retry
938 close_http(false, false);
939
940 // special case -- no delay after TCP EOF on first retry
943 else
944 reconnect_schedule(true);
945 }
946 }
947 }
948 catch (const std::exception &e)
949 {
951 t.description = std::string("http_done: ") + e.what();
952 if (!halt)
953 done(false, false);
954 }
955 }
956
958 {
959 openvpn_io::post(parent->io_context,
960 [self = Ptr(this)]()
961 { self->next_request(false); });
962 }
963
964 void http_keepalive_close(HTTPDelegate &hd, const int status, const std::string &description)
965 {
966 // this may be a no-op because ts->hsc.alive() is always tested before construction
967 // 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);
968 }
969
971 {
972 if (ts->post_connect)
973 ts->post_connect(*ts, sock);
974 }
975
976 bool http_status_should_retry(const int status) const
977 {
978 return status >= (ts->retry_on_http_4xx ? 400 : 500) && status < 600;
979 }
980
983 TransactionSet::Vector::const_iterator ts_iter;
985 BufferList::const_iterator out_iter;
986 unsigned int n_retries;
988 Time::Duration retry_duration;
991 bool halt;
993 };
994
995 void remove_client_id(const client_t client_id)
996 {
997 auto e = clients.find(client_id);
998 if (e != clients.end())
999 clients.erase(e);
1000 }
1001
1003 {
1004 while (true)
1005 {
1006 // find an ID that's not already in use
1007 const client_t id = next_id++;
1008 if (!clients.contains(id))
1009 return id;
1010 }
1011 }
1012
1013 openvpn_io::io_context &io_context;
1014 bool halt;
1017 std::map<client_t, Client::Ptr> clients;
1018};
1019
1020} // 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:908
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:198
static Parse from_components(const bool https, const std::string &host, const std::string &port, const std::string &uri)
Definition urlparse.hpp:141
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 retry(TransactionSet &ts, Transaction &t) override
void from_list(T first, Args... args)
void from_list(const char *arg)
void from_list(std::string arg)
HostRetry(T first, Args... args)
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:93
std::string indent(const std::string &str, const int first, const int remaining)
Definition string.hpp:369
std::string to_string(const T &t)
Convert a value to a string.
Definition to_string.hpp:45
CleanupType< F > Cleanup(F method) noexcept
Definition cleanup.hpp:43
RCPtr< BufferAllocatedRc > BufferPtr
Definition buffer.hpp:1896
void log_compress(const std::string &prefix, const size_t orig_size, const size_t new_size)
Definition complog.hpp:15
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:54
std::string get_value_trim(const std::string &key) const
Definition header.hpp:82
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
server addresses push_back(address)
proxy_host_port host
std::string ret
std::ostringstream os
static bool verbose
Definition test_ip.cpp:31
static const char config[]
const char message[]