#include "Client.hpp" #include "Socket.hpp" #include "Sender.hpp" #include "Receiver.hpp" #include #ifndef PLATFORM_WINDOWS # include # include # include # include # include #else # include #endif void Client::fetchAddr(const std::string& addr, unsigned port) { addrInfo = gethostbyname(addr.c_str()); auto error = errno; auto bad = addrInfo == nullptr || addrInfo->h_addr_list[0] == nullptr; // Check for error if (bad) { throw Exception("DNS failed to look up hostname: "+std::string(strerror(error))+" ("+addr+')'); } } Client::Client(const std::string& addr, unsigned port, AsyncManager& asyncManager) : aMan(asyncManager) { // Create socket connection = std::make_unique>(aMan, Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); //TODO: Care about IPv6 if (*connection < 0) [[unlikely]] { throw Exception("Failed to create TCP socket"); } // Fetch address fetchAddr(addr, port); // Connect to server struct sockaddr_in sain; sain.sin_family = AF_INET; sain.sin_port = htons(port); sain.sin_addr.s_addr = *reinterpret_cast(addrInfo->h_addr_list[0]); if (connect(*connection, reinterpret_cast(&sain), sizeof(sain)) != 0) [[unlikely]] { throw Exception("Connection has been declined"); } } basiccoro::AwaitableTask Client::ask(std::string_view prompt, const std::function (unsigned progress)>& on_progress, const std::function (std::string_view token)>& on_token) { std::string fres; // Send prompt length uint8_t len = prompt.length(); if (co_await connection->writeObject(len, true) == AsyncResult::Error) { co_return AsyncResult::Error; } // Send prompt if (co_await connection->write(prompt) == AsyncResult::Error) { co_return AsyncResult::Error; } // Receive progress for (;;) { uint8_t progress; // Receive percentage if (co_await connection->readObject(progress) == AsyncResult::Error) { co_return AsyncResult::Error; } // Run on_progress callback co_await on_progress(progress); // Stop at 100% if (progress == 100) break; } // Receive response for (;;) { // Receive response length if (co_await connection->readObject(len) == AsyncResult::Error) { co_return AsyncResult::Error; } // End if zero if (len == 0xFF) break; // Skip empty token if (len == 0) continue; // Receive response const auto token = co_await connection->read(len); if (token.empty()) { co_return AsyncResult::Error; } // Run on_token callback co_await on_token(token); } // No error co_return AsyncResult::Success; }