libfilezilla
raw_https.cpp

Demonstrates how to use sockets and the TLS layer.This example is a most-trivial HTTPS client that requests "/" on the passed host and outputs what the server sends verbatim.

#include <iostream>
#include <type_traits>
namespace {
struct logger : public fz::logger_interface
{
logger()
{
// For debugging
// set_all(static_cast<fz::logmsg::type>(~std::underlying_type_t<fz::logmsg::type>(0)));
}
virtual void do_log(fz::logmsg::type t, std::wstring && msg) {
std::cerr << "Log: " << int(t) << " " << fz::to_string(msg) << "\n";
}
};
}
// A simple event handler
class handler final : public fz::event_handler
{
public:
handler(fz::event_loop& l, std::string const& host)
: fz::event_handler(l)
, trust_store_(pool_)
{
s_ = std::make_unique<fz::socket>(pool_, this);
int res = s_->connect(fz::to_native(host), 443);
if (res) {
log_.log(fz::logmsg::error, "Connect failed with %s", fz::socket_error_description(res));
event_loop_.stop();
return;
}
tls_ = std::make_unique<fz::tls_layer>(event_loop_, this, *s_, &trust_store_, log_);
if (!tls_->client_handshake(nullptr, {}, fz::to_native(host))) {
log_.log(fz::logmsg::error, "Could not start handshake");
event_loop_.stop();
return;
}
snd_.append("GET / HTTP/1.1\r\nConnection: close\r\nUser-Agent: lfz (socket demo)\r\nHost: ");
snd_.append(host);
snd_.append("\r\n\r\n");
}
virtual ~handler()
{
// This _MUST_ be called to avoid a race so that operator()(fz::event_base const&) is not called on a partially destructed object.
}
bool success_{};
private:
// The event loop calls this function for every event sent to this handler.
virtual void operator()(fz::event_base const& ev)
{
// Dispatch the event to the correct function.
fz::dispatch<fz::socket_event>(ev, this, &handler::on_socket_event);
}
void on_socket_event(fz::socket_event_source*, fz::socket_event_flag type, int error) {
if (error) {
auto desc = fz::socket_error_description(error);
switch (type) {
log_.log(fz::logmsg::error, "Connection failed: %s", desc);
break;
log_.log(fz::logmsg::error, "Reading failed: %s", desc);
break;
log_.log(fz::logmsg::error, "Connection failed %s", desc);
break;
default:
log_.log(fz::logmsg::error, "Unknown error: %s", desc);
break;
}
event_loop_.stop();
return;
}
while (!snd_.empty()) {
int w = tls_->write(snd_.get(), snd_.size(), error);
if (w > 0) {
snd_.consume(w);
}
else {
if (w < 0) {
if (error == EAGAIN) {
return;
}
log_.log(fz::logmsg::error, "Error writing: %", fz::socket_error_description(error));
}
event_loop_.stop();
return;
}
}
log_.log(fz::logmsg::status, "Sent request");
}
else if (type == fz::socket_event_flag::read) {
char buf[1024];
while (true) {
int r = tls_->read(buf, 1024, error);
if (r > 0) {
std::cout << std::string_view(buf, r);
continue;
}
if (!r) {
log_.log(fz::logmsg::status, "Got eof");
success_ = true;
}
else {
if (error == EAGAIN) {
return;
}
log_.log(fz::logmsg::error, "Error reading: %s", fz::socket_error_description(error));
}
event_loop_.stop();
return;
}
}
}
logger log_;
std::unique_ptr<fz::socket> s_;
std::unique_ptr<fz::tls_layer> tls_;
fz::buffer snd_;
};
int main(int argc , char * argv[])
{
if (argc <= 1) {
std::cerr << "Need to pass hostname\n";
return 1;
}
std::string host = argv[1];
// Start an event loop
fz::event_loop l(fz::event_loop::threadless);
// Create a handler
handler h(l, host);
l.run();
// All done.
return h.success_ ? 0 : 1;
}
Declares fz::buffer.
The buffer class is a simple buffer where data can be appended at the end and consumed at the front....
Definition: buffer.hpp:27
Common base class for all events.
Definition: event.hpp:23
Simple handler for asynchronous event processing.
Definition: event_handler.hpp:55
void remove_handler()
Deactivates handler, removes all pending events and stops all timers for this handler.
virtual void operator()(event_base const &)=0
Called by the event loop in the worker thread with the event to process.
A threaded event loop that supports sending events and timers.
Definition: event_loop.hpp:34
void run()
Starts the loop in the caller's thread.
Abstract interface for logging strings.
Definition: logger.hpp:51
virtual void do_log(logmsg::type t, std::wstring &&msg)=0
The one thing you need to override.
All classes sending socket events should derive from this.
Definition: socket.hpp:85
A dumb thread-pool for asynchronous tasks.
Definition: thread_pool.hpp:64
Opaque class to load the system trust store asynchronously.
Definition: tls_system_trust_store.hpp:30
Declares the event_handler class.
Interface for logging.
type
Definition: logger.hpp:16
The namespace used by libfilezilla.
Definition: apply.hpp:17
native_string socket_error_description(int error)
Gets a human-readable, translated description of the error.
std::string to_string(std::wstring_view const &in)
Converts from std::wstring into std::string in system encoding.
socket_event_flag
The type of a socket event.
Definition: socket.hpp:35
native_string to_native(std::string_view const &in)
Converts std::string to native_string.
Socket classes for networking.
Declares thread_pool and async_task.
A Transport Layer Security (TLS) layer.
System trust store for TLS certificates.
Various utility functions.