fpwebclient.pp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. { **********************************************************************
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 2015 by the Free Pascal development team
  4. FPWebclient - abstraction for client execution of HTTP requests.
  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 fpwebclient;
  12. {$mode objfpc}{$H+}
  13. interface
  14. uses
  15. Classes, SysUtils;
  16. Type
  17. { TRequestResponse }
  18. TRequestResponse = Class(TObject)
  19. private
  20. FHeaders : TStrings;
  21. FStream : TStream;
  22. FOwnsStream : Boolean;
  23. Protected
  24. function GetHeaders: TStrings;virtual;
  25. function GetStream: TStream;virtual;
  26. Public
  27. Destructor Destroy; override;
  28. Procedure SetContentFromString(Const S : String) ;
  29. Function GetContentAsString : String;
  30. // Request headers or response headers
  31. Property Headers : TStrings Read GetHeaders;
  32. // Request content or response content
  33. Property Content: TStream Read GetStream;
  34. end;
  35. { TWebClientRequest }
  36. TWebClientRequest = Class(TRequestResponse)
  37. Private
  38. FExtraParams : TStrings;
  39. FResponseStream: TStream;
  40. Protected
  41. function GetExtraParams: TStrings; virtual;
  42. Public
  43. Destructor Destroy; override;
  44. Function ParamsAsQuery : String;
  45. // Query Parameters to include in request
  46. Property Params : TStrings Read GetExtraParams;
  47. // If you want the response to go to this stream, set this in the request
  48. Property ResponseContent : TStream Read FResponseStream Write FResponseStream;
  49. end;
  50. { TResponse }
  51. { TWebClientResponse }
  52. TWebClientResponse = Class(TRequestResponse)
  53. Protected
  54. Function GetStatusCode : Integer; virtual;
  55. Function GetStatusText : String; virtual;
  56. Public
  57. Constructor Create(ARequest : TWebClientRequest); virtual;
  58. // Status code of request
  59. Property StatusCode : Integer Read GetStatusCode;
  60. // Status text of request
  61. Property StatusText : String Read GetStatusText;
  62. end;
  63. { TAbstractRequestSigner }
  64. TAbstractRequestSigner = Class(TComponent)
  65. Protected
  66. Procedure DoSignRequest(ARequest : TWebClientRequest); virtual; abstract;
  67. Public
  68. Procedure SignRequest(ARequest : TWebClientRequest);
  69. end;
  70. { TAbstractResponseExaminer }
  71. TAbstractResponseExaminer = Class(TComponent)
  72. Protected
  73. Procedure DoExamineResponse(AResponse : TWebClientResponse); virtual; abstract;
  74. Public
  75. Procedure ExamineResponse(AResponse : TWebClientResponse);
  76. end;
  77. { TAbstractWebClient }
  78. TSSLVersion = (svAny,svSSLv2,svSSLv3,svTLSv1,svTLSv11,svTLSv12,svTLSv13);
  79. TSSLVersions = Set of TSSLVersion;
  80. TSSLVersionArray = Array of TSSLVersion;
  81. TAbstractWebClient = Class(TComponent)
  82. private
  83. FExaminer: TAbstractResponseExaminer;
  84. FSigner: TAbstractRequestSigner;
  85. FLogFile : String;
  86. FLogStream : TStream;
  87. FTrySSLVersion: TSSLVersion;
  88. Procedure LogRequest(AMethod, AURL: String; ARequest: TWebClientRequest);
  89. Procedure LogResponse(AResponse: TWebClientResponse);
  90. procedure SetLogFile(AValue: String);
  91. protected
  92. // Write a string to the log file
  93. procedure StringToStream(str: string);
  94. // Must execute the requested method using request/response. Must take ResponseCOntent stream into account
  95. Function DoHTTPMethod(Const AMethod,AURL : String; ARequest : TWebClientRequest) : TWebClientResponse; virtual; abstract;
  96. // Must create a request.
  97. Function DoCreateRequest : TWebClientRequest; virtual; abstract;
  98. Public
  99. Destructor Destroy; override;
  100. // Executes the HTTP method AMethod on AURL. Raises an exception on error.
  101. // On success, TWebClientResponse is returned. It must be freed by the caller.
  102. Function ExecuteRequest(Const AMethod,AURL : String; ARequest : TWebClientRequest) : TWebClientResponse;
  103. // Same as HTTPMethod, but signs the request first using signer.
  104. Function ExecuteSignedRequest(Const AMethod,AURL : String; ARequest : TWebClientRequest) : TWebClientResponse;
  105. // Create a new request. The caller is responsible for freeing the request.
  106. Function CreateRequest : TWebClientRequest;
  107. // These can be set to sign/examine the request/response.
  108. Property RequestSigner : TAbstractRequestSigner Read FSigner Write FSigner;
  109. Property ResponseExaminer : TAbstractResponseExaminer Read FExaminer Write FExaminer;
  110. Property LogFile : String Read FLogFile Write SetLogFile;
  111. property SSLVersion : TSSLVersion Read FTrySSLVersion Write FTrySSLVersion;
  112. end;
  113. TAbstractWebClientClass = Class of TAbstractWebClient;
  114. EFPWebClient = Class(Exception);
  115. Var
  116. DefaultWebClientClass : TAbstractWebClientClass = Nil;
  117. implementation
  118. uses httpdefs;
  119. { TAbstractRequestSigner }
  120. Procedure TAbstractRequestSigner.SignRequest(ARequest: TWebClientRequest);
  121. begin
  122. DoSignRequest(ARequest);
  123. end;
  124. { TAbstractResponseExaminer }
  125. Procedure TAbstractResponseExaminer.ExamineResponse(
  126. AResponse: TWebClientResponse);
  127. begin
  128. DoExamineResponse(AResponse);
  129. end;
  130. { TWebClientRequest }
  131. function TWebClientRequest.GetExtraParams: TStrings;
  132. begin
  133. if FExtraParams=Nil then
  134. FExtraParams:=TStringList.Create;
  135. Result:=FExtraParams;
  136. end;
  137. destructor TWebClientRequest.Destroy;
  138. begin
  139. FreeAndNil(FExtraParams);
  140. inherited Destroy;
  141. end;
  142. function TWebClientRequest.ParamsAsQuery: String;
  143. Var
  144. N,V : String;
  145. I : integer;
  146. begin
  147. Result:='';
  148. if Assigned(FextraParams) then
  149. For I:=0 to FextraParams.Count-1 do
  150. begin
  151. If Result<>'' then
  152. Result:=Result+'&';
  153. FextraParams.GetNameValue(I,N,V);
  154. Result:=Result+N+'='+HttpEncode(V);
  155. end;
  156. end;
  157. { TWebClientResponse }
  158. function TWebClientResponse.GetStatusCode: Integer;
  159. begin
  160. Result:=0;
  161. end;
  162. function TWebClientResponse.GetStatusText: String;
  163. begin
  164. Result:='';
  165. end;
  166. constructor TWebClientResponse.Create(ARequest: TWebClientRequest);
  167. begin
  168. FStream:=ARequest.ResponseContent;
  169. end;
  170. { TAbstractWebClient }
  171. procedure TAbstractWebClient.SetLogFile(AValue: String);
  172. begin
  173. if FLogFile=AValue then Exit;
  174. if Assigned(FlogStream) then
  175. FreeAndNil(FlogStream);
  176. FLogFile:=AValue;
  177. if (FLogFile<>'') then
  178. if FileExists(FLogFile) then
  179. FLogStream:=TFileStream.Create(FLogFile,fmOpenWrite or fmShareDenyWrite)
  180. else
  181. FLogStream:=TFileStream.Create(FLogFile,fmCreate or fmShareDenyWrite);
  182. end;
  183. procedure TAbstractWebClient.StringToStream(str: string);
  184. begin
  185. if Assigned(FLogStream) then
  186. begin
  187. Str:=Str+sLineBreak;
  188. FlogStream.Write(str[1],length(str));
  189. end;
  190. end;
  191. destructor TAbstractWebClient.Destroy;
  192. begin
  193. LogFile:='';
  194. inherited Destroy;
  195. end;
  196. procedure TAbstractWebClient.LogRequest(AMethod, AURL: String;
  197. ARequest: TWebClientRequest);
  198. Var
  199. I : Integer;
  200. begin
  201. StringToStream(StringOfChar('-',80));
  202. StringToStream('Request : '+AMethod+' '+AURL);
  203. StringToStream('Headers:');
  204. For I:=0 to ARequest.Headers.Count-1 do
  205. StringToStream(ARequest.Headers[I]);
  206. StringToStream('Body:');
  207. FLogStream.CopyFrom(ARequest.Content,0);
  208. ARequest.Content.Position:=0;
  209. StringToStream('');
  210. end;
  211. procedure TAbstractWebClient.LogResponse(AResponse: TWebClientResponse);
  212. Var
  213. I : Integer;
  214. begin
  215. StringToStream(StringOfChar('-',80));
  216. StringToStream('Response : '+IntToStr(AResponse.StatusCode)+' : '+AResponse.StatusText);
  217. StringToStream('Headers:');
  218. For I:=0 to AResponse.Headers.Count-1 do
  219. StringToStream(AResponse.Headers[I]);
  220. StringToStream('Body:');
  221. FLogStream.CopyFrom(AResponse.Content,0);
  222. AResponse.Content.Position:=0;
  223. StringToStream('');
  224. end;
  225. function TAbstractWebClient.ExecuteRequest(const AMethod, AURL: String;
  226. ARequest: TWebClientRequest): TWebClientResponse;
  227. begin
  228. if Assigned(FLogStream) then
  229. LogRequest(AMethod,AURL,ARequest);
  230. Result:=DoHTTPMethod(AMethod,AURL,ARequest);
  231. if Assigned(Result) then
  232. begin
  233. if Assigned(FLogStream) then
  234. LogResponse(Result);
  235. If Assigned(FExaminer) then
  236. FExaminer.ExamineResponse(Result);
  237. end;
  238. end;
  239. function TAbstractWebClient.ExecuteSignedRequest(const AMethod, AURL: String;
  240. ARequest: TWebClientRequest): TWebClientResponse;
  241. begin
  242. If Assigned(FSigner) and Assigned(ARequest) then
  243. FSigner.SignRequest(ARequest);
  244. Result:=ExecuteRequest(AMethod,AURl,ARequest);
  245. end;
  246. function TAbstractWebClient.CreateRequest: TWebClientRequest;
  247. begin
  248. Result:=DoCreateRequest;
  249. end;
  250. { TRequestResponse }
  251. function TRequestResponse.GetHeaders: TStrings;
  252. begin
  253. if FHeaders=Nil then
  254. begin
  255. FHeaders:=TStringList.Create;
  256. FHeaders.NameValueSeparator:=':';
  257. end;
  258. Result:=FHeaders;
  259. end;
  260. function TRequestResponse.GetStream: TStream;
  261. begin
  262. if (FStream=Nil) then
  263. begin
  264. FStream:=TMemoryStream.Create;
  265. FOwnsStream:=True;
  266. end;
  267. Result:=FStream;
  268. end;
  269. Destructor TRequestResponse.Destroy;
  270. begin
  271. FreeAndNil(FHeaders);
  272. If FOwnsStream then
  273. FreeAndNil(FStream);
  274. inherited Destroy;
  275. end;
  276. Procedure TRequestResponse.SetContentFromString(Const S: String);
  277. begin
  278. if (S<>'') then
  279. Content.WriteBuffer(S[1],SizeOf(Char)*Length(S));
  280. end;
  281. Function TRequestResponse.GetContentAsString: String;
  282. begin
  283. SetLength(Result,Content.Size);
  284. if (Length(Result)>0) then
  285. begin
  286. Content.Position:=0;
  287. Content.ReadBuffer(Result[1],Length(Result));
  288. end;
  289. end;
  290. end.