#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) { # ifdef HAS_ADDRINFO // Set up hints addrinfo hints; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_INET; //TODO: Care about IPv6 hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_CANONNAME; // Get port as C string char portStr[7]; sprintf(portStr, "%u", port); // Get addrinfo auto error = getaddrinfo(addr.c_str(), portStr, &hints, &addrInfo); auto bad = addrInfo == nullptr; # else addrInfo = gethostbyname(addr.c_str()); auto error = errno; auto bad = addrInfo == nullptr || addrInfo->h_addr_list[0] == nullptr; # endif // 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) { // Create socket connection = std::make_unique>(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); # ifdef HAS_ADDRINFO // Connect to server if (connect(*connection, addrInfo->ai_addr, addrInfo->ai_addrlen) != 0) [[unlikely]] { throw Exception("Connection for HTTP::Request has been declined"); } # else // 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"); } # endif } void Client::ask(std::string_view prompt, const std::function& on_progress, const std::function& on_token) { std::string fres; // Send prompt length uint8_t len = prompt.length(); connection->writeObject(len, true); // Send prompt connection->write(prompt); // Receive progress for (;;) { uint8_t progress; // Receive percentage connection->readObject(progress); // Run on_progress callback on_progress(progress); // Stop at 100% if (progress == 100) break; } // Receive response for (;;) { // Receive response length connection->readObject(len); // End if zero if (len == 0xFF) break; // Receive response const auto token = connection->read(len); // Run on_token callback on_token(token); } }