BrookUtility.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. (* _ _
  2. * | |__ _ __ ___ ___ | | __
  3. * | '_ \| '__/ _ \ / _ \| |/ /
  4. * | |_) | | | (_) | (_) | <
  5. * |_.__/|_| \___/ \___/|_|\_\
  6. *
  7. * Microframework which helps to develop web Pascal applications.
  8. *
  9. * Copyright (c) 2012-2021 Silvio Clecio <[email protected]>
  10. *
  11. * Brook framework is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation; either
  14. * version 2.1 of the License, or (at your option) any later version.
  15. *
  16. * Brook framework is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with Brook framework; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  24. *)
  25. { Utility functions of the framework. }
  26. unit BrookUtility;
  27. {$I BrookDefines.inc}
  28. interface
  29. uses
  30. RTLConsts,
  31. SysUtils,
  32. DateUtils,
  33. TypInfo,
  34. {$IFDEF FPC}
  35. SHA1,
  36. HttpProtocol,
  37. {$ELSE}
  38. System.Hash,
  39. System.NetEncoding,
  40. {$ENDIF}
  41. Marshalling,
  42. libsagui;
  43. const
  44. { Primitive kinds. }
  45. tkPrimitives = tkProperties -
  46. {$IFDEF FPC}
  47. [tkArray..tkObject] - [tkInterfaceRaw] - [tkProcVar] - [tkHelper..tkPointer]
  48. {$ELSE}
  49. [tkClass] - [tkArray..tkInterface] -
  50. [tkClassRef..{$IF CompilerVersion >= 32.0}tkMRecord{$ELSE}tkProcedure{$ENDIF}]
  51. {$ENDIF};
  52. type
  53. { Event signature used by stuff that handles errors.
  54. @param(ASender[in] Sender object.)
  55. @param(AException[in] Exception object.) }
  56. TBrookErrorEvent = procedure(ASender: TObject;
  57. AException: Exception) of object;
  58. { Global Sagui object containing general purpose functions. }
  59. Sagui = record
  60. { Returns the library version number.
  61. @returns(Library version packed into a single integer.) }
  62. class function Version: Cardinal; overload; static;
  63. { Returns the library version number.
  64. @param(AMajor[out] Major number.)
  65. @param(AMinor[out] Minor number.)
  66. @param(APatch[out] Patch number.)
  67. @returns(Library version packed into a single integer.) }
  68. class function Version(out AMajor, AMinor: Byte;
  69. out APatch: SmallInt): Cardinal; overload; static;
  70. { Returns the library version number as string in the
  71. format @code(<MAJOR>.<MINOR>.<PATCH>).
  72. @returns(Library version packed into a static string.) }
  73. class function VersionStr: string; static;
  74. { Allocates a new memory space.
  75. @param(ASize[in] Memory size to be allocated.)
  76. @returns(Pointer of the allocated zero-initialized memory.
  77. @bold(Returns values:)
  78. @definitionList(
  79. @itemLabel(@code(nil))
  80. @item(If size is @code(0) or no memory space.)
  81. )
  82. ) }
  83. class function Malloc(ASize: NativeUInt): Pointer; static;
  84. { Allocates a new zero-initialized memory space.
  85. @param(ASize[in] Memory size to be allocated.)
  86. @returns(Pointer of the allocated zero-initialized memory.
  87. @bold(Returns values:)
  88. @definitionList(
  89. @itemLabel(@code(nil))
  90. @item(If size is @code(0) or no memory space.)
  91. )
  92. ) }
  93. class function Alloc(ASize: NativeUInt): Pointer; static;
  94. { Reallocates an existing memory block.
  95. @param(APointer[in] Pointer of the memory to be reallocated.)
  96. @param(ASize[in] Memory size to be allocated.)
  97. @returns(Pointer of the reallocated memory.) }
  98. class function Realloc(APointer: Pointer;
  99. ASize: NativeUInt): Pointer; static;
  100. { Frees a memory space previous allocated by @code(Sagui.Malloc),
  101. @link(Sagui.Alloc) or @code(Sagui.Realloc).
  102. @param(APointer[in] Pointer of the memory to be freed.) }
  103. class procedure Free(APointer: Pointer); static;
  104. { Returns string describing an error number.
  105. @param(AErrorNum[in] Error number.)
  106. @param(AErrorMsg[out] Referenced string to store the error message.)
  107. @param(AErrorLen[in] Length of the error message.) }
  108. class procedure StrError(AErrorNum: Integer; out AErrorMsg: string;
  109. AErrorLen: Integer); overload; static; inline;
  110. { Returns string describing an error number.
  111. @param(AErrorNum[in] Error number.)
  112. @returns(Static string describing the error.) }
  113. class function StrError(AErrorNum: Integer): string; overload; static;
  114. { Checks if a string is an HTTP post method.
  115. @param(AMethod[in] HTTP verb.)
  116. @returns(True if given method is POST, PUT, DELETE or OPTIONS.) }
  117. class function IsPost(const AMethod: string): Boolean; static;
  118. { Extracts the entry-point of a path or resource. For example, given a path
  119. @code(/api1/customer), the part considered as entry-point is
  120. @code(/api1).
  121. @param(APath[in] Path as static string.)
  122. @returns(Entry-point as static string.) }
  123. class function ExtractEntryPoint(const APath: string): string; static;
  124. { Returns the system temporary directory.
  125. @returns(Temporary directory as static string.) }
  126. class function TmpDir: string; static;
  127. { Indicates the end-of-read processed in
  128. @code(TBrookHTTPResponse.SendStream).
  129. @param(AError[in] @True to return a value indicating a stream
  130. reading error.)
  131. @returns(Value to end a stream reading.) }
  132. class function EOR(AError: Boolean): NativeInt; static;
  133. { Obtains the IP of a socket handle into a string.
  134. @param(ASocket[in] Socket handle.)
  135. @return(Formatted IP into a string.) }
  136. class function IP(ASocket: Pointer): string; static;
  137. end;
  138. { Global Brook object containing general purpose functions. }
  139. Brook = record
  140. public const
  141. {$IFNDEF FPC}
  142. {$WRITEABLECONST ON}
  143. {$ENDIF}
  144. { Holds the name of days as 'Aaa' format. }
  145. DAYS: array[1..7] of string = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu',
  146. 'Fri', 'Sat');
  147. { Holds the name of months as 'Aaa' format. }
  148. MONTHS: array[1..12] of string = ('Jan', 'Feb', 'Mar', 'Apr', 'May',
  149. 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
  150. {$IFNDEF FPC}
  151. {$WRITEABLECONST OFF}
  152. {$ENDIF}
  153. { Fixes a path by including the leading path delimiter and excluding the
  154. trailing one.
  155. @param(APath[in] Path as static string.)
  156. @returns(Fixed path, e.g.: path -> /path and /path/ -> /path) }
  157. class function FixPath(const APath: string): string; static; inline;
  158. { Extracts and fixes an entry-point by including the leading path delimiter
  159. and excluding the trailing one.
  160. @param(APath[in] Path as static string.)
  161. @returns(Fixed entry-point, e.g.: /foo/bar -> /foo ) }
  162. class function FixEntryPoint(const APath: string): string; static; inline;
  163. { Converts a given local time to UTC (Coordinated Universal Time).
  164. @param(ADateTime[in] Local date/time.)
  165. @returns(Local time converted to UTC.) }
  166. class function DateTimeToUTC(ADateTime: TDateTime): TDateTime; static; inline;
  167. { Converts a given local time to GMT (Greenwich Mean Time).
  168. @param(ADateTime[in] Local date/time.)
  169. @returns(Local time converted to GMT string.) }
  170. class function DateTimeToGMT(ADateTime: TDateTime): string; static; inline;
  171. { Generates a given string to SHA-1 (Secure Hash Algorithm 1).
  172. @param(S[in] String to generate the SHA-1.)
  173. @returns(Generated SHA-1 as static string.) }
  174. class function SHA1(const S: string): string; static; inline;
  175. end;
  176. { HTTP verbs enumeration. }
  177. TBrookHTTPRequestMethod = (rmUnknown, rmGET, rmPOST, rmPUT, rmDELETE, rmPATCH,
  178. rmOPTIONS, rmHEAD);
  179. { Set of HTTP verbs. }
  180. TBrookHTTPRequestMethods = set of TBrookHTTPRequestMethod;
  181. { Type helper for HTTP verb conversion. }
  182. TBrookHTTPRequestMethodHelper = record helper for TBrookHTTPRequestMethod
  183. public const
  184. { Holds the name of HTTP verbs. }
  185. METHODS: array[TBrookHTTPRequestMethod] of string = ('Unknown', 'GET',
  186. 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD');
  187. public
  188. { Converts a @code(TBrookHTTPRequestMethod) to string. }
  189. function ToString: string; inline;
  190. { Returns a @code(TBrookHTTPRequestMethod) from a string. }
  191. function FromString(const AMethod: string): TBrookHTTPRequestMethod; inline;
  192. end;
  193. implementation
  194. { Sagui }
  195. class function Sagui.Version: Cardinal;
  196. begin
  197. SgLib.Check;
  198. Result := sg_version;
  199. end;
  200. class function Sagui.Version(out AMajor, AMinor: Byte;
  201. out APatch: SmallInt): Cardinal;
  202. begin
  203. SgLib.Check;
  204. Result := sg_version;
  205. AMajor := (Result shr 16) and $FF;
  206. AMinor := (Result shr 8) and $FF;
  207. APatch := Result and $FF;
  208. end;
  209. class function Sagui.VersionStr: string;
  210. begin
  211. SgLib.Check;
  212. Result := TMarshal.ToString(sg_version_str);
  213. end;
  214. class function Sagui.Malloc(ASize: NativeUInt): Pointer;
  215. begin
  216. SgLib.Check;
  217. Result := sg_malloc(ASize);
  218. end;
  219. class function Sagui.Alloc(ASize: NativeUInt): Pointer;
  220. begin
  221. SgLib.Check;
  222. Result := sg_alloc(ASize);
  223. end;
  224. class function Sagui.Realloc(APointer: Pointer; ASize: NativeUInt): Pointer;
  225. begin
  226. SgLib.Check;
  227. Result := sg_realloc(APointer, ASize);
  228. end;
  229. class procedure Sagui.Free(APointer: Pointer);
  230. begin
  231. SgLib.Check;
  232. sg_free(APointer);
  233. end;
  234. class procedure Sagui.StrError(AErrorNum: Integer; out AErrorMsg: string;
  235. AErrorLen: Integer);
  236. var
  237. P: array[0..Pred(SG_ERR_SIZE)] of cchar;
  238. begin
  239. SgLib.Check;
  240. P[0] := 0;
  241. sg_strerror(AErrorNum, @P[0], AErrorLen);
  242. AErrorMsg := TMarshal.ToString(@P[0]).TrimRight;
  243. end;
  244. class function Sagui.StrError(AErrorNum: Integer): string;
  245. begin
  246. Sagui.StrError(AErrorNum, Result, SG_ERR_SIZE);
  247. end;
  248. class function Sagui.IsPost(const AMethod: string): Boolean;
  249. var
  250. M: TMarshaller;
  251. begin
  252. SgLib.Check;
  253. Result := sg_is_post(M.ToCString(AMethod));
  254. end;
  255. class function Sagui.ExtractEntryPoint(const APath: string): string;
  256. var
  257. M: TMarshaller;
  258. S: Pcchar;
  259. begin
  260. SgLib.Check;
  261. S := sg_extract_entrypoint(M.ToCString(APath));
  262. try
  263. Result := TMarshal.ToString(S);
  264. finally
  265. sg_free(S);
  266. end;
  267. end;
  268. class function Sagui.TmpDir: string;
  269. var
  270. S: Pcchar;
  271. begin
  272. SgLib.Check;
  273. S := sg_tmpdir;
  274. try
  275. Result := TMarshal.ToString(S);
  276. finally
  277. sg_free(S);
  278. end;
  279. end;
  280. class function Sagui.EOR(AError: Boolean): NativeInt;
  281. begin
  282. SgLib.Check;
  283. Result := sg_eor(AError);
  284. end;
  285. class function Sagui.IP(ASocket: Pointer): string;
  286. var
  287. P: array[0..45] of cchar;
  288. begin
  289. if not Assigned(ASocket) then
  290. raise EArgumentNilException.CreateFmt(SParamIsNil, ['ASocket']);
  291. SgLib.Check;
  292. SgLib.CheckLastError(sg_ip(ASocket, @P[0], SizeOf(P)));
  293. Result := TMarshal.ToString(@P[0]);
  294. end;
  295. { Brook }
  296. class function Brook.FixPath(const APath: string): string;
  297. begin
  298. Result := APath;
  299. if not APath.StartsWith('/') then
  300. Result := Concat('/', Result);
  301. if (Length(APath) > SizeOf(Char)) and Result.EndsWith('/') then
  302. SetLength(Result, Length(Result) - Length('/'));
  303. end;
  304. class function Brook.FixEntryPoint(const APath: string): string;
  305. var
  306. PS: TArray<string>;
  307. begin
  308. PS := APath.Split(['/'], TStringSplitOptions.ExcludeEmpty);
  309. Result := '/';
  310. if Length(PS) > 0 then
  311. Result := Concat(Result, PS[0]);
  312. end;
  313. class function Brook.DateTimeToUTC(ADateTime: TDateTime): TDateTime;
  314. begin
  315. Result :=
  316. {$IFDEF FPC}
  317. LocalTimeToUniversal
  318. {$ELSE}
  319. TTimeZone.Local.ToUniversalTime
  320. {$ENDIF}(ADateTime);
  321. end;
  322. class function Brook.DateTimeToGMT(ADateTime: TDateTime): string;
  323. var
  324. Y, M, D: Word;
  325. begin
  326. DecodeDate(ADateTime, Y, M, D);
  327. DateTimeToString(Result, Format('"%s", dd "%s" yyy hh":"mm":"ss "GMT"', [
  328. DAYS[DayOfWeek(ADateTime)], MONTHS[M]]), ADateTime);
  329. end;
  330. class function Brook.SHA1(const S: string): string;
  331. begin
  332. Result :=
  333. {$IFDEF FPC}SHA1Print(SHA1String(S)){$ELSE}THashSHA1.GetHashString(S){$ENDIF};
  334. end;
  335. { TBrookHTTPRequestMethodHelper }
  336. function TBrookHTTPRequestMethodHelper.ToString: string;
  337. begin
  338. Result := METHODS[Self];
  339. end;
  340. function TBrookHTTPRequestMethodHelper.FromString(
  341. const AMethod: string): TBrookHTTPRequestMethod;
  342. var
  343. M: string;
  344. I: TBrookHTTPRequestMethod;
  345. begin
  346. M := AMethod.ToUpper;
  347. for I := Low(METHODS) to High(METHODS) do
  348. if SameStr(M, METHODS[I]) then
  349. Exit(I);
  350. Result := rmUnknown;
  351. end;
  352. end.