mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-10-30 16:38:11 +01:00 
			
		
		
		
	cleanup
This commit is contained in:
		| @@ -1,5 +1,5 @@ | ||||
| #pragma once | ||||
| #include "net.h" | ||||
| #include <utils/net.h> | ||||
| #include <dsp/stream.h> | ||||
| #include <dsp/types.h> | ||||
| #include <memory> | ||||
|   | ||||
| @@ -1,403 +0,0 @@ | ||||
| #include "net.h" | ||||
| #include <string.h> | ||||
| #include <codecvt> | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #define WOULD_BLOCK (WSAGetLastError() == WSAEWOULDBLOCK) | ||||
| #else | ||||
| #define WOULD_BLOCK (errno == EWOULDBLOCK) | ||||
| #endif | ||||
|  | ||||
| namespace net { | ||||
|     bool _init = false; | ||||
|      | ||||
|     // === Private functions === | ||||
|  | ||||
|     void init() { | ||||
|         if (_init) { return; } | ||||
| #ifdef _WIN32 | ||||
|         // Initialize WinSock2 | ||||
|         WSADATA wsa; | ||||
|         if (WSAStartup(MAKEWORD(2, 2), &wsa)) { | ||||
|             throw std::runtime_error("Could not initialize WinSock2"); | ||||
|             return; | ||||
|         } | ||||
| #else | ||||
|         // Disable SIGPIPE to avoid closing when the remote host disconnects | ||||
|         signal(SIGPIPE, SIG_IGN); | ||||
| #endif | ||||
|         _init = true; | ||||
|     } | ||||
|  | ||||
|     bool queryHost(uint32_t* addr, std::string host) { | ||||
|         hostent* ent = gethostbyname(host.c_str()); | ||||
|         if (!ent || !ent->h_addr_list[0]) { return false; } | ||||
|         *addr = *(uint32_t*)ent->h_addr_list[0]; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     void closeSocket(SockHandle_t sock) { | ||||
| #ifdef _WIN32 | ||||
|         shutdown(sock, SD_BOTH); | ||||
|         closesocket(sock); | ||||
| #else | ||||
|         shutdown(sock, SHUT_RDWR); | ||||
|         close(sock); | ||||
| #endif | ||||
|     } | ||||
|  | ||||
|     void setNonblocking(SockHandle_t sock) { | ||||
| #ifdef _WIN32 | ||||
|         u_long enabled = 1; | ||||
|         ioctlsocket(sock, FIONBIO, &enabled); | ||||
| #else | ||||
|         fcntl(sock, F_SETFL, O_NONBLOCK); | ||||
| #endif | ||||
|     } | ||||
|  | ||||
|     // === Address functions === | ||||
|  | ||||
|     Address::Address() { | ||||
|         memset(&addr, 0, sizeof(addr)); | ||||
|     } | ||||
|  | ||||
|     Address::Address(const std::string& host, int port) { | ||||
|         // Initialize WSA if needed | ||||
|         init(); | ||||
|          | ||||
|         // Lookup host | ||||
|         hostent* ent = gethostbyname(host.c_str()); | ||||
|         if (!ent || !ent->h_addr_list[0]) { | ||||
|             throw std::runtime_error("Unknown host"); | ||||
|         } | ||||
|          | ||||
|         // Build address | ||||
|         memset(&addr, 0, sizeof(addr)); | ||||
|         addr.sin_family = AF_INET; | ||||
|         addr.sin_addr.s_addr = *(uint32_t*)ent->h_addr_list[0]; | ||||
|         addr.sin_port = htons(port); | ||||
|     } | ||||
|  | ||||
|     Address::Address(IP_t ip, int port) { | ||||
|         memset(&addr, 0, sizeof(addr)); | ||||
|         addr.sin_family = AF_INET; | ||||
|         addr.sin_addr.s_addr = htonl(ip); | ||||
|         addr.sin_port = htons(port); | ||||
|     } | ||||
|  | ||||
|     std::string Address::getIPStr() { | ||||
|         char buf[128]; | ||||
|         IP_t ip = getIP(); | ||||
|         sprintf(buf, "%d.%d.%d.%d", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF); | ||||
|         return buf; | ||||
|     } | ||||
|  | ||||
|     IP_t Address::getIP() { | ||||
|         return htonl(addr.sin_addr.s_addr); | ||||
|     } | ||||
|  | ||||
|     void Address::setIP(IP_t ip) { | ||||
|         addr.sin_addr.s_addr = htonl(ip); | ||||
|     } | ||||
|  | ||||
|     int Address::getPort() { | ||||
|         return htons(addr.sin_port); | ||||
|     } | ||||
|  | ||||
|     void Address::setPort(int port) { | ||||
|         addr.sin_port = htons(port); | ||||
|     } | ||||
|  | ||||
|     // === Socket functions === | ||||
|  | ||||
|     Socket::Socket(SockHandle_t sock, const Address* raddr) { | ||||
|         this->sock = sock; | ||||
|         if (raddr) { | ||||
|             this->raddr = new Address(*raddr); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Socket::~Socket() { | ||||
|         close(); | ||||
|         if (raddr) { delete raddr; } | ||||
|     } | ||||
|  | ||||
|     void Socket::close() { | ||||
|         if (!open) { return; } | ||||
|         open = false; | ||||
|         closeSocket(sock); | ||||
|     } | ||||
|  | ||||
|     bool Socket::isOpen() { | ||||
|         return open; | ||||
|     } | ||||
|  | ||||
|     SocketType Socket::type() { | ||||
|         return raddr ? SOCKET_TYPE_UDP : SOCKET_TYPE_TCP; | ||||
|     } | ||||
|  | ||||
|     int Socket::send(const uint8_t* data, size_t len, const Address* dest) { | ||||
|         return sendto(sock, (const char*)data, len, 0, (sockaddr*)(dest ? &dest->addr : (raddr ? &raddr->addr : NULL)), sizeof(sockaddr_in)); | ||||
|     } | ||||
|  | ||||
|     int Socket::sendstr(const std::string& str, const Address* dest) { | ||||
|         return send((const uint8_t*)str.c_str(), str.length(), dest); | ||||
|     } | ||||
|  | ||||
|     int Socket::recv(uint8_t* data, size_t maxLen, bool forceLen, int timeout, Address* dest) { | ||||
|         // Create FD set | ||||
|         fd_set set; | ||||
|         FD_ZERO(&set); | ||||
|         FD_SET(sock, &set); | ||||
|  | ||||
|         // Define timeout | ||||
|         timeval tv; | ||||
|         tv.tv_sec = 0; | ||||
|         tv.tv_usec = timeout * 1000; | ||||
|  | ||||
|         int read = 0; | ||||
|         bool blocking = (timeout != NONBLOCKING); | ||||
|         do { | ||||
|             // Wait for data or error if  | ||||
|             if (blocking) { | ||||
|                 int err = select(sock+1, &set, NULL, &set, (timeout > 0) ? &tv : NULL); | ||||
|                 if (err <= 0) { return err; } | ||||
|             } | ||||
|  | ||||
|             // Receive | ||||
|             int addrLen = sizeof(sockaddr_in); | ||||
|             int err = ::recvfrom(sock, (char*)&data[read], maxLen - read, 0,(sockaddr*)(dest ? &dest->addr : NULL), (socklen_t*)(dest ? &addrLen : NULL)); | ||||
|             if (err <= 0 && !WOULD_BLOCK) { | ||||
|                 close(); | ||||
|                 return err; | ||||
|             } | ||||
|             read += err; | ||||
|         } | ||||
|         while (blocking && forceLen && read < maxLen); | ||||
|         return read; | ||||
|     } | ||||
|  | ||||
|     int Socket::recvline(std::string& str, int maxLen, int timeout, Address* dest) { | ||||
|         // Disallow nonblocking mode | ||||
|         if (timeout < 0) { return -1; } | ||||
|          | ||||
|         str.clear(); | ||||
|         int read = 0; | ||||
|         while (true) { | ||||
|             char c; | ||||
|             int err = recv((uint8_t*)&c, 1, false, timeout, dest); | ||||
|             if (err <= 0) { return err; } | ||||
|             if (c == '\n') { break; } | ||||
|             str += c; | ||||
|             read++; | ||||
|             if (maxLen && read >= maxLen) { break; } | ||||
|         } | ||||
|         return read; | ||||
|     } | ||||
|  | ||||
|     // === Listener functions === | ||||
|  | ||||
|     Listener::Listener(SockHandle_t sock) { | ||||
|         this->sock = sock; | ||||
|     } | ||||
|  | ||||
|     Listener::~Listener() { | ||||
|         stop(); | ||||
|     } | ||||
|  | ||||
|     void Listener::stop() { | ||||
|         closeSocket(sock); | ||||
|         open = false; | ||||
|     } | ||||
|  | ||||
|     bool Listener::listening() { | ||||
|         return open; | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Socket> Listener::accept(Address* dest, int timeout) { | ||||
|         // Create FD set | ||||
|         fd_set set; | ||||
|         FD_ZERO(&set); | ||||
|         FD_SET(sock, &set); | ||||
|  | ||||
|         // Define timeout | ||||
|         timeval tv; | ||||
|         tv.tv_sec = 0; | ||||
|         tv.tv_usec = timeout * 1000; | ||||
|  | ||||
|         // Wait for data or error | ||||
|         if (timeout != NONBLOCKING) { | ||||
|             int err = select(sock+1, &set, NULL, &set, (timeout > 0) ? &tv : NULL); | ||||
|             if (err <= 0) { return NULL; } | ||||
|         } | ||||
|  | ||||
|         // Accept | ||||
|         int addrLen = sizeof(sockaddr_in); | ||||
|         SockHandle_t s = ::accept(sock, (sockaddr*)(dest ? &dest->addr : NULL), (socklen_t*)(dest ? &addrLen : NULL)); | ||||
|         if ((int)s < 0) { | ||||
|             if (!WOULD_BLOCK) { stop(); } | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         // Enable nonblocking mode | ||||
|         setNonblocking(s); | ||||
|  | ||||
|         return std::make_shared<Socket>(s); | ||||
|     } | ||||
|  | ||||
|     // === Creation functions === | ||||
|  | ||||
|     std::map<std::string, InterfaceInfo> listInterfaces() { | ||||
|         // Init library if needed | ||||
|         init(); | ||||
|  | ||||
|         std::map<std::string, InterfaceInfo> ifaces; | ||||
| #ifdef _WIN32 | ||||
|         // Pre-allocate buffer | ||||
|         ULONG size = sizeof(IP_ADAPTER_ADDRESSES); | ||||
|         PIP_ADAPTER_ADDRESSES addresses = (PIP_ADAPTER_ADDRESSES)malloc(size); | ||||
|  | ||||
|         // Reallocate to real size | ||||
|         if (GetAdaptersAddresses(AF_INET, 0, NULL, addresses, &size) == ERROR_BUFFER_OVERFLOW) { | ||||
|             addresses = (PIP_ADAPTER_ADDRESSES)realloc(addresses, size); | ||||
|             if (GetAdaptersAddresses(AF_INET, 0, NULL, addresses, &size)) { | ||||
|                 throw std::exception("Could not list network interfaces"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Save data | ||||
|         std::wstring_convert<std::codecvt_utf8<wchar_t>> utfConv; | ||||
|         for (auto iface = addresses; iface; iface = iface->Next) { | ||||
|             InterfaceInfo info; | ||||
|             auto ip = iface->FirstUnicastAddress; | ||||
|             if (!ip || ip->Address.lpSockaddr->sa_family != AF_INET) { continue; } | ||||
|             info.address = ntohl(*(uint32_t*)&ip->Address.lpSockaddr->sa_data[2]); | ||||
|             info.netmask = ~((1 << (32 - ip->OnLinkPrefixLength)) - 1); | ||||
|             info.broadcast = info.address | (~info.netmask); | ||||
|             ifaces[utfConv.to_bytes(iface->FriendlyName)] = info; | ||||
|         } | ||||
|          | ||||
|         // Free tables | ||||
|         free(addresses); | ||||
| #else | ||||
|         // Get iface list | ||||
|         struct ifaddrs* addresses = NULL; | ||||
|         getifaddrs(&addresses); | ||||
|  | ||||
|         // Save data | ||||
|         for (auto iface = addresses; iface; iface = iface->ifa_next) { | ||||
|             if (iface->ifa_addr->sa_family != AF_INET) { continue; } | ||||
|             InterfaceInfo info; | ||||
|             info.address = ntohl(*(uint32_t*)&iface->ifa_addr->sa_data[2]); | ||||
|             info.netmask = ntohl(*(uint32_t*)&iface->ifa_netmask->sa_data[2]); | ||||
|             info.broadcast = info.address | (~info.netmask); | ||||
|             ifaces[iface->ifa_name] = info; | ||||
|         } | ||||
|  | ||||
|         // Free iface list | ||||
|         freeifaddrs(addresses); | ||||
| #endif | ||||
|  | ||||
|         return ifaces; | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Listener> listen(const Address& addr) { | ||||
|         // Init library if needed | ||||
|         init(); | ||||
|  | ||||
|         // Create socket | ||||
|         SockHandle_t s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||||
|         // TODO: Support non-blockign mode | ||||
|  | ||||
| #ifndef _WIN32 | ||||
|         // Allow port reusing if the app was killed or crashed | ||||
|         // and the socket is stuck in TIME_WAIT state. | ||||
|         // This option has a different meaning on Windows, | ||||
|         // so we use it only for non-Windows systems | ||||
|         int enable = 1; | ||||
|         if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { | ||||
|             closeSocket(s); | ||||
|             throw std::runtime_error("Could not configure socket"); | ||||
|             return NULL; | ||||
|         } | ||||
| #endif | ||||
|  | ||||
|         // Bind socket to the port | ||||
|         if (bind(s, (sockaddr*)&addr.addr, sizeof(sockaddr_in))) { | ||||
|             closeSocket(s); | ||||
|             throw std::runtime_error("Could not bind socket"); | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         // Enable listening | ||||
|         if (::listen(s, SOMAXCONN) != 0) { | ||||
|             throw std::runtime_error("Could start listening for connections"); | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         // Enable nonblocking mode | ||||
|         setNonblocking(s); | ||||
|  | ||||
|         // Return listener class | ||||
|         return std::make_shared<Listener>(s); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Listener> listen(std::string host, int port) { | ||||
|         return listen(Address(host, port)); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Socket> connect(const Address& addr) { | ||||
|         // Init library if needed | ||||
|         init(); | ||||
|  | ||||
|         // Create socket | ||||
|         SockHandle_t s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||||
|  | ||||
|         // Connect to server | ||||
|         if (::connect(s, (sockaddr*)&addr.addr, sizeof(sockaddr_in))) { | ||||
|             closeSocket(s); | ||||
|             throw std::runtime_error("Could not connect"); | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         // Enable nonblocking mode | ||||
|         setNonblocking(s); | ||||
|  | ||||
|         // Return socket class | ||||
|         return std::make_shared<Socket>(s); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Socket> connect(std::string host, int port) { | ||||
|         return connect(Address(host, port)); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Socket> openudp(const Address& raddr, const Address& laddr) { | ||||
|         // Init library if needed | ||||
|         init(); | ||||
|  | ||||
|         // Create socket | ||||
|         SockHandle_t s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | ||||
|  | ||||
|         // Bind socket to local port | ||||
|         if (bind(s, (sockaddr*)&laddr.addr, sizeof(sockaddr_in))) { | ||||
|             closeSocket(s); | ||||
|             throw std::runtime_error("Could not bind socket"); | ||||
|             return NULL; | ||||
|         } | ||||
|          | ||||
|         // Return socket class | ||||
|         return std::make_shared<Socket>(s, &raddr); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Socket> openudp(std::string rhost, int rport, const Address& laddr) { | ||||
|         return openudp(Address(rhost, rport), laddr); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Socket> openudp(const Address& raddr, std::string lhost, int lport) { | ||||
|         return openudp(raddr, Address(lhost, lport)); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Socket> openudp(std::string rhost, int rport, std::string lhost, int lport) { | ||||
|         return openudp(Address(rhost, rport), Address(lhost, lport)); | ||||
|     } | ||||
| } | ||||
| @@ -1,281 +0,0 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include <mutex> | ||||
| #include <memory> | ||||
| #include <map> | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #include <WinSock2.h> | ||||
| #include <WS2tcpip.h> | ||||
| #include <iphlpapi.h> | ||||
| #else | ||||
| #include <unistd.h> | ||||
| #include <strings.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/socket.h> | ||||
| #include <netinet/in.h> | ||||
| #include <netdb.h> | ||||
| #include <signal.h> | ||||
| #include <poll.h> | ||||
| #include <fcntl.h> | ||||
| #include <ifaddrs.h> | ||||
| #endif | ||||
|  | ||||
| namespace net { | ||||
| #ifdef _WIN32 | ||||
|     typedef SOCKET SockHandle_t; | ||||
|     typedef int socklen_t; | ||||
| #else | ||||
|     typedef int SockHandle_t; | ||||
| #endif | ||||
|     typedef uint32_t IP_t; | ||||
|  | ||||
|     class Socket; | ||||
|     class Listener; | ||||
|  | ||||
|     struct InterfaceInfo { | ||||
|         IP_t address; | ||||
|         IP_t netmask; | ||||
|         IP_t broadcast; | ||||
|     }; | ||||
|  | ||||
|     class Address { | ||||
|         friend Socket; | ||||
|         friend Listener; | ||||
|     public: | ||||
|         /** | ||||
|          * Default constructor. Corresponds to 0.0.0.0:0. | ||||
|          */ | ||||
|         Address(); | ||||
|  | ||||
|         /** | ||||
|          * Do not instantiate this class manually. Use the provided functions. | ||||
|          * @param host Hostname or IP. | ||||
|          * @param port TCP/UDP port. | ||||
|          */ | ||||
|         Address(const std::string& host, int port); | ||||
|  | ||||
|         /** | ||||
|          * Do not instantiate this class manually. Use the provided functions. | ||||
|          * @param ip IP in host byte order. | ||||
|          * @param port TCP/UDP port. | ||||
|          */ | ||||
|         Address(IP_t ip, int port); | ||||
|  | ||||
|         /** | ||||
|          * Get the IP address. | ||||
|          * @return IP address in standard string format. | ||||
|          */ | ||||
|         std::string getIPStr(); | ||||
|  | ||||
|         /** | ||||
|          * Get the IP address. | ||||
|          * @return IP address in host byte order. | ||||
|          */ | ||||
|         IP_t getIP(); | ||||
|  | ||||
|         /** | ||||
|          * Set the IP address. | ||||
|          * @param ip IP address in host byte order. | ||||
|          */ | ||||
|         void setIP(IP_t ip); | ||||
|  | ||||
|         /** | ||||
|          * Get the TCP/UDP port. | ||||
|          * @return TCP/UDP port number. | ||||
|          */ | ||||
|         int getPort(); | ||||
|  | ||||
|         /** | ||||
|          * Set the TCP/UDP port. | ||||
|          * @param port TCP/UDP port number. | ||||
|          */ | ||||
|         void setPort(int port); | ||||
|  | ||||
|         struct sockaddr_in addr; | ||||
|     }; | ||||
|  | ||||
|     enum { | ||||
|         NO_TIMEOUT  = -1, | ||||
|         NONBLOCKING = 0 | ||||
|     }; | ||||
|  | ||||
|     enum SocketType { | ||||
|         SOCKET_TYPE_TCP, | ||||
|         SOCKET_TYPE_UDP | ||||
|     }; | ||||
|  | ||||
|     class Socket { | ||||
|     public: | ||||
|         /** | ||||
|          * Do not instantiate this class manually. Use the provided functions. | ||||
|          */ | ||||
|         Socket(SockHandle_t sock, const Address* raddr = NULL); | ||||
|         ~Socket(); | ||||
|  | ||||
|         /** | ||||
|          * Close socket. The socket can no longer be used after this. | ||||
|          */ | ||||
|         void close(); | ||||
|  | ||||
|         /** | ||||
|          * Check if the socket is open. | ||||
|          * @return True if open, false if closed. | ||||
|          */ | ||||
|         bool isOpen(); | ||||
|  | ||||
|         /** | ||||
|          * Get socket type. Either TCP or UDP. | ||||
|          * @return Socket type. | ||||
|          */ | ||||
|         SocketType type(); | ||||
|  | ||||
|         /** | ||||
|          * Send data on socket. | ||||
|          * @param data Data to be sent. | ||||
|          * @param len Number of bytes to be sent. | ||||
|          * @param dest Destination address. NULL to use the default remote address. | ||||
|          * @return Number of bytes sent. | ||||
|          */ | ||||
|         int send(const uint8_t* data, size_t len, const Address* dest = NULL); | ||||
|  | ||||
|         /** | ||||
|          * Send string on socket. Terminating NULL byte is not sent, include one in the string if you need it. | ||||
|          * @param str String to be sent. | ||||
|          * @param dest Destination address. NULL to use the default remote address. | ||||
|          * @return Number of bytes sent. | ||||
|          */ | ||||
|         int sendstr(const std::string& str, const Address* dest = NULL); | ||||
|  | ||||
|         /** | ||||
|          * Receive data from socket. | ||||
|          * @param data Buffer to read the data into. | ||||
|          * @param maxLen Maximum number of bytes to read. | ||||
|          * @param forceLen Read the maximum number of bytes even if it requires multiple receive operations. | ||||
|          * @param timeout Timeout in milliseconds. Use NO_TIMEOUT or NONBLOCKING here if needed. | ||||
|          * @param dest Destination address. If multiple packets, this will contain the address of the last one. NULL if not used. | ||||
|          * @return Number of bytes read. 0 means timed out or closed. -1 means would block or error. | ||||
|          */ | ||||
|         int recv(uint8_t* data, size_t maxLen, bool forceLen = false, int timeout = NO_TIMEOUT, Address* dest = NULL); | ||||
|  | ||||
|         /** | ||||
|          * Receive line from socket. | ||||
|          * @param str String to read the data into. | ||||
|          * @param maxLen Maximum line length allowed, 0 for no limit. | ||||
|          * @param timeout Timeout in milliseconds.  Use NO_TIMEOUT or NONBLOCKING here if needed. | ||||
|          * @param dest Destination address. If multiple packets, this will contain the address of the last one. NULL if not used. | ||||
|          * @return Length of the returned string. 0 means timed out or closed. -1 means would block or error. | ||||
|          */ | ||||
|         int recvline(std::string& str, int maxLen = 0, int timeout = NO_TIMEOUT, Address* dest = NULL); | ||||
|  | ||||
|     private: | ||||
|         Address* raddr = NULL; | ||||
|         SockHandle_t sock; | ||||
|         bool open = true; | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     class Listener { | ||||
|     public: | ||||
|         /** | ||||
|          * Do not instantiate this class manually. Use the provided functions. | ||||
|          */ | ||||
|         Listener(SockHandle_t sock); | ||||
|         ~Listener(); | ||||
|  | ||||
|         /** | ||||
|          * Stop listening. The listener can no longer be used after this. | ||||
|          */ | ||||
|         void stop(); | ||||
|  | ||||
|         /** | ||||
|          * CHeck if the listener is still listening. | ||||
|          * @return True if listening, false if not. | ||||
|          */ | ||||
|         bool listening(); | ||||
|  | ||||
|         /** | ||||
|          * Accept connection. | ||||
|          * @param timeout Timeout in milliseconds. Use NO_TIMEOUT or NONBLOCKING here if needed. | ||||
|          * @return Socket of the connection. NULL means timed out, would block or closed. | ||||
|          */ | ||||
|         std::shared_ptr<Socket> accept(Address* dest = NULL, int timeout = NO_TIMEOUT); | ||||
|  | ||||
|     private: | ||||
|         SockHandle_t sock; | ||||
|         bool open = true; | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Get a list of the network interface. | ||||
|      * @return List of network interfaces and their addresses. | ||||
|      */ | ||||
|     std::map<std::string, InterfaceInfo> listInterfaces(); | ||||
|  | ||||
|     /** | ||||
|      * Create TCP listener. | ||||
|      * @param addr Address to listen on. | ||||
|      * @return Listener instance on success, Throws runtime_error otherwise. | ||||
|      */ | ||||
|     std::shared_ptr<Listener> listen(const Address& addr); | ||||
|  | ||||
|     /** | ||||
|      * Create TCP listener. | ||||
|      * @param host Hostname or IP to listen on ("0.0.0.0" for Any). | ||||
|      * @param port Port to listen on. | ||||
|      * @return Listener instance on success, Throws runtime_error otherwise. | ||||
|      */ | ||||
|     std::shared_ptr<Listener> listen(std::string host, int port); | ||||
|  | ||||
|     /** | ||||
|      * Create TCP connection. | ||||
|      * @param addr Remote address. | ||||
|      * @return Socket instance on success, Throws runtime_error otherwise. | ||||
|      */ | ||||
|     std::shared_ptr<Socket> connect(const Address& addr);   | ||||
|  | ||||
|     /** | ||||
|      * Create TCP connection. | ||||
|      * @param host Remote hostname or IP address. | ||||
|      * @param port Remote port. | ||||
|      * @return Socket instance on success, Throws runtime_error otherwise. | ||||
|      */ | ||||
|     std::shared_ptr<Socket> connect(std::string host, int port);   | ||||
|  | ||||
|     /** | ||||
|      * Create UDP socket. | ||||
|      * @param raddr Remote address. | ||||
|      * @param laddr Local address to bind the socket to. | ||||
|      * @return Socket instance on success, Throws runtime_error otherwise. | ||||
|      */ | ||||
|     std::shared_ptr<Socket> openudp(const Address& raddr, const Address& laddr); | ||||
|  | ||||
|     /** | ||||
|      * Create UDP socket. | ||||
|      * @param rhost Remote hostname or IP address. | ||||
|      * @param rport Remote port. | ||||
|      * @param laddr Local address to bind the socket to. | ||||
|      * @return Socket instance on success, Throws runtime_error otherwise. | ||||
|      */ | ||||
|     std::shared_ptr<Socket> openudp(std::string rhost, int rport, const Address& laddr); | ||||
|  | ||||
|     /** | ||||
|      * Create UDP socket. | ||||
|      * @param raddr Remote address. | ||||
|      * @param lhost Local hostname or IP used to bind the socket (optional, "0.0.0.0" for Any). | ||||
|      * @param lpost Local port used to bind the socket to (optional, 0 to allocate automatically). | ||||
|      * @return Socket instance on success, Throws runtime_error otherwise. | ||||
|      */ | ||||
|     std::shared_ptr<Socket> openudp(const Address& raddr, std::string lhost = "0.0.0.0", int lport = 0); | ||||
|  | ||||
|     /** | ||||
|      * Create UDP socket. | ||||
|      * @param rhost Remote hostname or IP address. | ||||
|      * @param rport Remote port. | ||||
|      * @param lhost Local hostname or IP used to bind the socket (optional, "0.0.0.0" for Any). | ||||
|      * @param lpost Local port used to bind the socket to (optional, 0 to allocate automatically). | ||||
|      * @return Socket instance on success, Throws runtime_error otherwise. | ||||
|      */ | ||||
|     std::shared_ptr<Socket> openudp(std::string rhost, int rport, std::string lhost = "0.0.0.0", int lport = 0);   | ||||
| } | ||||
| @@ -1,402 +0,0 @@ | ||||
| #include "net.h" | ||||
| #include <string.h> | ||||
| #include <codecvt> | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #define WOULD_BLOCK (WSAGetLastError() == WSAEWOULDBLOCK) | ||||
| #else | ||||
| #define WOULD_BLOCK (errno == EWOULDBLOCK) | ||||
| #endif | ||||
|  | ||||
| namespace net { | ||||
|     bool _init = false; | ||||
|      | ||||
|     // === Private functions === | ||||
|  | ||||
|     void init() { | ||||
|         if (_init) { return; } | ||||
| #ifdef _WIN32 | ||||
|         // Initialize WinSock2 | ||||
|         WSADATA wsa; | ||||
|         if (WSAStartup(MAKEWORD(2, 2), &wsa)) { | ||||
|             throw std::runtime_error("Could not initialize WinSock2"); | ||||
|             return; | ||||
|         } | ||||
| #else | ||||
|         // Disable SIGPIPE to avoid closing when the remote host disconnects | ||||
|         signal(SIGPIPE, SIG_IGN); | ||||
| #endif | ||||
|         _init = true; | ||||
|     } | ||||
|  | ||||
|     bool queryHost(uint32_t* addr, std::string host) { | ||||
|         hostent* ent = gethostbyname(host.c_str()); | ||||
|         if (!ent || !ent->h_addr_list[0]) { return false; } | ||||
|         *addr = *(uint32_t*)ent->h_addr_list[0]; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     void closeSocket(SockHandle_t sock) { | ||||
| #ifdef _WIN32 | ||||
|         shutdown(sock, SD_BOTH); | ||||
|         closesocket(sock); | ||||
| #else | ||||
|         shutdown(sock, SHUT_RDWR); | ||||
|         close(sock); | ||||
| #endif | ||||
|     } | ||||
|  | ||||
|     void setNonblocking(SockHandle_t sock) { | ||||
| #ifdef _WIN32 | ||||
|         u_long enabled = 1; | ||||
|         ioctlsocket(sock, FIONBIO, &enabled); | ||||
| #else | ||||
|         fcntl(sock, F_SETFL, O_NONBLOCK); | ||||
| #endif | ||||
|     } | ||||
|  | ||||
|     // === Address functions === | ||||
|  | ||||
|     Address::Address() { | ||||
|         memset(&addr, 0, sizeof(addr)); | ||||
|     } | ||||
|  | ||||
|     Address::Address(const std::string& host, int port) { | ||||
|         // Initialize WSA if needed | ||||
|         init(); | ||||
|          | ||||
|         // Lookup host | ||||
|         hostent* ent = gethostbyname(host.c_str()); | ||||
|         if (!ent || !ent->h_addr_list[0]) { | ||||
|             throw std::runtime_error("Unknown host"); | ||||
|         } | ||||
|          | ||||
|         // Build address | ||||
|         memset(&addr, 0, sizeof(addr)); | ||||
|         addr.sin_family = AF_INET; | ||||
|         addr.sin_addr.s_addr = *(uint32_t*)ent->h_addr_list[0]; | ||||
|         addr.sin_port = htons(port); | ||||
|     } | ||||
|  | ||||
|     Address::Address(IP_t ip, int port) { | ||||
|         memset(&addr, 0, sizeof(addr)); | ||||
|         addr.sin_family = AF_INET; | ||||
|         addr.sin_addr.s_addr = htonl(ip); | ||||
|         addr.sin_port = htons(port); | ||||
|     } | ||||
|  | ||||
|     std::string Address::getIPStr() { | ||||
|         char buf[128]; | ||||
|         IP_t ip = getIP(); | ||||
|         sprintf(buf, "%d.%d.%d.%d", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF); | ||||
|         return buf; | ||||
|     } | ||||
|  | ||||
|     IP_t Address::getIP() { | ||||
|         return htonl(addr.sin_addr.s_addr); | ||||
|     } | ||||
|  | ||||
|     void Address::setIP(IP_t ip) { | ||||
|         addr.sin_addr.s_addr = htonl(ip); | ||||
|     } | ||||
|  | ||||
|     int Address::getPort() { | ||||
|         return htons(addr.sin_port); | ||||
|     } | ||||
|  | ||||
|     void Address::setPort(int port) { | ||||
|         addr.sin_port = htons(port); | ||||
|     } | ||||
|  | ||||
|     // === Socket functions === | ||||
|  | ||||
|     Socket::Socket(SockHandle_t sock, const Address* raddr) { | ||||
|         this->sock = sock; | ||||
|         if (raddr) { | ||||
|             this->raddr = new Address(*raddr); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Socket::~Socket() { | ||||
|         close(); | ||||
|         if (raddr) { delete raddr; } | ||||
|     } | ||||
|  | ||||
|     void Socket::close() { | ||||
|         if (!open) { return; } | ||||
|         open = false; | ||||
|         closeSocket(sock); | ||||
|     } | ||||
|  | ||||
|     bool Socket::isOpen() { | ||||
|         return open; | ||||
|     } | ||||
|  | ||||
|     SocketType Socket::type() { | ||||
|         return raddr ? SOCKET_TYPE_UDP : SOCKET_TYPE_TCP; | ||||
|     } | ||||
|  | ||||
|     int Socket::send(const uint8_t* data, size_t len, const Address* dest) { | ||||
|         return sendto(sock, (const char*)data, len, 0, (sockaddr*)(dest ? &dest->addr : (raddr ? &raddr->addr : NULL)), sizeof(sockaddr_in)); | ||||
|     } | ||||
|  | ||||
|     int Socket::sendstr(const std::string& str, const Address* dest) { | ||||
|         return send((const uint8_t*)str.c_str(), str.length(), dest); | ||||
|     } | ||||
|  | ||||
|     int Socket::recv(uint8_t* data, size_t maxLen, bool forceLen, int timeout, Address* dest) { | ||||
|         // Create FD set | ||||
|         fd_set set; | ||||
|         FD_ZERO(&set); | ||||
|         FD_SET(sock, &set); | ||||
|  | ||||
|         // Define timeout | ||||
|         timeval tv; | ||||
|         tv.tv_sec = 0; | ||||
|         tv.tv_usec = timeout * 1000; | ||||
|  | ||||
|         int read = 0; | ||||
|         bool blocking = (timeout != NONBLOCKING); | ||||
|         do { | ||||
|             // Wait for data or error if  | ||||
|             if (blocking) { | ||||
|                 int err = select(sock+1, &set, NULL, &set, (timeout > 0) ? &tv : NULL); | ||||
|                 if (err <= 0) { return err; } | ||||
|             } | ||||
|  | ||||
|             // Receive | ||||
|             int addrLen = sizeof(sockaddr_in); | ||||
|             int err = ::recvfrom(sock, (char*)&data[read], maxLen - read, 0,(sockaddr*)(dest ? &dest->addr : NULL), (socklen_t*)(dest ? &addrLen : NULL)); | ||||
|             if (err <= 0 && !WOULD_BLOCK) { | ||||
|                 close(); | ||||
|                 return err; | ||||
|             } | ||||
|             read += err; | ||||
|         } | ||||
|         while (blocking && forceLen && read < maxLen); | ||||
|         return read; | ||||
|     } | ||||
|  | ||||
|     int Socket::recvline(std::string& str, int maxLen, int timeout, Address* dest) { | ||||
|         // Disallow nonblocking mode | ||||
|         if (!timeout) { return -1; } | ||||
|          | ||||
|         str.clear(); | ||||
|         int read = 0; | ||||
|         while (!maxLen || read < maxLen) { | ||||
|             char c; | ||||
|             int err = recv((uint8_t*)&c, 1, false, timeout, dest); | ||||
|             if (err <= 0) { return err; } | ||||
|             read++; | ||||
|             if (c == '\n') { break; } | ||||
|             str += c; | ||||
|         } | ||||
|         return read; | ||||
|     } | ||||
|  | ||||
|     // === Listener functions === | ||||
|  | ||||
|     Listener::Listener(SockHandle_t sock) { | ||||
|         this->sock = sock; | ||||
|     } | ||||
|  | ||||
|     Listener::~Listener() { | ||||
|         stop(); | ||||
|     } | ||||
|  | ||||
|     void Listener::stop() { | ||||
|         closeSocket(sock); | ||||
|         open = false; | ||||
|     } | ||||
|  | ||||
|     bool Listener::listening() { | ||||
|         return open; | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Socket> Listener::accept(Address* dest, int timeout) { | ||||
|         // Create FD set | ||||
|         fd_set set; | ||||
|         FD_ZERO(&set); | ||||
|         FD_SET(sock, &set); | ||||
|  | ||||
|         // Define timeout | ||||
|         timeval tv; | ||||
|         tv.tv_sec = 0; | ||||
|         tv.tv_usec = timeout * 1000; | ||||
|  | ||||
|         // Wait for data or error | ||||
|         if (timeout != NONBLOCKING) { | ||||
|             int err = select(sock+1, &set, NULL, &set, (timeout > 0) ? &tv : NULL); | ||||
|             if (err <= 0) { return NULL; } | ||||
|         } | ||||
|  | ||||
|         // Accept | ||||
|         int addrLen = sizeof(sockaddr_in); | ||||
|         SockHandle_t s = ::accept(sock, (sockaddr*)(dest ? &dest->addr : NULL), (socklen_t*)(dest ? &addrLen : NULL)); | ||||
|         if ((int)s < 0) { | ||||
|             if (!WOULD_BLOCK) { stop(); } | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         // Enable nonblocking mode | ||||
|         setNonblocking(s); | ||||
|  | ||||
|         return std::make_shared<Socket>(s); | ||||
|     } | ||||
|  | ||||
|     // === Creation functions === | ||||
|  | ||||
|     std::map<std::string, InterfaceInfo> listInterfaces() { | ||||
|         // Init library if needed | ||||
|         init(); | ||||
|  | ||||
|         std::map<std::string, InterfaceInfo> ifaces; | ||||
| #ifdef _WIN32 | ||||
|         // Pre-allocate buffer | ||||
|         ULONG size = sizeof(IP_ADAPTER_ADDRESSES); | ||||
|         PIP_ADAPTER_ADDRESSES addresses = (PIP_ADAPTER_ADDRESSES)malloc(size); | ||||
|  | ||||
|         // Reallocate to real size | ||||
|         if (GetAdaptersAddresses(AF_INET, 0, NULL, addresses, &size) == ERROR_BUFFER_OVERFLOW) { | ||||
|             addresses = (PIP_ADAPTER_ADDRESSES)realloc(addresses, size); | ||||
|             if (GetAdaptersAddresses(AF_INET, 0, NULL, addresses, &size)) { | ||||
|                 throw std::exception("Could not list network interfaces"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Save data | ||||
|         std::wstring_convert<std::codecvt_utf8<wchar_t>> utfConv; | ||||
|         for (auto iface = addresses; iface; iface = iface->Next) { | ||||
|             InterfaceInfo info; | ||||
|             auto ip = iface->FirstUnicastAddress; | ||||
|             if (!ip || ip->Address.lpSockaddr->sa_family != AF_INET) { continue; } | ||||
|             info.address = ntohl(*(uint32_t*)&ip->Address.lpSockaddr->sa_data[2]); | ||||
|             info.netmask = ~((1 << (32 - ip->OnLinkPrefixLength)) - 1); | ||||
|             info.broadcast = info.address | (~info.netmask); | ||||
|             ifaces[utfConv.to_bytes(iface->FriendlyName)] = info; | ||||
|         } | ||||
|          | ||||
|         // Free tables | ||||
|         free(addresses); | ||||
| #else | ||||
|         // Get iface list | ||||
|         struct ifaddrs* addresses = NULL; | ||||
|         getifaddrs(&addresses); | ||||
|  | ||||
|         // Save data | ||||
|         for (auto iface = addresses; iface; iface = iface->ifa_next) { | ||||
|             if (iface->ifa_addr->sa_family != AF_INET) { continue; } | ||||
|             InterfaceInfo info; | ||||
|             info.address = ntohl(*(uint32_t*)&iface->ifa_addr->sa_data[2]); | ||||
|             info.netmask = ntohl(*(uint32_t*)&iface->ifa_netmask->sa_data[2]); | ||||
|             info.broadcast = info.address | (~info.netmask); | ||||
|             ifaces[iface->ifa_name] = info; | ||||
|         } | ||||
|  | ||||
|         // Free iface list | ||||
|         freeifaddrs(addresses); | ||||
| #endif | ||||
|  | ||||
|         return ifaces; | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Listener> listen(const Address& addr) { | ||||
|         // Init library if needed | ||||
|         init(); | ||||
|  | ||||
|         // Create socket | ||||
|         SockHandle_t s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||||
|         // TODO: Support non-blockign mode | ||||
|  | ||||
| #ifndef _WIN32 | ||||
|         // Allow port reusing if the app was killed or crashed | ||||
|         // and the socket is stuck in TIME_WAIT state. | ||||
|         // This option has a different meaning on Windows, | ||||
|         // so we use it only for non-Windows systems | ||||
|         int enable = 1; | ||||
|         if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { | ||||
|             closeSocket(s); | ||||
|             throw std::runtime_error("Could not configure socket"); | ||||
|             return NULL; | ||||
|         } | ||||
| #endif | ||||
|  | ||||
|         // Bind socket to the port | ||||
|         if (bind(s, (sockaddr*)&addr.addr, sizeof(sockaddr_in))) { | ||||
|             closeSocket(s); | ||||
|             throw std::runtime_error("Could not bind socket"); | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         // Enable listening | ||||
|         if (::listen(s, SOMAXCONN) != 0) { | ||||
|             throw std::runtime_error("Could start listening for connections"); | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         // Enable nonblocking mode | ||||
|         setNonblocking(s); | ||||
|  | ||||
|         // Return listener class | ||||
|         return std::make_shared<Listener>(s); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Listener> listen(std::string host, int port) { | ||||
|         return listen(Address(host, port)); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Socket> connect(const Address& addr) { | ||||
|         // Init library if needed | ||||
|         init(); | ||||
|  | ||||
|         // Create socket | ||||
|         SockHandle_t s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||||
|  | ||||
|         // Connect to server | ||||
|         if (::connect(s, (sockaddr*)&addr.addr, sizeof(sockaddr_in))) { | ||||
|             closeSocket(s); | ||||
|             throw std::runtime_error("Could not connect"); | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         // Enable nonblocking mode | ||||
|         setNonblocking(s); | ||||
|  | ||||
|         // Return socket class | ||||
|         return std::make_shared<Socket>(s); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Socket> connect(std::string host, int port) { | ||||
|         return connect(Address(host, port)); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Socket> openudp(const Address& raddr, const Address& laddr) { | ||||
|         // Init library if needed | ||||
|         init(); | ||||
|  | ||||
|         // Create socket | ||||
|         SockHandle_t s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | ||||
|  | ||||
|         // Bind socket to local port | ||||
|         if (bind(s, (sockaddr*)&laddr.addr, sizeof(sockaddr_in))) { | ||||
|             closeSocket(s); | ||||
|             throw std::runtime_error("Could not bind socket"); | ||||
|             return NULL; | ||||
|         } | ||||
|          | ||||
|         // Return socket class | ||||
|         return std::make_shared<Socket>(s, &raddr); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Socket> openudp(std::string rhost, int rport, const Address& laddr) { | ||||
|         return openudp(Address(rhost, rport), laddr); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Socket> openudp(const Address& raddr, std::string lhost, int lport) { | ||||
|         return openudp(raddr, Address(lhost, lport)); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Socket> openudp(std::string rhost, int rport, std::string lhost, int lport) { | ||||
|         return openudp(Address(rhost, rport), Address(lhost, lport)); | ||||
|     } | ||||
| } | ||||
| @@ -1,281 +0,0 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include <mutex> | ||||
| #include <memory> | ||||
| #include <map> | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #include <WinSock2.h> | ||||
| #include <WS2tcpip.h> | ||||
| #include <iphlpapi.h> | ||||
| #else | ||||
| #include <unistd.h> | ||||
| #include <strings.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/socket.h> | ||||
| #include <netinet/in.h> | ||||
| #include <netdb.h> | ||||
| #include <signal.h> | ||||
| #include <poll.h> | ||||
| #include <fcntl.h> | ||||
| #include <ifaddrs.h> | ||||
| #endif | ||||
|  | ||||
| namespace net { | ||||
| #ifdef _WIN32 | ||||
|     typedef SOCKET SockHandle_t; | ||||
|     typedef int socklen_t; | ||||
| #else | ||||
|     typedef int SockHandle_t; | ||||
| #endif | ||||
|     typedef uint32_t IP_t; | ||||
|  | ||||
|     class Socket; | ||||
|     class Listener; | ||||
|  | ||||
|     struct InterfaceInfo { | ||||
|         IP_t address; | ||||
|         IP_t netmask; | ||||
|         IP_t broadcast; | ||||
|     }; | ||||
|  | ||||
|     class Address { | ||||
|         friend Socket; | ||||
|         friend Listener; | ||||
|     public: | ||||
|         /** | ||||
|          * Default constructor. Corresponds to 0.0.0.0:0. | ||||
|          */ | ||||
|         Address(); | ||||
|  | ||||
|         /** | ||||
|          * Do not instantiate this class manually. Use the provided functions. | ||||
|          * @param host Hostname or IP. | ||||
|          * @param port TCP/UDP port. | ||||
|          */ | ||||
|         Address(const std::string& host, int port); | ||||
|  | ||||
|         /** | ||||
|          * Do not instantiate this class manually. Use the provided functions. | ||||
|          * @param ip IP in host byte order. | ||||
|          * @param port TCP/UDP port. | ||||
|          */ | ||||
|         Address(IP_t ip, int port); | ||||
|  | ||||
|         /** | ||||
|          * Get the IP address. | ||||
|          * @return IP address in standard string format. | ||||
|          */ | ||||
|         std::string getIPStr(); | ||||
|  | ||||
|         /** | ||||
|          * Get the IP address. | ||||
|          * @return IP address in host byte order. | ||||
|          */ | ||||
|         IP_t getIP(); | ||||
|  | ||||
|         /** | ||||
|          * Set the IP address. | ||||
|          * @param ip IP address in host byte order. | ||||
|          */ | ||||
|         void setIP(IP_t ip); | ||||
|  | ||||
|         /** | ||||
|          * Get the TCP/UDP port. | ||||
|          * @return TCP/UDP port number. | ||||
|          */ | ||||
|         int getPort(); | ||||
|  | ||||
|         /** | ||||
|          * Set the TCP/UDP port. | ||||
|          * @param port TCP/UDP port number. | ||||
|          */ | ||||
|         void setPort(int port); | ||||
|  | ||||
|         struct sockaddr_in addr; | ||||
|     }; | ||||
|  | ||||
|     enum { | ||||
|         NO_TIMEOUT  = -1, | ||||
|         NONBLOCKING = 0 | ||||
|     }; | ||||
|  | ||||
|     enum SocketType { | ||||
|         SOCKET_TYPE_TCP, | ||||
|         SOCKET_TYPE_UDP | ||||
|     }; | ||||
|  | ||||
|     class Socket { | ||||
|     public: | ||||
|         /** | ||||
|          * Do not instantiate this class manually. Use the provided functions. | ||||
|          */ | ||||
|         Socket(SockHandle_t sock, const Address* raddr = NULL); | ||||
|         ~Socket(); | ||||
|  | ||||
|         /** | ||||
|          * Close socket. The socket can no longer be used after this. | ||||
|          */ | ||||
|         void close(); | ||||
|  | ||||
|         /** | ||||
|          * Check if the socket is open. | ||||
|          * @return True if open, false if closed. | ||||
|          */ | ||||
|         bool isOpen(); | ||||
|  | ||||
|         /** | ||||
|          * Get socket type. Either TCP or UDP. | ||||
|          * @return Socket type. | ||||
|          */ | ||||
|         SocketType type(); | ||||
|  | ||||
|         /** | ||||
|          * Send data on socket. | ||||
|          * @param data Data to be sent. | ||||
|          * @param len Number of bytes to be sent. | ||||
|          * @param dest Destination address. NULL to use the default remote address. | ||||
|          * @return Number of bytes sent. | ||||
|          */ | ||||
|         int send(const uint8_t* data, size_t len, const Address* dest = NULL); | ||||
|  | ||||
|         /** | ||||
|          * Send string on socket. Terminating NULL byte is not sent, include one in the string if you need it. | ||||
|          * @param str String to be sent. | ||||
|          * @param dest Destination address. NULL to use the default remote address. | ||||
|          * @return Number of bytes sent. | ||||
|          */ | ||||
|         int sendstr(const std::string& str, const Address* dest = NULL); | ||||
|  | ||||
|         /** | ||||
|          * Receive data from socket. | ||||
|          * @param data Buffer to read the data into. | ||||
|          * @param maxLen Maximum number of bytes to read. | ||||
|          * @param forceLen Read the maximum number of bytes even if it requires multiple receive operations. | ||||
|          * @param timeout Timeout in milliseconds. Use NO_TIMEOUT or NONBLOCKING here if needed. | ||||
|          * @param dest Destination address. If multiple packets, this will contain the address of the last one. NULL if not used. | ||||
|          * @return Number of bytes read. 0 means timed out or closed. -1 means would block or error. | ||||
|          */ | ||||
|         int recv(uint8_t* data, size_t maxLen, bool forceLen = false, int timeout = NO_TIMEOUT, Address* dest = NULL); | ||||
|  | ||||
|         /** | ||||
|          * Receive line from socket. | ||||
|          * @param str String to read the data into. | ||||
|          * @param maxLen Maximum line length allowed, 0 for no limit. | ||||
|          * @param timeout Timeout in milliseconds.  Use NO_TIMEOUT or NONBLOCKING here if needed. | ||||
|          * @param dest Destination address. If multiple packets, this will contain the address of the last one. NULL if not used. | ||||
|          * @return Length of the returned string. 0 means timed out or closed. -1 means would block or error. | ||||
|          */ | ||||
|         int recvline(std::string& str, int maxLen = 0, int timeout = NO_TIMEOUT, Address* dest = NULL); | ||||
|  | ||||
|     private: | ||||
|         Address* raddr = NULL; | ||||
|         SockHandle_t sock; | ||||
|         bool open = true; | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     class Listener { | ||||
|     public: | ||||
|         /** | ||||
|          * Do not instantiate this class manually. Use the provided functions. | ||||
|          */ | ||||
|         Listener(SockHandle_t sock); | ||||
|         ~Listener(); | ||||
|  | ||||
|         /** | ||||
|          * Stop listening. The listener can no longer be used after this. | ||||
|          */ | ||||
|         void stop(); | ||||
|  | ||||
|         /** | ||||
|          * CHeck if the listener is still listening. | ||||
|          * @return True if listening, false if not. | ||||
|          */ | ||||
|         bool listening(); | ||||
|  | ||||
|         /** | ||||
|          * Accept connection. | ||||
|          * @param timeout Timeout in milliseconds. Use NO_TIMEOUT or NONBLOCKING here if needed. | ||||
|          * @return Socket of the connection. NULL means timed out, would block or closed. | ||||
|          */ | ||||
|         std::shared_ptr<Socket> accept(Address* dest = NULL, int timeout = NO_TIMEOUT); | ||||
|  | ||||
|     private: | ||||
|         SockHandle_t sock; | ||||
|         bool open = true; | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Get a list of the network interface. | ||||
|      * @return List of network interfaces and their addresses. | ||||
|      */ | ||||
|     std::map<std::string, InterfaceInfo> listInterfaces(); | ||||
|  | ||||
|     /** | ||||
|      * Create TCP listener. | ||||
|      * @param addr Address to listen on. | ||||
|      * @return Listener instance on success, Throws runtime_error otherwise. | ||||
|      */ | ||||
|     std::shared_ptr<Listener> listen(const Address& addr); | ||||
|  | ||||
|     /** | ||||
|      * Create TCP listener. | ||||
|      * @param host Hostname or IP to listen on ("0.0.0.0" for Any). | ||||
|      * @param port Port to listen on. | ||||
|      * @return Listener instance on success, Throws runtime_error otherwise. | ||||
|      */ | ||||
|     std::shared_ptr<Listener> listen(std::string host, int port); | ||||
|  | ||||
|     /** | ||||
|      * Create TCP connection. | ||||
|      * @param addr Remote address. | ||||
|      * @return Socket instance on success, Throws runtime_error otherwise. | ||||
|      */ | ||||
|     std::shared_ptr<Socket> connect(const Address& addr);   | ||||
|  | ||||
|     /** | ||||
|      * Create TCP connection. | ||||
|      * @param host Remote hostname or IP address. | ||||
|      * @param port Remote port. | ||||
|      * @return Socket instance on success, Throws runtime_error otherwise. | ||||
|      */ | ||||
|     std::shared_ptr<Socket> connect(std::string host, int port);   | ||||
|  | ||||
|     /** | ||||
|      * Create UDP socket. | ||||
|      * @param raddr Remote address. | ||||
|      * @param laddr Local address to bind the socket to. | ||||
|      * @return Socket instance on success, Throws runtime_error otherwise. | ||||
|      */ | ||||
|     std::shared_ptr<Socket> openudp(const Address& raddr, const Address& laddr); | ||||
|  | ||||
|     /** | ||||
|      * Create UDP socket. | ||||
|      * @param rhost Remote hostname or IP address. | ||||
|      * @param rport Remote port. | ||||
|      * @param laddr Local address to bind the socket to. | ||||
|      * @return Socket instance on success, Throws runtime_error otherwise. | ||||
|      */ | ||||
|     std::shared_ptr<Socket> openudp(std::string rhost, int rport, const Address& laddr); | ||||
|  | ||||
|     /** | ||||
|      * Create UDP socket. | ||||
|      * @param raddr Remote address. | ||||
|      * @param lhost Local hostname or IP used to bind the socket (optional, "0.0.0.0" for Any). | ||||
|      * @param lpost Local port used to bind the socket to (optional, 0 to allocate automatically). | ||||
|      * @return Socket instance on success, Throws runtime_error otherwise. | ||||
|      */ | ||||
|     std::shared_ptr<Socket> openudp(const Address& raddr, std::string lhost = "0.0.0.0", int lport = 0); | ||||
|  | ||||
|     /** | ||||
|      * Create UDP socket. | ||||
|      * @param rhost Remote hostname or IP address. | ||||
|      * @param rport Remote port. | ||||
|      * @param lhost Local hostname or IP used to bind the socket (optional, "0.0.0.0" for Any). | ||||
|      * @param lpost Local port used to bind the socket to (optional, 0 to allocate automatically). | ||||
|      * @return Socket instance on success, Throws runtime_error otherwise. | ||||
|      */ | ||||
|     std::shared_ptr<Socket> openudp(std::string rhost, int rport, std::string lhost = "0.0.0.0", int lport = 0);   | ||||
| } | ||||
| @@ -1,304 +0,0 @@ | ||||
| #include "http.h" | ||||
| #include <inttypes.h> | ||||
|  | ||||
| namespace net::http { | ||||
|     std::string MessageHeader::serialize() { | ||||
|         std::string data; | ||||
|  | ||||
|         // Add start line | ||||
|         data += serializeStartLine() + "\r\n"; | ||||
|  | ||||
|         // Add fields | ||||
|         for (const auto& [key, value] : fields) { | ||||
|             data += key + ": " + value + "\r\n"; | ||||
|         } | ||||
|  | ||||
|         // Add terminator | ||||
|         data += "\r\n"; | ||||
|  | ||||
|         return data; | ||||
|     } | ||||
|  | ||||
|     void MessageHeader::deserialize(const std::string& data) { | ||||
|         // Clear existing fields | ||||
|         fields.clear(); | ||||
|  | ||||
|         // Parse first line | ||||
|         std::string line; | ||||
|         int offset = readLine(data, line); | ||||
|         deserializeStartLine(line); | ||||
|  | ||||
|         // Parse fields | ||||
|         while (offset < data.size()) { | ||||
|             // Read line | ||||
|             offset = readLine(data, line, offset); | ||||
|  | ||||
|             // If empty line, the header is done | ||||
|             if (line.empty()) { break; } | ||||
|  | ||||
|             // Read until first ':' for the key | ||||
|             int klen = 0; | ||||
|             for (; klen < line.size(); klen++) { | ||||
|                 if (line[klen] == ':') { break; } | ||||
|             } | ||||
|              | ||||
|             // Find offset of value | ||||
|             int voff = klen + 1; | ||||
|             for (; voff < line.size(); voff++) { | ||||
|                 if (line[voff] != ' ' && line[voff] != '\t') { break; } | ||||
|             } | ||||
|  | ||||
|             // Save field | ||||
|             fields[line.substr(0, klen)] = line.substr(voff); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     std::map<std::string, std::string>& MessageHeader::getFields() { | ||||
|         return fields; | ||||
|     } | ||||
|  | ||||
|     bool MessageHeader::hasField(const std::string& name) { | ||||
|         return fields.find(name) != fields.end(); | ||||
|     } | ||||
|  | ||||
|     std::string MessageHeader::getField(const std::string name) { | ||||
|         // TODO: Check if exists | ||||
|         return fields[name]; | ||||
|     } | ||||
|  | ||||
|     void MessageHeader::setField(const std::string& name, const std::string& value) { | ||||
|         fields[name] = value; | ||||
|     } | ||||
|  | ||||
|     void MessageHeader::clearField(const std::string& name) { | ||||
|         // TODO: Check if exists (but maybe no error?) | ||||
|         fields.erase(name); | ||||
|     } | ||||
|  | ||||
|     int MessageHeader::readLine(const std::string& str, std::string& line, int start) { | ||||
|         // Get line length | ||||
|         int len = 0; | ||||
|         bool cr = false; | ||||
|         for (int i = start; i < str.size(); i++) { | ||||
|             if (str[i] == '\n') { | ||||
|                 if (len && str[i-1] == '\r') { cr = true; } | ||||
|                 break; | ||||
|             } | ||||
|             len++; | ||||
|         } | ||||
|  | ||||
|         // Copy line | ||||
|         line = str.substr(start, len - (cr ? 1:0)); | ||||
|         return start + len + 1; | ||||
|     } | ||||
|  | ||||
|     RequestHeader::RequestHeader(Method method, std::string uri, std::string host) { | ||||
|         this->method = method; | ||||
|         this->uri = uri; | ||||
|         setField("Host", host); | ||||
|     } | ||||
|  | ||||
|     RequestHeader::RequestHeader(const std::string& data) { | ||||
|         deserialize(data); | ||||
|     } | ||||
|  | ||||
|     Method RequestHeader::getMethod() { | ||||
|         return method; | ||||
|     } | ||||
|  | ||||
|     void RequestHeader::setMethod(Method method) { | ||||
|         this->method = method; | ||||
|     } | ||||
|  | ||||
|     std::string RequestHeader::getURI() { | ||||
|         return uri; | ||||
|     } | ||||
|  | ||||
|     void RequestHeader::setURI(const std::string& uri) { | ||||
|         this->uri = uri; | ||||
|     } | ||||
|  | ||||
|     void RequestHeader::deserializeStartLine(const std::string& data) { | ||||
|         // TODO | ||||
|     } | ||||
|  | ||||
|     std::string RequestHeader::serializeStartLine() { | ||||
|         // TODO: Allow to specify version | ||||
|         return MethodStrings[method] + " " + uri + " HTTP/1.1"; | ||||
|     } | ||||
|  | ||||
|     ResponseHeader::ResponseHeader(StatusCode statusCode) { | ||||
|         this->statusCode = statusCode; | ||||
|         if (StatusCodeStrings.find(statusCode) != StatusCodeStrings.end()) { | ||||
|             this->statusString = StatusCodeStrings[statusCode]; | ||||
|         } | ||||
|         else { | ||||
|             this->statusString = "UNKNOWN"; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     ResponseHeader::ResponseHeader(StatusCode statusCode, const std::string& statusString) { | ||||
|         this->statusCode = statusCode; | ||||
|         this->statusString = statusString; | ||||
|     } | ||||
|  | ||||
|     ResponseHeader::ResponseHeader(const std::string& data) { | ||||
|         deserialize(data); | ||||
|     } | ||||
|  | ||||
|     StatusCode ResponseHeader::getStatusCode() { | ||||
|         return statusCode; | ||||
|     } | ||||
|  | ||||
|     void ResponseHeader::setStatusCode(StatusCode statusCode) { | ||||
|         this->statusCode = statusCode; | ||||
|     } | ||||
|  | ||||
|     std::string ResponseHeader::getStatusString() { | ||||
|         return statusString; | ||||
|     } | ||||
|  | ||||
|     void ResponseHeader::setStatusString(const std::string& statusString) { | ||||
|         this->statusString = statusString; | ||||
|     } | ||||
|  | ||||
|     void ResponseHeader::deserializeStartLine(const std::string& data) { | ||||
|         // Parse version | ||||
|         int offset = 0; | ||||
|         for (; offset < data.size(); offset++) { | ||||
|             if (data[offset] == ' ') { break; } | ||||
|         } | ||||
|         // TODO: Error if null length | ||||
|         // TODO: Parse version | ||||
|  | ||||
|         // Skip spaces | ||||
|         for (; offset < data.size(); offset++) { | ||||
|             if (data[offset] != ' ' && data[offset] != '\t') { break; } | ||||
|         } | ||||
|          | ||||
|         // Parse status code | ||||
|         int codeOffset = offset; | ||||
|         for (; offset < data.size(); offset++) { | ||||
|             if (data[offset] == ' ') { break; } | ||||
|         } | ||||
|         // TODO: Error if null length | ||||
|         statusCode = (StatusCode)std::stoi(data.substr(codeOffset, codeOffset - offset)); | ||||
|      | ||||
|         // Skip spaces | ||||
|         for (; offset < data.size(); offset++) { | ||||
|             if (data[offset] != ' ' && data[offset] != '\t') { break; } | ||||
|         } | ||||
|          | ||||
|         // Parse status string | ||||
|         int stringOffset = offset; | ||||
|         for (; offset < data.size(); offset++) { | ||||
|             if (data[offset] == ' ') { break; } | ||||
|         } | ||||
|         // TODO: Error if null length (maybe?) | ||||
|         statusString = data.substr(stringOffset, stringOffset - offset); | ||||
|     } | ||||
|  | ||||
|     std::string ResponseHeader::serializeStartLine() { | ||||
|         char buf[1024]; | ||||
|         sprintf(buf, "%d %s", (int)statusCode, statusString.c_str()); | ||||
|         return buf; | ||||
|     } | ||||
|  | ||||
|     ChunkHeader::ChunkHeader(size_t length) { | ||||
|         this->length = length; | ||||
|     } | ||||
|  | ||||
|     ChunkHeader::ChunkHeader(const std::string& data) { | ||||
|         deserialize(data); | ||||
|     } | ||||
|  | ||||
|     std::string ChunkHeader::serialize() { | ||||
|         char buf[64]; | ||||
|         sprintf(buf, "%" PRIX64 "\r\n", length); | ||||
|         return buf; | ||||
|     } | ||||
|  | ||||
|     void ChunkHeader::deserialize(const std::string& data) { | ||||
|         // Parse length | ||||
|         int offset = 0; | ||||
|         for (; offset < data.size(); offset++) { | ||||
|             if (data[offset] == ' ') { break; } | ||||
|         } | ||||
|         // TODO: Error if null length | ||||
|         length = (StatusCode)std::stoull(data.substr(0, offset), nullptr, 16); | ||||
|  | ||||
|         // TODO: Parse rest | ||||
|     } | ||||
|  | ||||
|     size_t ChunkHeader::getLength() { | ||||
|         return length; | ||||
|     } | ||||
|  | ||||
|     void ChunkHeader::setLength(size_t length) { | ||||
|         this->length = length; | ||||
|     } | ||||
|  | ||||
|     Client::Client(std::shared_ptr<Socket> sock) { | ||||
|         this->sock = sock; | ||||
|     } | ||||
|  | ||||
|     int Client::sendRequestHeader(RequestHeader& req) { | ||||
|         return sock->sendstr(req.serialize()); | ||||
|     } | ||||
|  | ||||
|     int Client::recvRequestHeader(RequestHeader& req, int timeout) { | ||||
|         // Non-blocking mode not alloowed | ||||
|         if (!timeout) { return -1; } | ||||
|  | ||||
|         // Read response | ||||
|         std::string respData; | ||||
|         int err = recvHeader(respData, timeout); | ||||
|         if (err) { return err; } | ||||
|  | ||||
|         // Deserialize | ||||
|         req.deserialize(respData); | ||||
|     } | ||||
|  | ||||
|     int Client::sendResponseHeader(ResponseHeader& resp) { | ||||
|         return sock->sendstr(resp.serialize()); | ||||
|     } | ||||
|  | ||||
|     int Client::recvResponseHeader(ResponseHeader& resp, int timeout) { | ||||
|         // Non-blocking mode not alloowed | ||||
|         if (!timeout) { return -1; } | ||||
|  | ||||
|         // Read response | ||||
|         std::string respData; | ||||
|         int err = recvHeader(respData, timeout); | ||||
|         if (err) { return err; } | ||||
|  | ||||
|         // Deserialize | ||||
|         resp.deserialize(respData); | ||||
|     } | ||||
|  | ||||
|     int Client::sendChunkHeader(ChunkHeader& chdr) { | ||||
|         return sock->sendstr(chdr.serialize()); | ||||
|     } | ||||
|  | ||||
|     int Client::recvChunkHeader(ChunkHeader& chdr, int timeout) { | ||||
|         std::string respData; | ||||
|         int err = sock->recvline(respData, 0, timeout); | ||||
|         if (err <= 0) { return err; } | ||||
|         if (respData[respData.size()-1] == '\r') { | ||||
|             respData.pop_back(); | ||||
|         } | ||||
|         chdr.deserialize(respData); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     int Client::recvHeader(std::string& data, int timeout) { | ||||
|         while (sock->isOpen()) { | ||||
|             std::string line; | ||||
|             int ret = sock->recvline(line); | ||||
|             if (line == "\r") { break; } | ||||
|             if (ret <= 0) { return ret; } | ||||
|             data += line + "\n"; | ||||
|         } | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| @@ -1,276 +0,0 @@ | ||||
| #pragma once | ||||
| #include <map> | ||||
| #include <string> | ||||
| #include "../net.h" | ||||
|  | ||||
| namespace net::http { | ||||
|     enum Method { | ||||
|         METHOD_OPTIONS, | ||||
|         METHOD_GET, | ||||
|         METHOD_HEAD, | ||||
|         METHOD_POST, | ||||
|         METHOD_PUT, | ||||
|         METHOD_DELETE, | ||||
|         METHOD_TRACE, | ||||
|         METHOD_CONNECT | ||||
|     }; | ||||
|  | ||||
|     inline std::map<Method, std::string> MethodStrings { | ||||
|         { METHOD_OPTIONS,   "OPTIONS" }, | ||||
|         { METHOD_GET,       "GET"     }, | ||||
|         { METHOD_HEAD,      "HEAD"    }, | ||||
|         { METHOD_POST,      "POST"    }, | ||||
|         { METHOD_PUT,       "PUT"     }, | ||||
|         { METHOD_DELETE,    "DELETE"  }, | ||||
|         { METHOD_TRACE,     "TRACE"   }, | ||||
|         { METHOD_CONNECT,   "CONNECT" } | ||||
|     }; | ||||
|  | ||||
|     enum StatusCode { | ||||
|         STATUS_CODE_CONTINUE                    = 100, | ||||
|         STATUS_CODE_SWITCH_PROTO                = 101, | ||||
|  | ||||
|         STATUS_CODE_OK                          = 200, | ||||
|         STATUS_CODE_CREATED                     = 201, | ||||
|         STATUS_CODE_ACCEPTED                    = 202, | ||||
|         STATUS_CODE_NON_AUTH_INFO               = 203, | ||||
|         STATUS_CODE_NO_CONTENT                  = 204, | ||||
|         STATUS_CODE_RESET_CONTENT               = 205, | ||||
|         STATUS_CODE_PARTIAL_CONTENT             = 206, | ||||
|          | ||||
|         STATUS_CODE_MULTIPLE_CHOICES            = 300, | ||||
|         STATUS_CODE_MOVED_PERMANENTLY           = 301, | ||||
|         STATUS_CODE_FOUND                       = 302, | ||||
|         STATUS_CODE_SEE_OTHER                   = 303, | ||||
|         STATUS_CODE_NOT_MODIFIED                = 304, | ||||
|         STATUS_CODE_USE_PROXY                   = 305, | ||||
|         STATUS_CODE_TEMP_REDIRECT               = 307, | ||||
|  | ||||
|         STATUS_CODE_BAD_REQUEST                 = 400, | ||||
|         STATUS_CODE_UNAUTHORIZED                = 401, | ||||
|         STATUS_CODE_PAYMENT_REQUIRED            = 402, | ||||
|         STATUS_CODE_FORBIDDEN                   = 403, | ||||
|         STATUS_CODE_NOT_FOUND                   = 404, | ||||
|         STATUS_CODE_METHOD_NOT_ALLOWED          = 405, | ||||
|         STATUS_CODE_NOT_ACCEPTABLE              = 406, | ||||
|         STATUS_CODE_PROXY_AUTH_REQ              = 407, | ||||
|         STATUS_CODE_REQUEST_TIEMOUT             = 408, | ||||
|         STATUS_CODE_CONFLICT                    = 409, | ||||
|         STATUS_CODE_GONE                        = 410, | ||||
|         STATUS_CODE_LENGTH_REQUIRED             = 411, | ||||
|         STATUS_CODE_PRECONDITION_FAILED         = 412, | ||||
|         STATUS_CODE_REQ_ENTITY_TOO_LARGE        = 413, | ||||
|         STATUS_CODE_REQ_URI_TOO_LONG            = 414, | ||||
|         STATUS_CODE_UNSUPPORTED_MEDIA_TYPE      = 415, | ||||
|         STATUS_CODE_REQ_RANGE_NOT_SATISFIABLE   = 416, | ||||
|         STATUS_CODE_EXPECTATION_FAILED          = 417, | ||||
|         STATUS_CODE_IM_A_TEAPOT                 = 418, | ||||
|         STATUS_CODE_ENHANCE_YOUR_CALM           = 420, | ||||
|  | ||||
|         STATUS_CODE_INTERNAL_SERVER_ERROR       = 500, | ||||
|         STATUS_CODE_NOT_IMPLEMENTED             = 501, | ||||
|         STATUS_CODE_BAD_GATEWAY                 = 502, | ||||
|         STATUS_CODE_SERVICE_UNAVAILABLE         = 503, | ||||
|         STATUS_CODE_GATEWAY_TIMEOUT             = 504, | ||||
|         STATUS_CODE_HTTP_VERSION_UNSUPPORTED    = 505 | ||||
|     }; | ||||
|  | ||||
|     inline std::map<StatusCode, std::string> StatusCodeStrings { | ||||
|         { STATUS_CODE_CONTINUE                 , "CONTINUE"                 }, | ||||
|         { STATUS_CODE_SWITCH_PROTO             , "SWITCH_PROTO"             }, | ||||
|  | ||||
|         { STATUS_CODE_OK                       , "OK"                       }, | ||||
|         { STATUS_CODE_CREATED                  , "CREATED"                  }, | ||||
|         { STATUS_CODE_ACCEPTED                 , "ACCEPTED"                 }, | ||||
|         { STATUS_CODE_NON_AUTH_INFO            , "NON_AUTH_INFO"            }, | ||||
|         { STATUS_CODE_NO_CONTENT               , "NO_CONTENT"               }, | ||||
|         { STATUS_CODE_RESET_CONTENT            , "RESET_CONTENT"            }, | ||||
|         { STATUS_CODE_PARTIAL_CONTENT          , "PARTIAL_CONTENT"          }, | ||||
|          | ||||
|         { STATUS_CODE_MULTIPLE_CHOICES         , "MULTIPLE_CHOICES"         }, | ||||
|         { STATUS_CODE_MOVED_PERMANENTLY        , "MOVED_PERMANENTLY"        }, | ||||
|         { STATUS_CODE_FOUND                    , "FOUND"                    }, | ||||
|         { STATUS_CODE_SEE_OTHER                , "SEE_OTHER"                }, | ||||
|         { STATUS_CODE_NOT_MODIFIED             , "NOT_MODIFIED"             }, | ||||
|         { STATUS_CODE_USE_PROXY                , "USE_PROXY"                }, | ||||
|         { STATUS_CODE_TEMP_REDIRECT            , "TEMP_REDIRECT"            }, | ||||
|  | ||||
|         { STATUS_CODE_BAD_REQUEST              , "BAD_REQUEST"              }, | ||||
|         { STATUS_CODE_UNAUTHORIZED             , "UNAUTHORIZED"             }, | ||||
|         { STATUS_CODE_PAYMENT_REQUIRED         , "PAYMENT_REQUIRED"         }, | ||||
|         { STATUS_CODE_FORBIDDEN                , "FORBIDDEN"                }, | ||||
|         { STATUS_CODE_NOT_FOUND                , "NOT_FOUND"                }, | ||||
|         { STATUS_CODE_METHOD_NOT_ALLOWED       , "METHOD_NOT_ALLOWED"       }, | ||||
|         { STATUS_CODE_NOT_ACCEPTABLE           , "NOT_ACCEPTABLE"           }, | ||||
|         { STATUS_CODE_PROXY_AUTH_REQ           , "PROXY_AUTH_REQ"           }, | ||||
|         { STATUS_CODE_REQUEST_TIEMOUT          , "REQUEST_TIEMOUT"          }, | ||||
|         { STATUS_CODE_CONFLICT                 , "CONFLICT"                 }, | ||||
|         { STATUS_CODE_GONE                     , "GONE"                     }, | ||||
|         { STATUS_CODE_LENGTH_REQUIRED          , "LENGTH_REQUIRED"          }, | ||||
|         { STATUS_CODE_PRECONDITION_FAILED      , "PRECONDITION_FAILED"      }, | ||||
|         { STATUS_CODE_REQ_ENTITY_TOO_LARGE     , "REQ_ENTITY_TOO_LARGE"     }, | ||||
|         { STATUS_CODE_REQ_URI_TOO_LONG         , "REQ_URI_TOO_LONG"         }, | ||||
|         { STATUS_CODE_UNSUPPORTED_MEDIA_TYPE   , "UNSUPPORTED_MEDIA_TYPE"   }, | ||||
|         { STATUS_CODE_REQ_RANGE_NOT_SATISFIABLE, "REQ_RANGE_NOT_SATISFIABLE"}, | ||||
|         { STATUS_CODE_EXPECTATION_FAILED       , "EXPECTATION_FAILED"       }, | ||||
|         { STATUS_CODE_IM_A_TEAPOT              , "IM_A_TEAPOT"              }, | ||||
|         { STATUS_CODE_ENHANCE_YOUR_CALM        , "ENHANCE_YOUR_CALM"        }, | ||||
|  | ||||
|         { STATUS_CODE_INTERNAL_SERVER_ERROR    , "INTERNAL_SERVER_ERROR"    }, | ||||
|         { STATUS_CODE_NOT_IMPLEMENTED          , "NOT_IMPLEMENTED"          }, | ||||
|         { STATUS_CODE_BAD_GATEWAY              , "BAD_GATEWAY"              }, | ||||
|         { STATUS_CODE_SERVICE_UNAVAILABLE      , "SERVICE_UNAVAILABLE"      }, | ||||
|         { STATUS_CODE_GATEWAY_TIMEOUT          , "GATEWAY_TIMEOUT"          }, | ||||
|         { STATUS_CODE_HTTP_VERSION_UNSUPPORTED , "HTTP_VERSION_UNSUPPORTED" } | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * HTTP Message Header | ||||
|      */ | ||||
|     class MessageHeader { | ||||
|     public: | ||||
|         /** | ||||
|          * Serialize header to string. | ||||
|          * @return Header in string form. | ||||
|          */ | ||||
|         std::string serialize(); | ||||
|  | ||||
|         /** | ||||
|          * Deserialize header from string. | ||||
|          * @param data Header in string form. | ||||
|          */ | ||||
|         void deserialize(const std::string& data); | ||||
|  | ||||
|         /** | ||||
|          * Get field list. | ||||
|          * @return Map from field name to field. | ||||
|          */ | ||||
|         std::map<std::string, std::string>& getFields(); | ||||
|  | ||||
|         /** | ||||
|          * Check if a field exists in the header. | ||||
|          * @return True if the field exists, false otherwise. | ||||
|          */ | ||||
|         bool hasField(const std::string& name); | ||||
|  | ||||
|         /** | ||||
|          * Get field value. | ||||
|          * @param name Name of the field. | ||||
|          * @return Field value. | ||||
|          */ | ||||
|         std::string getField(const std::string name); | ||||
|  | ||||
|         /** | ||||
|          * Set field. | ||||
|          * @param name Field name. | ||||
|          * @param value Field value. | ||||
|          */ | ||||
|         void setField(const std::string& name, const std::string& value); | ||||
|  | ||||
|         /** | ||||
|          * Delete field. | ||||
|          * @param name Field name. | ||||
|          */ | ||||
|         void clearField(const std::string& name); | ||||
|  | ||||
|     private: | ||||
|         int readLine(const std::string& str, std::string& line, int start = 0); | ||||
|         virtual std::string serializeStartLine() = 0; | ||||
|         virtual void deserializeStartLine(const std::string& data) = 0; | ||||
|         std::map<std::string, std::string> fields; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * HTTP Request Header | ||||
|      */ | ||||
|     class RequestHeader : public MessageHeader { | ||||
|     public: | ||||
|         RequestHeader() {} | ||||
|  | ||||
|         /** | ||||
|          * Create request header from the mandatory parameters. | ||||
|          * @param method HTTP Method. | ||||
|          * @param uri URI of request. | ||||
|          * @param host Server host passed in the 'Host' field. | ||||
|          */ | ||||
|         RequestHeader(Method method, std::string uri, std::string host); | ||||
|  | ||||
|         /** | ||||
|          * Create request header from its serialized string form. | ||||
|          * @param data Request header in string form. | ||||
|          */ | ||||
|         RequestHeader(const std::string& data); | ||||
|          | ||||
|         /** | ||||
|          * Get HTTP Method. | ||||
|          * @return HTTP Method. | ||||
|          */ | ||||
|         Method getMethod(); | ||||
|         void setMethod(Method method); | ||||
|         std::string getURI(); | ||||
|         void setURI(const std::string& uri); | ||||
|  | ||||
|     private: | ||||
|         void deserializeStartLine(const std::string& data); | ||||
|         std::string serializeStartLine(); | ||||
|  | ||||
|         Method method; | ||||
|         std::string uri; | ||||
|     }; | ||||
|  | ||||
|     class ResponseHeader : public MessageHeader { | ||||
|     public: | ||||
|         ResponseHeader() {} | ||||
|         ResponseHeader(StatusCode statusCode); | ||||
|         ResponseHeader(StatusCode statusCode, const std::string& statusString); | ||||
|         ResponseHeader(const std::string& data); | ||||
|  | ||||
|         StatusCode getStatusCode(); | ||||
|         void setStatusCode(StatusCode statusCode); | ||||
|         std::string getStatusString(); | ||||
|         void setStatusString(const std::string& statusString); | ||||
|  | ||||
|     private: | ||||
|         void deserializeStartLine(const std::string& data); | ||||
|         std::string serializeStartLine(); | ||||
|  | ||||
|         StatusCode statusCode; | ||||
|         std::string statusString; | ||||
|     }; | ||||
|  | ||||
|     class ChunkHeader { | ||||
|     public: | ||||
|         ChunkHeader() {} | ||||
|         ChunkHeader(size_t length); | ||||
|         ChunkHeader(const std::string& data); | ||||
|  | ||||
|         std::string serialize(); | ||||
|         void deserialize(const std::string& data); | ||||
|  | ||||
|         size_t getLength(); | ||||
|         void setLength(size_t length); | ||||
|  | ||||
|     private: | ||||
|         size_t length; | ||||
|     }; | ||||
|  | ||||
|     class Client { | ||||
|     public: | ||||
|         Client() {} | ||||
|         Client(std::shared_ptr<Socket> sock); | ||||
|  | ||||
|         int sendRequestHeader(RequestHeader& req); | ||||
|         int recvRequestHeader(RequestHeader& req, int timeout = -1); | ||||
|         int sendResponseHeader(ResponseHeader& resp); | ||||
|         int recvResponseHeader(ResponseHeader& resp, int timeout = -1); | ||||
|         int sendChunkHeader(ChunkHeader& chdr); | ||||
|         int recvChunkHeader(ChunkHeader& chdr, int timeout = -1); | ||||
|  | ||||
|     private: | ||||
|         int recvHeader(std::string& data, int timeout = -1); | ||||
|         std::shared_ptr<Socket> sock; | ||||
|  | ||||
|     }; | ||||
|  | ||||
|      | ||||
| } | ||||
| @@ -3,7 +3,7 @@ | ||||
| #include <dsp/types.h> | ||||
| #include <string> | ||||
| #include <thread> | ||||
| #include "proto/http.h" | ||||
| #include <utils/proto/http.h> | ||||
|  | ||||
| class SpectranHTTPClient { | ||||
| public: | ||||
|   | ||||
| @@ -279,9 +279,9 @@ private: | ||||
|         AARTSAAPI_Packet pkt = { sizeof(AARTSAAPI_Packet) }; | ||||
|         while (AARTSAAPI_GetPacket(&_this->dev, 0, 0, &pkt) == AARTSAAPI_EMPTY) { | ||||
| #ifdef _WIN32 | ||||
|                 Sleep(1); | ||||
|             Sleep(1); | ||||
| #else | ||||
|                 usleep(1000); | ||||
|             usleep(1000); | ||||
| #endif | ||||
|         } | ||||
|  | ||||
| @@ -371,7 +371,7 @@ private: | ||||
|         if (_this->agcModeId) { SmGui::BeginDisabled(); } | ||||
|         SmGui::LeftLabel("Ref Level"); | ||||
|         SmGui::FillWidth(); | ||||
|         if (SmGui::SliderFloatWithSteps(CONCAT("##_spectran_ref_", _this->name), &_this->refLevel, -20.0f, 10.0f, 0.5f, SmGui::FMT_STR_FLOAT_DB_ONE_DECIMAL)) { | ||||
|         if (SmGui::SliderFloatWithSteps(CONCAT("##_spectran_ref_", _this->name), &_this->refLevel, _this->minRef, _this->maxRef, _this->refStep, SmGui::FMT_STR_FLOAT_DB_ONE_DECIMAL)) { | ||||
|             if (_this->running) { | ||||
|                 AARTSAAPI_Config config; | ||||
|                 AARTSAAPI_ConfigFind(&_this->dev, &_this->croot, &config, L"main/reflevel"); | ||||
| @@ -442,6 +442,22 @@ private: | ||||
|         else { | ||||
|             AARTSAAPI_ConfigSetString(&dev, &config, L"None"); | ||||
|         } | ||||
|         updateRef(); | ||||
|     } | ||||
|  | ||||
|     void updateRef() { | ||||
|         // Get and update bounds | ||||
|         AARTSAAPI_Config config; | ||||
|         AARTSAAPI_ConfigInfo refInfo; | ||||
|         AARTSAAPI_ConfigFind(&dev, &croot, &config, L"main/reflevel"); | ||||
|         AARTSAAPI_ConfigGetInfo(&dev, &config, &refInfo); | ||||
|         minRef = refInfo.minValue; | ||||
|         maxRef = refInfo.maxValue; | ||||
|         refStep = refInfo.stepValue; | ||||
|         refLevel = std::clamp<float>(refLevel, minRef, maxRef); | ||||
|  | ||||
|         // Apply new ref level | ||||
|         AARTSAAPI_ConfigSetFloat(&dev, &config, refLevel); | ||||
|     } | ||||
|  | ||||
|     const double clockRates[4] = { | ||||
| @@ -482,6 +498,9 @@ private: | ||||
|     float refLevel = -20.0f; | ||||
|     bool amp = false; | ||||
|     bool preAmp = false; | ||||
|     float minRef = -20.0f; | ||||
|     float maxRef = 10.0f; | ||||
|     float refStep = 0.5; | ||||
|  | ||||
|     struct SRCombo { | ||||
|         bool operator==(const SRCombo& b) { | ||||
|   | ||||
| @@ -136,7 +136,7 @@ public: | ||||
|         // List samplerates | ||||
|         samplerates.clear(); | ||||
|         auto srList = dev->get_rx_rates(chanId); | ||||
|         for (auto& l : srList) { | ||||
|         for (const auto& l : srList) { | ||||
|             double step = (l.step() == 0.0) ? 100e3 : l.step(); | ||||
|             for (double f = l.start(); f <= l.stop(); f += step) { | ||||
|                 samplerates.define(f, getBandwdithScaled(f), f); | ||||
| @@ -173,7 +173,7 @@ public: | ||||
|             clockSources.define(s, name, s); | ||||
|             spdlog::warn(s); | ||||
|         } | ||||
|  | ||||
|          | ||||
|         // Load settings | ||||
|         srId = 0; | ||||
|         antId = 0; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user