From 4cdfbc954249a9fc6304afb92e948f016d615c74 Mon Sep 17 00:00:00 2001 From: Mike Nolan Date: Thu, 23 Apr 2026 02:23:22 -0500 Subject: [PATCH] Add server sent events, change vfs structure, dark mode error pages and dark mode anonydrop --- .gitea/workflows/tag.yaml | 22 +- Packaging/edit-formula.sh | 19 ++ changelog.md | 3 + examples/webserverex.cpp | 21 +- include/TessesFramework/Common.hpp | 3 +- include/TessesFramework/Http/HttpServer.hpp | 24 +- .../TessesFramework/Streams/NetworkStream.hpp | 11 +- src/Http/HttpServer.cpp | 103 ++++++++ src/Http/HttpStream.cpp | 18 +- src/Streams/NetworkStream.cpp | 235 ++++++++++++------ src/Streams/Stream.cpp | 2 + src/TF_Init.cpp | 16 ++ 12 files changed, 392 insertions(+), 85 deletions(-) create mode 100644 Packaging/edit-formula.sh diff --git a/.gitea/workflows/tag.yaml b/.gitea/workflows/tag.yaml index bed9f7a..1837ea9 100644 --- a/.gitea/workflows/tag.yaml +++ b/.gitea/workflows/tag.yaml @@ -6,7 +6,8 @@ on: env: PACKAGE_AND_BREW: ${{ secrets.PACKAGE_AND_BREW }} - + VERSION: ${{ gitea.ref_name }} + jobs: build-arch: runs-on: arch-builder @@ -20,3 +21,22 @@ jobs: - run: chown build:build /home/build/PKGBUILD - run: chown build:build /home/build/build-arch.sh - run: su build -c /home/build/build-arch.sh + + update-tap: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/checkout@v4 + with: + ref: "master" + path: "tapdir" + repository: "tesses50/tesses-tap.git" + token: ${{ env.PACKAGE_AND_BREW }} + - run: | + cd tapdir + bash ../Packaging/edit-formula.sh + git config user.name "Tesses Gitea Bot" + git config user.email "noreply@tesses.net" + git add . + git commit -m "Push tesses-framework=${{ env.VERSION }}" + git push diff --git a/Packaging/edit-formula.sh b/Packaging/edit-formula.sh new file mode 100644 index 0000000..f142e2c --- /dev/null +++ b/Packaging/edit-formula.sh @@ -0,0 +1,19 @@ +export HASH=`curl https://git.tesses.org/tesses50/tessesframework/archive/$VERSION.tar.gz 2> /dev/null | shasum -a 256 | awk '{print $1}'` + +echo "class Tessesframework < Formula" > "Formula/tessesframework.rb" +echo " desc \"\"" >> "Formula/tessesframework.rb" +echo " homepage \"\"" >> "Formula/tessesframework.rb" +echo " url \"https://git.tesses.org/tesses50/tessesframework/archive/$VERSION.tar.gz\"" >> "Formula/tessesframework.rb" +echo " sha256 \"$HASH\"" >> "Formula/tessesframework.rb" +echo " license \"MIT\"" >> "Formula/tessesframework.rb" +echo " depends_on \"cmake\" => :build" >> "Formula/tessesframework.rb" +echo " depends_on \"mbedtls@3\"" >> "Formula/tessesframework.rb" +echo " def install" >> "Formula/tessesframework.rb" +echo " system \"cmake\", \"-S\", \".\", \"-B\", \"build\", \"-DTESSESFRAMEWORK_FETCHCONTENT=OFF\", *std_cmake_args" >> "Formula/tessesframework.rb" +echo " system \"cmake\", \"--build\", \"build\"" >> "Formula/tessesframework.rb" +echo " system \"cmake\", \"--install\", \"build\"" >> "Formula/tessesframework.rb" +echo " end" >> "Formula/tessesframework.rb" +echo " test do" >> "Formula/tessesframework.rb" +echo " system \"true\"" >> "Formula/tessesframework.rb" +echo " end" >> "Formula/tessesframework.rb" +echo "end" >> "Formula/tessesframework.rb" \ No newline at end of file diff --git a/changelog.md b/changelog.md index b3b357e..c0b1ef6 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,8 @@ # Changelog +## 0.0.3 +Add server sent events, change vfs structure, dark mode error pages and dark mode anonydrop + ## 0.0.2 Add UUIDs diff --git a/examples/webserverex.cpp b/examples/webserverex.cpp index 536023e..387d20d 100644 --- a/examples/webserverex.cpp +++ b/examples/webserverex.cpp @@ -8,6 +8,9 @@ using namespace Tesses::Framework::Http; using namespace Tesses::Framework::Streams; using namespace Tesses::Framework::TextStreams; using namespace Tesses::Framework::Threading; + +std::shared_ptr sse = std::make_shared(); + class Johnny : public ServerContextData { public: @@ -65,7 +68,7 @@ class MyWebServer : public IHttpServer { for(size_t i=0;i<10000; i++) { writer.WriteLine("
  • " + std::to_string(i) + "
  • "); - + TF_Sleep(10); } writer.WriteLine(""); @@ -74,6 +77,15 @@ class MyWebServer : public IHttpServer { writer.WriteLine(""); return true; } + else if(ctx.path == "/ssetest.html") + { + + } + else if(ctx.path == "/sse") + { + ctx.SendServerSentEvents(sse); + return true; + } else if(ctx.path == "/main.js") { @@ -162,6 +174,13 @@ class MyOtherWebServer : public IHttpServer int main(int argc, char** argv) { TF_InitWithConsole(); + int64_t timer = 0; + auto timerHDL= TF_Timer([&timer]()->void{ + timer++; + + sse->SendData("Timer has ticked " + std::to_string(timer) + " times now"); + }); + std::shared_ptr routeSvr = std::make_shared(); routeSvr->Get("/name/{name}/greeting",[](ServerContext& ctx)->bool{ std::string name; diff --git a/include/TessesFramework/Common.hpp b/include/TessesFramework/Common.hpp index facc3c3..26dafb7 100644 --- a/include/TessesFramework/Common.hpp +++ b/include/TessesFramework/Common.hpp @@ -67,7 +67,7 @@ namespace Tesses::Framework std::shared_ptr TF_Timer(); std::shared_ptr TF_Timer(std::function cb, int64_t interval=1000, bool enabled=true); std::shared_ptr TF_Timer(std::function cb, std::chrono::milliseconds interval, bool enabled=true); - + void TF_Init(); void TF_InitWithConsole(); void TF_AllowPortable(std::string argv0); @@ -76,6 +76,7 @@ namespace Tesses::Framework void TF_RunEventLoop(); void TF_RunEventLoopItteration(); bool TF_IsRunning(); + void TF_Sleep(uint32_t sleepMS); void TF_SetIsRunning(bool _isRunning); void TF_Quit(); bool TF_GetConsoleEventsEnabled(); diff --git a/include/TessesFramework/Http/HttpServer.hpp b/include/TessesFramework/Http/HttpServer.hpp index bc1714b..9c4c60a 100644 --- a/include/TessesFramework/Http/HttpServer.hpp +++ b/include/TessesFramework/Http/HttpServer.hpp @@ -13,12 +13,30 @@ namespace Tesses::Framework::Http public: virtual ~ServerContextData(); }; - + class ServerContext; + class ServerSentEvents { + std::vector> strms; + Tesses::Framework::Threading::Mutex mtx; + private: + void SendEventRaw(const std::string& evt); + public: + void SendRetry(uint32_t ms); + void SendRetry(std::chrono::milliseconds ms); + void SendRetry(Tesses::Framework::Date::TimeSpan ts); + void SendData(const std::string& message); + void SendData(const std::string& message, const std::string& dataType); + void SendId(const std::string& idVal); + void SendCustomEvent(const std::string& type, const std::string& value); + void SendComment(const std::string& comment); + friend class ServerContext; + }; class ServerContext { + bool sent; bool debug; + std::vector> sse; std::shared_ptr strm; std::map data; std::queue> headerhandlers; @@ -54,6 +72,7 @@ namespace Tesses::Framework::Http void SendNotFound(); void SendBadRequest(); void SendException(std::exception& ex); + void SendServerSentEvents(std::shared_ptr sse); std::shared_ptr OpenResponseStream(); std::shared_ptr OpenRequestStream(); ServerContext& WithStatusCode(StatusCode code); @@ -87,6 +106,9 @@ namespace Tesses::Framework::Http data[name] = item; return item; } + friend class ServerSentEvents; + + }; class IHttpServer { diff --git a/include/TessesFramework/Streams/NetworkStream.hpp b/include/TessesFramework/Streams/NetworkStream.hpp index 3d8e930..951f7fd 100644 --- a/include/TessesFramework/Streams/NetworkStream.hpp +++ b/include/TessesFramework/Streams/NetworkStream.hpp @@ -4,7 +4,7 @@ namespace Tesses::Framework::Streams { class NetworkStream; - + class TcpServer { int32_t sock; bool owns; @@ -20,7 +20,7 @@ namespace Tesses::Framework::Streams ~TcpServer(); bool IsValid(); void Close(); - }; + }; enum class SocketType { ST_IPv4_TCP, ST_IPv4_UDP, @@ -46,14 +46,19 @@ namespace Tesses::Framework::Streams void Listen(int32_t backlog); void Bind(std::string ip, uint16_t port); void SetBroadcast(bool bC); + void SetReuseAddress(bool reuse); + void SetReusePort(bool reuse); + void SetMulticastTTL(uint8_t ttl); + void SetMulticastMembership(std::string multicastAddress, std::string ifaceIP="0.0.0.0"); std::shared_ptr Accept(std::string& ip, uint16_t& port); size_t Read(uint8_t* buff, size_t sz); size_t Write(const uint8_t* buff, size_t sz); size_t ReadFrom(uint8_t* buff, size_t sz, std::string& ip, uint16_t& port); size_t WriteTo(const uint8_t* buff, size_t sz, std::string ip, uint16_t port); static std::vector> GetIPs(bool ipV6=false); + ~NetworkStream(); void SetNoDelay(bool noDelay); void Close(); }; -} \ No newline at end of file +} diff --git a/src/Http/HttpServer.cpp b/src/Http/HttpServer.cpp index 30b4358..af301dd 100644 --- a/src/Http/HttpServer.cpp +++ b/src/Http/HttpServer.cpp @@ -27,6 +27,79 @@ using namespace Tesses::Framework::TextStreams; namespace Tesses::Framework::Http { + + void ServerSentEvents::SendEventRaw(const std::string& evt) + { + this->mtx.Lock(); + for(auto& item : this->strms) + { + try { + StreamWriter writer(item); + writer.newline = "\r\n"; + writer.WriteLine(evt); + + + }catch(...) { + + } + } + this->mtx.Unlock(); + } + + void ServerSentEvents::SendId(const std::string& id) + { + SendCustomEvent("id",id); + } + + void ServerSentEvents::SendData(const std::string& message) + { + SendCustomEvent("data", message); + } + void ServerSentEvents::SendComment(const std::string& comment) + { + SendCustomEvent("", comment); + } + void ServerSentEvents::SendCustomEvent(const std::string& etype, const std::string& message) + { + std::string text = etype + ": "; + for(auto item : message) + { + if(item == '\r') continue; + if(item == '\n') { + text += "\r\n" + etype + ": "; + } + else text += item; + } + + SendEventRaw(text); + } + void ServerSentEvents::SendData(const std::string& message, const std::string& mtype) + { + std::string text = "event: " + mtype + "\r\ndata: "; + for(auto item : message) + { + if(item == '\r') continue; + if(item == '\n') { + text += "\r\ndata: "; + } + else text += item; + } + + SendEventRaw(text); + } + void ServerSentEvents::SendRetry(uint32_t ms) + { + SendCustomEvent("retry", std::to_string(ms)); + } + void ServerSentEvents::SendRetry(std::chrono::milliseconds ms) + { + SendCustomEvent("retry", std::to_string(ms.count())); + } + void ServerSentEvents::SendRetry(Tesses::Framework::Date::TimeSpan ts) + { + SendRetry((uint32_t)ts.TotalSeconds() * 1000); + } + class WSServer { public: @@ -282,6 +355,7 @@ namespace Tesses::Framework::Http this->conn->OnClose(false); } }; + /* static int _header_field(multipart_parser* p, const char *at, size_t length) { @@ -344,6 +418,7 @@ namespace Tesses::Framework::Http data->currentHeaders.Clear(); return 0; }*/ + std::string ServerContext::GetUrlWithQuery() { if(this->queryParams.kvp.empty()) return this->path; @@ -364,6 +439,34 @@ namespace Tesses::Framework::Http } } + + void ServerContext::SendServerSentEvents(std::shared_ptr sse) + { + this->responseHeaders.SetValue("X-Accel-Buffering","no"); + this->responseHeaders.SetValue("Content-Type","text/event-stream"); + this->responseHeaders.SetValue("Cache-Control","no-cache"); + auto strm = this->OpenResponseStream(); + if(strm == nullptr || this->method == "HEAD") return; + sse->mtx.Lock(); + sse->strms.push_back(strm); + sse->mtx.Unlock(); + while(!this->strm->EndOfStream()) + { + TF_Sleep(10); + } + sse->mtx.Lock(); + for(auto index = sse->strms.begin(); index != sse->strms.end(); index++) + { + if(*index == strm) + { + sse->strms.erase(index); + break; + } + } + + sse->mtx.Unlock(); + } + std::string ServerContext::ReadString() { if(strm == nullptr) return {}; diff --git a/src/Http/HttpStream.cpp b/src/Http/HttpStream.cpp index ef3f10c..eb80d8a 100644 --- a/src/Http/HttpStream.cpp +++ b/src/Http/HttpStream.cpp @@ -160,20 +160,30 @@ namespace Tesses::Framework::Http if(this->length == -1 && this->http1_1 && !done && !this->recv) { this->done=true; - StreamWriter writer(this->strm); - writer.newline = "\r\n"; - writer.WriteLine("0"); - writer.WriteLine(); + try { + + StreamWriter writer(this->strm); + writer.newline = "\r\n"; + writer.WriteLine("0"); + writer.WriteLine(); + }catch(...){ + + } } } HttpStream::~HttpStream() { if(this->length == -1 && this->http1_1 && !done && !this->recv) { + try { + StreamWriter writer(this->strm); writer.newline = "\r\n"; writer.WriteLine("0"); writer.WriteLine(); + }catch(...) { + + } } } } \ No newline at end of file diff --git a/src/Streams/NetworkStream.cpp b/src/Streams/NetworkStream.cpp index 445cb75..9bbbcc0 100644 --- a/src/Streams/NetworkStream.cpp +++ b/src/Streams/NetworkStream.cpp @@ -1,8 +1,10 @@ #include "TessesFramework/Streams/NetworkStream.hpp" #include "TessesFramework/Http/HttpUtils.hpp" +#include #include #include using HttpUtils = Tesses::Framework::Http::HttpUtils; + #if defined(TESSESFRAMEWORK_ENABLE_NETWORKING) @@ -11,7 +13,7 @@ using HttpUtils = Tesses::Framework::Http::HttpUtils; #define ss_family sin_family #endif -#if defined(GEKKO) && !(defined(TESSESFRAMEWORK_USE_WII_SOCKET) && defined(HW_RVL)) +#if defined(GEKKO) && !(defined(TESSESFRAMEWORK_USE_WII_SOCKET) && defined(HW_RVL)) #include #define NETWORK_GETSOCKNAME net_getsockname #define NETWORK_RECV net_recv @@ -34,6 +36,8 @@ using HttpUtils = Tesses::Framework::Http::HttpUtils; #endif #undef min #pragma comment(lib, "ws2_32.lib") + + #else extern "C" { @@ -47,6 +51,7 @@ extern "C" { #if defined(AF_UNIX) && !defined(GEKKO) && !defined(__SWITCH__) && !defined(__PS2__) #include #endif + #include } #endif @@ -88,7 +93,7 @@ namespace Tesses::Framework::Streams { std::vector> NetworkStream::GetIPs(bool ipV6) { std::vector> ipConfig; - + #if defined(GEKKO) //if_config( char *local_ip, char *netmask, char *gateway,bool use_dhcp, int max_retries); char localIp[16]; @@ -103,7 +108,7 @@ namespace Tesses::Framework::Streams { ULONG size = 15000; PIP_ADAPTER_ADDRESSES addresses = NULL; addresses = (PIP_ADAPTER_ADDRESSES)malloc(size); - + int retval = GetAdaptersAddresses(family, flags, 0, addresses, &size); if(retval != 0) { free(addresses); @@ -145,15 +150,15 @@ namespace Tesses::Framework::Streams { if (ifa->ifa_addr == NULL) continue; if (ifa->ifa_addr->sa_family == AF_INET) { // IPv4 - + ipConfig.push_back(std::pair(ifa->ifa_name, StringifyIP(ifa->ifa_addr))); - + } #if defined(AF_INET6) if (ifa->ifa_addr->sa_family == AF_INET6 && ipV6) { // IPv6 - + ipConfig.push_back(std::pair(ifa->ifa_name, StringifyIP(ifa->ifa_addr))); - + } #endif } @@ -161,7 +166,7 @@ namespace Tesses::Framework::Streams { freeifaddrs(ifAddrStruct); #endif return ipConfig; - + } void SetPort(struct sockaddr* addr, uint16_t port) { @@ -175,10 +180,10 @@ namespace Tesses::Framework::Streams { { struct sockaddr_in6* a = (struct sockaddr_in6*)addr;\ a->sin6_port = htons(port); - + } #endif - + } static uint16_t getPort(struct sockaddr* addr) { @@ -192,13 +197,13 @@ namespace Tesses::Framework::Streams { { struct sockaddr_in6* a = (struct sockaddr_in6*)addr;\ return ntohs(a->sin6_port); - + } #endif return 0; } bool IPParse(std::string str,struct sockaddr_storage* addr) - { + { memset(addr,0,sizeof(struct sockaddr_storage)); uint8_t ip[16]; @@ -218,7 +223,7 @@ namespace Tesses::Framework::Streams { { struct sockaddr_in6* inAddr = (struct sockaddr_in6*)addr; - + inAddr->sin6_family = AF_INET6; memcpy(&inAddr->sin6_addr,ip,16); return 6; @@ -281,9 +286,9 @@ namespace Tesses::Framework::Streams { HttpUtils::NibbleToHex(ip[15] & 0x0F), }); - - - + + + } #endif return ""; @@ -293,7 +298,7 @@ namespace Tesses::Framework::Streams { uint32_t addr; uint8_t addr_parts[4]; } my_addr_t; - + bool NetworkStream::DataAvailable(int timeout) { pollfd fd; @@ -314,7 +319,7 @@ namespace Tesses::Framework::Streams { } return false; } - + NetworkStream::NetworkStream(std::string unixPath,bool isServer) { this->endOfStream=false; @@ -328,12 +333,12 @@ namespace Tesses::Framework::Streams { return; } struct sockaddr_un unx; - + memset(&unx, 0, sizeof(unx)); unx.sun_family = AF_UNIX; - + strncpy(unx.sun_path, unixPath.c_str(),sizeof(unx.sun_path)-1); - + if(isServer) { unlink(unixPath.c_str()); @@ -344,7 +349,7 @@ namespace Tesses::Framework::Streams { return; } } - else + else { if(NETWORK_CONNECT(this->sock,(const sockaddr*)&unx, (socklen_t)sizeof(unx)) != 0) { @@ -360,7 +365,7 @@ namespace Tesses::Framework::Streams { TcpServer::TcpServer(std::string unixPath,int32_t backlog) { - + this->owns=true; this->valid=false; #if defined(AF_UNIX) && !defined(GEKKO) && !defined(__PS2__) && !defined(__SWITCH__) && ((defined(_WIN32) && defined(HAS_AFUNIX) ) || !defined(_WIN32)) @@ -372,7 +377,7 @@ namespace Tesses::Framework::Streams { return; } struct sockaddr_un unx; - + memset(&unx, 0, sizeof(unx)); unx.sun_family = AF_UNIX; unlink(unixPath.c_str()); @@ -384,7 +389,7 @@ namespace Tesses::Framework::Streams { return; } - if(NETWORK_LISTEN(this->sock, backlog) != 0) + if(NETWORK_LISTEN(this->sock, backlog) != 0) { std::cout << "FAILED TO LISTEN FOR SOME REASON" << std::endl; this->valid = false; @@ -397,14 +402,14 @@ namespace Tesses::Framework::Streams { TcpServer::TcpServer(uint16_t port, int32_t backlog) { this->owns=true; - this->sock = NETWORK_SOCKET(AF_INET, SOCK_STREAM, 0); - - if(this->sock < 0) + this->sock = NETWORK_SOCKET(AF_INET, SOCK_STREAM, 0); + + if(this->sock < 0) { std::cout << "FAILED TO CREATE SOCKET FOR SOME REASON" << std::endl; this->valid=false; return; - } + } struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); @@ -436,7 +441,7 @@ namespace Tesses::Framework::Streams { return; } - if(NETWORK_LISTEN(this->sock, backlog) != 0) + if(NETWORK_LISTEN(this->sock, backlog) != 0) { std::cout << "FAILED TO LISTEN FOR SOME REASON" << std::endl; this->valid = false; @@ -496,24 +501,24 @@ namespace Tesses::Framework::Streams { uint8_t ipBytes[16]; bool success = IPParse(ip, &addr); - if(!success) + if(!success) { this->valid=false; return; } - + SetPort((struct sockaddr*)&addr, port); - this->sock = NETWORK_SOCKET((int)addr.ss_family, SOCK_STREAM, 0); - if(this->sock < 0) + this->sock = NETWORK_SOCKET((int)addr.ss_family, SOCK_STREAM, 0); + if(this->sock < 0) { this->valid=false; return; - } - - - - + } + + + + if(NETWORK_BIND(this->sock, (const sockaddr*)&addr, (socklen_t)sizeof(addr)) != 0) { @@ -522,7 +527,7 @@ namespace Tesses::Framework::Streams { return; } - if(NETWORK_LISTEN(this->sock, backlog) != 0) + if(NETWORK_LISTEN(this->sock, backlog) != 0) { this->valid = false; return; @@ -554,7 +559,7 @@ namespace Tesses::Framework::Streams { struct sockaddr_storage storage; memset(&storage,0, sizeof(storage)); socklen_t addrlen=(socklen_t)sizeof(storage); - + int s = NETWORK_ACCEPT(this->sock, (struct sockaddr*)&storage, &addrlen); if(s < 0) { @@ -563,7 +568,7 @@ namespace Tesses::Framework::Streams { ip = StringifyIP((struct sockaddr*)&storage); port = getPort((struct sockaddr*)&storage); - + return std::make_shared(s,true); } bool NetworkStream::CanRead() @@ -584,32 +589,32 @@ namespace Tesses::Framework::Streams { case SocketType::ST_IPv4_TCP: #if defined(AF_INET) this->sock = NETWORK_SOCKET(AF_INET,SOCK_STREAM, 0); - + #endif break; case SocketType::ST_IPv4_UDP: #if defined(AF_INET) this->sock = NETWORK_SOCKET(AF_INET,SOCK_DGRAM, 0); - + #endif break; case SocketType::ST_IPv6_TCP: #if defined(AF_INET6) this->sock = NETWORK_SOCKET(AF_INET6,SOCK_STREAM, 0); - + #endif break; case SocketType::ST_IPv6_UDP: #if defined(AF_INET6) this->sock = NETWORK_SOCKET(AF_INET6,SOCK_DGRAM, 0); - + #endif break; case SocketType::ST_UNIX: #if defined(AF_UNIX) && ((defined(_WIN32) && defined(HAS_AFUNIX) ) || !defined(_WIN32)) this->sock = NETWORK_SOCKET(AF_UNIX,SOCK_DGRAM, 0); - + #endif break; } @@ -621,7 +626,7 @@ namespace Tesses::Framework::Streams { this->owns=true; this->success=false; std::string portStr = std::to_string((uint32_t)port); - + struct addrinfo hint; memset(&hint, 0, sizeof(hint)); #if defined(AF_INET6) @@ -631,7 +636,7 @@ namespace Tesses::Framework::Streams { #endif hint.ai_socktype = datagram ? SOCK_DGRAM : SOCK_STREAM; - struct addrinfo* result; + struct addrinfo* result; int status = NETWORK_GETADDRINFO(ipOrFqdn.c_str(),portStr.c_str(), &hint, &result); @@ -680,11 +685,11 @@ namespace Tesses::Framework::Streams { return this->endOfStream; } void NetworkStream::Listen(int32_t backlog) - { + { if(this->success) NETWORK_LISTEN(this->sock, backlog); } - + void NetworkStream::Bind(std::string ip, uint16_t port) { if(!this->success) return; @@ -693,14 +698,14 @@ namespace Tesses::Framework::Streams { uint8_t ipBytes[16]; bool success = IPParse(ip, &addr); - if(!success) + if(!success) { this->success=false; if(this->owns) NETWORK_CLOSE(this->sock); return; } - + SetPort((struct sockaddr*)&addr, port); int on=1; #if defined(SO_REUSEPORT) @@ -720,16 +725,81 @@ namespace Tesses::Framework::Streams { return; } } - void NetworkStream::SetBroadcast(bool bC) + void NetworkStream::SetReuseAddress(bool reuse) { if(!this->success) return; - int broadcast = 1; - if (NETWORK_SETSOCKOPT(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)) != 0) + int no = reuse ? 1 : 0; + if (NETWORK_SETSOCKOPT(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&no, sizeof(no)) != 0) { this->success=false; if(this->owns) NETWORK_CLOSE(this->sock); - + + } + } + void NetworkStream::SetReusePort(bool reuse) + { + if(!this->success) return; + int no = reuse ? 1 : 0; + if (NETWORK_SETSOCKOPT(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&no, sizeof(no)) != 0) + { + this->success=false; + if(this->owns) + NETWORK_CLOSE(this->sock); + + } + } + void NetworkStream::SetMulticastTTL(uint8_t ttl) + { + if(!this->success) return; + #if defined(IPPROTO_IP) && defined(IP_MULTICAST_TTL) + if (NETWORK_SETSOCKOPT(sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof(ttl)) != 0) + { + this->success=false; + if(this->owns) + NETWORK_CLOSE(this->sock); + + } + #endif + } + void NetworkStream::SetMulticastMembership(std::string multicastAddress, std::string ifaceIP) + { + if(!this->success) return; + #if defined(IPPROTO_IP) && defined(IP_MULTICAST_TTL) + struct sockaddr_storage maddr; + struct sockaddr_storage iaddr; + + bool success = IPParse(multicastAddress, &maddr) && IPParse(ifaceIP, &iaddr); + if(success && maddr.ss_family == AF_INET && iaddr.ss_family == AF_INET) + { + + struct ip_mreq req; + req.imr_multiaddr = ((struct sockaddr_in*)&maddr)->sin_addr; + req.imr_interface = ((struct sockaddr_in*)&iaddr)->sin_addr; + + if(NETWORK_SETSOCKOPT(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&req, sizeof(req)) != 0) + { + + this->success=false; + if(this->owns) + NETWORK_CLOSE(this->sock); + + } + + } + #endif + } + + void NetworkStream::SetBroadcast(bool bC) + { + if(!this->success) return; + int broadcast = bC ? 1 : 0; + if (NETWORK_SETSOCKOPT(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)) != 0) + { + this->success=false; + if(this->owns) + NETWORK_CLOSE(this->sock); + } } std::shared_ptr NetworkStream::Accept(std::string& ip, uint16_t& port) @@ -745,12 +815,12 @@ namespace Tesses::Framework::Streams { ip = StringifyIP((struct sockaddr*)&storage); port = getPort((struct sockaddr*)&storage); - + return std::make_shared((int32_t)s,(bool)true); } size_t NetworkStream::Read(uint8_t* buff, size_t sz) { - + if(!this->success) return 0; auto r = NETWORK_RECV(this->sock,(char*)buff,sz,0); @@ -759,20 +829,20 @@ namespace Tesses::Framework::Streams { this->endOfStream=true; return 0; } - + return (size_t)r; } size_t NetworkStream::Write(const uint8_t* buff, size_t sz) { if(!this->success) return 0; - + auto sz2 = NETWORK_SEND(this->sock,(const char*)buff,sz, 0); if(sz2 <= 0) { this->endOfStream=true; return 0; } - + return (size_t)sz; } size_t NetworkStream::ReadFrom(uint8_t* buff, size_t sz, std::string& ip, uint16_t& port) @@ -786,10 +856,10 @@ namespace Tesses::Framework::Streams { if(r < 0) return 0; return (size_t)r; - - - + + + } size_t NetworkStream::WriteTo(const uint8_t* buff, size_t sz, std::string ip, uint16_t port) { @@ -800,14 +870,14 @@ namespace Tesses::Framework::Streams { uint8_t ipBytes[16]; bool success = IPParse(ip, &addr); - if(!success) + if(!success) { this->success=false; if(this->owns) NETWORK_CLOSE(this->sock); return 0; } - + SetPort((struct sockaddr*)&addr, port); auto sz2 = NETWORK_SENDTO(this->sock,(const char*)buff,sz, 0, (const sockaddr*)&addr, (socklen_t)sizeof(addr)); if(sz2 < 0) return 0; @@ -827,11 +897,11 @@ namespace Tesses::Framework::Streams { } void NetworkStream::SetNoDelay(bool noDelay) { - + int noDelay2 = noDelay; NETWORK_SETSOCKOPT(this->sock, SOL_SOCKET, TCP_NODELAY, (const char*)&noDelay2,(socklen_t)sizeof(noDelay2)); } - + } #else namespace Tesses::Framework::Streams { @@ -850,7 +920,7 @@ TcpServer::TcpServer(std::string ip, uint16_t port, int32_t backlog) } TcpServer::TcpServer(std::string unixPath,int32_t backlog) { - + } std::shared_ptr TcpServer::GetStream(std::string& ip, uint16_t& port) { @@ -866,7 +936,7 @@ bool TcpServer::IsValid() } void TcpServer::Close() { - + } bool NetworkStream::EndOfStream() { return true; @@ -877,7 +947,7 @@ bool NetworkStream::CanRead() { bool NetworkStream::CanWrite() { return false; } - + NetworkStream::NetworkStream(SocketType type) { @@ -888,7 +958,7 @@ NetworkStream::NetworkStream(std::string ipOrFqdn, uint16_t port, bool datagram, } NetworkStream::NetworkStream(std::string unixPath, bool isServer) { - + } NetworkStream::NetworkStream(int32_t sock, bool owns) { @@ -906,6 +976,23 @@ void NetworkStream::SetBroadcast(bool bC) { } +void NetworkStream::SetReuseAddress(bool reuse) +{ + +} +void NetworkStream::SetReusePort(bool reuse) +{ + +} +void NetworkStream::SetMulticastTTL(uint8_t ttl) +{ + +} +void NetworkStream::SetMulticastMembership(std::string multicastAddress, std::string ifaceIP="0.0.0.0") +{ + +} + std::shared_ptr NetworkStream::Accept(std::string& ip, uint16_t& port) { return nullptr; @@ -936,7 +1023,7 @@ NetworkStream::~NetworkStream() } void NetworkStream::SetNoDelay(bool noDelay) { - + } void NetworkStream::Close() { diff --git a/src/Streams/Stream.cpp b/src/Streams/Stream.cpp index 3ae4582..2384249 100644 --- a/src/Streams/Stream.cpp +++ b/src/Streams/Stream.cpp @@ -49,7 +49,9 @@ namespace Tesses::Framework::Streams { read = len; if(read > 0) { + size_t r0=read; read=this->Write(buffer,read); + if(read == 0) { throw std::out_of_range("Failed to write!"); diff --git a/src/TF_Init.cpp b/src/TF_Init.cpp index da59df8..428afda 100644 --- a/src/TF_Init.cpp +++ b/src/TF_Init.cpp @@ -53,6 +53,10 @@ static GXRModeObj *rmode = NULL; #endif +#if !defined(_WIN32) +#include +#endif + namespace Tesses::Framework { @@ -85,7 +89,19 @@ namespace Tesses::Framework cb(); #endif } + void TF_Sleep(uint32_t sleepMS) + { + #if defined(_WIN32) + Sleep((DWORD)sleepMS); + #else + struct timespec ts; + + ts.tv_sec = (time_t)(sleepMS / 1000); + ts.tv_nsec = (sleepMS % 1000) * 1000000; + nanosleep(&ts,NULL); + #endif + } void TF_ConnectToSelf(uint16_t port) { Tesses::Framework::Streams::NetworkStream ns("127.0.0.1",port,false,false,false);