BrookUtility.pas 12 KB

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