From 0f93a62163129347babd9c43c47830340a77f64a Mon Sep 17 00:00:00 2001 From: cxl Date: Sat, 24 Feb 2018 16:04:24 +0000 Subject: [PATCH] Core: WebSocket improvements git-svn-id: svn://ultimatepp.org/upp/trunk@11798 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppsrc/Core/Inet.h | 9 +++- uppsrc/Core/WebSocket.cpp | 56 ++++++++++++++++--------- uppsrc/Core/src.tpp/WebSocket_en-us.tpp | 12 +++++- 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/uppsrc/Core/Inet.h b/uppsrc/Core/Inet.h index add6093f8..6c45315c5 100644 --- a/uppsrc/Core/Inet.h +++ b/uppsrc/Core/Inet.h @@ -607,6 +607,7 @@ class WebSocket { String host; IpAddrInfo addrinfo; bool ssl; + String request_header; String data; int data_pos; @@ -646,6 +647,8 @@ class WebSocket { CLOSE = 0x8, PING = 0x9, PONG = 0xa, + + MASK = 0x80, }; void Clear(); @@ -667,13 +670,14 @@ class WebSocket { int GetFinIndex() const; - void SendRaw(int hdr, const String& data); + void SendRaw(int hdr, const String& data, dword mask = 0); void Do0(); static String FormatBlock(const String& s); public: WebSocket& NonBlocking(bool b = true) { socket->Timeout(b ? 0 : Null); return *this; } + WebSocket& RequestHeader(const String& s) { request_header = s; return *this; } bool IsBlocking() const { return IsNull(socket->GetTimeout()); } @@ -691,6 +695,7 @@ public: bool IsBinary() const { return current_opcode & BINARY; } void SendText(const String& data) { SendRaw(FIN|TEXT, data); } + void SendTextMasked(const String& data) { SendRaw(FIN|TEXT, data, MASK); } void SendBinary(const String& data) { SendRaw(FIN|BINARY, data); } void Ping(const String& data) { SendRaw(FIN|PING, data); } @@ -699,7 +704,7 @@ public: void Continue(const String& data) { SendRaw(0, data); } void Fin(const String& data) { SendRaw(FIN, data); } - void Close(const String& msg = Null); + void Close(const String& msg = Null, bool wait_reply = false); bool IsOpen() const { return socket->IsOpen(); } bool IsClosed() const { return !IsOpen(); } diff --git a/uppsrc/Core/WebSocket.cpp b/uppsrc/Core/WebSocket.cpp index 51bb144a4..5316c4a44 100644 --- a/uppsrc/Core/WebSocket.cpp +++ b/uppsrc/Core/WebSocket.cpp @@ -104,17 +104,18 @@ void WebSocket::SendRequest() for(int i = 0; i < 20; i++) h.Cat(Random()); Out( // needs to be the first thing to sent after the connection is established - "GET " + uri + " HTTP/1.1\r\n" - "Host: " + host + "\r\n" - "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" - "Accept-Language: cs,en-US;q=0.7,en;q=0.3\r\n" - "Sec-WebSocket-Version: 13\r\n" - "Sec-WebSocket-Extensions: permessage-deflate\r\n" - "Sec-WebSocket-Key: " + Base64Encode(h) + "\r\n" - "Connection: keep-alive, Upgrade\r\n" - "Pragma: no-cache\r\n" - "Cache-Control: no-cache\r\n" - "Upgrade: websocket\r\n\r\n" + Nvl(request_header, + "GET " + uri + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" + "Accept-Language: cs,en-US;q=0.7,en;q=0.3\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Extensions: permessage-deflate\r\n" + "Sec-WebSocket-Key: " + Base64Encode(h) + "\r\n" + "Connection: keep-alive, Upgrade\r\n" + "Pragma: no-cache\r\n" + "Cache-Control: no-cache\r\n" + "Upgrade: websocket\r\n\r\n") ); opcode = HTTP_RESPONSE_HEADER; } @@ -291,13 +292,13 @@ void WebSocket::FrameHeader() } } -void WebSocket::Close(const String& msg) +void WebSocket::Close(const String& msg, bool wait_reply) { LLOG("Sending CLOSE"); SendRaw(CLOSE|FIN, msg); close_sent = true; if(IsBlocking()) - while(!IsClosed() && !IsError() && socket->IsOpen()) + while((wait_reply ? IsOpen() : out_queue.GetCount()) && !IsError() && socket->IsOpen()) Do0(); } @@ -425,7 +426,7 @@ void WebSocket::Output() } } -void WebSocket::SendRaw(int hdr, const String& data) +void WebSocket::SendRaw(int hdr, const String& data, dword mask) { if(IsError()) return; @@ -437,7 +438,7 @@ void WebSocket::SendRaw(int hdr, const String& data) header.Cat(hdr); int len = data.GetCount(); if(len > 65535) { - header.Cat(127); + header.Cat(127 | mask); header.Cat(0); header.Cat(0); header.Cat(0); @@ -456,11 +457,28 @@ void WebSocket::SendRaw(int hdr, const String& data) else header.Cat((int)len); - Out(header); + if(mask) { + byte Cle[4]; + for(int i = 0; i < 4; i++) + header.Cat(Cle[i] = (byte)Random()); - if(data.GetCount() == 0) - return; - Out(data); + Out(header); + + if(data.GetCount()) { + StringBuffer buf(data.GetCount()); + int n = data.GetCount(); + for(int i = 0; i < n; i++) + buf[i] = data[i] ^ Cle[i & 3]; + Out(buf); + } + } + else { + Out(header); + + if(data.GetCount() == 0) + return; + Out(data); + } } bool WebSocket::WebAccept(TcpSocket& socket_, HttpHeader& hdr) diff --git a/uppsrc/Core/src.tpp/WebSocket_en-us.tpp b/uppsrc/Core/src.tpp/WebSocket_en-us.tpp index 5687e8201..da6fa1013 100644 --- a/uppsrc/Core/src.tpp/WebSocket_en-us.tpp +++ b/uppsrc/Core/src.tpp/WebSocket_en-us.tpp @@ -1,4 +1,5 @@ topic "WebSocket"; +[2 $$0,0#00000000000000000000000000000000:Default] [i448;a25;kKO9;2 $$1,0#37138531426314131252341829483380:class] [l288;2 $$2,2#27521748481378242620020725143825:desc] [0 $$3,0#96390100711032703541132217272105:end] @@ -8,7 +9,6 @@ topic "WebSocket"; [l288;i1121;b17;O9;~~~.1408;2 $$7,0#10431211400427159095818037425705:param] [i448;b42;O9;2 $$8,8#61672508125594000341940100500538:tparam] [b42;2 $$9,9#13035079074754324216151401829390:normal] -[2 $$0,0#00000000000000000000000000000000:Default] [{_} [ {{10000@(113.42.0) [s0;%% [*@7;4 WebSocket]]}}&] [s3; &] @@ -24,6 +24,11 @@ topic "WebSocket"; is blocking mode.&] [s3; &] [s4; &] +[s5;:Upp`:`:WebSocket`:`:RequestHeader`(const Upp`:`:String`&`): WbeSocket[@(0.0.255) `& +]_[* RequestHeader]([@(0.0.255) const]_[_^Upp`:`:String^ String][@(0.0.255) `&]_[*@3 s])&] +[s2;%% Overrides the HTTP the header to be used with Connect.&] +[s3;%% &] +[s4; &] [s5;:Upp`:`:WebSocket`:`:IsBlocking`(`)const: [@(0.0.255) bool]_[* IsBlocking]()_[@(0.0.255) c onst]&] [s2;%% Returns true if WebSocket is in the blocking mode.&] @@ -82,6 +87,11 @@ calls Do, so there is no need to call it separately.&] [s2;%% Sends a single frame (non`-fragmented) text message.&] [s3;%% &] [s4; &] +[s5;:Upp`:`:WebSocket`:`:SendTextMasked`(const Upp`:`:String`&`): [@(0.0.255) void]_[* Se +ndTextMasked]([@(0.0.255) const]_[_^Upp`:`:String^ String][@(0.0.255) `&]_[*@3 data])&] +[s2;%% Sends masked text message.&] +[s3;%% &] +[s4; &] [s5;:Upp`:`:WebSocket`:`:SendBinary`(const Upp`:`:String`&`): [@(0.0.255) void]_[* SendBi nary]([@(0.0.255) const]_[_^Upp`:`:String^ String][@(0.0.255) `&]_[*@3 data])&] [s2;%% Sends a single frame (non`-fragmented) binary message.&]