wasm.pas2js.websocket.handler.pas 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. unit wasm.pas2js.websocket.handler;
  2. {$mode ObjFPC}
  3. interface
  4. {$mode objfpc}
  5. {$modeswitch externalclass}
  6. uses
  7. JS
  8. , SysUtils
  9. , Rtl.WorkerCommands
  10. , Rtl.WebThreads
  11. , wasm.websocket.shared
  12. , wasm.pas2js.websocketapi
  13. // , W3D.Api.WasmHost
  14. ;
  15. type
  16. { TApplication }
  17. TWebSocketCommand = record
  18. Operation : Integer;
  19. WebsocketID : integer;
  20. IntData : Integer;
  21. Data1 : TJSUInt8Array;
  22. Data2 : TJSUInt8Array;
  23. end;
  24. { TWasmWebSocketAPIHandler }
  25. TWasmWebSocketAPIHandler = class(TWasmWebSocketAPI)
  26. const
  27. SizeInt32 = 4; // Size in bytes
  28. private
  29. FSharedMem: TJSSharedArrayBuffer;
  30. FArray32: TJSInt32Array;
  31. FArray8: TJSUInt8Array;
  32. FView : TJSDataView;
  33. function HandleCloseCmd(aCmd: TWebSocketCommand): TWasmWebsocketResult;
  34. function HandleCreateCmd(aCmd: TWebSocketCommand): TWasmWebsocketResult;
  35. function HandleFreeCmd(aCmd: TWebSocketCommand): TWasmWebsocketResult;
  36. function HandleSendCmd(aCmd: TWebSocketCommand): TWasmWebsocketResult;
  37. function ProcessCommand(aValue: JSValue): JSValue;
  38. procedure SetSharedMem(AValue: TJSSharedArrayBuffer);
  39. procedure WatchSemaphore;
  40. protected
  41. procedure ReleasePacket(aWebSocketID : TWasmWebsocketID; aPointer : TWasmPointer);
  42. published
  43. Property SharedMem : TJSSharedArrayBuffer Read FSharedMem Write SetSharedMem;
  44. end;
  45. TSetSharedMemWorkerCommand = class external name 'Object' (TCustomWorkerCommand)
  46. Buffer : TJSSharedArrayBuffer;
  47. end;
  48. implementation
  49. function TWasmWebSocketAPIHandler.HandleCreateCmd(aCmd: TWebSocketCommand): TWasmWebsocketResult;
  50. var
  51. lURL,lProtocols : String;
  52. lData : TJSUint8Array;
  53. lSock : TWasmWebsocket;
  54. begin
  55. // Decoder does not like shared mem
  56. if assigned(aCmd.Data1) then
  57. begin
  58. lData:=TJSUint8Array(aCmd.Data1.slice(0));
  59. lURL:=Decoder.Decode(LData);
  60. end;
  61. if Assigned(aCmd.Data2) then
  62. begin
  63. lData:=TJSUint8Array(aCmd.Data2.slice(0));
  64. lProtocols:=Decoder.Decode(LData);
  65. end;
  66. {$IFNDEF NOLOGAPICALLS}
  67. If LogAPICalls then
  68. LogCall('HTTP.HandleCreateCmd("%s","%s",%d,%d)',[lURL,lProtocols,aCmd.IntData,aCmd.WebsocketID]);
  69. {$ENDIF}
  70. lSock:=CreateWebSocket(aCmd.WebsocketID,aCmd.IntData,lURL,lProtocols);
  71. if lSock=Nil then
  72. Result:=WASMWS_RESULT_ERROR
  73. else
  74. Result:=WASMWS_RESULT_SUCCESS;
  75. // No need to do anything
  76. end;
  77. function TWasmWebSocketAPIHandler.HandleFreeCmd(aCmd: TWebSocketCommand): TWasmWebsocketResult;
  78. begin
  79. {$IFNDEF NOLOGAPICALLS}
  80. If LogAPICalls then
  81. LogCall('HTTP.HandleFreeCmd(%d)',[aCmd.WebsocketID]);
  82. {$ENDIF}
  83. if FreeWebSocket(aCmd.WebsocketID) then
  84. Result:=WASMWS_RESULT_SUCCESS
  85. else
  86. Result:=WASMWS_RESULT_INVALIDID;
  87. end;
  88. function TWasmWebSocketAPIHandler.HandleCloseCmd(aCmd: TWebSocketCommand): TWasmWebsocketResult;
  89. var
  90. lSock : TWasmWebsocket;
  91. lData : TJSUint8Array;
  92. lReason : String;
  93. begin
  94. lSock:=GetWebsocket(aCmd.WebsocketID);
  95. if not assigned(lSock) then
  96. Result:=WASMWS_RESULT_INVALIDID
  97. else
  98. begin
  99. lData:=TJSUint8Array(aCmd.Data1.slice(0));
  100. lReason:=Decoder.Decode(lData);
  101. lSock.Close(acmd.IntData,lReason);
  102. Result:=WASMWS_RESULT_SUCCESS;
  103. end;
  104. end;
  105. function TWasmWebSocketAPIHandler.HandleSendCmd(aCmd: TWebSocketCommand): TWasmWebsocketResult;
  106. var
  107. lSock : TWasmWebsocket;
  108. lData: TJSUint8Array;
  109. lText : String;
  110. begin
  111. lSock:=GetWebsocket(aCmd.WebsocketID);
  112. if Not assigned(lSock) then
  113. result:=WASMWS_RESULT_INVALIDID
  114. else
  115. begin
  116. // Neither SendBinarry nor Decoder like shared mem, need to copy...
  117. lData:=TJSUint8Array(aCmd.Data1.slice(0));
  118. if aCmd.IntData=WASMWS_MESSAGE_TYPE_BINARY then
  119. lSock.SendBinary(lData.buffer)
  120. else
  121. begin
  122. lText:=Decoder.Decode(lData);
  123. lSock.SendText(lText);
  124. end;
  125. Result:=WASMWS_RESULT_SUCCESS;
  126. end;
  127. end;
  128. function TWasmWebSocketAPIHandler.ProcessCommand(aValue: JSValue): JSValue;
  129. var
  130. Cmd : TWebSocketCommand;
  131. lOffset,lLength : Integer;
  132. lResult:TWasmWebsocketResult;
  133. begin
  134. {$IFNDEF NOLOGAPICALLS}
  135. If LogAPICalls then
  136. LogCall('TWasmWebSocketAPIHandler Processing command');
  137. {$ENDIF}
  138. Result:=null;
  139. if not (String(aValue)='ok') then exit;
  140. Cmd:=Default(TWebSocketCommand);
  141. Cmd.WebsocketID:=FView.getInt32(WASM_SHMSG_WEBSOCKETID,Env.IsLittleEndian);
  142. Cmd.Operation:=FView.getUInt8(WASM_SHMSG_OPERATION);
  143. lResult:=WASMWS_RESULT_SUCCESS;
  144. case Cmd.Operation of
  145. WASM_WSOPERATION_CREATE:
  146. begin
  147. {$IFNDEF NOLOGAPICALLS}
  148. If LogAPICalls then
  149. LogCall('TWasmWebSocketAPIHandler Processing CREATE command');
  150. {$ENDIF}
  151. lLength:=FView.getInt32(WASM_SHMSG_CREATE_URL_LENGTH,Env.IsLittleEndian);
  152. if lLength>0 then
  153. Cmd.Data1:=TJSUint8Array.New(FSharedMem,WASM_SHMSG_CREATE_URL_DATA,lLength);
  154. lOffset:=WASM_SHMSG_CREATE_PROTOCOL_DATA_OFFSET+lLength;
  155. lLength:=FView.getInt32(WASM_SHMSG_CREATE_PROTOCOL_LENGTH,Env.IsLittleEndian);
  156. if lLength>0 then
  157. Cmd.Data2:=TJSUint8Array.New(FSharedMem,lOffset,lLength);
  158. Cmd.IntData:=FView.getInt32(WASM_SHMSG_CREATE_USERDATA,Env.IsLittleEndian);
  159. lResult:=HandleCreateCmd(Cmd);
  160. {$IFNDEF NOLOGAPICALLS}
  161. If LogAPICalls then
  162. LogCall('TWasmWebSocketAPIHandler Processed CREATE command. Result: %d',[lResult]);
  163. {$ENDIF}
  164. end;
  165. WASM_WSOPERATION_FREE:
  166. lResult:=HandleFreeCmd(Cmd);
  167. WASM_WSOPERATION_SEND:
  168. begin
  169. {$IFNDEF NOLOGAPICALLS}
  170. If LogAPICalls then
  171. LogCall('TWasmWebSocketAPIHandler Processing SEND command');
  172. {$ENDIF}
  173. lLength:=FView.getInt32(WASM_SHMSG_SEND_DATA_LENGTH,Env.IsLittleEndian);
  174. lOffset:=FView.getInt32(WASM_SHMSG_SEND_DATA_ADDRESS,Env.IsLittleEndian);
  175. Cmd.Data1:=TJSUint8Array.New(Self.getModuleMemoryDataView.buffer,lOffset,lLength);
  176. Cmd.IntData:=FView.getInt32(WASM_SHMSG_SEND_DATA_TYPE,Env.IsLittleEndian);
  177. {$IFNDEF NOLOGAPICALLS}
  178. If LogAPICalls then
  179. LogCall('TWasmWebSocketAPIHandler Processing send command. Data type: %d',[Cmd.IntData]);
  180. {$ENDIF}
  181. lResult:=HandleSendCmd(Cmd);
  182. ReleasePacket(cmd.WebsocketID,lOffset);
  183. end;
  184. WASM_WSOPERATION_CLOSE:
  185. begin
  186. {$IFNDEF NOLOGAPICALLS}
  187. If LogAPICalls then
  188. LogCall('TWasmWebSocketAPIHandler Processing CLOSE command');
  189. {$ENDIF}
  190. Cmd.IntData:=FView.getInt32(WASM_SHMSG_CLOSE_CODE,Env.IsLittleEndian);
  191. lOffset:=FView.getInt32(WASM_SHMSG_CLOSE_REASON_DATA,Env.IsLittleEndian);
  192. lLength:=FView.getInt32(WASM_SHMSG_CLOSE_REASON_LENGTH,Env.IsLittleEndian);
  193. Cmd.Data1:=TJSUint8Array.New(FSharedMem,lOffset,lLength);
  194. lResult:=HandleCloseCmd(Cmd);
  195. end
  196. else
  197. lResult:=WASMWS_RESULT_ERROR;
  198. end;
  199. // Store result
  200. TJSAtomics.store(FArray32,WASM_SHMSG_RESULT,lResult);
  201. // Set semaphore
  202. TJSAtomics.store(FArray32,WASM_SHMSG_SEMAPHORE,WASM_SEM_NOT_SET);
  203. // Notify
  204. TJSAtomics.notify(FArray32, WASM_SHMSG_SEMAPHORE, 4);
  205. // Restart watch...
  206. WatchSemaphore;
  207. end;
  208. procedure TWasmWebSocketAPIHandler.SetSharedMem(AValue: TJSSharedArrayBuffer);
  209. begin
  210. {$IFNDEF NOLOGAPICALLS}
  211. If LogAPICalls then
  212. LogCall('TWasmWebSocketAPIHandler, setting shared mem.');
  213. {$ENDIF}
  214. if FSharedMem=AValue then Exit;
  215. FSharedMem:=AValue;
  216. if assigned(FSharedMem) then
  217. begin
  218. FArray32:=TJSInt32Array.New(FSharedMem);
  219. FArray8:=TJSUInt8Array.New(FSharedMem);
  220. FView:=TJSDataView.New(FSharedMem);
  221. WatchSemaphore;
  222. end
  223. else
  224. begin
  225. FArray8:=Nil;
  226. FArray32:=Nil;
  227. FView:=Nil;
  228. end;
  229. end;
  230. procedure TWasmWebSocketAPIHandler.WatchSemaphore;
  231. var
  232. lResult : TJSAtomicWaitResult;
  233. begin
  234. lResult:=TJSAtomics.waitAsync(FArray32,WASM_SHMSG_SEMAPHORE,WASM_SEM_NOT_SET);
  235. if lResult.async then
  236. lResult.valueAsPromise._then(@ProcessCommand)
  237. else if lResult.valueAsString='ok' then
  238. ProcessCommand('ok');
  239. end;
  240. procedure TWasmWebSocketAPIHandler.ReleasePacket(aWebSocketID : TWasmWebsocketID; aPointer: TWasmPointer);
  241. Type
  242. TReleasePacketFunc = function(aWebSocketID : Longint; aUserData : TWasmPointer; aPointer: TWasmPointer) : Integer;
  243. var
  244. lRes : Boolean;
  245. lFunc : TReleasePacketFunc;
  246. lSock : TWasmWebsocket;
  247. begin
  248. lSock:=GetWebsocket(aWebsocketID);
  249. if not Assigned(lSock) then exit;
  250. lFunc:=TReleasePacketFunc(InstanceExports['__wasm_websocket_release_packet']);
  251. lRes:=Assigned(lFunc);
  252. if lRes then
  253. lRes:=lFunc(aWebSocketID,lSock.UserData,aPointer)=0;
  254. end;
  255. end.