From 48e58356e66982dbfeccc95ede9058964bd6e8cf Mon Sep 17 00:00:00 2001 From: cxl Date: Wed, 1 Jan 2014 16:14:50 +0000 Subject: [PATCH] Core: WebSocket support refactored git-svn-id: svn://ultimatepp.org/upp/trunk@6712 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppsrc/Core/Inet.h | 18 +++++-- uppsrc/Core/WebSocket.cpp | 66 ++++++++++++------------- uppsrc/Core/src.tpp/WebSocket$en-us.tpp | 45 ++++++++++++++--- 3 files changed, 85 insertions(+), 44 deletions(-) diff --git a/uppsrc/Core/Inet.h b/uppsrc/Core/Inet.h index 34e164a19..b9b3fc657 100644 --- a/uppsrc/Core/Inet.h +++ b/uppsrc/Core/Inet.h @@ -523,12 +523,13 @@ bool HttpResponse(TcpSocket& socket, bool scgi, int code, const char *phrase, const char *content_type = NULL, const String& data = Null, const char *server = NULL, bool gzip = false); -class WebSocket : public TcpSocket { +class WebSocket { int64 ReadLen(int n); - int opcode; - String data; - int64 maxlen; + int opcode; + String data; + int64 maxlen; + TcpSocket *socket; void Reset(); bool Handshake(); @@ -547,7 +548,8 @@ public: PONG = 0xa, }; - bool WebAccept(TcpSocket& server); + bool WebAccept(TcpSocket& socket, HttpHeader& hdr); + bool WebAccept(TcpSocket& socket); bool RecieveRaw(); String Recieve(); @@ -568,6 +570,12 @@ public: bool SendBinary(const String& data, bool fin = true) { return SendBinary(~data, data.GetCount(), fin); } void Close(); + + bool IsOpen() const { return socket && socket->IsOpen(); } + bool IsError() const { return socket && socket->IsError(); } + void ClearError() { if(socket) socket->ClearError(); } + int GetError() const { return socket ? socket->GetError() : 0; } + String GetErrorDesc() const { return socket ? socket->GetErrorDesc() : String(); } WebSocket& MaxLen(int64 maxlen_) { maxlen = maxlen_; return *this; } diff --git a/uppsrc/Core/WebSocket.cpp b/uppsrc/Core/WebSocket.cpp index c7827d6ac..3c5520560 100644 --- a/uppsrc/Core/WebSocket.cpp +++ b/uppsrc/Core/WebSocket.cpp @@ -1,36 +1,22 @@ #include "Core.h" +#define LLOG(x) // DLOG(x) + NAMESPACE_UPP -bool WebSocket::WebAccept(TcpSocket& server) +bool WebSocket::WebAccept(TcpSocket& socket_, HttpHeader& hdr) { - return Accept(server) && Handshake(); -} - -bool WebSocket::Handshake() -{ - HttpHeader hdr; - if(!hdr.Read(*this)) { - SetSockError("websocket handshake", ERROR_NOHEADER, "Failed to read HTTP header"); - return false; - } + socket = &socket_; String key = hdr["sec-websocket-key"]; if(IsNull(key)) { - SetSockError("websocket handshake", ERROR_NOKEY, "Missing sec-websocket-key"); + socket->SetSockError("websocket handshake", ERROR_NOKEY, "Missing sec-websocket-key"); return false; } byte sha1[20]; SHA1(sha1, key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); - DLOG( - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: " + Base64Encode((char *)sha1, 20) + "\r\n\r\n" - ); - - return PutAll( + return socket->PutAll( "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" @@ -38,11 +24,21 @@ bool WebSocket::Handshake() ); } +bool WebSocket::WebAccept(TcpSocket& socket) +{ + HttpHeader hdr; + if(!hdr.Read(socket)) { + socket.SetSockError("websocket handshake", ERROR_NOHEADER, "Failed to read HTTP header"); + return false; + } + return WebAccept(socket, hdr); +} + int64 WebSocket::ReadLen(int n) { int64 len = 0; while(n-- > 0) - len = (len << 8) | (byte)Get(); + len = (len << 8) | (byte)socket->Get(); return len; } @@ -51,8 +47,8 @@ bool WebSocket::RecieveRaw() if(IsError()) return false; - opcode = Get(); - int64 len = Get(); + opcode = socket->Get(); + int64 len = socket->Get(); bool mask = len & 128; len &= 127; if(len == 127) @@ -62,22 +58,22 @@ bool WebSocket::RecieveRaw() byte key[4]; if(mask) - Get(key, 4); + socket->Get(key, 4); if(IsError()) { - SetSockError("websocket recieve", ERROR_DATA, "Invalid data"); + socket->SetSockError("websocket recieve", ERROR_DATA, "Invalid data"); return false; } if(len > maxlen) { - SetSockError("websocket recieve", ERROR_LEN_LIMIT, "Frame limit exceeded, size " + AsString(len)); + socket->SetSockError("websocket recieve", ERROR_LEN_LIMIT, "Frame limit exceeded, size " + AsString(len)); return false; } StringBuffer frame((int)len); // TODO int64 char *buffer = ~frame; - if(!GetAll(buffer, (int)len)) { - SetSockError("websocket recieve", ERROR_DATA, "Invalid data"); + if(!socket->GetAll(buffer, (int)len)) { + socket->SetSockError("websocket recieve", ERROR_DATA, "Invalid data"); return false; } @@ -133,8 +129,8 @@ bool WebSocket::SendRaw(int hdr, const void *data, int64 len) else b.Cat((int)len); - if(IsError() || !PutAll(~b, b.GetLength()) || !PutAll(data, (int)len)) { - SetSockError("websocket send", ERROR_SEND, "Failed to send data"); + if(IsError() || !socket->PutAll(~b, b.GetLength()) || !socket->PutAll(data, (int)len)) { + socket->SetSockError("websocket send", ERROR_SEND, "Failed to send data"); return false; } @@ -146,14 +142,18 @@ void WebSocket::Reset() opcode = 0; data.Clear(); maxlen = 10 * 1024 * 1024; + socket = NULL; } void WebSocket::Close() { - TcpSocket::Close(); - opcode = 0; - data.Clear(); + if(socket) { + socket->Close(); + opcode = 0; + data.Clear(); + socket = NULL; + } } diff --git a/uppsrc/Core/src.tpp/WebSocket$en-us.tpp b/uppsrc/Core/src.tpp/WebSocket$en-us.tpp index 8f8f7eefd..f56e1ad20 100644 --- a/uppsrc/Core/src.tpp/WebSocket$en-us.tpp +++ b/uppsrc/Core/src.tpp/WebSocket$en-us.tpp @@ -12,16 +12,25 @@ topic "WebSocket"; [{_}%EN-US [ {{10000@(113.42.0) [s0; [*@7;4 WebSocket]]}}&] [s3;%- &] -[s1;:WebSocket`:`:class:%- [@(0.0.255)3 class][3 _][*3 WebSocket][3 _:_][@(0.0.255)3 public][3 _][*@3;3 T -cpSocket]&] -[s2; Provides the WebSocket protocol support.&] +[s1;:WebSocket`:`:class:%- [@(0.0.255)3 class][3 _][*3 WebSocket]&] +[s2; Provides the WebSocket protocol support. WebSocket is a wrapper +over existing TcpSocket connection, which performs server handshake +by calling WebAccept method.&] [s0;i448;a25;kKO9;:noref:@(0.0.255)%- &] [ {{10000F(128)G(128)@1 [s0; [* Public Method List]]}}&] [s3;%- &] +[s5;:WebSocket`:`:WebAccept`(TcpSocket`&`,HttpHeader`&`):%- [@(0.0.255) bool]_[* WebAccep +t]([_^TcpSocket^ TcpSocket][@(0.0.255) `&]_[*@3 socket], [_^HttpHeader^ HttpHeader][@(0.0.255) `& +]_[*@3 hdr])&] +[s2; Attempts to open websocket connection with [%-*@3 socket], [%-*@3 hdr] +is HTTP header read from socket `- prereading allows to distinguish +between websocket and normal HTTP connection to server.&] +[s3; &] +[s4;%- &] [s5;:WebSocket`:`:WebAccept`(TcpSocket`&`):%- [@(0.0.255) bool]_[* WebAccept]([_^TcpSocket^ T -cpSocket][@(0.0.255) `&]_[*@3 server])&] -[s2; Accepts a websocekt connectiont from listening socket [%-*@3 server] -and performs a handshake. Returns true on success.&] +cpSocket][@(0.0.255) `&]_[*@3 socket])&] +[s2; Reads HTTP header from [%-*@3 socket] and calls other WebAccept +variant.&] [s3; &] [s4; &] [s5;:WebSocket`:`:RecieveRaw`(`):%- [@(0.0.255) bool]_[* RecieveRaw]()&] @@ -89,6 +98,30 @@ ary]([@(0.0.255) const]_[@(0.0.255) void]_`*[*@3 data], [_^int64^ int64]_[*@3 le [s5;:WebSocket`:`:Close`(`):%- [@(0.0.255) void]_[* Close]()&] [s2; Closes the websocket.&] [s3; &] +[s4;%- &] +[s5;:WebSocket`:`:IsOpen`(`)const:%- [@(0.0.255) bool]_[* IsOpen]()_[@(0.0.255) const]&] +[s2; Returns true if WebSocket is associated with open socket.&] +[s3;%- &] +[s4;%- &] +[s5;:WebSocket`:`:IsError`(`)const:%- [@(0.0.255) bool]_[* IsError]()_[@(0.0.255) const]&] +[s2; Returns true if WebSocket is associated with socket and this +socket returns IsError.&] +[s3;%- &] +[s4;%- &] +[s5;:WebSocket`:`:ClearError`(`):%- [@(0.0.255) void]_[* ClearError]()&] +[s2; Clears error in associated socket (if any).&] +[s3;%- &] +[s4;%- &] +[s5;:WebSocket`:`:GetError`(`)const:%- [@(0.0.255) int]_[* GetError]()_[@(0.0.255) const]&] +[s2; Returns error`-code in associated socket, or zero if there is +none.&] +[s3;%- &] +[s4;%- &] +[s5;:WebSocket`:`:GetErrorDesc`(`)const:%- [_^String^ String]_[* GetErrorDesc]()_[@(0.0.255) c +onst]&] +[s2; Returns error descroption in associated socket, or empty string +if there is none.&] +[s3;%- &] [s4; &] [s5;:WebSocket`:`:MaxLen`(int64`):%- [_^WebSocket^ WebSocket][@(0.0.255) `&]_[* MaxLen]([_^int64^ i nt64]_[*@3 maxlen`_])&]