wasm.pas2js.httpapi.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  1. {
  2. This file is part of the Pas2JS run time library.
  3. Provides a Webassembly module with HTTP protocol capabilities
  4. Copyright (c) 2024 by Michael Van Canneyt
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit wasm.pas2js.httpapi;
  12. {$mode ObjFPC}
  13. {$modeswitch externalclass}
  14. { $DEFINE NOLOGAPICALLS}
  15. interface
  16. uses
  17. {$IFDEF FPC_DOTTEDUNITS}
  18. System.SysUtils, JSApi.JS, BrowserApi.WebOrWorker, {$IFDEF JOB_WORKER} BrowserApi.WebWorker {$ELSE} BrowserApi.Web {$ENDIF}, Wasi.Env, wasm.http.shared, Rtl.WorkerCommands;
  19. {$ELSE}
  20. SysUtils, JS, WebOrWorker, {$IFDEF JOB_WORKER} WebWorker {$ELSE} Web {$ENDIF}, WasiEnv, types, wasm.http.shared, Rtl.WorkerCommands;
  21. {$ENDIF}
  22. const
  23. cmdOpenURL = 'openUrl';
  24. Type
  25. TWasmHTTPAPI = Class;
  26. TWasmHTTPFetch = Class;
  27. TOpenURLCommand = class external name 'Object' (TCustomWorkerCommand)
  28. URL : String;
  29. Flags : Integer;
  30. end;
  31. { TOpenURLCommandHelper }
  32. TOpenURLCommandHelper = class helper (TCustomWorkerCommandHelper) for TOpenURLCommand
  33. class function CreateURL(aURL : String; aFlags : Integer) : TOpenURLCommand; static;
  34. end;
  35. TWasmHTTPRequest = Record
  36. Url : String;
  37. Method : String;
  38. Headers : TStringDynArray;
  39. BodyIsString : Boolean;
  40. Body : TJSArrayBuffer;
  41. BodyAsText : string;
  42. Integrity : String;
  43. Redirect: string;
  44. Cache : String;
  45. KeepAlive : Boolean;
  46. Mode : String;
  47. Priority : String;
  48. Referrer : String;
  49. ReferrerPolicy : String;
  50. AbortSignal : Boolean;
  51. Credentials: String;
  52. end;
  53. PWasmHTTPRequest = TWasmPointer;
  54. PLongint = TWasmPointer;
  55. PByte = TWasmPointer;
  56. { TWasmHTTPFetch }
  57. TWasmHTTPFetch = Class(TObject)
  58. Private
  59. FAPI : TWasmHTTPAPI;
  60. FID : TWasmHTTPRequestID;
  61. FUserData : TWasmPointer;
  62. FRequestData : TWasmHTTPRequest;
  63. FResponse : TJSResponse;
  64. FheaderNames : TStringDynArray;
  65. FAbortController : TJSAbortController;
  66. FResultBody : TJSArrayBuffer;
  67. FRequestError : String;
  68. FInProgress : Boolean;
  69. function GetHeaderName(aIndex : Longint): String;
  70. function GetHeaderCount: Integer;
  71. Public
  72. Constructor Create(aAPI: TWasmHTTPAPI; aID : TWasmHTTPRequestID; aUserData : TWasmPointer; const aRequestData : TWasmHTTPRequest);
  73. Procedure Execute; async;
  74. Property ID : TWasmHTTPRequestID Read FID;
  75. Property UserData : TWasmPointer Read FUserData;
  76. Property RequestData : TWasmHTTPRequest Read FRequestData;
  77. Property Response : TJSResponse Read FResponse;
  78. Property HeaderNames[aIndex : Longint] : String Read GetHeaderName;
  79. Property HeaderCount : Integer Read GetHeaderCount;
  80. Property InProgress : Boolean Read FInProgress;
  81. Property RequestError : String Read FRequestError;
  82. end;
  83. { TWasmHTTPAPI }
  84. TWasmHTTPAPI = class(TImportExtension)
  85. private
  86. FNextRequestID : TWasmHTTPRequestID;
  87. FRequests : TJSOBject;
  88. class function ContentTypeIsString(aType: String): boolean;
  89. function GetLogApiCalls: Boolean;
  90. procedure HandleOpenURLMessage(aCommand: TOpenURLCommand);
  91. function ReadRequest(aRequest :PWasmHTTPRequest) : TWasmHTTPRequest;
  92. function RequestExecute(aRequestID: TWasmHTTPRequestID): TWasmHTTPResult;
  93. procedure SetLogApiCalls(AValue: Boolean);
  94. Protected
  95. Procedure LogCall(const Msg : String);
  96. Procedure LogCall(Const Fmt : String; const Args : Array of const);
  97. Procedure DoneRequest(aFetch : TWasmHTTPFetch);
  98. Function CreateRequestID : TWasmHTTPRequestID;
  99. Function FetchByID(aID : TWasmHTTPRequestID) : TWasmHTTPFetch;
  100. procedure DoOpenURL(aURL: String; aFlags: integer);
  101. function HandleOpenURL(aURL: TWasmPointer; aURLLen: Longint; aFlags: Integer): Integer;
  102. function RequestAllocate(aRequest : PWasmHTTPRequest; aUserData : TWasmPointer; aRequestID : PWasmHTTPRequestID) : TWasmHTTPResult;
  103. function RequestDeallocate(aRequestID : TWasmHTTPRequestID) : TWasmHTTPResult;
  104. function RequestAbort(aRequestID : TWasmHTTPRequestID) : TWasmHTTPResult;
  105. function ResponseGetStatus(aRequestID : TWasmHTTPRequestID; aStatus : PLongint) : TWasmHTTPResult;
  106. function ResponseGetStatusText(aRequestID: TWasmHTTPRequestID; aStatusText: PByte; aMaxTextLen: PLongint): TWasmHTTPResult;
  107. function ResponseGetHeaderCount(aRequestID : TWasmHTTPRequestID; aHeaderCount : PLongint) : TWasmHTTPResult;
  108. function ResponseGetHeaderName(aRequestID : TWasmHTTPRequestID; aHeaderIdx: Longint; aHeader : PByte; aMaxHeaderLen : PLongint) : TWasmHTTPResult;
  109. function ResponseGetHeader(aRequestID : TWasmHTTPRequestID; aHeaderName: PByte; aHeaderLen : PLongint; aHeader : PByte; aMaxHeaderLen : Longint) : TWasmHTTPResult;
  110. function ResponseGetBody(aRequestID : TWasmHTTPRequestID; aBody : PByte; aMaxBodyLen : PLongint) : TWasmHTTPResult;
  111. Public
  112. Constructor Create(aEnv: TPas2JSWASIEnvironment); override;
  113. Procedure FillImportObject(aObject: TJSObject); override;
  114. Function ImportName : String; override;
  115. property LogApiCalls : Boolean Read GetLogApiCalls Write SetLogApiCalls;
  116. end;
  117. Function CacheToString(aCache : Integer) : String;
  118. implementation
  119. uses strutils;
  120. Const
  121. CacheNames : Array[0..5] of string = ('default','no-store','reload','no-cache','force-cache','only-if-cached');
  122. ModeNames : Array[0..4] of string = ('cors','same-origin','no-cors','navigate','websocket');
  123. PriorityNames : Array[0..2] of string = ('auto','low','high');
  124. RedirectNames : Array[0..2] of string = ('follow','error','manual');
  125. CredentialNames : Array[0..2] of string = ('same-origin','omit','include');
  126. Function CacheToString(aCache : Integer) : String;
  127. begin
  128. Result:='';
  129. if (aCache>=0) and (aCache<=5) then
  130. Result:=CacheNames[aCache];
  131. end;
  132. Function RedirectToString(aRedirect : Integer) : String;
  133. begin
  134. Result:='';
  135. if (aRedirect>=0) and (aRedirect<=2) then
  136. Result:=RedirectNames[aRedirect];
  137. end;
  138. function KeepAliveToBool(const aKeepAlive : Integer) : boolean;
  139. begin
  140. Result:=aKeepAlive<>0;
  141. end;
  142. function AbortSignalToBool(const aKeepAlive : Integer) : boolean;
  143. begin
  144. Result:=aKeepAlive<>0;
  145. end;
  146. function ModeToString(const aMode : Integer) : string;
  147. begin
  148. Result:='';
  149. if (aMode>=0) and (aMode<=4) then
  150. Result:=ModeNames[aMode];
  151. end;
  152. function PriorityToString(const aPriority : Integer) : string;
  153. begin
  154. Result:='';
  155. if (aPriority>=0) and (aPriority<=2) then
  156. Result:=PriorityNames[aPriority];
  157. end;
  158. function CredentialsToString(const aCredentials : Integer) : string;
  159. begin
  160. Result:='';
  161. if (aCredentials>=0) and (aCredentials<=2) then
  162. Result:=CredentialNames[aCredentials];
  163. end;
  164. { TWasmHTTPFetch }
  165. function TWasmHTTPFetch.GetHeaderCount: Integer;
  166. var
  167. It : TJSIterator;
  168. Itm : TJSIteratorValue;
  169. begin
  170. if (Length(FheaderNames)=0) and Assigned(FResponse) then
  171. begin
  172. It:=FResponse.headers.Keys;
  173. Itm:=It.next;
  174. While not Itm.done do
  175. begin
  176. TJSArray(FheaderNames).Push(Itm.value);
  177. Itm:=It.Next;
  178. end;
  179. end;
  180. Result:=Length(FHeaderNames);
  181. end;
  182. function TWasmHTTPFetch.GetHeaderName(aIndex : Longint): String;
  183. begin
  184. if (aIndex>=0) and (aIndex<Length(FHeaderNames)) then
  185. Result:=FHeaderNames[aIndex]
  186. else
  187. Result:='';
  188. end;
  189. constructor TWasmHTTPFetch.Create(aAPI: TWasmHTTPAPI; aID: TWasmHTTPRequestID; aUserData: TWasmPointer;
  190. const aRequestData: TWasmHTTPRequest);
  191. begin
  192. FAPI:=aAPI;
  193. FID:=aID;
  194. FUserData:=aUserData;
  195. FRequestData:=aRequestData;
  196. FheaderNames:=[];
  197. FInProgress:=True;
  198. end;
  199. procedure TWasmHTTPFetch.Execute; async;
  200. var
  201. lResponse : TJSResponse;
  202. lBuf : TJSarrayBuffer;
  203. lRequest : TJSRequest;
  204. lHeaders,lRequestInit : TJSObject;
  205. HNV : TStringDynArray;
  206. H,N,V : String;
  207. Procedure MaybeInit(const aName,aValue : String);
  208. begin
  209. if aValue<>'' then
  210. lRequestInit[aName]:=aValue;
  211. end;
  212. begin
  213. lRequestInit:=TJSObject.New;
  214. if Length(FRequestData.Headers)>0 then
  215. begin
  216. lHeaders:=TJSObject.new;
  217. lRequestInit['headers']:=lHeaders;
  218. for H in FRequestData.Headers do
  219. begin
  220. HNV:=TJSString(H).split(':');
  221. V:='';
  222. N:=Trim(HNV[0]);
  223. if Length(HNV)>1 then
  224. V:=Trim(HNV[1]);
  225. lHeaders[N]:=V;
  226. end;
  227. end;
  228. With FRequestData do
  229. begin
  230. MaybeInit('mode',Mode);
  231. MaybeInit('method',Method);
  232. MaybeInit('cache',Cache);
  233. MaybeInit('integrity',Integrity);
  234. if Assigned(Body) then
  235. lRequestInit['body']:=Body
  236. else if BodyIsString and (BodyAsText<>'') then
  237. lRequestInit['body']:=BodyAsText;
  238. if KeepAlive then
  239. lRequestInit['keepalive']:=KeepAlive;
  240. MaybeInit('redirect',Redirect);
  241. MaybeInit('priority',Priority);
  242. MaybeInit('referrer',Referrer);
  243. MaybeInit('referrerPolicy',ReferrerPolicy);
  244. if AbortSignal then
  245. begin
  246. FAbortController:=TJSAbortController.New;
  247. lRequestInit['signal']:=FAbortController.Signal;
  248. end;
  249. end;
  250. lRequest:=TJSRequest.New(FRequestData.Url,lRequestInit);
  251. lBuf:=Nil;
  252. try
  253. {$IFDEF JOB_WORKER}
  254. lResponse:=aWait(TJSResponse,webworker.fetch(lRequest));
  255. {$ELSE}
  256. lResponse:=aWait(Window.Asyncfetch(lRequest));
  257. {$ENDIF}
  258. lBuf:=aWait(TJSArrayBuffer,lResponse.arrayBuffer);
  259. fResultBody:=lBuf;
  260. FResponse:=lResponse;
  261. except
  262. on E : TJSError do
  263. FRequestError:=e.Message;
  264. on O : TJSObject do
  265. if O.hasOwnProperty('message') and IsString(O.Properties['message']) then
  266. FRequestError:=String(O.Properties['message']);
  267. end;
  268. FInProgress:=False;
  269. // Notify the API
  270. if assigned(FAPI) then
  271. FAPI.DoneRequest(Self);
  272. end;
  273. { TOpenURLCommandHelper }
  274. class function TOpenURLCommandHelper.CreateURL(aURL: String; aFlags: Integer): TOpenURLCommand;
  275. begin
  276. Result:=TOpenURLCommand(CreateCommand(cmdOpenURL));
  277. TOpenURLCommand(Result).URL:=aURL;
  278. TOpenURLCommand(Result).Flags:=aFlags;
  279. end;
  280. { TWasmHTTPAPI }
  281. class function TWasmHTTPAPI.ContentTypeIsString(aType : String) : boolean;
  282. begin
  283. Result:=False;
  284. aType:=LowerCase(ExtractWord(1,aType,';'));
  285. case LowerCase(aType) of
  286. 'application/json',
  287. 'text/text',
  288. 'text/html' : Result:=True;
  289. end;
  290. end;
  291. function TWasmHTTPAPI.GetLogApiCalls: Boolean;
  292. begin
  293. Result:=LogAPI;
  294. end;
  295. function TWasmHTTPAPI.ReadRequest(aRequest: PWasmHTTPRequest): TWasmHTTPRequest;
  296. Var
  297. P : TWasmPointer;
  298. V : TJSDataView;
  299. HeaderCount : Integer;
  300. Function GetInt32 : longint;
  301. begin
  302. Result:=v.getInt32(P,Env.IsLittleEndian);
  303. Inc(P,SizeInt32);
  304. end;
  305. Function GetString : string;
  306. var
  307. Ptr,Len : Longint;
  308. begin
  309. Ptr:=v.getInt32(P,Env.IsLittleEndian);
  310. Inc(P,SizeInt32);
  311. Len:=v.getInt32(P,Env.IsLittleEndian);
  312. Inc(P,SizeInt32);
  313. Result:=Env.GetUTF8StringFromMem(Ptr,Len);
  314. end;
  315. Function GetStringFromAddr(Ptr : Longint) : string;
  316. var
  317. SPtr,Len : Longint;
  318. begin
  319. SPtr:=v.getInt32(Ptr,Env.IsLittleEndian);
  320. Inc(Ptr,SizeInt32);
  321. Len:=v.getInt32(Ptr,Env.IsLittleEndian);
  322. Result:=Env.GetUTF8StringFromMem(SPtr,Len);
  323. end;
  324. Function GetBuffer : TJSArrayBuffer;
  325. var
  326. Ptr,Len : Longint;
  327. begin
  328. Result:=Nil;
  329. Ptr:=v.getInt32(P,Env.IsLittleEndian);
  330. Inc(P,SizeInt32);
  331. Len:=v.getInt32(P,Env.IsLittleEndian);
  332. Inc(P,SizeInt32);
  333. if Len>0 then
  334. Result:=Env.Memory.buffer.slice(Ptr,Ptr+Len);
  335. end;
  336. var
  337. i : Integer;
  338. Hdrs : Longint;
  339. lHeader,lHeaderName,lHeaderValue : String;
  340. begin
  341. v:=getModuleMemoryDataView;
  342. P:=aRequest;
  343. // Order is important !
  344. Result.Url:=GetString;
  345. Result.Method:=GetString;
  346. HeaderCount:=v.getInt32(P,Env.IsLittleEndian);
  347. SetLength(Result.Headers,HeaderCount);
  348. inc(P,SizeInt32);
  349. // Pointer to list of strings
  350. Hdrs:=v.getInt32(P,Env.IsLittleEndian);
  351. inc(P,SizeInt32);
  352. for I:=0 to HeaderCount-1 do
  353. begin
  354. lHeader:=GetStringFromAddr(Hdrs);
  355. Result.Headers[i]:=lHeader;
  356. lHeaderName:=Trim(ExtractWord(1,lHeader,':'));
  357. lHeaderValue:=Trim(ExtractWord(2,lHeader,':'));
  358. if SameText(lheaderName,'Content-Type') then
  359. Result.BodyIsString:=ContentTypeIsString(lHeaderValue);
  360. inc(Hdrs,SizeInt32*2);
  361. end;
  362. if Result.BodyIsString then
  363. Result.BodyAsText:=GetString
  364. else
  365. Result.Body:=GetBuffer;
  366. Result.Integrity:=GetString;
  367. Result.Redirect:=RedirectToString(GetInt32);
  368. Result.Cache:=CacheToString(GetInt32);
  369. Result.KeepAlive:=KeepAliveToBool(GetInt32);
  370. Result.Mode:=ModeToString(GetInt32);
  371. Result.Priority:=PriorityToString(GetInt32);
  372. Result.Referrer:=GetString;
  373. Result.ReferrerPolicy:=GetString;
  374. Result.AbortSignal:=AbortSignalToBool(GetInt32);
  375. Result.Credentials:=CredentialsToString(GetInt32);
  376. end;
  377. procedure TWasmHTTPAPI.LogCall(const Msg: String);
  378. begin
  379. {$IFNDEF NOLOGAPICALLS}
  380. if LogAPI then
  381. DoLog(Msg);
  382. {$ENDIF}
  383. end;
  384. procedure TWasmHTTPAPI.LogCall(const Fmt: String; const Args: array of const);
  385. begin
  386. {$IFNDEF NOLOGAPICALLS}
  387. if LogAPI then
  388. DoLog(Fmt,Args);
  389. {$ENDIF}
  390. end;
  391. type
  392. TDoneCallback = Function(aRequestID : TWasmHTTPRequestID; aUserData : TWasmPointer; aStatus : TWasmHTTPResponseStatus) : TWasmHTTPResponseResult;
  393. procedure TWasmHTTPAPI.DoneRequest(aFetch: TWasmHTTPFetch);
  394. var
  395. Exp : JSValue;
  396. Callback : TDoneCallback absolute exp;
  397. Res,Stat : Longint;
  398. doDispose : Boolean;
  399. begin
  400. doDispose:=True;
  401. Exp:=InstanceExports[httpFN_ResponseCallback];
  402. if aFetch.FRequestError<>'' then
  403. Stat:=-1
  404. else
  405. Stat:=aFetch.Response.status;
  406. if isFunction(Exp) then
  407. begin
  408. Res:=Callback(aFetch.ID,aFetch.UserData,Stat);
  409. DoDispose:=(Res=WASMHTTP_RESPONSE_DEALLOCATE);
  410. end
  411. else
  412. console.error('No request callback available!');
  413. if DoDispose then
  414. begin
  415. FRequests[IntToStr(aFetch.ID)]:=undefined;
  416. FreeAndNil(aFetch);
  417. end;
  418. end;
  419. function TWasmHTTPAPI.CreateRequestID: TWasmHTTPRequestID;
  420. begin
  421. Inc(FNextRequestID);
  422. Result:=FNextRequestID;
  423. end;
  424. function TWasmHTTPAPI.FetchByID(aID: TWasmHTTPRequestID): TWasmHTTPFetch;
  425. var
  426. Value : JSValue;
  427. begin
  428. Value:=FRequests[IntToStr(aID)];
  429. if isObject(Value) then
  430. Result:=TWasmHTTPFetch(Value)
  431. else
  432. Result:=Nil;
  433. end;
  434. function TWasmHTTPAPI.RequestAllocate(aRequest: PWasmHTTPRequest; aUserData: TWasmPointer; aRequestID: PWasmHTTPRequestID
  435. ): TWasmHTTPResult;
  436. var
  437. lReq : TWasmHTTPRequest;
  438. lID : TWasmHTTPRequestID;
  439. lfetch : TWasmHTTPFetch;
  440. begin
  441. {$IFNDEF NOLOGAPICALLS}
  442. If LogAPICalls then
  443. LogCall('HTTP.RequestAllocate([%x],[%x],[%x])',[aRequest,aUserData,aRequestID]);
  444. {$ENDIF}
  445. lReq:=ReadRequest(aRequest);
  446. if (lReq.Url='') then
  447. Exit(WASMHTTP_RESULT_NO_URL);
  448. lID:=CreateRequestID;
  449. lFetch:=TWasmHTTPFetch.Create(Self,lID,aUserData,lReq);
  450. FRequests[IntToStr(lID)]:=lFetch;
  451. env.SetMemInfoInt32(aRequestID,lID);
  452. Result:=WASMHTTP_RESULT_SUCCESS;
  453. {$IFNDEF NOLOGAPICALLS}
  454. If LogAPICalls then
  455. LogCall('HTTP.RequestAllocate([%x],[%x]) => %d',[aRequest,aUserData,lID]);
  456. {$ENDIF}
  457. end;
  458. function TWasmHTTPAPI.RequestExecute(aRequestID: TWasmHTTPRequestID): TWasmHTTPResult;
  459. var
  460. lfetch : TWasmHTTPFetch;
  461. begin
  462. {$IFNDEF NOLOGAPICALLS}
  463. If LogAPICalls then
  464. LogCall('HTTP.RequestExecute(%d)',[aRequestID]);
  465. {$ENDIF}
  466. lfetch:=FetchByID(aRequestID);
  467. if not Assigned(lFetch) then
  468. Exit(WASMHTTP_RESULT_INVALIDID);
  469. lFetch.Execute;
  470. Result:=WASMHTTP_RESULT_SUCCESS;
  471. end;
  472. procedure TWasmHTTPAPI.SetLogApiCalls(AValue: Boolean);
  473. begin
  474. LogAPI:=aValue;
  475. end;
  476. function TWasmHTTPAPI.RequestDeallocate(aRequestID: TWasmHTTPRequestID): TWasmHTTPResult;
  477. var
  478. lFetch : TWasmHTTPFetch;
  479. begin
  480. {$IFNDEF NOLOGAPICALLS}
  481. If LogAPICalls then
  482. LogCall('HTTP.RequestDeAllocate(%d)',[aRequestID]);
  483. {$ENDIF}
  484. lfetch:=FetchByID(aRequestID);
  485. if not Assigned(lFetch) then
  486. Exit(WASMHTTP_RESULT_INVALIDID);
  487. end;
  488. function TWasmHTTPAPI.RequestAbort(aRequestID: TWasmHTTPRequestID): TWasmHTTPResult;
  489. var
  490. lFetch : TWasmHTTPFetch;
  491. begin
  492. {$IFNDEF NOLOGAPICALLS}
  493. If LogAPICalls then
  494. LogCall('HTTP.RequestAbort(%d)',[aRequestID]);
  495. {$ENDIF}
  496. lfetch:=FetchByID(aRequestID);
  497. if not Assigned(lFetch) then
  498. Exit(WASMHTTP_RESULT_INVALIDID);
  499. end;
  500. function TWasmHTTPAPI.ResponseGetStatus(aRequestID: TWasmHTTPRequestID; aStatus: PLongint): TWasmHTTPResult;
  501. var
  502. lFetch : TWasmHTTPFetch;
  503. begin
  504. {$IFNDEF NOLOGAPICALLS}
  505. If LogAPICalls then
  506. LogCall('HTTP.ResponseGetStatus(%d,[%x])',[aRequestID,aStatus]);
  507. {$ENDIF}
  508. lfetch:=FetchByID(aRequestID);
  509. if not Assigned(lFetch) then
  510. Exit(WASMHTTP_RESULT_INVALIDID);
  511. if lFetch.InProgress then
  512. Exit(WASMHTTP_RESULT_INPROGRESS);
  513. Env.SetMemInfoInt32(aStatus,lFetch.Response.status);
  514. Result:=WASMHTTP_RESULT_SUCCESS;
  515. end;
  516. function TWasmHTTPAPI.ResponseGetStatusText(aRequestID: TWasmHTTPRequestID; aStatusText: PByte; aMaxTextLen: PLongint
  517. ): TWasmHTTPResult;
  518. var
  519. lFetch : TWasmHTTPFetch;
  520. v : TJSDataView;
  521. Written,MaxLen : Longint;
  522. S : String;
  523. begin
  524. {$IFNDEF NOLOGAPICALLS}
  525. If LogAPICalls then
  526. LogCall('HTTP.ResponseGetStatusText(%d,[%x],[%x])',[aRequestID,aStatusText,aMaxTextlen]);
  527. {$ENDIF}
  528. lfetch:=FetchByID(aRequestID);
  529. if not Assigned(lFetch) then
  530. Exit(WASMHTTP_RESULT_INVALIDID);
  531. if lFetch.InProgress then
  532. Exit(WASMHTTP_RESULT_INPROGRESS);
  533. v:=getModuleMemoryDataView;
  534. MaxLen:=v.getInt32(aMaxTextLen,Env.IsLittleEndian);
  535. S:=lFetch.Response.statusText;
  536. Written:=Env.SetUTF8StringInMem(aStatusText,MaxLen,S);
  537. Env.SetMemInfoInt32(aMaxTextLen,Abs(Written));
  538. if Written<0 then
  539. Result:=WASMHTTP_RESULT_INSUFFICIENTMEM
  540. else
  541. Result:=WASMHTTP_RESULT_SUCCESS;
  542. end;
  543. function TWasmHTTPAPI.ResponseGetHeaderCount(aRequestID: TWasmHTTPRequestID; aHeaderCount: PLongint): TWasmHTTPResult;
  544. var
  545. lFetch : TWasmHTTPFetch;
  546. lCount : Longint;
  547. begin
  548. {$IFNDEF NOLOGAPICALLS}
  549. If LogAPICalls then
  550. LogCall('HTTP.ResponseGetHeaderCount(%d,[%x])',[aRequestID,aHeaderCount]);
  551. {$ENDIF}
  552. lfetch:=FetchByID(aRequestID);
  553. if not Assigned(lFetch) then
  554. Exit(WASMHTTP_RESULT_INVALIDID);
  555. if lFetch.InProgress then
  556. Exit(WASMHTTP_RESULT_INPROGRESS);
  557. lCount:=lFetch.HeaderCount;
  558. Env.SetMemInfoInt32(aHeaderCount,lCount);
  559. Result:=WASMHTTP_RESULT_SUCCESS;
  560. end;
  561. function TWasmHTTPAPI.ResponseGetHeaderName(aRequestID: TWasmHTTPRequestID; aHeaderIdx: Longint; aHeader: PByte;
  562. aMaxHeaderLen: PLongint): TWasmHTTPResult;
  563. var
  564. lFetch : TWasmHTTPFetch;
  565. S : String;
  566. MaxLen,Written : Longint;
  567. v : TJSDataView;
  568. begin
  569. {$IFNDEF NOLOGAPICALLS}
  570. If LogAPICalls then
  571. LogCall('HTTP.RequestGetHeaderName(%d,%d,[%x],[%x])',[aRequestID,aHeaderIdx,aHeader,aMaxHeaderLen]);
  572. {$ENDIF}
  573. lfetch:=FetchByID(aRequestID);
  574. if not Assigned(lFetch) then
  575. Exit(WASMHTTP_RESULT_INVALIDID);
  576. if lFetch.InProgress then
  577. Exit(WASMHTTP_RESULT_INPROGRESS);
  578. V:=getModuleMemoryDataView;
  579. MaxLen:=v.getInt32(aMaxheaderLen,Env.IsLittleEndian);
  580. S:=lFetch.HeaderNames[aHeaderIdx];
  581. Written:=Env.SetUTF8StringInMem(aHeader,MaxLen,S);
  582. Env.SetMemInfoInt32(aMaxheaderLen,Abs(Written));
  583. if Written<0 then
  584. Result:=WASMHTTP_RESULT_INSUFFICIENTMEM
  585. else
  586. Result:=WASMHTTP_RESULT_SUCCESS;
  587. end;
  588. function TWasmHTTPAPI.ResponseGetHeader(aRequestID: TWasmHTTPRequestID; aHeaderName: PByte; aHeaderLen: PLongint; aHeader: PByte;
  589. aMaxHeaderLen: Longint): TWasmHTTPResult;
  590. var
  591. lFetch : TWasmHTTPFetch;
  592. lHeader, lName : String;
  593. Written,Maxlen : Longint;
  594. v : TJSDataView;
  595. begin
  596. v:=getModuleMemoryDataView;
  597. lName:=Env.GetUTF8StringFromMem(aHeaderName,aHeaderLen);
  598. MaxLen:=v.getInt32(aMaxHeaderLen,Env.IsLittleEndian);
  599. {$IFNDEF NOLOGAPICALLS}
  600. If LogAPICalls then
  601. LogCall('HTTP.ResponseGetHeader(%d,"%s",[%x])',[aRequestID,lName,aHeader,aMaxHeaderLen]);
  602. {$ENDIF}
  603. lfetch:=FetchByID(aRequestID);
  604. if not Assigned(lFetch) then
  605. Exit(WASMHTTP_RESULT_INVALIDID);
  606. if lFetch.InProgress then
  607. Exit(WASMHTTP_RESULT_INPROGRESS);
  608. lHeader:=lfetch.Response.headers[lName];
  609. Written:=Env.SetUTF8StringInMem(aHeader,MaxLen,lheader);
  610. Env.SetMemInfoInt32(aMaxheaderLen,Abs(Written));
  611. if Written<0 then
  612. Result:=WASMHTTP_RESULT_INSUFFICIENTMEM
  613. else
  614. Result:=WASMHTTP_RESULT_SUCCESS;
  615. end;
  616. function TWasmHTTPAPI.ResponseGetBody(aRequestID: TWasmHTTPRequestID; aBody: PByte; aMaxBodyLen: PLongint): TWasmHTTPResult;
  617. var
  618. lFetch : TWasmHTTPFetch;
  619. lwasmMem,lUint8Array : TJSUint8Array;
  620. v : TJSDataView;
  621. bodyLen,maxLen : longint;
  622. begin
  623. {$IFNDEF NOLOGAPICALLS}
  624. If LogAPICalls then
  625. LogCall('HTTP.ResponseGetBody([%x],[%x],[%x])',[aRequestID,aBody,aMaxBodyLen]);
  626. {$ENDIF}
  627. lfetch:=FetchByID(aRequestID);
  628. if not Assigned(lFetch) then
  629. Exit(WASMHTTP_RESULT_INVALIDID);
  630. if lFetch.InProgress then
  631. Exit(WASMHTTP_RESULT_INPROGRESS);
  632. if Not Assigned(lFetch.FResultBody) then
  633. begin
  634. Env.SetMemInfoInt32(aMaxBodyLen,0);
  635. exit;
  636. end;
  637. v:=getModuleMemoryDataView;
  638. MaxLen:=v.getInt32(aMaxBodyLen,Env.IsLittleEndian);
  639. bodyLen:=lFetch.FResultBody.byteLength;
  640. Env.SetMemInfoInt32(aMaxBodyLen,bodyLen);
  641. if (MaxLen<bodyLen) then
  642. Exit(WASMHTTP_RESULT_INSUFFICIENTMEM);
  643. lUint8Array:=TJSUint8Array.new(lFetch.FResultBody);
  644. lwasmMem:=TJSUint8Array.New(v.buffer);
  645. lWasmMem._set(lUint8Array,aBody);
  646. Exit(WASMHTTP_RESULT_SUCCESS);
  647. end;
  648. constructor TWasmHTTPAPI.Create(aEnv: TPas2JSWASIEnvironment);
  649. begin
  650. inherited Create(aEnv);
  651. FRequests:=TJSOBject.new;
  652. TCommandDispatcher.Instance.specialize AddCommandHandler<TOpenURLCommand>(cmdOpenURL,@HandleOpenURLMessage);
  653. end;
  654. procedure TWasmHTTPAPI.DoOpenURL(aURL : String; aFlags : integer);
  655. {$IFNDEF JOB_WORKER}
  656. var
  657. win: TJSWindow;
  658. {$ENDIF}
  659. begin
  660. {$IFNDEF JOB_WORKER}
  661. win:=Window.open(aURL,'_blank');
  662. {$ENDIF}
  663. end;
  664. procedure TWasmHTTPAPI.HandleOpenURLMessage(aCommand : TOpenURLCommand);
  665. begin
  666. {$IFNDEF JOB_WORKER}
  667. Writeln('Handling open url message');
  668. DoOpenURL(aCommand.URL,aCommand.Flags);
  669. {$ELSE}
  670. Writeln('forwarding open url message');
  671. aCommand[cFldSender]:=undefined;
  672. TCommandDispatcher.Instance.SendCommand(aCommand);
  673. {$ENDIF}
  674. end;
  675. function TWasmHTTPAPI.HandleOpenURL(aURL : TWasmPointer; aURLLen : Longint; aFlags : Integer) : Integer;
  676. var
  677. lURL : String;
  678. begin
  679. Result:=WASMHTTP_RESULT_SUCCESS;
  680. lURL:=Env.GetUTF8StringFromMem(aURL,aURLLen);
  681. Writeln('Handling open url call ',aURL);
  682. {$IFNDEF NOLOGAPICALLS}
  683. If LogAPICalls then
  684. LogCall('HTTP.OpenURL(%s,%d)',[lURL,aFlags]);
  685. {$ENDIF}
  686. {$IFDEF JOB_WORKER}
  687. Writeln('sending openurl command');
  688. TCommandDispatcher.Instance.SendCommand(TOpenURLCommand.CreateURL(lURL,aFlags));
  689. {$ELSE}
  690. DoOpenURL(lURL,aFlags);
  691. {$ENDIF}
  692. end;
  693. procedure TWasmHTTPAPI.FillImportObject(aObject: TJSObject);
  694. begin
  695. AObject[httpFN_RequestAllocate]:=@RequestAllocate;
  696. AObject[httpFN_RequestExecute]:=@RequestExecute;
  697. AObject[httpFN_RequestDeAllocate]:=@RequestDeallocate;
  698. AObject[httpFN_RequestAbort]:=@RequestAbort;
  699. AObject[httpFN_ResponseGetStatus]:=@ResponseGetStatus;
  700. AObject[httpFN_ResponseGetStatusText]:=@ResponseGetStatusText;
  701. AObject[httpFN_ResponseGetHeaderName]:=@ResponseGetHeaderName;
  702. AObject[httpFN_ResponseGetHeaderCount]:=@ResponseGetHeaderCount;
  703. AObject[httpFN_ResponseGetHeader]:=@ResponseGetHeader;
  704. AObject[httpFN_ResponseGetBody]:=@ResponseGetBody;
  705. AObject[httpFN_OpenURL]:=@HandleOpenURL;
  706. end;
  707. function TWasmHTTPAPI.ImportName: String;
  708. begin
  709. Result:=httpExportName
  710. end;
  711. end.