mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-08-31 07:12:39 -06:00
Core: WebSocket support refactored
git-svn-id: svn://ultimatepp.org/upp/trunk@6712 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
95fa1d9126
commit
48e58356e6
3 changed files with 85 additions and 44 deletions
|
|
@ -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; }
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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`_])&]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue