diff --git a/CMakeLists.txt b/CMakeLists.txt index 5839233..6f1e65b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ src/Http/HttpUtils.cpp src/Http/HttpClient.cpp src/Http/HttpStream.cpp src/Http/ContentDisposition.cpp +src/Http/WebSocket.cpp src/Mail/Smtp.cpp src/Serialization/Json.cpp src/Streams/FileStream.cpp @@ -74,6 +75,16 @@ endif() set(MBEDTLS_DIR "" CACHE PATH "Mbed tls directory") function(TESSESFRAMEWORK_LINKDEPS TessesFramework_TARGET) +target_include_directories(${TessesFramework_TARGET} + PUBLIC + "$" + "$" +) +target_include_directories(${TessesFramework_TARGET} + PUBLIC + "$" + "$" +) if(WIN32) target_link_libraries(${TessesFramework_TARGET} PUBLIC iphlpapi) endif() @@ -100,16 +111,7 @@ target_link_directories(${TessesFramework_TARGET} PUBLIC ${MBEDTLS_DIR}/lib) endif() target_link_libraries(${TessesFramework_TARGET} PUBLIC mbedtls mbedx509 mbedcrypto) endif() -target_include_directories(${TessesFramework_TARGET} - PUBLIC - "$" - "$" -) -target_include_directories(${TessesFramework_TARGET} - PUBLIC - "$" - "$" -) + if("${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoWii" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoGameCube") target_link_libraries(${TessesFramework_TARGET} PUBLIC fat) endif() @@ -203,7 +205,10 @@ if(TESSESFRAMEWORK_ENABLE_EXAMPLES) add_executable(printjsondecodedemoji examples/printjsondecodedemoji.cpp) target_link_libraries(printjsondecodedemoji PUBLIC tessesframework) - + + add_executable(wsecho examples/wsecho.cpp) + target_link_libraries(wsecho PUBLIC tessesframework) + endif() if(TESSESFRAMEWORK_ENABLE_APPS) diff --git a/apps/tfetch.cpp b/apps/tfetch.cpp index 9c27bb0..2506a53 100644 --- a/apps/tfetch.cpp +++ b/apps/tfetch.cpp @@ -10,12 +10,8 @@ int main(int argc, char** argv) printf("USAGE: %s \n",argv[0]); return 1; } - FileStream strm(argv[2],"wb"); - - HttpRequest req; - req.url = argv[1]; - HttpResponse resp(req); - resp.CopyToStream(&strm); + std::string path = argv[2]; + Tesses::Framework::Http::DownloadToFileSimple(argv[1],path); return 0; diff --git a/examples/wsecho.cpp b/examples/wsecho.cpp new file mode 100644 index 0000000..9b61577 --- /dev/null +++ b/examples/wsecho.cpp @@ -0,0 +1,48 @@ +#include "TessesFramework/Http/HttpClient.hpp" +#include "TessesFramework/Threading/Thread.hpp" +#include +using namespace Tesses::Framework::Http; + +class WebSocketConn : public WebSocketConnection +{ + + public: + void OnOpen(std::function sendMessage, std::function ping, std::function close) + { + while(true) + { + std::cout << "> "; + std::string req; + std::getline(std::cin, req); + if(req == "exit") + break; + else + SendWebSocketMessage(sendMessage,req); + } + close(); + + } + void OnReceive(WebSocketMessage& message) + { + std::cout << "Message: " << message.ToString() << std::endl; + + + } + void OnClose(bool clean) + { + std::cout << (clean ? "Closed cleanly" : "Closed unclean") << std::endl; + } + +}; + +int main(int argc, char** argv) +{ + Tesses::Framework::TF_Init(); + HttpDictionary reqHeaders; + WebSocketConn conn; + WebSocketClient("ws://echo.websocket.org/",reqHeaders,conn); + + return 0; +} + + diff --git a/include/TessesFramework/Http/HttpClient.hpp b/include/TessesFramework/Http/HttpClient.hpp index ba251d9..788af19 100644 --- a/include/TessesFramework/Http/HttpClient.hpp +++ b/include/TessesFramework/Http/HttpClient.hpp @@ -1,7 +1,9 @@ #pragma once #include "../Streams/Stream.hpp" #include "HttpUtils.hpp" - +#include "WebSocket.hpp" +#include "TessesFramework/Filesystem/LocalFS.hpp" +#include "TessesFramework/Filesystem/VFSFix.hpp" namespace Tesses::Framework::Http { @@ -64,9 +66,19 @@ namespace Tesses::Framework::Http std::string ReadAsString(); Tesses::Framework::Streams::Stream* ReadAsStream(); void CopyToStream(Tesses::Framework::Streams::Stream* strm); - + Tesses::Framework::Streams::Stream* GetInternalStream(); ~HttpResponse(); }; + void DownloadToStreamSimple(std::string url, Tesses::Framework::Streams::Stream* strm); + void DownloadToStreamSimple(std::string url, Tesses::Framework::Streams::Stream& strm); + + void DownloadToFileSimple(std::string url, Tesses::Framework::Filesystem::VFS* vfs, Tesses::Framework::Filesystem::VFSPath path); + void DownloadToFileSimple(std::string url, Tesses::Framework::Filesystem::VFS& vfs, Tesses::Framework::Filesystem::VFSPath path); + void DownloadToFileSimple(std::string url, Tesses::Framework::Filesystem::VFSPath path); + std::string DownloadToStringSimple(std::string url); + bool WebSocketClientSuccessDefault(HttpDictionary& dict,bool v); + void WebSocketClient(std::string url, HttpDictionary& requestHeaders, WebSocketConnection& wsc, std::function cb=WebSocketClientSuccessDefault); + void WebSocketClient(std::string url, HttpDictionary& requestHeaders, WebSocketConnection* wsc, std::function cb=WebSocketClientSuccessDefault); } \ No newline at end of file diff --git a/include/TessesFramework/Http/HttpServer.hpp b/include/TessesFramework/Http/HttpServer.hpp index 6e00fce..e17ab32 100644 --- a/include/TessesFramework/Http/HttpServer.hpp +++ b/include/TessesFramework/Http/HttpServer.hpp @@ -5,6 +5,7 @@ #include "../Threading/Thread.hpp" #include "../Date/Date.hpp" #include +#include "WebSocket.hpp" namespace Tesses::Framework::Http { class ServerContextData { @@ -12,36 +13,7 @@ namespace Tesses::Framework::Http virtual ~ServerContextData(); }; - class WebSocketMessage { - public: - std::vector data; - bool isBinary; - WebSocketMessage(); - WebSocketMessage(std::vector data); - WebSocketMessage(const void* data, size_t len); - WebSocketMessage(std::string message); - std::string ToString(); - }; - - class WebSocketConnection { - public: - virtual void OnOpen(std::function sendMessage, std::function ping)=0; - virtual void OnReceive(WebSocketMessage& message)=0; - virtual void OnClose(bool clean)=0; - virtual ~WebSocketConnection(); - }; - class CallbackWebSocketConnection : public WebSocketConnection { - public: - std::function,std::function)> onOpen; - std::function onReceive; - std::function onClose; - CallbackWebSocketConnection(); - CallbackWebSocketConnection(std::function,std::function)> onOpen, std::function onReceive, std::function onClose); - - void OnOpen(std::function sendMessage, std::function ping); - void OnReceive(WebSocketMessage& message); - void OnClose(bool clean); - }; + class ServerContext { bool sent; @@ -86,7 +58,7 @@ namespace Tesses::Framework::Http ServerContext& WithMimeType(std::string mime); ServerContext& WithContentDisposition(std::string filename, bool isInline); ServerContext& WriteHeaders(); - void StartWebSocketSession(std::function,std::function)> onOpen, std::function onReceive, std::function onClose); + void StartWebSocketSession(std::function,std::function,std::function)> onOpen, std::function onReceive, std::function onClose); void StartWebSocketSession(WebSocketConnection& connection); template diff --git a/include/TessesFramework/Http/WebSocket.hpp b/include/TessesFramework/Http/WebSocket.hpp new file mode 100644 index 0000000..aff277c --- /dev/null +++ b/include/TessesFramework/Http/WebSocket.hpp @@ -0,0 +1,43 @@ +#pragma once +#include +#include +#include +#include +#include +namespace Tesses::Framework::Http +{ + class WebSocketMessage { + public: + std::vector data; + bool isBinary; + WebSocketMessage(); + WebSocketMessage(std::vector data); + WebSocketMessage(const void* data, size_t len); + WebSocketMessage(std::string message); + std::string ToString(); + + + + }; + void SendWebSocketMessage(std::function cb, std::string text); + class WebSocketConnection { + + public: + virtual void OnOpen(std::function sendMessage, std::function ping,std::function close)=0; + virtual void OnReceive(WebSocketMessage& message)=0; + virtual void OnClose(bool clean)=0; + virtual ~WebSocketConnection(); + }; + class CallbackWebSocketConnection : public WebSocketConnection { + public: + std::function,std::function,std::function)> onOpen; + std::function onReceive; + std::function onClose; + CallbackWebSocketConnection(); + CallbackWebSocketConnection(std::function,std::function,std::function)> onOpen, std::function onReceive, std::function onClose); + + void OnOpen(std::function sendMessage, std::function ping,std::function closeFn); + void OnReceive(WebSocketMessage& message); + void OnClose(bool clean); + }; +} \ No newline at end of file diff --git a/src/Http/HttpClient.cpp b/src/Http/HttpClient.cpp index ec1e4ac..d4c28e0 100644 --- a/src/Http/HttpClient.cpp +++ b/src/Http/HttpClient.cpp @@ -1,10 +1,14 @@ #include "TessesFramework/Http/HttpClient.hpp" #include "TessesFramework/Crypto/ClientTLSStream.hpp" +#include "TessesFramework/Crypto/MbedHelpers.hpp" #include "TessesFramework/Streams/NetworkStream.hpp" #include "TessesFramework/TextStreams/StreamWriter.hpp" #include "TessesFramework/TextStreams/StreamReader.hpp" #include "TessesFramework/Http/HttpStream.hpp" #include "TessesFramework/Streams/BufferedStream.hpp" +#include "TessesFramework/Threading/Mutex.hpp" +#include "TessesFramework/Threading/Thread.hpp" + #include #include using Stream = Tesses::Framework::Streams::Stream; @@ -90,7 +94,6 @@ namespace Tesses::Framework::Http } std::string request = method + " " + uri.GetPathAndQuery() + " HTTP/1.1\r\nHost: " + uri.HostPort() + "\r\n"; - for(auto headers : requestHeaders.kvp) { for(auto item : headers.second) @@ -117,11 +120,11 @@ namespace Tesses::Framework::Http Stream* HttpRequest::EstablishConnection(Uri uri, bool ignoreSSLErrors, std::string trusted_root_cert_bundle) { - if(uri.scheme == "http:") + if(uri.scheme == "http:" || uri.scheme == "ws:") { return new NetworkStream(uri.host,uri.GetPort(),false,false,false); } - else if(uri.scheme == "https:") + else if(uri.scheme == "https:" || uri.scheme == "wss:") { auto netStrm = new NetworkStream(uri.host,uri.GetPort(),false,false,false); if(netStrm == nullptr) return netStrm; @@ -130,6 +133,10 @@ namespace Tesses::Framework::Http return nullptr; } + Tesses::Framework::Streams::Stream* HttpResponse::GetInternalStream() + { + return this->handleStrm; + } HttpResponse::~HttpResponse() { @@ -276,4 +283,370 @@ namespace Tesses::Framework::Http return new HttpStream(this->handleStrm,false,length,true,version=="HTTP/1.1"); } + + void DownloadToStreamSimple(std::string url, Tesses::Framework::Streams::Stream* strm) + { + if(strm == nullptr) throw TextException("strm is null"); + HttpRequest request; + request.url = url; + request.followRedirects=true; + request.method = "GET"; + HttpResponse response(request); + if(response.statusCode < 200 || response.statusCode > 299) throw TextException("Status code does not indicate success: " + std::to_string(response.statusCode) + " " + HttpUtils::StatusCodeString(response.statusCode)); + response.CopyToStream(strm); + } + void DownloadToStreamSimple(std::string url, Tesses::Framework::Streams::Stream& strm) + { + DownloadToStreamSimple(url,&strm); + } + + void DownloadToFileSimple(std::string url, Tesses::Framework::Filesystem::VFS* vfs, Tesses::Framework::Filesystem::VFSPath path) + { + if(vfs == nullptr) throw TextException("vfs is null"); + auto strm = vfs->OpenFile(path,"wb"); + if(strm == nullptr) throw TextException("strm is null"); + DownloadToStreamSimple(url,strm); + delete strm; + } + void DownloadToFileSimple(std::string url, Tesses::Framework::Filesystem::VFS& vfs, Tesses::Framework::Filesystem::VFSPath path) + { + auto strm = vfs.OpenFile(path,"wb"); + if(strm == nullptr) throw TextException("strm is null"); + DownloadToStreamSimple(url,strm); + delete strm; + } + void DownloadToFileSimple(std::string url, Tesses::Framework::Filesystem::VFSPath path) + { + DownloadToFileSimple(url,Tesses::Framework::Filesystem::LocalFS,path); + } + std::string DownloadToStringSimple(std::string url) + { + HttpRequest request; + request.url = url; + request.followRedirects=true; + request.method = "GET"; + HttpResponse response(request); + if(response.statusCode < 200 || response.statusCode > 299) throw TextException("Status code does not indicate success: " + std::to_string(response.statusCode) + " " + HttpUtils::StatusCodeString(response.statusCode)); + return response.ReadAsString(); + } + bool WebSocketClientSuccessDefault(HttpDictionary& dict,bool v) + { + return true; + } + void WebSocketClient(std::string url, HttpDictionary& requestHeaders, WebSocketConnection& wsc, std::function cb) + { + WebSocketClient(url,requestHeaders, &wsc,cb); + } + + class WSClient { + public: + std::atomic closed; + Threading::Mutex mtx; + + WebSocketConnection* conn; + Stream* strm; + void close() + { + mtx.Lock(); + this->closed=true; + uint8_t finField = 0b10000000 ; + uint8_t firstByte= finField | 0x9; + strm->WriteByte(firstByte); + strm->WriteByte(0); + + mtx.Unlock(); + + } + void write_len_bytes(uint64_t len) + { + if(len < 126) + { + strm->WriteByte((uint8_t)len | (uint8_t)128U); + } + else if(len < 65535) + { + uint8_t b[3]; + b[0] = 254; + b[1] = (uint8_t)(len >> 8); + b[2] = (uint8_t)len; + + strm->WriteBlock(b,sizeof(b)); + } + else { + + uint8_t b[9]; + b[0] = 255; + + b[1] = (uint8_t)(len >> 56); + b[2] = (uint8_t)(len >> 48); + b[3] = (uint8_t)(len >> 40); + b[4] = (uint8_t)(len >> 32); + b[5] = (uint8_t)(len >> 24); + b[6] = (uint8_t)(len >> 16); + b[7] = (uint8_t)(len >> 8); + b[8] = (uint8_t)len; + + strm->WriteBlock(b,sizeof(b)); + } + } + uint64_t get_long() + { + uint8_t buff[8]; + if(strm->ReadBlock(buff,sizeof(buff)) != sizeof(buff)) return 0; + + uint64_t v = 0; + v |= (uint64_t)buff[0] << 56; + v |= (uint64_t)buff[1] << 48; + v |= (uint64_t)buff[2] << 40; + v |= (uint64_t)buff[3] << 32; + v |= (uint64_t)buff[4] << 24; + v |= (uint64_t)buff[5] << 16; + v |= (uint64_t)buff[6] << 8; + v |= (uint64_t)buff[7]; + return v; + } + uint16_t get_short() + { + uint8_t buff[2]; + if(strm->ReadBlock(buff,sizeof(buff)) != sizeof(buff)) return 0; + + uint16_t v = 0; + v |= (uint16_t)buff[0] << 8; + v |= (uint16_t)buff[1]; + return v; + } + void send_msg(WebSocketMessage* msg) + { + mtx.Lock(); + + uint8_t opcode = msg->isBinary ? 0x2 : 0x1; + + size_t lengthLastByte = msg->data.size() % 4096; + size_t fullPackets = (msg->data.size() - lengthLastByte) / 4096; + size_t noPackets = lengthLastByte > 0 ? fullPackets+1 : fullPackets; + size_t offset = 0; + std::vector mask; + mask.resize(4); + for(size_t i = 0; i < noPackets; i++) + { + bool fin = i == noPackets - 1; + uint8_t finField = fin ? 0b10000000 : 0; + uint8_t opcode2 = i == 0 ? opcode : 0; + uint8_t firstByte = finField | (opcode2 & 0xF); + + size_t len = std::min((size_t)4096,msg->data.size()- offset); + + strm->WriteByte(firstByte); + write_len_bytes((uint64_t)len); + + Crypto::RandomBytes(mask,"Mask it"); + strm->WriteBlock(mask.data(),mask.size()); + for(size_t i = offset; i < offset+len; i++) + msg->data[i] ^= mask[i%4]; + strm->WriteBlock(msg->data.data() + offset,len); + for(size_t i = offset; i < offset+len; i++) + msg->data[i] ^= mask[i%4]; + offset += len; + } + mtx.Unlock(); + } + void ping_send(std::vector& pData) + { + mtx.Lock(); + + uint8_t finField = 0b10000000 ; + uint8_t firstByte= finField | 0x9; + strm->WriteByte(firstByte); + write_len_bytes((uint64_t)pData.size()); + std::vector mask; + mask.resize(4); + Crypto::RandomBytes(mask,"Mask it"); + strm->WriteBlock(mask.data(),mask.size()); + for(size_t i = 0; i < pData.size(); i++) + pData[i] ^= mask[i%4]; + strm->WriteBlock(pData.data(),pData.size()); + for(size_t i = 0; i < pData.size(); i++) + pData[i] ^= mask[i%4]; + mtx.Unlock(); + } + void pong_send(std::vector& pData) + { + mtx.Lock(); + + uint8_t finField = 0b10000000 ; + uint8_t firstByte= finField | 0xA; + strm->WriteByte(firstByte); + write_len_bytes((uint64_t)pData.size()); + strm->WriteBlock(pData.data(),pData.size()); + mtx.Unlock(); + } + bool read_packet(uint8_t len,std::vector& data) + { + + uint8_t realLen=len & 127; + bool masked=(len & 0b10000000) > 0; + uint64_t reallen2 = realLen >= 126 ? realLen > 126 ? get_long() : get_short() : realLen; + uint8_t mask[4]; + if(masked) + { + if(strm->ReadBlock(mask,sizeof(mask)) != sizeof(mask)) return false; + } + size_t offset = data.size(); + data.resize(offset+(size_t)reallen2); + if(data.size() < ((uint64_t)offset+reallen2)) return false; + if(strm->ReadBlock(data.data()+offset,(size_t)reallen2) != (size_t)reallen2) return false; + if(masked) + { + for(size_t i = 0; i < (size_t)reallen2; i++) + { + data[i+offset] ^= mask[i%4]; + } + } + return true; + } + + WSClient(Tesses::Framework::Streams::Stream* strm,WebSocketConnection* conn) + { + this->strm = strm; + this->conn = conn; + + } + void Start() + { + this->closed=false; + bool hasMessage =false; + + WebSocketMessage message; + message.isBinary=false; + message.data={}; + + + while(!strm->EndOfStream()) + { + + uint8_t frame_start[2]; + if(strm->ReadBlock(frame_start,2) != 2) return; + + + + uint8_t opcode = frame_start[0] & 0xF; + bool fin = (frame_start[0] & 0b10000000) > 0; + switch(opcode) + { + case 0x0: + if(!hasMessage) break; + read_packet(frame_start[1], message.data); + break; + case 0x1: + case 0x2: + hasMessage=true; + message.data = {}; + message.isBinary = opcode == 0x2; + + read_packet(frame_start[1], message.data); + break; + case 0x8: + if(!this->closed) this->close(); + this->conn->OnClose(true); + return; + case 0x9: + { + std::vector b; + read_packet(frame_start[1],b); + pong_send(b); + } + break; + case 0xA: + { + std::vector b; + read_packet(frame_start[1],b); + } + } + if(fin && hasMessage) + { + hasMessage=false; + this->conn->OnReceive(message); + message.data={}; + } + } + this->conn->OnClose(false); + } + }; + + + + void WebSocketClient(std::string url, HttpDictionary& requestHeaders, WebSocketConnection* wsc, std::function cb) + { + HttpRequest req; + req.url = url; + req.requestHeaders = requestHeaders; + req.followRedirects=true; + + std::string hash = ""; + + if(Crypto::HaveCrypto()) + { + std::vector code; + code.resize(16); + Crypto::RandomBytes(code,"Tesses::Framework::Http::WebSocketClient"); + hash = Crypto::Base64_Encode(code); + } + + req.requestHeaders.SetValue("Sec-WebSocket-Key", hash); + req.requestHeaders.SetValue("Sec-WebSocket-Version","13"); + req.requestHeaders.SetValue("Upgrade","websocket"); + req.requestHeaders.SetValue("Connection","Upgrade"); + + HttpResponse resp(req); + + std::string accept="0uytbGS5rkQTa6saiHK4AQ=="; + + if(resp.statusCode != 101 || !resp.responseHeaders.TryGetFirst("Sec-WebSocket-Accept",accept) || !resp.responseHeaders.AnyEquals("Connection","Upgrade") || !resp.responseHeaders.AnyEquals("Upgrade","websocket")) + { + cb(resp.responseHeaders,false); + return; + } + + + if(Crypto::HaveCrypto()) + { + std::string txt=hash; + hash += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + if(Crypto::Base64_Encode(Crypto::Sha1::ComputeHash((const uint8_t*)hash.data(),hash.size())) != accept) + { + cb(resp.responseHeaders,false); + return; + } + } + else if(accept != "Swjoe56alHg6mvKmKyiDd3tpNqc=") + { + cb(resp.responseHeaders,false); + return; + } + if(!cb(resp.responseHeaders,true)) + { + + return; + } + + WSClient clt(resp.GetInternalStream(),wsc); + + Threading::Thread thrd([&clt,wsc]()->void{ + try { + wsc->OnOpen([&clt](WebSocketMessage& msg)->void { + clt.send_msg(&msg); + },[&clt]()->void { + std::vector p = {(uint8_t)'p',(uint8_t)'i',(uint8_t)'n',(uint8_t)'g'}; + clt.ping_send(p); + },[&clt]()->void {clt.close();}); + }catch(...) { + + } + }); + + + clt.Start(); + thrd.Join(); + } + } \ No newline at end of file diff --git a/src/Http/HttpServer.cpp b/src/Http/HttpServer.cpp index c5e47ef..9543aaf 100644 --- a/src/Http/HttpServer.cpp +++ b/src/Http/HttpServer.cpp @@ -33,6 +33,21 @@ namespace Tesses::Framework::Http WebSocketConnection* conn; Stream* strm; std::atomic_bool hasInit; + std::atomic closed; + void close() + { + mtx.Lock(); + closed=true; + uint8_t finField = 0b10000000 ; + uint8_t firstByte= finField | 0x9; + strm->WriteByte(firstByte); + strm->WriteByte(0); + + delete strm; + this->strm = nullptr; + mtx.Unlock(); + this->conn->OnClose(true); + } void write_len_bytes(uint64_t len) { if(len < 126) @@ -95,6 +110,7 @@ namespace Tesses::Framework::Http { while(!hasInit); mtx.Lock(); + uint8_t opcode = msg->isBinary ? 0x2 : 0x1; size_t lengthLastByte = msg->data.size() % 4096; @@ -119,7 +135,9 @@ namespace Tesses::Framework::Http } void ping_send(std::vector& pData) { + mtx.Lock(); + uint8_t finField = 0b10000000 ; uint8_t firstByte= finField | 0x9; strm->WriteByte(firstByte); @@ -130,6 +148,7 @@ namespace Tesses::Framework::Http void pong_send(std::vector& pData) { mtx.Lock(); + uint8_t finField = 0b10000000 ; uint8_t firstByte= finField | 0xA; strm->WriteByte(firstByte); @@ -139,6 +158,7 @@ namespace Tesses::Framework::Http } bool read_packet(uint8_t len,std::vector& data) { + uint8_t realLen=len & 127; bool masked=(len & 0b10000000) > 0; uint64_t reallen2 = realLen >= 126 ? realLen > 126 ? get_long() : get_short() : realLen; @@ -172,6 +192,7 @@ namespace Tesses::Framework::Http } void Start() { + this->closed=false; std::string key; if(ctx->requestHeaders.TryGetFirst("Sec-WebSocket-Key", key) && !key.empty()) { @@ -209,11 +230,11 @@ namespace Tesses::Framework::Http message.data={}; hasInit=true; - while(!strm->EndOfStream()) + while( !strm->EndOfStream()) { uint8_t frame_start[2]; - if(strm->ReadBlock(frame_start,2) != 2) return; + if( strm->ReadBlock(frame_start,2) != 2) return; uint8_t opcode = frame_start[0] & 0xF; @@ -233,6 +254,8 @@ namespace Tesses::Framework::Http read_packet(frame_start[1], message.data); break; case 0x8: + + if(!this->closed) this->close(); this->conn->OnClose(true); return; case 0x9: @@ -1007,7 +1030,7 @@ namespace Tesses::Framework::Http { } - void ServerContext::StartWebSocketSession(std::function,std::function)> onOpen, std::function onReceive, std::function onClose) + void ServerContext::StartWebSocketSession(std::function,std::function,std::function)> onOpen, std::function onReceive, std::function onClose) { CallbackWebSocketConnection wsc(onOpen,onReceive,onClose); StartWebSocketSession(wsc); @@ -1023,7 +1046,7 @@ namespace Tesses::Framework::Http },[&svr]()->void { std::vector p = {(uint8_t)'p',(uint8_t)'i',(uint8_t)'n',(uint8_t)'g'}; svr.ping_send(p); - }); + },[&svr]()->void{svr.close();}); }catch(...) { } @@ -1033,60 +1056,4 @@ namespace Tesses::Framework::Http thrd.Join(); } - CallbackWebSocketConnection::CallbackWebSocketConnection() - { - - } - CallbackWebSocketConnection::CallbackWebSocketConnection(std::function,std::function)> onOpen, std::function onReceive, std::function onClose) - { - this->onOpen = onOpen; - this->onReceive = onReceive; - this->onClose = onClose; - } - - void CallbackWebSocketConnection::OnOpen(std::function sendMessage, std::function ping) - { - if(this->onOpen) - this->onOpen(sendMessage,ping); - } - void CallbackWebSocketConnection::OnReceive(WebSocketMessage& message) - { - if(this->onReceive) - this->onReceive(message); - } - void CallbackWebSocketConnection::OnClose(bool clean) - { - if(this->onClose) - this->onClose(clean); - } - - - WebSocketMessage::WebSocketMessage() - { - this->isBinary=false; - this->data={}; - } - WebSocketMessage::WebSocketMessage(std::vector data) - { - this->isBinary = true; - this->data = data; - } - WebSocketMessage::WebSocketMessage(const void* data, size_t len) - { - this->isBinary=true; - this->data={}; - this->data.insert(this->data.end(),(uint8_t*)data,((uint8_t*)data)+len); - } - WebSocketMessage::WebSocketMessage(std::string message) - { - this->isBinary=false; - this->data={}; - this->data.insert(this->data.end(),message.begin(), message.end()); - } - std::string WebSocketMessage::ToString() - { - std::string str = {}; - str.insert(str.end(),this->data.begin(),this->data.end()); - return str; - } } diff --git a/src/Http/HttpUtils.cpp b/src/Http/HttpUtils.cpp index 96299cf..d19d388 100644 --- a/src/Http/HttpUtils.cpp +++ b/src/Http/HttpUtils.cpp @@ -74,9 +74,9 @@ namespace Tesses::Framework::Http { { if(this->port != 0) return this->port; - if(this->scheme == "http:") + if(this->scheme == "http:" || this->scheme == "ws:") return 80; - if(this->scheme == "https:") + if(this->scheme == "https:" || this->scheme == "wss:") return 443; if(this->scheme == "sftp:") return 22; diff --git a/src/Http/WebSocket.cpp b/src/Http/WebSocket.cpp new file mode 100644 index 0000000..7bc6a3c --- /dev/null +++ b/src/Http/WebSocket.cpp @@ -0,0 +1,67 @@ +#include "TessesFramework/Http/WebSocket.hpp" +namespace Tesses::Framework::Http +{ + + CallbackWebSocketConnection::CallbackWebSocketConnection() + { + + } + CallbackWebSocketConnection::CallbackWebSocketConnection(std::function,std::function,std::function)> onOpen, std::function onReceive, std::function onClose) + { + this->onOpen = onOpen; + this->onReceive = onReceive; + this->onClose = onClose; + } + + void CallbackWebSocketConnection::OnOpen(std::function sendMessage, std::function ping, std::function closeConnection) + { + if(this->onOpen) + this->onOpen(sendMessage,ping,closeConnection); + } + void CallbackWebSocketConnection::OnReceive(WebSocketMessage& message) + { + if(this->onReceive) + this->onReceive(message); + } + void CallbackWebSocketConnection::OnClose(bool clean) + { + if(this->onClose) + this->onClose(clean); + } + + + WebSocketMessage::WebSocketMessage() + { + this->isBinary=false; + this->data={}; + } + WebSocketMessage::WebSocketMessage(std::vector data) + { + this->isBinary = true; + this->data = data; + } + WebSocketMessage::WebSocketMessage(const void* data, size_t len) + { + this->isBinary=true; + this->data={}; + this->data.insert(this->data.end(),(uint8_t*)data,((uint8_t*)data)+len); + } + WebSocketMessage::WebSocketMessage(std::string message) + { + this->isBinary=false; + this->data={}; + this->data.insert(this->data.end(),message.begin(), message.end()); + } + std::string WebSocketMessage::ToString() + { + std::string str = {}; + str.insert(str.end(),this->data.begin(),this->data.end()); + return str; + } + + void SendWebSocketMessage(std::function cb, std::string text) + { + WebSocketMessage message(text); + cb(message); + } +} \ No newline at end of file diff --git a/src/Streams/NetworkStream.cpp b/src/Streams/NetworkStream.cpp index 35220da..1a79418 100644 --- a/src/Streams/NetworkStream.cpp +++ b/src/Streams/NetworkStream.cpp @@ -2,7 +2,6 @@ #include "TessesFramework/Http/HttpUtils.hpp" #include using HttpUtils = Tesses::Framework::Http::HttpUtils; - #if defined(TESSESFRAMEWORK_ENABLE_NETWORKING) @@ -26,7 +25,6 @@ using HttpUtils = Tesses::Framework::Http::HttpUtils; #include #undef min #pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "Iphlpapi2.lib") #else extern "C" { #include @@ -468,6 +466,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)