| 57 | } // namespace |
| 58 | |
| 59 | int caf_main(caf::actor_system& sys, const config& cfg) { |
| 60 | signal(SIGTERM, [](int) { shutdown_flag = true; }); |
| 61 | // Get URI from config (positional argument). |
| 62 | auto remainder = cfg.remainder(); |
| 63 | if (remainder.size() != 1) { |
| 64 | sys.println("*** expected mandatory positional argument: URL"); |
| 65 | return EXIT_FAILURE; |
| 66 | } |
| 67 | uri resource; |
| 68 | if (auto err = parse(remainder[0], resource)) { |
| 69 | sys.println("*** failed to parse URI: {} ", err); |
| 70 | return EXIT_FAILURE; |
| 71 | } |
| 72 | auto ca_file = caf::get_as<std::string>(cfg, "tls.ca-file"); |
| 73 | auto method = caf::get_or(cfg, "method", default_method); |
| 74 | auto payload = caf::get_or(cfg, "payload", ""sv); |
| 75 | auto result |
| 76 | = http::with(sys) |
| 77 | // Lazy load TLS when connecting to HTTPS endpoints. |
| 78 | .context_factory([ca_file, resource]() { |
| 79 | return ssl::emplace_client(ssl::tls::v1_2)() |
| 80 | .and_then(ssl::load_verify_file_if(ca_file)) |
| 81 | .and_then(ssl::use_sni_hostname(resource)); |
| 82 | }) |
| 83 | // Connect to the address of the resource. |
| 84 | .connect(resource) |
| 85 | // If we don't succeed at first, try up to 5 times with 1s delay. |
| 86 | .retry_delay(1s) |
| 87 | .max_retry_count(5) |
| 88 | // Wait up to 250ms for establishing a connection. |
| 89 | .connection_timeout(250ms) |
| 90 | // After connecting, send a get request. |
| 91 | .add_header_field("User-Agent", "CAF-Client") |
| 92 | .request(method, payload); |
| 93 | if (!result) { |
| 94 | sys.println("*** Failed to initiate connection: {}", result.error()); |
| 95 | return EXIT_FAILURE; |
| 96 | } |
| 97 | sys.spawn([res = result->first](event_based_actor* self) { |
| 98 | res.bind_to(self).then( |
| 99 | [self](const http::response& r) { |
| 100 | self->println("Server responded with HTTP {}: {}", |
| 101 | static_cast<uint16_t>(r.code()), phrase(r.code())); |
| 102 | self->println("Header fields:"); |
| 103 | for (const auto& [key, value] : r.header_fields()) |
| 104 | self->println("- {}: {}", key, value); |
| 105 | if (r.body().empty()) |
| 106 | return; |
| 107 | if (is_valid_utf8(r.body())) { |
| 108 | self->println("Payload: {}", to_string_view(r.body())); |
| 109 | } else { |
| 110 | auto split_at = [](const_byte_span bytes, size_t at) { |
| 111 | if (bytes.size() > at) |
| 112 | return std::pair{bytes.subspan(0, at), bytes.subspan(at)}; |
| 113 | return std::pair{bytes, const_byte_span{}}; |
| 114 | }; |
| 115 | // Print 8 bytes per row in hex. |
| 116 | self->println("Payload:"); |
nothing calls this directly
no test coverage detected