|
@@ -42,6 +42,10 @@ Type
|
|
|
|
|
|
TWasmWebsocket = class(TComponent)
|
|
|
private
|
|
|
+ class var _NextID : TWasmWebSocketID;
|
|
|
+ class function GetNextWebsocketID : TWasmWebSocketID;
|
|
|
+ private
|
|
|
+ FKeepDataAlive: Boolean;
|
|
|
FOnClose: TWasmWebsocketCloseEvent;
|
|
|
FOnError: TWasmWebsocketErrorEvent;
|
|
|
FOnMessage: TWasmWebsocketMessageEvent;
|
|
@@ -50,7 +54,10 @@ Type
|
|
|
FURL: String;
|
|
|
FWebSocketID: TWasmWebSocketID;
|
|
|
FClosed : Boolean;
|
|
|
+ FData : Array of TBytes;
|
|
|
+ FDataCount : Integer;
|
|
|
procedure DoSendMessage(aBytes: TBytes; aType: longint);
|
|
|
+ procedure SetKeepDataAlive(AValue: Boolean);
|
|
|
Protected
|
|
|
procedure CheckWebsocketRes(aResult: TWasmWebsocketResult; const aMsg: String; aLogOnly: Boolean=false);
|
|
|
Procedure DoOpen(const aURL : String; const aProtocols : String); virtual;
|
|
@@ -60,7 +67,9 @@ Type
|
|
|
procedure HandleOpen; virtual;
|
|
|
procedure HandleMessage(aType : Longint; aMessage : TBytes); virtual;
|
|
|
procedure HandleClose(aCode : Longint; aReason : string; aIsClean : Boolean); virtual;
|
|
|
-
|
|
|
+ // Data management
|
|
|
+ procedure ReleaseAllData;
|
|
|
+ Procedure KeepData(const aData : TBytes);
|
|
|
Public
|
|
|
Constructor create(aOwner : TComponent); override;
|
|
|
Destructor Destroy; override;
|
|
@@ -68,6 +77,7 @@ Type
|
|
|
Procedure Close(aCode : Longint; aReason: UTF8String);
|
|
|
Procedure SendMessage(aBytes : TBytes);
|
|
|
Procedure SendMessage(const aString : String);
|
|
|
+ function ReleaseData(const aData : TBytes) : boolean;
|
|
|
Property WebSocketID : TWasmWebSocketID Read FWebSocketID;
|
|
|
Property OnError : TWasmWebsocketErrorEvent Read FOnError Write FOnError;
|
|
|
Property OnMessage : TWasmWebsocketMessageEvent Read FOnMessage Write FOnMessage;
|
|
@@ -75,6 +85,7 @@ Type
|
|
|
Property OnOpen : TWasmWebsocketOpenEvent Read FOnOpen Write FOnOpen;
|
|
|
Property URL : String Read FURL;
|
|
|
Property Protocols : String Read FProtocols;
|
|
|
+ Property KeepDataAlive : Boolean Read FKeepDataAlive Write SetKeepDataAlive;
|
|
|
end;
|
|
|
|
|
|
{ TWasmWebSocketManager }
|
|
@@ -85,10 +96,17 @@ Type
|
|
|
private
|
|
|
Flist : TFPList; // Todo: change to thread list.
|
|
|
protected
|
|
|
- class procedure HandleClose(aWebSocketID: TWasmWebSocketID; aUserData: Pointer; aCode: Longint; const aReason: String; aClean: Boolean); static;
|
|
|
- class procedure HandleError(aWebSocketID: TWasmWebSocketID; aUserData: Pointer); static;
|
|
|
- class procedure HandleMessage(aWebSocketID: TWasmWebSocketID; aUserData: Pointer; aMessageType: TWasmWebSocketMessageType; aMessage: TBytes); static;
|
|
|
- class procedure HandleOpen(aWebSocketID: TWasmWebSocketID; aUserData: Pointer); static;
|
|
|
+ class procedure HandleReleasePackageCallBack(aWebsocketID: TWasmWebSocketID; aUserData: Pointer; aPacket: Pointer; var Result: boolean); static;
|
|
|
+ class procedure HandleCloseCallBack(aWebSocketID: TWasmWebSocketID; aUserData: Pointer; aCode: Longint; const aReason: String; aClean: Boolean); static;
|
|
|
+ class procedure HandleErrorCallBack(aWebSocketID: TWasmWebSocketID; aUserData: Pointer); static;
|
|
|
+ class procedure HandleMessageCallBack(aWebSocketID: TWasmWebSocketID; aUserData: Pointer; aMessageType: TWasmWebSocketMessageType; aMessage: TBytes); static;
|
|
|
+ class procedure HandleOpenCallBack(aWebSocketID: TWasmWebSocketID; aUserData: Pointer); static;
|
|
|
+ function HandleReleasePacket(aSocket : TWasmWebSocket; aPacket : Pointer) : Boolean; virtual;
|
|
|
+ procedure HandleClose(aSocket : TWasmWebSocket; aCode: Longint; const aReason: String; aClean: Boolean); virtual;
|
|
|
+ procedure HandleError(aSocket: TWasmWebSocket); virtual;
|
|
|
+ procedure HandleMessage(aSocket: TWasmWebSocket; aMessageType: TWasmWebSocketMessageType; aMessage: TBytes); virtual;
|
|
|
+ procedure HandleOpen(aSocket: TWasmWebSocket); virtual;
|
|
|
+
|
|
|
procedure RegisterWebSocket(aWebSocket : TWasmWebSocket);
|
|
|
procedure UnRegisterWebSocket(aWebSocket : TWasmWebSocket);
|
|
|
function IsValidWebSocket(aWebSocketID: TWasmWebSocketID; aUserData: Pointer) : Boolean;
|
|
@@ -102,8 +120,16 @@ Type
|
|
|
|
|
|
implementation
|
|
|
|
|
|
+
|
|
|
+
|
|
|
{ TWasmWebsocket }
|
|
|
|
|
|
+class procedure TWasmWebSocketManager.HandleReleasePackageCallBack(aWebsocketID: TWasmWebSocketID; aUserData: Pointer; aPacket: Pointer; var Result: boolean);
|
|
|
+begin
|
|
|
+ If Instance.IsValidWebSocket(aWebSocketID,aUserData) then
|
|
|
+ Result:=Instance.HandleReleasePacket(TWasmWebSocket(aUserData),aPacket);
|
|
|
+end;
|
|
|
+
|
|
|
constructor TWasmWebsocket.create(aOwner : TComponent);
|
|
|
|
|
|
begin
|
|
@@ -112,7 +138,6 @@ begin
|
|
|
FClosed:=False
|
|
|
end;
|
|
|
|
|
|
-
|
|
|
procedure TWasmWebsocket.DoClose(aCode: Longint; aReason: UTF8String; aRaiseError: Boolean);
|
|
|
|
|
|
var
|
|
@@ -125,28 +150,24 @@ begin
|
|
|
CheckWebsocketRes(Res,'close',not aRaiseError);
|
|
|
end;
|
|
|
|
|
|
-
|
|
|
procedure TWasmWebsocket.HandleError;
|
|
|
begin
|
|
|
if assigned(FonError) then
|
|
|
FOnError(Self);
|
|
|
end;
|
|
|
|
|
|
-
|
|
|
procedure TWasmWebsocket.HandleOpen;
|
|
|
begin
|
|
|
if assigned(FonOpen) then
|
|
|
FOnOpen(Self);
|
|
|
end;
|
|
|
|
|
|
-
|
|
|
procedure TWasmWebsocket.HandleMessage(aType: Longint; aMessage: TBytes);
|
|
|
begin
|
|
|
if assigned(FOnMessage) then
|
|
|
FOnMessage(Self,aType=WASMWS_MESSAGE_TYPE_TEXT,aMessage);
|
|
|
end;
|
|
|
|
|
|
-
|
|
|
procedure TWasmWebsocket.HandleClose(aCode: Longint; aReason: string; aIsClean: Boolean);
|
|
|
begin
|
|
|
FClosed:=True;
|
|
@@ -154,7 +175,6 @@ begin
|
|
|
FOnClose(Self,aCode,aReason,aIsClean);
|
|
|
end;
|
|
|
|
|
|
-
|
|
|
procedure TWasmWebsocket.DoOpen(const aURL: String; const aProtocols: String);
|
|
|
|
|
|
var
|
|
@@ -165,11 +185,10 @@ begin
|
|
|
FProtocols:=aProtocols;
|
|
|
lURL:=UTF8Encode(aURL);
|
|
|
lProtocols:=UTF8Encode(aProtocols);
|
|
|
- if __wasm_websocket_allocate(PByte(lURL),Length(lURL),PByte(lProtocols),Length(lProtocols),Self,@FWebSocketID)<>WASMWS_RESULT_SUCCESS then
|
|
|
+ if __wasm_websocket_allocate(PByte(lURL),Length(lURL),PByte(lProtocols),Length(lProtocols),Self,FWebSocketID)<>WASMWS_RESULT_SUCCESS then
|
|
|
Raise EWasmWebsocket.CreateFmt('Failed to allocate websocket for URL %s',[aURL]);
|
|
|
end;
|
|
|
|
|
|
-
|
|
|
destructor TWasmWebsocket.Destroy;
|
|
|
|
|
|
var
|
|
@@ -182,16 +201,16 @@ begin
|
|
|
CheckWebsocketRes(Res,'Deallocating websocket',True);
|
|
|
FWebSocketID:=0;
|
|
|
TWasmWebSocketManager.Instance.UnRegisterWebSocket(Self);
|
|
|
+ ReleaseAllData;
|
|
|
inherited Destroy;
|
|
|
end;
|
|
|
|
|
|
-
|
|
|
procedure TWasmWebsocket.Open(const aURL: String; const aProtocols: String);
|
|
|
begin
|
|
|
+ FWebSocketID:=GetNextWebsocketID;
|
|
|
DoOpen(aURL,aProtocols);
|
|
|
end;
|
|
|
|
|
|
-
|
|
|
procedure TWasmWebsocket.Close(aCode: Longint; aReason: UTF8String);
|
|
|
|
|
|
begin
|
|
@@ -199,7 +218,6 @@ begin
|
|
|
FClosed:=True;
|
|
|
end;
|
|
|
|
|
|
-
|
|
|
procedure TWasmWebsocket.CheckWebsocketRes(aResult : TWasmWebsocketResult; const aMsg :String; aLogOnly : Boolean = false);
|
|
|
|
|
|
var
|
|
@@ -214,6 +232,10 @@ begin
|
|
|
Raise EWasmWebsocket.Create(Err);
|
|
|
end;
|
|
|
|
|
|
+class function TWasmWebsocket.GetNextWebsocketID: TWasmWebSocketID;
|
|
|
+begin
|
|
|
+ Result:=InterlockedIncrement(_NextID);
|
|
|
+end;
|
|
|
|
|
|
procedure TWasmWebsocket.DoSendMessage(aBytes: TBytes; aType : longint);
|
|
|
|
|
@@ -229,9 +251,52 @@ begin
|
|
|
if DataLen=0 then
|
|
|
exit;
|
|
|
Res:=__wasm_websocket_send(FWebsocketID,PByte(aBytes),DataLen,aType);
|
|
|
+ if (Res=WASMWS_RESULT_SUCCESS) and KeepDataAlive then
|
|
|
+ KeepData(aBytes);
|
|
|
CheckWebsocketRes(Res,'Failed to send '+aTypes[aType=WASMWS_MESSAGE_TYPE_TEXT]+' data on websocket');
|
|
|
end;
|
|
|
|
|
|
+procedure TWasmWebsocket.SetKeepDataAlive(AValue: Boolean);
|
|
|
+begin
|
|
|
+ if FKeepDataAlive=AValue then Exit;
|
|
|
+ FKeepDataAlive:=AValue;
|
|
|
+ if not FKeepDataAlive then
|
|
|
+ ReleaseAllData;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TWasmWebsocket.ReleaseAllData;
|
|
|
+
|
|
|
+begin
|
|
|
+ SetLength(FData,0);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TWasmWebsocket.KeepData(const aData: TBytes);
|
|
|
+var
|
|
|
+ lLen : Integer;
|
|
|
+begin
|
|
|
+ lLen:=Length(FData);
|
|
|
+ if (FDataCount=lLen) then
|
|
|
+ SetLength(FData,lLen+10);
|
|
|
+ FData[FDataCount]:=aData;
|
|
|
+ Inc(FDataCount);
|
|
|
+end;
|
|
|
+
|
|
|
+function TWasmWebsocket.ReleaseData(const aData: TBytes): boolean;
|
|
|
+var
|
|
|
+ lIdx : Integer;
|
|
|
+begin
|
|
|
+ Result:=False;
|
|
|
+ lIdx:=FDataCount-1;
|
|
|
+ While (lIdx>=0) and (FData[lIdx]<>aData) do
|
|
|
+ Dec(lIdx);
|
|
|
+ if (lIdx<0) then
|
|
|
+ exit;
|
|
|
+ if lIdx<FDataCount-1 then
|
|
|
+ FData[lIdx]:=FData[FDataCount-1];
|
|
|
+ FData[FDataCount-1]:=Nil;
|
|
|
+ Dec(FDataCount);
|
|
|
+ Result:=True;
|
|
|
+end;
|
|
|
|
|
|
procedure TWasmWebsocket.SendMessage(aBytes: TBytes);
|
|
|
|
|
@@ -239,7 +304,6 @@ begin
|
|
|
DoSendMessage(aBytes,WASMWS_MESSAGE_TYPE_BINARY);
|
|
|
end;
|
|
|
|
|
|
-
|
|
|
procedure TWasmWebsocket.SendMessage(const aString: String);
|
|
|
|
|
|
var
|
|
@@ -268,83 +332,103 @@ begin
|
|
|
begin
|
|
|
C:=DefaultInstanceType;
|
|
|
if C=Nil then C:=TWasmWebSocketManager;
|
|
|
- _instance:=TWasmWebSocketManager.Create;
|
|
|
+ _instance:=C.Create;
|
|
|
end;
|
|
|
- Result:=_instance;
|
|
|
+ Result:=_Instance;
|
|
|
end;
|
|
|
|
|
|
-
|
|
|
procedure TWasmWebSocketManager.RegisterWebSocket(aWebSocket: TWasmWebSocket);
|
|
|
begin
|
|
|
- Writeln(Format('adding websocket [%p]',[Pointer(aWebSocket)]));
|
|
|
Flist.Add(aWebSocket);
|
|
|
end;
|
|
|
|
|
|
-
|
|
|
procedure TWasmWebSocketManager.UnRegisterWebSocket(aWebSocket: TWasmWebSocket);
|
|
|
begin
|
|
|
Flist.Remove(aWebSocket);
|
|
|
end;
|
|
|
|
|
|
-
|
|
|
function TWasmWebSocketManager.IsValidWebSocket(aWebSocketID: TWasmWebSocketID; aUserData: Pointer): Boolean;
|
|
|
begin
|
|
|
Result:=FList.IndexOf(aUserData)<>-1;
|
|
|
If Result then
|
|
|
Result:=TWasmWebSocket(aUserData).WebSocketID=aWebSocketID;
|
|
|
if not Result then
|
|
|
+ begin
|
|
|
__wasmwebsocket_log(wllError,'Invalid websocket received: %d [%p]',[aWebsocketID,aUserData]);
|
|
|
+ end;
|
|
|
end;
|
|
|
|
|
|
+class procedure TWasmWebSocketManager.HandleErrorCallBack(aWebSocketID: TWasmWebSocketID; aUserData: Pointer);
|
|
|
+begin
|
|
|
+ If Instance.IsValidWebSocket(aWebSocketID,aUserData) then
|
|
|
+ Instance.HandleError(TWasmWebSocket(aUserData));
|
|
|
+end;
|
|
|
|
|
|
-class procedure TWasmWebSocketManager.HandleError(aWebSocketID : TWasmWebSocketID; aUserData : Pointer);
|
|
|
+class procedure TWasmWebSocketManager.HandleMessageCallBack(aWebSocketID: TWasmWebSocketID; aUserData: Pointer;
|
|
|
+ aMessageType: TWasmWebSocketMessageType; aMessage: TBytes);
|
|
|
begin
|
|
|
If Instance.IsValidWebSocket(aWebSocketID,aUserData) then
|
|
|
- TWasmWebSocket(aUserData).HandleError;
|
|
|
+ Instance.HandleMessage(TWasmWebSocket(aUserData),aMessageType,aMessage);
|
|
|
end;
|
|
|
|
|
|
+class procedure TWasmWebSocketManager.HandleCloseCallBack(aWebSocketID: TWasmWebSocketID; aUserData: Pointer; aCode: Longint;
|
|
|
+ const aReason: String; aClean: Boolean);
|
|
|
+begin
|
|
|
+ If Instance.IsValidWebSocket(aWebSocketID,aUserData) then
|
|
|
+ Instance.HandleClose(TWasmWebSocket(aUserData),aCode,aReason,aClean);
|
|
|
+end;
|
|
|
|
|
|
-class procedure TWasmWebSocketManager.HandleMessage(aWebSocketID : TWasmWebSocketID; aUserData : Pointer; aMessageType : TWasmWebSocketMessageType; aMessage : TBytes);
|
|
|
+class procedure TWasmWebSocketManager.HandleOpenCallBack(aWebSocketID: TWasmWebSocketID; aUserData: Pointer);
|
|
|
begin
|
|
|
If Instance.IsValidWebSocket(aWebSocketID,aUserData) then
|
|
|
- TWasmWebSocket(aUserData).HandleMessage(aMessageType,aMessage);
|
|
|
+ Instance.HandleOpen(TWasmWebSocket(aUserData));
|
|
|
end;
|
|
|
|
|
|
+function TWasmWebSocketManager.HandleReleasePacket(aSocket: TWasmWebSocket; aPacket: Pointer): Boolean;
|
|
|
+begin
|
|
|
+ aSocket.ReleaseData(TBytes(aPacket));
|
|
|
+end;
|
|
|
|
|
|
-class procedure TWasmWebSocketManager.HandleClose(aWebSocketID : TWasmWebSocketID; aUserData : Pointer; aCode: Longint; const aReason : String; aClean : Boolean);
|
|
|
+procedure TWasmWebSocketManager.HandleClose(aSocket: TWasmWebSocket; aCode: Longint; const aReason: String; aClean: Boolean);
|
|
|
begin
|
|
|
- If Instance.IsValidWebSocket(aWebSocketID,aUserData) then
|
|
|
- TWasmWebSocket(aUserData).HandleClose(aCode,aReason,aClean)
|
|
|
+ aSocket.HandleClose(aCode,aReason,aClean);
|
|
|
end;
|
|
|
|
|
|
+procedure TWasmWebSocketManager.HandleError(aSocket: TWasmWebSocket);
|
|
|
+begin
|
|
|
+ aSocket.HandleError;
|
|
|
+end;
|
|
|
|
|
|
-class procedure TWasmWebSocketManager.HandleOpen(aWebSocketID : TWasmWebSocketID; aUserData : Pointer);
|
|
|
+procedure TWasmWebSocketManager.HandleMessage(aSocket: TWasmWebSocket; aMessageType: TWasmWebSocketMessageType; aMessage: TBytes);
|
|
|
begin
|
|
|
- If Instance.IsValidWebSocket(aWebSocketID,aUserData) then
|
|
|
- TWasmWebSocket(aUserData).HandleOpen;
|
|
|
+ aSocket.HandleMessage(aMessageType,aMessage);
|
|
|
end;
|
|
|
|
|
|
-class constructor TWasmWebSocketManager.init;
|
|
|
+procedure TWasmWebSocketManager.HandleOpen(aSocket: TWasmWebSocket);
|
|
|
begin
|
|
|
- WebSocketErrorCallback:=@HandleError;
|
|
|
- WebSocketMessageCallback:=@HandleMessage;
|
|
|
- WebSocketCloseCallback:=@HandleClose;
|
|
|
- WebSocketOpenCallback:=@HandleOpen;
|
|
|
+ aSocket.HandleOpen;
|
|
|
end;
|
|
|
|
|
|
|
|
|
+class constructor TWasmWebSocketManager.init;
|
|
|
+begin
|
|
|
+ WebSocketErrorCallback:=@HandleErrorCallBack;
|
|
|
+ WebSocketMessageCallback:=@HandleMessageCallBack;
|
|
|
+ WebSocketCloseCallback:=@HandleCloseCallBack;
|
|
|
+ WebSocketOpenCallback:=@HandleOpenCallBack;
|
|
|
+ WebSocketReleasePackageCallBack:=@HandleReleasePackageCallBack;
|
|
|
+end;
|
|
|
+
|
|
|
constructor TWasmWebSocketManager.create;
|
|
|
begin
|
|
|
Flist:=TFPList.Create;
|
|
|
end;
|
|
|
|
|
|
-
|
|
|
destructor TWasmWebSocketManager.destroy;
|
|
|
begin
|
|
|
FreeAndNil(Flist);
|
|
|
inherited destroy;
|
|
|
end;
|
|
|
|
|
|
-
|
|
|
end.
|
|
|
|