mirror of
https://gitlab.com/niansa/llama_nds.git
synced 2025-03-06 20:53:28 +01:00
113 lines
3 KiB
C++
113 lines
3 KiB
C++
#include "Client.hpp"
|
|
#include "Socket.hpp"
|
|
#include "Sender.hpp"
|
|
#include "Receiver.hpp"
|
|
|
|
#include <cerrno>
|
|
#ifndef PLATFORM_WINDOWS
|
|
# include <sys/types.h>
|
|
# include <sys/socket.h>
|
|
# include <netinet/in.h>
|
|
# include <arpa/inet.h>
|
|
# include <netdb.h>
|
|
#else
|
|
# include <ws2tcpip.h>
|
|
#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<SocketConnection<Sender::Simple, Receiver::Simple>>(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<unsigned long *>(addrInfo->h_addr_list[0]);
|
|
if (connect(*connection, reinterpret_cast<sockaddr *>(&sain), sizeof(sain)) != 0) [[unlikely]] {
|
|
throw Exception("Connection has been declined");
|
|
}
|
|
# endif
|
|
}
|
|
|
|
void Client::ask(std::string_view prompt, const std::function<void (unsigned progress)>& on_progress, const std::function<void (std::string_view token)>& 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);
|
|
}
|
|
}
|