IdIOHandlerSocket.pas 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 10213: IdIOHandlerSocket.pas
  11. {
  12. { Rev 1.4 4/24/04 12:51:50 PM RLebeau
  13. { Added setter method to UseNagle property
  14. }
  15. {
  16. { Rev 1.3 10/15/03 1:44:26 PM RLebeau
  17. { Updated TIdConnectThread to store the socket's last error number if an
  18. { EIdSocketError is thrown, so that TIdIOHandlerSocket::ConnectClient() can
  19. { throw an EIdSocketError instead of an EIdConmectException when appropriate.
  20. }
  21. {
  22. { Rev 1.2 2/16/2003 03:36:00 PM JPMugaas
  23. { Added comment about new patch.
  24. }
  25. {
  26. { Rev 1.1 2/15/2003 03:02:10 PM JPMugaas
  27. { Now can create a SocksInfo object at design time. Not sure if this will have
  28. { any unintended consequences.
  29. }
  30. {
  31. { Rev 1.0 2002.11.12 10:42:34 PM czhower
  32. }
  33. unit IdIOHandlerSocket;
  34. interface
  35. uses
  36. Classes,
  37. IdGlobal, IdSocks, IdSocketHandle, IdIOHandler, IdException;
  38. type
  39. TIdIOHandlerSocket = class(TIdIOHandler)
  40. protected
  41. FBinding: TIdSocketHandle;
  42. FUseNagle: boolean;
  43. FSocksInfo: TIdSocksInfo;
  44. procedure SetSocksInfo(ASocks: TIdSocksInfo);
  45. function GetSocksInfo: TIdSocksInfo;
  46. procedure SetUseNagle(AValue: Boolean);
  47. procedure SetNagleOpt(AEnabled: Boolean);
  48. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  49. public
  50. procedure Close; override;
  51. procedure ConnectClient(const AHost: string; const APort: Integer; const ABoundIP: string;
  52. const ABoundPort: Integer; const ABoundPortMin: Integer; const ABoundPortMax: Integer;
  53. const ATimeout: Integer = IdTimeoutDefault); override;
  54. function Connected: Boolean; override;
  55. constructor Create(AOwner: TComponent); override;
  56. destructor Destroy; override;
  57. procedure Open; override;
  58. function Readable(AMSec: integer = IdTimeoutDefault): boolean; override;
  59. function Recv(var ABuf; ALen: integer): integer; override;
  60. function Send(var ABuf; ALen: integer): integer; override;
  61. //
  62. property Binding: TIdSocketHandle read FBinding;
  63. published
  64. property SocksInfo: TIdSocksInfo read GetSocksInfo write SetSocksInfo;
  65. property UseNagle: boolean read FUseNagle write SetUseNagle default True;
  66. end;
  67. implementation
  68. uses
  69. IdAntiFreezeBase, IdStackConsts, IdResourceStrings, IdStack, IdTCPConnection,
  70. IdComponent,
  71. SysUtils;
  72. type
  73. TIdConnectThread = class(TThread)
  74. protected
  75. FBinding: TIdSocketHandle;
  76. FExceptionMessage: string;
  77. FLastSocketError: Integer;
  78. public
  79. procedure Execute; override;
  80. end;
  81. { TIdIOHandlerSocket }
  82. procedure TIdIOHandlerSocket.Close;
  83. begin
  84. inherited Close;
  85. if Assigned(FBinding) then begin
  86. FBinding.CloseSocket;
  87. end;
  88. end;
  89. procedure TIdIOHandlerSocket.ConnectClient(const AHost: string;
  90. const APort: Integer; const ABoundIP: string; const ABoundPort,
  91. ABoundPortMin, ABoundPortMax: Integer; const ATimeout: Integer = IdTimeoutDefault);
  92. procedure ConnectTimeout(ATimeout: Integer);
  93. var
  94. LSleepTime: Integer;
  95. LInfinite: Boolean;
  96. begin
  97. LInfinite := ATimeout = IdTimeoutInfinite;
  98. with TIdConnectThread.Create(True) do try
  99. FBinding := Binding;
  100. Resume;
  101. // Sleep
  102. if TIdAntiFreezeBase.ShouldUse then begin
  103. LSleepTime := Min(GAntiFreeze.IdleTimeOut, 125);
  104. end else begin
  105. LSleepTime := 125;
  106. end;
  107. if LInfinite then begin
  108. ATimeout := LSleepTime + 1;
  109. end;
  110. while ATimeout > LSleepTime do begin
  111. IdGlobal.Sleep(LSleepTime);
  112. ATimeout := ATimeout - LSleepTime;
  113. if LInfinite then begin
  114. ATimeout := LSleepTime + 1;
  115. end;
  116. TIdAntiFreezeBase.DoProcess;
  117. if Terminated then begin
  118. ATimeout := 0;
  119. Break;
  120. end;
  121. end;
  122. IdGlobal.Sleep(ATimeout);
  123. //
  124. if Terminated then begin
  125. if Length(FExceptionMessage) > 0 then begin
  126. if FLastSocketError <> 0 then begin
  127. raise EIdSocketError.CreateError(FLastSocketError, FExceptionMessage);
  128. end else begin
  129. raise EIdConnectException.Create(FExceptionMessage);
  130. end;
  131. end;
  132. end else begin
  133. Terminate;
  134. Close;
  135. WaitFor;
  136. raise EIdConnectTimeout.Create(RSConnectTimeout);
  137. end;
  138. finally Free; end;
  139. end;
  140. Var
  141. LHost: String;
  142. LPort: Integer;
  143. begin
  144. // Socks support
  145. if SocksInfo.Version in [svSocks4, svSocks4A, svSocks5] then begin
  146. LHost := SocksInfo.Host;
  147. LPort := SocksInfo.Port;
  148. end else begin
  149. LHost := AHost;
  150. LPort := APort;
  151. end;
  152. inherited ConnectClient(LHost, LPort, ABoundIP, ABoundPort, ABoundPortMin, ABoundPortMax, ATimeout);
  153. with Binding do begin
  154. AllocateSocket;
  155. IP := ABoundIP;
  156. Port := ABoundPort;
  157. ClientPortMin := ABoundPortMin;
  158. ClientPortMax := ABoundPortMax;
  159. Bind;
  160. end;
  161. if not GStack.IsIP(LHost) then begin
  162. DoStatus(hsResolving, [LHost]);
  163. end;
  164. // Tell the binding what its destination is
  165. Binding.SetPeer(GStack.ResolveHost(LHost), LPort);
  166. SetNagleOpt(UseNagle);
  167. // Connect
  168. DoStatus(hsConnecting, [Binding.PeerIP]);
  169. if (ATimeout = IdTimeoutDefault) or (ATimeout = 0) then begin
  170. if TIdAntiFreezeBase.ShouldUse then begin
  171. ConnectTimeout(120000); // 2 Min
  172. end else begin
  173. GStack.CheckForSocketError(Binding.Connect);
  174. end;
  175. end else begin
  176. ConnectTimeout(ATimeout);
  177. end;
  178. SocksInfo.MakeSocksConnection(AHost, APort);
  179. end;
  180. function TIdIOHandlerSocket.Connected: Boolean;
  181. begin
  182. Result := FBinding <> nil;
  183. if Result then begin
  184. Result := FBinding.HandleAllocated;
  185. end;
  186. end;
  187. constructor TIdIOHandlerSocket.Create(AOwner: TComponent);
  188. begin
  189. inherited Create(AOwner);
  190. FUseNagle := True;
  191. end;
  192. procedure TIdIOHandlerSocket.Open;
  193. begin
  194. inherited Open;
  195. if not Assigned(FBinding) then begin
  196. FBinding := TIdSocketHandle.Create(nil);
  197. end
  198. else
  199. FBinding.Reset(true);
  200. end;
  201. function TIdIOHandlerSocket.Readable(AMSec: integer): boolean;
  202. begin
  203. Result := Binding.Readable(AMSec);
  204. end;
  205. function TIdIOHandlerSocket.Recv(var ABuf; ALen: integer): integer;
  206. begin
  207. if Connected then
  208. begin
  209. Result := Binding.Recv(ABuf, ALen, 0);
  210. end
  211. else begin
  212. raise EIdClosedSocket.Create(RSStatusDisconnected);
  213. end;
  214. end;
  215. function TIdIOHandlerSocket.Send(var ABuf; ALen: integer): integer;
  216. begin
  217. if Connected then
  218. begin
  219. Result := Binding.Send(ABuf, ALen, 0);
  220. end
  221. else begin
  222. raise EIdClosedSocket.Create(RSStatusDisconnected);
  223. end;
  224. end;
  225. procedure TIdIOHandlerSocket.SetSocksInfo(ASocks: TIdSocksInfo);
  226. begin
  227. // All this is to preserve the compatibility with old version
  228. // In the case when we have SocksInfo as object created in runtime without owner form it is treated as temporary object
  229. // In the case when the ASocks points to an object with owner it is treated as component on form.
  230. if Assigned(ASocks) then begin
  231. if not Assigned(ASocks.Owner) then begin
  232. if Assigned(SocksInfo.Owner) then begin
  233. FSocksInfo := nil;
  234. end;
  235. SocksInfo.Assign(ASocks); // This will construct the default SocksInfo
  236. end
  237. else begin
  238. if Assigned(FSocksInfo) then begin
  239. if not Assigned(FSocksInfo.Owner) then begin
  240. FreeAndNil(FSocksInfo);
  241. end;
  242. end;
  243. FSocksInfo := ASocks;
  244. FSocksInfo.FreeNotification(self);
  245. end;
  246. FSocksInfo.IOHandler := Self;
  247. end
  248. else begin
  249. FSocksInfo := ASocks;
  250. end;
  251. end;
  252. function TIdIOHandlerSocket.GetSocksInfo: TIdSocksInfo;
  253. begin
  254. {
  255. Note that we didn't create the Socks Object at design-time for some reason
  256. but I forgot what that reason was. If this introduces unintended consequence, this patch ill
  257. be removed and things may have to be reworked.
  258. }
  259. // if (not (csDesigning in ComponentState)) and (not Assigned(FSocksInfo)) then begin
  260. if (not Assigned(FSocksInfo)) then begin
  261. FSocksInfo := TIdSocksInfo.Create(nil);
  262. end;
  263. result := FSocksInfo;
  264. end;
  265. destructor TIdIOHandlerSocket.Destroy;
  266. begin
  267. if Assigned(FSocksInfo) then begin
  268. if FSocksInfo.Owner = nil then begin
  269. FreeAndNil(FSocksInfo);
  270. end;
  271. end;
  272. FreeAndNil(FBinding);
  273. inherited Destroy;
  274. end;
  275. procedure TIdIOHandlerSocket.SetUseNagle(AValue: Boolean);
  276. begin
  277. if FUseNagle <> AValue then begin
  278. FUseNagle := AValue;
  279. SetNagleOpt(FUseNagle);
  280. end;
  281. end;
  282. procedure TIdIOHandlerSocket.SetNagleOpt(AEnabled: Boolean);
  283. const
  284. Options: array[Boolean] of Integer = (1, 0);
  285. begin
  286. if Connected then begin
  287. Binding.SetSockOpt(Id_IPPROTO_TCP, Id_TCP_NODELAY, PChar(@Options[AEnabled]), SizeOf(Options[AEnabled]));
  288. end;
  289. end;
  290. procedure TIdIOHandlerSocket.Notification(AComponent: TComponent; Operation: TOperation);
  291. begin
  292. inherited Notification(AComponent, OPeration);
  293. if (Operation = opRemove) then begin
  294. if (AComponent = FSocksInfo) then begin
  295. FSocksInfo := nil;
  296. end;
  297. end;
  298. end;
  299. { TIdConnectThread }
  300. procedure TIdConnectThread.Execute;
  301. begin
  302. try
  303. // Id_WSAEBADF (9) on Linux, Id_WSAENOTSOCK (10038) on Windows
  304. GStack.CheckForSocketError(FBinding.Connect, [Id_WSAEBADF, Id_WSAENOTSOCK]);
  305. except on
  306. E: Exception do begin
  307. FExceptionMessage := E.Message;
  308. if E is EIdSocketError then begin
  309. FLastSocketError := EIdSocketError(E).LastError;
  310. end;
  311. end;
  312. end;
  313. // Necessary as caller checks this
  314. Terminate;
  315. end;
  316. end.