PageRenderTime 56ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/delphi/src/Thrift.Socket.pas

http://github.com/apache/thrift
Pascal | 1617 lines | 1195 code | 209 blank | 213 comment | 140 complexity | 4c556c350c09a5583ad9226331bacf2c MD5 | raw file
Possible License(s): Apache-2.0, MPL-2.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. (*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. *)
  19. unit Thrift.Socket;
  20. {$I Thrift.Defines.inc}
  21. {$I-} // prevent annoying errors with default log delegate and no console
  22. interface
  23. {$IFNDEF OLD_SOCKETS} // not for OLD_SOCKETS
  24. uses
  25. Winapi.Windows, Winapi.Winsock2;
  26. const
  27. AI_PASSIVE = $00000001; // Socket address will be used in bind() call
  28. AI_CANONNAME = $00000002; // Return canonical name in first ai_canonname
  29. AI_NUMERICHOST = $00000004; // Nodename must be a numeric address string
  30. AI_NUMERICSERV = $00000008; // Servicename must be a numeric port number
  31. AI_ALL = $00000100; // Query both IP6 and IP4 with AI_V4MAPPED
  32. AI_ADDRCONFIG = $00000400; // Resolution only if global address configured
  33. AI_V4MAPPED = $00000800; // On v6 failure, query v4 and convert to V4MAPPED format
  34. AI_NON_AUTHORITATIVE = $00004000; // LUP_NON_AUTHORITATIVE
  35. AI_SECURE = $00008000; // LUP_SECURE
  36. AI_RETURN_PREFERRED_NAMES = $00010000; // LUP_RETURN_PREFERRED_NAMES
  37. AI_FQDN = $00020000; // Return the FQDN in ai_canonname
  38. AI_FILESERVER = $00040000; // Resolving fileserver name resolution
  39. type
  40. PAddrInfoA = ^TAddrInfoA;
  41. TAddrInfoA = record
  42. ai_flags: Integer;
  43. ai_family: Integer;
  44. ai_socktype: Integer;
  45. ai_protocol: Integer;
  46. ai_addrlen: NativeUInt;
  47. ai_canonname: PAnsiChar;
  48. ai_addr: PSockAddr;
  49. ai_next: PAddrInfoA;
  50. end;
  51. PAddrInfoW = ^TAddrInfoW;
  52. TAddrInfoW = record
  53. ai_flags: Integer;
  54. ai_family: Integer;
  55. ai_socktype: Integer;
  56. ai_protocol: Integer;
  57. ai_addrlen: NativeUInt;
  58. ai_canonname: PChar;
  59. ai_addr: PSockAddr;
  60. ai_next: PAddrInfoW;
  61. end;
  62. TAddressFamily = USHORT;
  63. TIn6Addr = record
  64. case Integer of
  65. 0: (_Byte: array[0..15] of UCHAR);
  66. 1: (_Word: array[0..7] of USHORT);
  67. end;
  68. TScopeId = record
  69. public
  70. Value: ULONG;
  71. strict private
  72. function GetBitField(Loc: Integer): Integer; inline;
  73. procedure SetBitField(Loc: Integer; const aValue: Integer); inline;
  74. public
  75. property Zone: Integer index $0028 read GetBitField write SetBitField;
  76. property Level: Integer index $2804 read GetBitField write SetBitField;
  77. end;
  78. TSockAddrIn6 = record
  79. sin6_family: TAddressFamily;
  80. sin6_port: USHORT;
  81. sin6_flowinfo: ULONG;
  82. sin6_addr: TIn6Addr;
  83. case Integer of
  84. 0: (sin6_scope_id: ULONG);
  85. 1: (sin6_scope_struct: TScopeId);
  86. end;
  87. PSockAddrIn6 = ^TSockAddrIn6;
  88. const
  89. NI_NOFQDN = $01; // Only return nodename portion for local hosts
  90. NI_NUMERICHOST = $02; // Return numeric form of the host's address
  91. NI_NAMEREQD = $04; // Error if the host's name not in DNS
  92. NI_NUMERICSERV = $08; // Return numeric form of the service (port #)
  93. NI_DGRAM = $10; // Service is a datagram service
  94. NI_MAXHOST = 1025; // Max size of a fully-qualified domain name
  95. NI_MAXSERV = 32; // Max size of a service name
  96. function getaddrinfo(pNodeName, pServiceName: PAnsiChar; const pHints: TAddrInfoA; var ppResult: PAddrInfoA): Integer; stdcall;
  97. function GetAddrInfoW(pNodeName, pServiceName: PWideChar; const pHints: TAddrInfoW; var ppResult: PAddrInfoW): Integer; stdcall;
  98. procedure freeaddrinfo(pAddrInfo: PAddrInfoA); stdcall;
  99. procedure FreeAddrInfoW(pAddrInfo: PAddrInfoW); stdcall;
  100. function getnameinfo(const pSockaddr: TSockAddr; SockaddrLength: Integer; pNodeBuffer: PAnsiChar; NodeBufferSize: DWORD; pServiceBuffer: PAnsiChar;
  101. ServiceBufferSize: DWORD; Flags: Integer): Integer; stdcall;
  102. function GetNameInfoW(const pSockaddr: TSockAddr; SockaddrLength: Integer; pNodeBuffer: PWideChar; NodeBufferSize: DWORD; pServiceBuffer: PWideChar;
  103. ServiceBufferSize: DWORD; Flags: Integer): Integer; stdcall;
  104. type
  105. TSmartPointerDestroyer<T> = reference to procedure(Value: T);
  106. ISmartPointer<T> = reference to function: T;
  107. TSmartPointer<T> = class(TInterfacedObject, ISmartPointer<T>)
  108. strict private
  109. FValue: T;
  110. FDestroyer: TSmartPointerDestroyer<T>;
  111. public
  112. constructor Create(AValue: T; ADestroyer: TSmartPointerDestroyer<T>);
  113. destructor Destroy; override;
  114. function Invoke: T;
  115. end;
  116. TBaseSocket = class abstract
  117. public type
  118. TLogDelegate = reference to procedure( const str: string);
  119. strict private
  120. FPort: Integer;
  121. FSocket: Winapi.Winsock2.TSocket;
  122. FSendTimeout,
  123. FRecvTimeout: Longword;
  124. FKeepAlive: Boolean;
  125. FLogDelegate: TLogDelegate;
  126. class constructor Create;
  127. class destructor Destroy;
  128. class procedure DefaultLogDelegate(const Str: string);
  129. strict protected type
  130. IGetAddrInfoWrapper = interface
  131. function Init: Integer;
  132. function GetRes: PAddrInfoW;
  133. property Res: PAddrInfoW read GetRes;
  134. end;
  135. TGetAddrInfoWrapper = class(TInterfacedObject, IGetAddrInfoWrapper)
  136. strict private
  137. FNode: string;
  138. FService: string;
  139. FHints,
  140. FRes: PAddrInfoW;
  141. public
  142. constructor Create(ANode, AService: string; AHints: PAddrInfoW);
  143. destructor Destroy; override;
  144. function Init: Integer;
  145. function GetRes: PAddrInfoW;
  146. property Res: PAddrInfoW read GetRes;
  147. end;
  148. strict protected
  149. procedure CommonInit; virtual;
  150. function CreateSocket(AAddress: string; APort: Integer): IGetAddrInfoWrapper;
  151. procedure SetRecvTimeout(ARecvTimeout: Longword); virtual;
  152. procedure SetSendTimeout(ASendTimeout: Longword); virtual;
  153. procedure SetKeepAlive(AKeepAlive: Boolean); virtual;
  154. procedure SetSocket(ASocket: Winapi.Winsock2.TSocket);
  155. property LogDelegate: TLogDelegate read FLogDelegate;
  156. public
  157. //
  158. // Constructs a new socket. Note that this does NOT actually connect the
  159. // socket.
  160. //
  161. constructor Create(ALogDelegate: TLogDelegate = nil); overload;
  162. constructor Create(APort: Integer; ALogDelegate: TLogDelegate = nil); overload;
  163. //
  164. // Destroys the socket object, closing it if necessary.
  165. //
  166. destructor Destroy; override;
  167. //
  168. // Shuts down communications on the socket
  169. //
  170. procedure Close; virtual;
  171. // The port that the socket is connected to
  172. property Port: Integer read FPort write FPort;
  173. // The receive timeout
  174. property RecvTimeout: Longword read FRecvTimeout write SetRecvTimeout;
  175. // The send timeout
  176. property SendTimeout: Longword read FSendTimeout write SetSendTimeout;
  177. // Set SO_KEEPALIVE
  178. property KeepAlive: Boolean read FKeepAlive write SetKeepAlive;
  179. // The underlying socket descriptor
  180. property Socket: Winapi.Winsock2.TSocket read FSocket write SetSocket;
  181. end;
  182. TSocket = class(TBaseSocket)
  183. strict private type
  184. TCachedPeerAddr = record
  185. case Integer of
  186. 0: (ipv4: TSockAddrIn);
  187. 1: (ipv6: TSockAddrIn6);
  188. end;
  189. strict private
  190. FHost: string;
  191. FPeerHost: string;
  192. FPeerAddress: string;
  193. FPeerPort: Integer;
  194. FInterruptListener: ISmartPointer<Winapi.Winsock2.TSocket>;
  195. FConnTimeout: Longword;
  196. FLingerOn: Boolean;
  197. FLingerVal: Integer;
  198. FNoDelay: Boolean;
  199. FMaxRecvRetries: Longword;
  200. FCachedPeerAddr: TCachedPeerAddr;
  201. procedure InitPeerInfo;
  202. procedure OpenConnection(Res: TBaseSocket.IGetAddrInfoWrapper);
  203. procedure LocalOpen;
  204. procedure SetGenericTimeout(S: Winapi.Winsock2.TSocket; Timeout: Longword; OptName: Integer);
  205. function GetIsOpen: Boolean;
  206. procedure SetNoDelay(ANoDelay: Boolean);
  207. function GetSocketInfo: string;
  208. function GetPeerHost: string;
  209. function GetPeerAddress: string;
  210. function GetPeerPort: Integer;
  211. function GetOrigin: string;
  212. strict protected
  213. procedure CommonInit; override;
  214. procedure SetRecvTimeout(ARecvTimeout: Longword); override;
  215. procedure SetSendTimeout(ASendTimeout: Longword); override;
  216. procedure SetKeepAlive(AKeepAlive: Boolean); override;
  217. public
  218. //
  219. // Constructs a new socket. Note that this does NOT actually connect the
  220. // socket.
  221. //
  222. constructor Create(ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
  223. //
  224. // Constructs a new socket. Note that this does NOT actually connect the
  225. // socket.
  226. //
  227. // @param host An IP address or hostname to connect to
  228. // @param port The port to connect on
  229. //
  230. constructor Create(AHost: string; APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
  231. //
  232. // Constructor to create socket from socket descriptor.
  233. //
  234. constructor Create(ASocket: Winapi.Winsock2.TSocket; ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
  235. //
  236. // Constructor to create socket from socket descriptor that
  237. // can be interrupted safely.
  238. //
  239. constructor Create(ASocket: Winapi.Winsock2.TSocket; AInterruptListener: ISmartPointer<Winapi.Winsock2.TSocket>;
  240. ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
  241. //
  242. // Creates and opens the socket
  243. //
  244. // @throws ETransportationException If the socket could not connect
  245. //
  246. procedure Open;
  247. //
  248. // Shuts down communications on the socket
  249. //
  250. procedure Close; override;
  251. //
  252. // Reads from the underlying socket.
  253. // \returns the number of bytes read or 0 indicates EOF
  254. // \throws TTransportException of types:
  255. // Interrupted means the socket was interrupted
  256. // out of a blocking call
  257. // NotOpen means the socket has been closed
  258. // TimedOut means the receive timeout expired
  259. // Unknown means something unexpected happened
  260. //
  261. function Read(var Buf; Len: Integer): Integer;
  262. //
  263. // Writes to the underlying socket. Loops until done or fail.
  264. //
  265. procedure Write(const Buf; Len: Integer);
  266. //
  267. // Writes to the underlying socket. Does single send() and returns result.
  268. //
  269. function WritePartial(const Buf; Len: Integer): Integer;
  270. //
  271. // Returns a cached copy of the peer address.
  272. //
  273. function GetCachedAddress(out Len: Integer): PSockAddr;
  274. //
  275. // Set a cache of the peer address (used when trivially available: e.g.
  276. // accept() or connect()). Only caches IPV4 and IPV6; unset for others.
  277. //
  278. procedure SetCachedAddress(const Addr: TSockAddr; Len: Integer);
  279. //
  280. // Controls whether the linger option is set on the socket.
  281. //
  282. // @param on Whether SO_LINGER is on
  283. // @param linger If linger is active, the number of seconds to linger for
  284. //
  285. procedure SetLinger(LingerOn: Boolean; LingerVal: Integer);
  286. //
  287. // Calls select() on the socket to see if there is more data available.
  288. //
  289. function Peek: Boolean;
  290. // Whether the socket is alive
  291. property IsOpen: Boolean read GetIsOpen;
  292. // The host that the socket is connected to
  293. property Host: string read FHost write FHost;
  294. // Whether to enable or disable Nagle's algorithm
  295. property NoDelay: Boolean read FNoDelay write SetNoDelay;
  296. // Connect timeout
  297. property ConnTimeout: Longword read FConnTimeout write FConnTimeout;
  298. // The max number of recv retries in the case of a WSAEWOULDBLOCK
  299. property MaxRecvRetries: Longword read FMaxRecvRetries write FMaxRecvRetries;
  300. // Socket information formatted as a string <Host: x Port: x>
  301. property SocketInfo: string read GetSocketInfo;
  302. // The DNS name of the host to which the socket is connected
  303. property PeerHost: string read GetPeerHost;
  304. // The address of the host to which the socket is connected
  305. property PeerAddress: string read GetPeerAddress;
  306. // The port of the host to which the socket is connected
  307. property PeerPort: Integer read GetPeerPort;
  308. // The origin the socket is connected to
  309. property Origin: string read GetOrigin;
  310. end;
  311. TServerSocketFunc = reference to procedure(sock: Winapi.Winsock2.TSocket);
  312. TServerSocket = class(TBaseSocket)
  313. strict private
  314. FAddress: string;
  315. FAcceptBacklog,
  316. FRetryLimit,
  317. FRetryDelay,
  318. FTcpSendBuffer,
  319. FTcpRecvBuffer: Integer;
  320. FAcceptTimeout: Longword;
  321. FListening,
  322. FInterruptableChildren: Boolean;
  323. FInterruptSockWriter, // is notified on Interrupt()
  324. FInterruptSockReader, // is used in select with FSocket for interruptability
  325. FChildInterruptSockWriter: Winapi.Winsock2.TSocket; // is notified on InterruptChildren()
  326. FChildInterruptSockReader: ISmartPointer<Winapi.Winsock2.TSocket>; // if FnterruptableChildren this is shared with child TSockets
  327. FListenCallback,
  328. FAcceptCallback: TServerSocketFunc;
  329. function CreateSocketObj(Client: Winapi.Winsock2.TSocket): TSocket;
  330. procedure Notify(NotifySocket: Winapi.Winsock2.TSocket);
  331. procedure SetInterruptableChildren(AValue: Boolean);
  332. strict protected
  333. procedure CommonInit; override;
  334. public const
  335. DEFAULT_BACKLOG = 1024;
  336. public
  337. //
  338. // Constructor.
  339. //
  340. // @param port Port number to bind to
  341. //
  342. constructor Create(APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
  343. //
  344. // Constructor.
  345. //
  346. // @param port Port number to bind to
  347. // @param sendTimeout Socket send timeout
  348. // @param recvTimeout Socket receive timeout
  349. //
  350. constructor Create(APort: Integer; ASendTimeout, ARecvTimeout: Longword; ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
  351. //
  352. // Constructor.
  353. //
  354. // @param address Address to bind to
  355. // @param port Port number to bind to
  356. //
  357. constructor Create(AAddress: string; APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
  358. procedure Listen;
  359. function Accept: TSocket;
  360. procedure Interrupt;
  361. procedure InterruptChildren;
  362. procedure Close; override;
  363. property AcceptBacklog: Integer read FAcceptBacklog write FAcceptBacklog;
  364. property AcceptTimeout: Longword read FAcceptTimeout write FAcceptTimeout;
  365. property RetryLimit: Integer read FRetryLimit write FRetryLimit;
  366. property RetryDelay: Integer read FRetryDelay write FRetryDelay;
  367. property TcpSendBuffer: Integer read FTcpSendBuffer write FTcpSendBuffer;
  368. property TcpRecvBuffer: Integer read FTcpRecvBuffer write FTcpRecvBuffer;
  369. // When enabled (the default), new children TSockets will be constructed so
  370. // they can be interrupted by TServerTransport.InterruptChildren().
  371. // This is more expensive in terms of system calls (poll + recv) however
  372. // ensures a connected client cannot interfere with TServer.Stop().
  373. //
  374. // When disabled, TSocket children do not incur an additional poll() call.
  375. // Server-side reads are more efficient, however a client can interfere with
  376. // the server's ability to shutdown properly by staying connected.
  377. //
  378. // Must be called before listen(); mode cannot be switched after that.
  379. // \throws EPropertyError if listen() has been called
  380. property InterruptableChildren: Boolean read FInterruptableChildren write SetInterruptableChildren;
  381. // listenCallback gets called just before listen, and after all Thrift
  382. // setsockopt calls have been made. If you have custom setsockopt
  383. // things that need to happen on the listening socket, this is the place to do it.
  384. property ListenCallback: TServerSocketFunc read FListenCallback write FListenCallback;
  385. // acceptCallback gets called after each accept call, on the newly created socket.
  386. // It is called after all Thrift setsockopt calls have been made. If you have
  387. // custom setsockopt things that need to happen on the accepted
  388. // socket, this is the place to do it.
  389. property AcceptCallback: TServerSocketFunc read FAcceptCallback write FAcceptCallback;
  390. end;
  391. {$ENDIF} // not for OLD_SOCKETS
  392. implementation
  393. {$IFNDEF OLD_SOCKETS} // not for OLD_SOCKETS
  394. uses
  395. System.SysUtils, System.Math, System.DateUtils, Thrift.Transport;
  396. constructor TBaseSocket.TGetAddrInfoWrapper.Create(ANode, AService: string; AHints: PAddrInfoW);
  397. begin
  398. inherited Create;
  399. FNode := ANode;
  400. FService := AService;
  401. FHints := AHints;
  402. FRes := nil;
  403. end;
  404. destructor TBaseSocket.TGetAddrInfoWrapper.Destroy;
  405. begin
  406. if Assigned(FRes) then
  407. FreeAddrInfoW(FRes);
  408. inherited Destroy;
  409. end;
  410. function TBaseSocket.TGetAddrInfoWrapper.Init: Integer;
  411. begin
  412. if FRes = nil then
  413. Exit(GetAddrInfoW(@FNode[1], @FService[1], FHints^, FRes));
  414. Result := 0;
  415. end;
  416. function TBaseSocket.TGetAddrInfoWrapper.GetRes: PAddrInfoW;
  417. begin
  418. Result := FRes;
  419. end;
  420. procedure DestroyerOfFineSockets(ssock: Winapi.Winsock2.TSocket);
  421. begin
  422. closesocket(ssock);
  423. end;
  424. function TScopeId.GetBitField(Loc: Integer): Integer;
  425. begin
  426. Result := (Value shr (Loc shr 8)) and ((1 shl (Loc and $FF)) - 1);
  427. end;
  428. procedure TScopeId.SetBitField(Loc: Integer; const aValue: Integer);
  429. begin
  430. Value := (Value and ULONG((not ((1 shl (Loc and $FF)) - 1)))) or ULONG(aValue shl (Loc shr 8));
  431. end;
  432. function getaddrinfo; external 'ws2_32.dll' name 'getaddrinfo';
  433. function GetAddrInfoW; external 'ws2_32.dll' name 'GetAddrInfoW';
  434. procedure freeaddrinfo; external 'ws2_32.dll' name 'freeaddrinfo';
  435. procedure FreeAddrInfoW; external 'ws2_32.dll' name 'FreeAddrInfoW';
  436. function getnameinfo; external 'ws2_32.dll' name 'getnameinfo';
  437. function GetNameInfoW; external 'ws2_32.dll' name 'GetNameInfoW';
  438. constructor TSmartPointer<T>.Create(AValue: T; ADestroyer: TSmartPointerDestroyer<T>);
  439. begin
  440. inherited Create;
  441. FValue := AValue;
  442. FDestroyer := ADestroyer;
  443. end;
  444. destructor TSmartPointer<T>.Destroy;
  445. begin
  446. if Assigned(FDestroyer) then FDestroyer(FValue);
  447. inherited Destroy;
  448. end;
  449. function TSmartPointer<T>.Invoke: T;
  450. begin
  451. Result := FValue;
  452. end;
  453. class constructor TBaseSocket.Create;
  454. var
  455. Version: WORD;
  456. Data: WSAData;
  457. Error: Integer;
  458. begin
  459. Version := $0202;
  460. FillChar(Data, SizeOf(Data), 0);
  461. Error := WSAStartup(Version, Data);
  462. if Error <> 0 then
  463. raise Exception.Create('Failed to initialize Winsock.');
  464. end;
  465. class destructor TBaseSocket.Destroy;
  466. begin
  467. WSACleanup;
  468. end;
  469. class procedure TBaseSocket.DefaultLogDelegate(const Str: string);
  470. var
  471. OutStr: string;
  472. begin
  473. OutStr := Format('Thrift: %s %s', [DateTimeToStr(Now, TFormatSettings.Create), Str]);
  474. try
  475. Writeln(OutStr);
  476. if IoResult <> 0 then OutputDebugString(PChar(OutStr));
  477. except
  478. OutputDebugString(PChar(OutStr));
  479. end;
  480. end;
  481. procedure TBaseSocket.CommonInit;
  482. begin
  483. FSocket := INVALID_SOCKET;
  484. FPort := 0;
  485. FSendTimeout := 0;
  486. FRecvTimeout := 0;
  487. FKeepAlive := False;
  488. FLogDelegate := DefaultLogDelegate;
  489. end;
  490. function TBaseSocket.CreateSocket(AAddress: string; APort: Integer): IGetAddrInfoWrapper;
  491. var
  492. Hints: TAddrInfoW;
  493. Res: PAddrInfoW;
  494. ThePort: array[0..5] of Char;
  495. Error: Integer;
  496. begin
  497. FillChar(Hints, SizeOf(Hints), 0);
  498. Hints.ai_family := PF_UNSPEC;
  499. Hints.ai_socktype := SOCK_STREAM;
  500. Hints.ai_flags := AI_PASSIVE;
  501. StrFmt(ThePort, '%d', [FPort]);
  502. Result := TGetAddrInfoWrapper.Create(AAddress, ThePort, @Hints);
  503. Error := Result.Init;
  504. if Error <> 0 then begin
  505. LogDelegate(Format('GetAddrInfoW %d: %s', [Error, SysErrorMessage(Error)]));
  506. Close;
  507. raise TTransportExceptionNotOpen.Create('Could not resolve host for server socket.');
  508. end;
  509. // Pick the ipv6 address first since ipv4 addresses can be mapped
  510. // into ipv6 space.
  511. Res := Result.Res;
  512. while Assigned(Res) do begin
  513. if (Res^.ai_family = AF_INET6) or (not Assigned(Res^.ai_next)) then
  514. Break;
  515. Res := Res^.ai_next;
  516. end;
  517. FSocket := Winapi.Winsock2.socket(Res^.ai_family, Res^.ai_socktype, Res^.ai_protocol);
  518. if FSocket = INVALID_SOCKET then begin
  519. Error := WSAGetLastError;
  520. LogDelegate(Format('TBaseSocket.CreateSocket() socket() %s', [SysErrorMessage(Error)]));
  521. Close;
  522. raise TTransportExceptionNotOpen.Create(Format('socket(): %s', [SysErrorMessage(Error)]));
  523. end;
  524. end;
  525. procedure TBaseSocket.SetRecvTimeout(ARecvTimeout: Longword);
  526. begin
  527. FRecvTimeout := ARecvTimeout;
  528. end;
  529. procedure TBaseSocket.SetSendTimeout(ASendTimeout: Longword);
  530. begin
  531. FSendTimeout := ASendTimeout;
  532. end;
  533. procedure TBaseSocket.SetKeepAlive(AKeepAlive: Boolean);
  534. begin
  535. FKeepAlive := AKeepAlive;
  536. end;
  537. procedure TBaseSocket.SetSocket(ASocket: Winapi.Winsock2.TSocket);
  538. begin
  539. if FSocket <> INVALID_SOCKET then
  540. Close;
  541. FSocket := ASocket;
  542. end;
  543. constructor TBaseSocket.Create(ALogDelegate: TLogDelegate);
  544. begin
  545. inherited Create;
  546. CommonInit;
  547. if Assigned(ALogDelegate) then FLogDelegate := ALogDelegate;
  548. end;
  549. constructor TBaseSocket.Create(APort: Integer; ALogDelegate: TLogDelegate);
  550. begin
  551. inherited Create;
  552. CommonInit;
  553. FPort := APort;
  554. if Assigned(ALogDelegate) then FLogDelegate := ALogDelegate;
  555. end;
  556. destructor TBaseSocket.Destroy;
  557. begin
  558. Close;
  559. inherited Destroy;
  560. end;
  561. procedure TBaseSocket.Close;
  562. begin
  563. if FSocket <> INVALID_SOCKET then begin
  564. shutdown(FSocket, SD_BOTH);
  565. closesocket(FSocket);
  566. end;
  567. FSocket := INVALID_SOCKET;
  568. end;
  569. procedure TSocket.InitPeerInfo;
  570. begin
  571. FCachedPeerAddr.ipv4.sin_family := AF_UNSPEC;
  572. FPeerHost := '';
  573. FPeerAddress := '';
  574. FPeerPort := 0;
  575. end;
  576. procedure TSocket.CommonInit;
  577. begin
  578. inherited CommonInit;
  579. FHost := '';
  580. FInterruptListener := nil;
  581. FConnTimeout := 0;
  582. FLingerOn := True;
  583. FLingerVal := 0;
  584. FNoDelay := True;
  585. FMaxRecvRetries := 5;
  586. InitPeerInfo;
  587. end;
  588. procedure TSocket.OpenConnection(Res: TBaseSocket.IGetAddrInfoWrapper);
  589. label
  590. Done;
  591. var
  592. ErrnoCopy: Integer;
  593. Ret,
  594. Ret2: Integer;
  595. Fds: TFdSet;
  596. TVal: TTimeVal;
  597. PTVal: PTimeVal;
  598. Val,
  599. Lon: Integer;
  600. One,
  601. Zero: Cardinal;
  602. begin
  603. if SendTimeout > 0 then SetSendTimeout(SendTimeout);
  604. if RecvTimeout > 0 then SetRecvTimeout(RecvTimeout);
  605. if KeepAlive then SetKeepAlive(KeepAlive);
  606. SetLinger(FLingerOn, FLingerVal);
  607. SetNoDelay(FNoDelay);
  608. // Set the socket to be non blocking for connect if a timeout exists
  609. Zero := 0;
  610. if FConnTimeout > 0 then begin
  611. One := 1;
  612. if ioctlsocket(Socket, Integer(FIONBIO), One) = SOCKET_ERROR then begin
  613. ErrnoCopy := WSAGetLastError;
  614. LogDelegate(Format('TSocket.OpenConnection() ioctlsocket() %s %s', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
  615. raise TTransportExceptionNotOpen.Create(Format('ioctlsocket() failed: %s', [SysErrorMessage(ErrnoCopy)]));
  616. end;
  617. end
  618. else begin
  619. if ioctlsocket(Socket, Integer(FIONBIO), Zero) = SOCKET_ERROR then begin
  620. ErrnoCopy := WSAGetLastError;
  621. LogDelegate(Format('TSocket.OpenConnection() ioctlsocket() %s %s', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
  622. raise TTransportExceptionNotOpen.Create(Format('ioctlsocket() failed: %s', [SysErrorMessage(ErrnoCopy)]));
  623. end;
  624. end;
  625. Ret := connect(Socket, Res.Res^.ai_addr^, Res.Res^.ai_addrlen);
  626. if Ret = 0 then goto Done;
  627. ErrnoCopy := WSAGetLastError;
  628. if (ErrnoCopy <> WSAEINPROGRESS) and (ErrnoCopy <> WSAEWOULDBLOCK) then begin
  629. LogDelegate(Format('TSocket.OpenConnection() connect() ', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
  630. raise TTransportExceptionNotOpen.Create(Format('connect() failed: %s', [SysErrorMessage(ErrnoCopy)]));
  631. end;
  632. FD_ZERO(Fds);
  633. _FD_SET(Socket, Fds);
  634. if FConnTimeout > 0 then begin
  635. TVal.tv_sec := FConnTimeout div 1000;
  636. TVal.tv_usec := (FConnTimeout mod 1000) * 1000;
  637. PTVal := @TVal;
  638. end
  639. else
  640. PTVal := nil;
  641. Ret := select(1, nil, @Fds, nil, PTVal);
  642. if Ret > 0 then begin
  643. // Ensure the socket is connected and that there are no errors set
  644. Lon := SizeOf(Val);
  645. Ret2 := getsockopt(Socket, SOL_SOCKET, SO_ERROR, @Val, Lon);
  646. if Ret2 = SOCKET_ERROR then begin
  647. ErrnoCopy := WSAGetLastError;
  648. LogDelegate(Format('TSocket.OpenConnection() getsockopt() ', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
  649. raise TTransportExceptionNotOpen.Create(Format('getsockopt(): %s', [SysErrorMessage(ErrnoCopy)]));
  650. end;
  651. // no errors on socket, go to town
  652. if Val = 0 then goto Done;
  653. LogDelegate(Format('TSocket.OpenConnection() error on socket (after select()) ', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
  654. raise TTransportExceptionNotOpen.Create(Format('socket OpenConnection() error: %s', [SysErrorMessage(Val)]));
  655. end
  656. else if Ret = 0 then begin
  657. // socket timed out
  658. LogDelegate(Format('TSocket.OpenConnection() timed out ', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
  659. raise TTransportExceptionNotOpen.Create('OpenConnection() timed out');
  660. end
  661. else begin
  662. // error on select()
  663. ErrnoCopy := WSAGetLastError;
  664. LogDelegate(Format('TSocket.OpenConnection() select() ', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
  665. raise TTransportExceptionNotOpen.Create(Format('select() failed: %s', [SysErrorMessage(ErrnoCopy)]));
  666. end;
  667. Done:
  668. // Set socket back to normal mode (blocking)
  669. ioctlsocket(Socket, Integer(FIONBIO), Zero);
  670. SetCachedAddress(Res.Res^.ai_addr^, Res.Res^.ai_addrlen);
  671. end;
  672. procedure TSocket.LocalOpen;
  673. var
  674. Res: TBaseSocket.IGetAddrInfoWrapper;
  675. begin
  676. if IsOpen then Exit;
  677. // Validate port number
  678. if (Port < 0) or (Port > $FFFF) then
  679. raise TTransportExceptionBadArgs.Create('Specified port is invalid');
  680. Res := CreateSocket(Host, Port);
  681. OpenConnection(Res);
  682. end;
  683. procedure TSocket.SetGenericTimeout(S: Winapi.Winsock2.TSocket; Timeout: Longword; OptName: Integer);
  684. var
  685. Time: DWORD;
  686. begin
  687. if S = INVALID_SOCKET then
  688. Exit;
  689. Time := Timeout;
  690. if setsockopt(S, SOL_SOCKET, OptName, @Time, SizeOf(Time)) = SOCKET_ERROR then
  691. LogDelegate(Format('SetGenericTimeout() setsockopt() %s', [SysErrorMessage(WSAGetLastError)]));
  692. end;
  693. function TSocket.GetIsOpen: Boolean;
  694. begin
  695. Result := Socket <> INVALID_SOCKET;
  696. end;
  697. procedure TSocket.SetNoDelay(ANoDelay: Boolean);
  698. var
  699. V: Integer;
  700. begin
  701. FNoDelay := ANoDelay;
  702. if Socket = INVALID_SOCKET then
  703. Exit;
  704. V := IfThen(FNoDelay, 1, 0);
  705. if setsockopt(Socket, IPPROTO_TCP, TCP_NODELAY, @V, SizeOf(V)) = SOCKET_ERROR then
  706. LogDelegate(Format('TSocket.SetNoDelay() setsockopt() %s %s', [SocketInfo, SysErrorMessage(WSAGetLastError)]));
  707. end;
  708. function TSocket.GetSocketInfo: string;
  709. begin
  710. if (FHost = '') or (Port = 0) then
  711. Result := '<Host: ' + GetPeerAddress + ' Port: ' + GetPeerPort.ToString + '>'
  712. else
  713. Result := '<Host: ' + FHost + ' Port: ' + Port.ToString + '>';
  714. end;
  715. function TSocket.GetPeerHost: string;
  716. var
  717. Addr: TSockAddrStorage;
  718. AddrPtr: PSockAddr;
  719. AddrLen: Integer;
  720. ClientHost: array[0..NI_MAXHOST-1] of Char;
  721. ClientService: array[0..NI_MAXSERV-1] of Char;
  722. begin
  723. if FPeerHost = '' then begin
  724. if Socket = INVALID_SOCKET then
  725. Exit(FPeerHost);
  726. AddrPtr := GetCachedAddress(AddrLen);
  727. if AddrPtr = nil then begin
  728. AddrLen := SizeOf(Addr);
  729. if getpeername(Socket, PSockAddr(@Addr)^, AddrLen) <> 0 then
  730. Exit(FPeerHost);
  731. AddrPtr := PSockAddr(@Addr);
  732. SetCachedAddress(AddrPtr^, AddrLen);
  733. end;
  734. GetNameInfoW(AddrPtr^, AddrLen, ClientHost, NI_MAXHOST, ClientService, NI_MAXSERV, 0);
  735. FPeerHost := ClientHost;
  736. end;
  737. Result := FPeerHost;
  738. end;
  739. function TSocket.GetPeerAddress: string;
  740. var
  741. Addr: TSockAddrStorage;
  742. AddrPtr: PSockAddr;
  743. AddrLen: Integer;
  744. ClientHost: array[0..NI_MAXHOST-1] of Char;
  745. ClientService: array[0..NI_MAXSERV-1] of Char;
  746. begin
  747. if FPeerAddress = '' then begin
  748. if Socket = INVALID_SOCKET then
  749. Exit(FPeerAddress);
  750. AddrPtr := GetCachedAddress(AddrLen);
  751. if AddrPtr = nil then begin
  752. AddrLen := SizeOf(Addr);
  753. if getpeername(Socket, PSockAddr(@Addr)^, AddrLen) <> 0 then
  754. Exit(FPeerHost);
  755. AddrPtr := PSockAddr(@Addr);
  756. SetCachedAddress(AddrPtr^, AddrLen);
  757. end;
  758. GetNameInfoW(AddrPtr^, AddrLen, ClientHost, NI_MAXHOST, ClientService, NI_MAXSERV, NI_NUMERICHOST or NI_NUMERICSERV);
  759. FPeerAddress := ClientHost;
  760. TryStrToInt(ClientService, FPeerPort);
  761. end;
  762. Result := FPeerAddress
  763. end;
  764. function TSocket.GetPeerPort: Integer;
  765. begin
  766. GetPeerAddress;
  767. Result := FPeerPort;
  768. end;
  769. function TSocket.GetOrigin: string;
  770. begin
  771. Result := GetPeerHost + ':' + GetPeerPort.ToString;
  772. end;
  773. procedure TSocket.SetRecvTimeout(ARecvTimeout: Longword);
  774. begin
  775. inherited SetRecvTimeout(ARecvTimeout);
  776. SetGenericTimeout(Socket, ARecvTimeout, SO_RCVTIMEO);
  777. end;
  778. procedure TSocket.SetSendTimeout(ASendTimeout: Longword);
  779. begin
  780. inherited SetSendTimeout(ASendTimeout);
  781. SetGenericTimeout(Socket, ASendTimeout, SO_SNDTIMEO);
  782. end;
  783. procedure TSocket.SetKeepAlive(AKeepAlive: Boolean);
  784. var
  785. Value: Integer;
  786. begin
  787. inherited SetKeepAlive(AKeepAlive);
  788. Value := IfThen(KeepAlive, 1, 0);
  789. if setsockopt(Socket, SOL_SOCKET, SO_KEEPALIVE, @Value, SizeOf(Value)) = SOCKET_ERROR then
  790. LogDelegate(Format('TSocket.SetKeepAlive() setsockopt() %s %s', [SocketInfo, SysErrorMessage(WSAGetLastError)]));
  791. end;
  792. constructor TSocket.Create(ALogDelegate: TBaseSocket.TLogDelegate = nil);
  793. begin
  794. // Not needed, but just a placeholder
  795. inherited Create(ALogDelegate);
  796. end;
  797. constructor TSocket.Create(AHost: string; APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate);
  798. begin
  799. inherited Create(APort, ALogDelegate);
  800. FHost := AHost;
  801. end;
  802. constructor TSocket.Create(ASocket: Winapi.Winsock2.TSocket; ALogDelegate: TBaseSocket.TLogDelegate);
  803. begin
  804. inherited Create(ALogDelegate);
  805. Socket := ASocket;
  806. end;
  807. constructor TSocket.Create(ASocket: Winapi.Winsock2.TSocket; AInterruptListener: ISmartPointer<Winapi.Winsock2.TSocket>;
  808. ALogDelegate: TBaseSocket.TLogDelegate);
  809. begin
  810. inherited Create(ALogDelegate);
  811. Socket := ASocket;
  812. FInterruptListener := AInterruptListener;
  813. end;
  814. procedure TSocket.Open;
  815. begin
  816. if IsOpen then Exit;
  817. LocalOpen;
  818. end;
  819. procedure TSocket.Close;
  820. begin
  821. inherited Close;
  822. InitPeerInfo;
  823. end;
  824. function TSocket.Read(var Buf; Len: Integer): Integer;
  825. label
  826. TryAgain;
  827. var
  828. Retries: Longword;
  829. EAgainThreshold,
  830. ReadElapsed: UInt64;
  831. Start: TDateTime;
  832. Got: Integer;
  833. Fds: TFdSet;
  834. ErrnoCopy: Integer;
  835. TVal: TTimeVal;
  836. PTVal: PTimeVal;
  837. Ret: Integer;
  838. begin
  839. if Socket = INVALID_SOCKET then
  840. raise TTransportExceptionNotOpen.Create('Called read on non-open socket');
  841. Retries := 0;
  842. // THRIFT_EAGAIN can be signalled both when a timeout has occurred and when
  843. // the system is out of resources (an awesome undocumented feature).
  844. // The following is an approximation of the time interval under which
  845. // THRIFT_EAGAIN is taken to indicate an out of resources error.
  846. EAgainThreshold := 0;
  847. if RecvTimeout <> 0 then
  848. // if a readTimeout is specified along with a max number of recv retries, then
  849. // the threshold will ensure that the read timeout is not exceeded even in the
  850. // case of resource errors
  851. EAgainThreshold := RecvTimeout div IfThen(FMaxRecvRetries > 0, FMaxRecvRetries, 2);
  852. TryAgain:
  853. // Read from the socket
  854. if RecvTimeout > 0 then
  855. Start := Now
  856. else
  857. // if there is no read timeout we don't need the TOD to determine whether
  858. // an THRIFT_EAGAIN is due to a timeout or an out-of-resource condition.
  859. Start := 0;
  860. if Assigned(FInterruptListener) then begin
  861. FD_ZERO(Fds);
  862. _FD_SET(Socket, Fds);
  863. _FD_SET(FInterruptListener, Fds);
  864. if RecvTimeout > 0 then begin
  865. TVal.tv_sec := RecvTimeout div 1000;
  866. TVal.tv_usec := (RecvTimeout mod 1000) * 1000;
  867. PTVal := @TVal;
  868. end
  869. else
  870. PTVal := nil;
  871. Ret := select(2, @Fds, nil, nil, PTVal);
  872. ErrnoCopy := WSAGetLastError;
  873. if Ret < 0 then begin
  874. // error cases
  875. if (ErrnoCopy = WSAEINTR) and (Retries < FMaxRecvRetries) then begin
  876. Inc(Retries);
  877. goto TryAgain;
  878. end;
  879. LogDelegate(Format('TSocket.Read() select() %s', [SysErrorMessage(ErrnoCopy)]));
  880. raise TTransportExceptionUnknown.Create(Format('Unknown: %s', [SysErrorMessage(ErrnoCopy)]));
  881. end
  882. else if Ret > 0 then begin
  883. // Check the interruptListener
  884. if FD_ISSET(FInterruptListener, Fds) then
  885. raise TTransportExceptionInterrupted.Create('Interrupted');
  886. end
  887. else // Ret = 0
  888. raise TTransportExceptionTimedOut.Create('WSAEWOULDBLOCK (timed out)');
  889. // falling through means there is something to recv and it cannot block
  890. end;
  891. Got := recv(Socket, Buf, Len, 0);
  892. ErrnoCopy := WSAGetLastError;
  893. // Check for error on read
  894. if Got < 0 then begin
  895. if ErrnoCopy = WSAEWOULDBLOCK then begin
  896. // if no timeout we can assume that resource exhaustion has occurred.
  897. if RecvTimeout = 0 then
  898. raise TTransportExceptionTimedOut.Create('WSAEWOULDBLOCK (unavailable resources)');
  899. // check if this is the lack of resources or timeout case
  900. ReadElapsed := MilliSecondsBetween(Now, Start);
  901. if (EAgainThreshold = 0) or (ReadElapsed < EAgainThreshold) then begin
  902. if Retries < FMaxRecvRetries then begin
  903. Inc(Retries);
  904. Sleep(1);
  905. goto TryAgain;
  906. end
  907. else
  908. raise TTransportExceptionTimedOut.Create('WSAEWOULDBLOCK (unavailable resources)');
  909. end
  910. else
  911. // infer that timeout has been hit
  912. raise TTransportExceptionTimedOut.Create('WSAEWOULDBLOCK (timed out)');
  913. end;
  914. // If interrupted, try again
  915. if (ErrnoCopy = WSAEINTR) and (Retries < FMaxRecvRetries) then begin
  916. Inc(Retries);
  917. goto TryAgain;
  918. end;
  919. if ErrnoCopy = WSAECONNRESET then
  920. Exit(0);
  921. // This ish isn't open
  922. if ErrnoCopy = WSAENOTCONN then
  923. raise TTransportExceptionNotOpen.Create('WSAENOTCONN');
  924. // Timed out!
  925. if ErrnoCopy = WSAETIMEDOUT then
  926. raise TTransportExceptionNotOpen.Create('WSAETIMEDOUT');
  927. // Now it's not a try again case, but a real probblez
  928. LogDelegate(Format('TSocket.Read() recv() %s %s', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
  929. // Some other error, whatevz
  930. raise TTransportExceptionUnknown.Create(Format('Unknown: %s', [SysErrorMessage(ErrnoCopy)]));
  931. end;
  932. Result := Got;
  933. end;
  934. procedure TSocket.Write(const Buf; Len: Integer);
  935. var
  936. Sent, B: Integer;
  937. begin
  938. Sent := 0;
  939. while Sent < Len do begin
  940. B := WritePartial((PByte(@Buf) + Sent)^, Len - Sent);
  941. if B = 0 then
  942. // This should only happen if the timeout set with SO_SNDTIMEO expired.
  943. // Raise an exception.
  944. raise TTransportExceptionTimedOut.Create('send timeout expired');
  945. Inc(Sent, B);
  946. end;
  947. end;
  948. function TSocket.WritePartial(const Buf; Len: Integer): Integer;
  949. var
  950. B: Integer;
  951. ErrnoCopy: Integer;
  952. begin
  953. if Socket = INVALID_SOCKET then
  954. raise TTransportExceptionNotOpen.Create('Called write on non-open socket');
  955. B := send(Socket, Buf, Len, 0);
  956. if B < 0 then begin
  957. // Fail on a send error
  958. ErrnoCopy := WSAGetLastError;
  959. if ErrnoCopy = WSAEWOULDBLOCK then
  960. Exit(0);
  961. LogDelegate(Format('TSocket.WritePartial() send() %s %s', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
  962. if (ErrnoCopy = WSAECONNRESET) or (ErrnoCopy = WSAENOTCONN) then begin
  963. Close;
  964. raise TTransportExceptionNotOpen.Create(Format('write() send(): %s', [SysErrorMessage(ErrnoCopy)]));
  965. end;
  966. raise TTransportExceptionUnknown.Create(Format('write() send(): %s', [SysErrorMessage(ErrnoCopy)]));
  967. end;
  968. // Fail on blocked send
  969. if B = 0 then
  970. raise TTransportExceptionNotOpen.Create('Socket send returned 0.');
  971. Result := B;
  972. end;
  973. function TSocket.GetCachedAddress(out Len: Integer): PSockAddr;
  974. begin
  975. case FCachedPeerAddr.ipv4.sin_family of
  976. AF_INET: begin
  977. Len := SizeOf(TSockAddrIn);
  978. Result := PSockAddr(@FCachedPeerAddr.ipv4);
  979. end;
  980. AF_INET6: begin
  981. Len := SizeOf(TSockAddrIn6);
  982. Result := PSockAddr(@FCachedPeerAddr.ipv6);
  983. end;
  984. else
  985. Len := 0;
  986. Result := nil;
  987. end;
  988. end;
  989. procedure TSocket.SetCachedAddress(const Addr: TSockAddr; Len: Integer);
  990. begin
  991. case Addr.sa_family of
  992. AF_INET: if Len = SizeOf(TSockAddrIn) then FCachedPeerAddr.ipv4 := PSockAddrIn(@Addr)^;
  993. AF_INET6: if Len = SizeOf(TSockAddrIn6) then FCachedPeerAddr.ipv6 := PSockAddrIn6(@Addr)^;
  994. end;
  995. FPeerAddress := '';
  996. FPeerHost := '';
  997. FPeerPort := 0;
  998. end;
  999. procedure TSocket.SetLinger(LingerOn: Boolean; LingerVal: Integer);
  1000. var
  1001. L: TLinger;
  1002. begin
  1003. FLingerOn := LingerOn;
  1004. FLingerVal := LingerVal;
  1005. if Socket = INVALID_SOCKET then
  1006. Exit;
  1007. L.l_onoff := IfThen(FLingerOn, 1, 0);
  1008. L.l_linger := LingerVal;
  1009. if setsockopt(Socket, SOL_SOCKET, SO_LINGER, @L, SizeOf(L)) = SOCKET_ERROR then
  1010. LogDelegate(Format('TSocket.SetLinger() setsockopt() %s %s', [SocketInfo, SysErrorMessage(WSAGetLastError)]));
  1011. end;
  1012. function TSocket.Peek: Boolean;
  1013. var
  1014. Retries: Longword;
  1015. Fds: TFdSet;
  1016. TVal: TTimeVal;
  1017. PTVal: PTimeVal;
  1018. Ret: Integer;
  1019. ErrnoCopy: Integer;
  1020. Buf: Byte;
  1021. begin
  1022. if not IsOpen then Exit(False);
  1023. if Assigned(FInterruptListener) then begin
  1024. Retries := 0;
  1025. while true do begin
  1026. FD_ZERO(Fds);
  1027. _FD_SET(Socket, Fds);
  1028. _FD_SET(FInterruptListener, Fds);
  1029. if RecvTimeout > 0 then begin
  1030. TVal.tv_sec := RecvTimeout div 1000;
  1031. TVal.tv_usec := (RecvTimeout mod 1000) * 1000;
  1032. PTVal := @TVal;
  1033. end
  1034. else
  1035. PTVal := nil;
  1036. Ret := select(2, @Fds, nil, nil, PTVal);
  1037. ErrnoCopy := WSAGetLastError;
  1038. if Ret < 0 then begin
  1039. // error cases
  1040. if (ErrnoCopy = WSAEINTR) and (Retries < FMaxRecvRetries) then begin
  1041. Inc(Retries);
  1042. Continue;
  1043. end;
  1044. LogDelegate(Format('TSocket.Peek() select() %s', [SysErrorMessage(ErrnoCopy)]));
  1045. raise TTransportExceptionUnknown.Create(Format('Unknown: %s', [SysErrorMessage(ErrnoCopy)]));
  1046. end
  1047. else if Ret > 0 then begin
  1048. // Check the interruptListener
  1049. if FD_ISSET(FInterruptListener, Fds) then
  1050. Exit(False);
  1051. // There must be data or a disconnection, fall through to the PEEK
  1052. Break;
  1053. end
  1054. else
  1055. // timeout
  1056. Exit(False);
  1057. end;
  1058. end;
  1059. // Check to see if data is available or if the remote side closed
  1060. Ret := recv(Socket, Buf, 1, MSG_PEEK);
  1061. if Ret = SOCKET_ERROR then begin
  1062. ErrnoCopy := WSAGetLastError;
  1063. if ErrnoCopy = WSAECONNRESET then begin
  1064. Close;
  1065. Exit(False);
  1066. end;
  1067. LogDelegate(Format('TSocket.Peek() recv() %s %s', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
  1068. raise TTransportExceptionUnknown.Create(Format('recv(): %s', [SysErrorMessage(ErrnoCopy)]));
  1069. end;
  1070. Result := Ret > 0;
  1071. end;
  1072. function TServerSocket.CreateSocketObj(Client: Winapi.Winsock2.TSocket): TSocket;
  1073. begin
  1074. if FInterruptableChildren then
  1075. Result := TSocket.Create(Client, FChildInterruptSockReader)
  1076. else
  1077. Result := TSocket.Create(Client);
  1078. end;
  1079. procedure TServerSocket.Notify(NotifySocket: Winapi.Winsock2.TSocket);
  1080. var
  1081. Byt: Byte;
  1082. begin
  1083. if NotifySocket <> INVALID_SOCKET then begin
  1084. Byt := 0;
  1085. if send(NotifySocket, Byt, SizeOf(Byt), 0) = SOCKET_ERROR then
  1086. LogDelegate(Format('TServerSocket.Notify() send() %s', [SysErrorMessage(WSAGetLastError)]));
  1087. end;
  1088. end;
  1089. procedure TServerSocket.SetInterruptableChildren(AValue: Boolean);
  1090. begin
  1091. if FListening then
  1092. raise Exception.Create('InterruptableChildren cannot be set after listen()');
  1093. FInterruptableChildren := AValue;
  1094. end;
  1095. procedure TServerSocket.CommonInit;
  1096. begin
  1097. inherited CommonInit;
  1098. FInterruptableChildren := True;
  1099. FAcceptBacklog := DEFAULT_BACKLOG;
  1100. FAcceptTimeout := 0;
  1101. FRetryLimit := 0;
  1102. FRetryDelay := 0;
  1103. FTcpSendBuffer := 0;
  1104. FTcpRecvBuffer := 0;
  1105. FListening := False;
  1106. FInterruptSockWriter := INVALID_SOCKET;
  1107. FInterruptSockReader := INVALID_SOCKET;
  1108. FChildInterruptSockWriter := INVALID_SOCKET;
  1109. end;
  1110. constructor TServerSocket.Create(APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate = nil);
  1111. begin
  1112. // Unnecessary, but here for documentation purposes
  1113. inherited Create(APort, ALogDelegate);
  1114. end;
  1115. constructor TServerSocket.Create(APort: Integer; ASendTimeout, ARecvTimeout: Longword; ALogDelegate: TBaseSocket.TLogDelegate);
  1116. begin
  1117. inherited Create(APort, ALogDelegate);
  1118. SendTimeout := ASendTimeout;
  1119. RecvTimeout := ARecvTimeout;
  1120. end;
  1121. constructor TServerSocket.Create(AAddress: string; APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate);
  1122. begin
  1123. inherited Create(APort, ALogDelegate);
  1124. FAddress := AAddress;
  1125. end;
  1126. procedure TServerSocket.Listen;
  1127. function CreateSocketPair(var Reader, Writer: Winapi.Winsock2.TSocket): Integer;
  1128. label
  1129. Error;
  1130. type
  1131. TSAUnion = record
  1132. case Integer of
  1133. 0: (inaddr: TSockAddrIn);
  1134. 1: (addr: TSockAddr);
  1135. end;
  1136. var
  1137. a: TSAUnion;
  1138. listener: Winapi.Winsock2.TSocket;
  1139. e: Integer;
  1140. addrlen: Integer;
  1141. flags: DWORD;
  1142. reuse: Integer;
  1143. begin
  1144. addrlen := SizeOf(a.inaddr);
  1145. flags := 0;
  1146. reuse := 1;
  1147. listener := Winapi.Winsock2.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  1148. if listener = INVALID_SOCKET then
  1149. Exit(SOCKET_ERROR);
  1150. FillChar(a, SizeOf(a), 0);
  1151. a.inaddr.sin_family := AF_INET;
  1152. a.inaddr.sin_addr.s_addr := htonl(INADDR_LOOPBACK);
  1153. a.inaddr.sin_port := 0;
  1154. Reader := INVALID_SOCKET;
  1155. Writer := INVALID_SOCKET;
  1156. // ignore errors coming out of this setsockopt. This is because
  1157. // SO_EXCLUSIVEADDRUSE requires admin privileges on WinXP, but we don't
  1158. // want to force socket pairs to be an admin.
  1159. setsockopt(listener, SOL_SOCKET, Integer(SO_EXCLUSIVEADDRUSE), @reuse, SizeOf(reuse));
  1160. if bind(listener, a.addr, SizeOf(a.inaddr)) = SOCKET_ERROR then
  1161. goto Error;
  1162. if getsockname(listener, a.addr, addrlen) = SOCKET_ERROR then
  1163. goto Error;
  1164. if Winapi.Winsock2.listen(listener, 1) = SOCKET_ERROR then
  1165. goto Error;
  1166. Reader := WSASocket(AF_INET, SOCK_STREAM, 0, nil, 0, flags);
  1167. if Reader = INVALID_SOCKET then
  1168. goto Error;
  1169. if connect(Reader, a.addr, SizeOf(a.inaddr)) = SOCKET_ERROR then
  1170. goto Error;
  1171. Writer := Winapi.Winsock2.accept(listener, nil, nil);
  1172. if Writer = INVALID_SOCKET then
  1173. goto Error;
  1174. closesocket(listener);
  1175. Exit(0);
  1176. Error:
  1177. e := WSAGetLastError;
  1178. closesocket(listener);
  1179. closesocket(Reader);
  1180. closesocket(Writer);
  1181. WSASetLastError(e);
  1182. Result := SOCKET_ERROR;
  1183. end;
  1184. var
  1185. TempIntReader,
  1186. TempIntWriter: Winapi.Winsock2.TSocket;
  1187. One: Cardinal;
  1188. ErrnoCopy: Integer;
  1189. Ling: TLinger;
  1190. Retries: Integer;
  1191. AddrInfo: IGetAddrInfoWrapper;
  1192. SA: TSockAddrStorage;
  1193. Len: Integer;
  1194. begin
  1195. // Create the socket pair used to interrupt
  1196. if CreateSocketPair(TempIntReader, TempIntWriter) = SOCKET_ERROR then begin
  1197. LogDelegate(Format('TServerSocket.Listen() CreateSocketPair() Interrupt %s', [SysErrorMessage(WSAGetLastError)]));
  1198. FInterruptSockReader := INVALID_SOCKET;
  1199. FInterruptSockWriter := INVALID_SOCKET;
  1200. end
  1201. else begin
  1202. FInterruptSockReader := TempIntReader;
  1203. FInterruptSockWriter := TempIntWriter;
  1204. end;
  1205. // Create the socket pair used to interrupt all clients
  1206. if CreateSocketPair(TempIntReader, TempIntWriter) = SOCKET_ERROR then begin
  1207. LogDelegate(Format('TServerSocket.Listen() CreateSocketPair() ChildInterrupt %s', [SysErrorMessage(WSAGetLastError)]));
  1208. FChildInterruptSockReader := TSmartPointer<Winapi.Winsock2.TSocket>.Create(INVALID_SOCKET, nil);
  1209. FChildInterruptSockWriter := INVALID_SOCKET;
  1210. end
  1211. else begin
  1212. FChildInterruptSockReader := TSmartPointer<Winapi.Winsock2.TSocket>.Create(TempIntReader, DestroyerOfFineSockets);
  1213. FChildInterruptSockWriter := TempIntWriter;
  1214. end;
  1215. if (Port < 0) or (Port > $FFFF) then
  1216. raise TTransportExceptionBadArgs.Create('Specified port is invalid');
  1217. AddrInfo := CreateSocket(FAddress, Port);
  1218. // Set SO_EXCLUSIVEADDRUSE to prevent 2MSL delay on accept
  1219. One := 1;
  1220. setsockopt(Socket, SOL_SOCKET, Integer(SO_EXCLUSIVEADDRUSE), @one, SizeOf(One));
  1221. // ignore errors coming out of this setsockopt on Windows. This is because
  1222. // SO_EXCLUSIVEADDRUSE requires admin privileges on WinXP, but we don't
  1223. // want to force servers to be an admin.
  1224. // Set TCP buffer sizes
  1225. if FTcpSendBuffer > 0 then begin
  1226. if setsockopt(Socket, SOL_SOCKET, SO_SNDBUF, @FTcpSendBuffer, SizeOf(FTcpSendBuffer)) = SOCKET_ERROR then begin
  1227. ErrnoCopy := WSAGetLastError;
  1228. LogDelegate(Format('TServerSocket.Listen() setsockopt() SO_SNDBUF %s', [SysErrorMessage(ErrnoCopy)]));
  1229. raise TTransportExceptionNotOpen.Create(Format('Could not set SO_SNDBUF: %s', [SysErrorMessage(ErrnoCopy)]));
  1230. end;
  1231. end;
  1232. if FTcpRecvBuffer > 0 then begin
  1233. if setsockopt(Socket, SOL_SOCKET, SO_RCVBUF, @FTcpRecvBuffer, SizeOf(FTcpRecvBuffer)) = SOCKET_ERROR then begin
  1234. ErrnoCopy := WSAGetLastError;
  1235. LogDelegate(Format('TServerSocket.Listen() setsockopt() SO_RCVBUF %s', [SysErrorMessage(ErrnoCopy)]));
  1236. raise TTransportExceptionNotOpen.Create(Format('Could not set SO_RCVBUF: %s', [SysErrorMessage(ErrnoCopy)]));
  1237. end;
  1238. end;
  1239. // Turn linger off, don't want to block on calls to close
  1240. Ling.l_onoff := 0;
  1241. Ling.l_linger := 0;
  1242. if setsockopt(Socket, SOL_SOCKET, SO_LINGER, @Ling, SizeOf(Ling)) = SOCKET_ERROR then begin
  1243. ErrnoCopy := WSAGetLastError;
  1244. LogDelegate(Format('TServerSocket.Listen() setsockopt() SO_LINGER %s', [SysErrorMessage(ErrnoCopy)]));
  1245. raise TTransportExceptionNotOpen.Create(Format('Could not set SO_LINGER: %s', [SysErrorMessage(ErrnoCopy)]));
  1246. end;
  1247. // TCP Nodelay, speed over bandwidth
  1248. if setsockopt(Socket, IPPROTO_TCP, TCP_NODELAY, @One, SizeOf(One)) = SOCKET_ERROR then begin
  1249. ErrnoCopy := WSAGetLastError;
  1250. LogDelegate(Format('TServerSocket.Listen() setsockopt() TCP_NODELAY %s', [SysErrorMessage(ErrnoCopy)]));
  1251. raise TTransportExceptionNotOpen.Create(Format('Could not set TCP_NODELAY: %s', [SysErrorMessage(ErrnoCopy)]));
  1252. end;
  1253. // Set NONBLOCK on the accept socket
  1254. if ioctlsocket(Socket, Integer(FIONBIO), One) = SOCKET_ERROR then begin
  1255. ErrnoCopy := WSAGetLastError;
  1256. LogDelegate(Format('TServerSocket.Listen() ioctlsocket() FIONBIO %s', [SysErrorMessage(ErrnoCopy)]));
  1257. raise TTransportExceptionNotOpen.Create(Format('ioctlsocket() FIONBIO: %s', [SysErrorMessage(ErrnoCopy)]));
  1258. end;
  1259. // prepare the port information
  1260. // we may want to try to bind more than once, since THRIFT_NO_SOCKET_CACHING doesn't
  1261. // always seem to work. The client can configure the retry variables.
  1262. Retries := 0;
  1263. while True do begin
  1264. if bind(Socket, AddrInfo.Res^.ai_addr^, AddrInfo.Res^.ai_addrlen) = 0 then
  1265. Break;
  1266. Inc(Retries);
  1267. if Retries > FRetryLimit then
  1268. Break;
  1269. Sleep(FRetryDelay * 1000);
  1270. end;
  1271. // retrieve bind info
  1272. if (Port = 0) and (Retries < FRetryLimit) then begin
  1273. Len := SizeOf(SA);
  1274. FillChar(SA, Len, 0);
  1275. if getsockname(Socket, PSockAddr(@SA)^, Len) = SOCKET_ERROR then
  1276. LogDelegate(Format('TServerSocket.Listen() getsockname() %s', [SysErrorMessage(WSAGetLastError)]))
  1277. else begin
  1278. if SA.ss_family = AF_INET6 then
  1279. Port := ntohs(PSockAddrIn6(@SA)^.sin6_port)
  1280. else
  1281. Port := ntohs(PSockAddrIn(@SA)^.sin_port);
  1282. end;
  1283. end;
  1284. // throw an error if we failed to bind properly
  1285. if (Retries > FRetryLimit) then begin
  1286. LogDelegate(Format('TServerSocket.Listen() BIND %d', [Port]));
  1287. Close;
  1288. raise TTransportExceptionNotOpen.Create(Format('Could not bind: %s', [SysErrorMessage(WSAGetLastError)]));
  1289. end;
  1290. if Assigned(FListenCallback) then
  1291. FListenCallback(Socket);
  1292. // Call listen
  1293. if Winapi.Winsock2.listen(Socket, FAcceptBacklog) = SOCKET_ERROR then begin
  1294. ErrnoCopy := WSAGetLastError;
  1295. LogDelegate(Format('TServerSocket.Listen() listen() %s', [SysErrorMessage(ErrnoCopy)]));
  1296. raise TTransportExceptionNotOpen.Create(Format('Could not listen: %s', [SysErrorMessage(ErrnoCopy)]));
  1297. end;
  1298. // The socket is now listening!
  1299. end;
  1300. function TServerSocket.Accept: TSocket;
  1301. var
  1302. Fds: TFdSet;
  1303. MaxEInters,
  1304. NumEInters: Integer;
  1305. TVal: TTimeVal;
  1306. PTVal: PTimeVal;
  1307. ErrnoCopy: Integer;
  1308. Buf: Byte;
  1309. ClientAddress: TSockAddrStorage;
  1310. Size: Integer;
  1311. ClientSocket: Winapi.Winsock2.TSocket;
  1312. Zero: Cardinal;
  1313. Client: TSocket;
  1314. Ret: Integer;
  1315. begin
  1316. MaxEInters := 5;
  1317. NumEInters := 0;
  1318. while True do begin
  1319. FD_ZERO(Fds);
  1320. _FD_SET(Socket, Fds);
  1321. _FD_SET(FInterruptSockReader, Fds);
  1322. if FAcceptTimeout > 0 then begin
  1323. TVal.tv_sec := FAcceptTimeout div 1000;
  1324. TVal.tv_usec := (FAcceptTimeout mod 1000) * 1000;
  1325. PTVal := @TVal;
  1326. end
  1327. else
  1328. PTVal := nil;
  1329. // TODO: if WSAEINTR is received, we'll restart the timeout.
  1330. // To be accurate, we need to fix this in the future.
  1331. Ret := select(2, @Fds, nil, nil, PTVal);
  1332. if Ret < 0 then begin
  1333. // error cases
  1334. if (WSAGetLastError = WSAEINTR) and (NumEInters < MaxEInters) then begin
  1335. // THRIFT_EINTR needs to be handled manually and we can tolerate
  1336. // a certain number
  1337. Inc(NumEInters);
  1338. Continue;
  1339. end;
  1340. ErrnoCopy := WSAGetLastError;
  1341. LogDelegate(Format('TServerSocket.Accept() select() %s', [SysErrorMessage(ErrnoCopy)]));
  1342. raise TTransportExceptionUnknown.Create(Format('Unknown: %s', [SysErrorMessage(ErrnoCopy)]));
  1343. end
  1344. else if Ret > 0 then begin
  1345. // Check for an interrupt signal
  1346. if (FInterruptSockReader <> INVALID_SOCKET) and FD_ISSET(FInterruptSockReader, Fds) then begin
  1347. if recv(FInterruptSockReader, Buf, SizeOf(Buf), 0) = SOCKET_ERROR then
  1348. LogDelegate(Format('TServerSocket.Accept() recv() interrupt %s', [SysErrorMessage(WSAGetLastError)]));
  1349. raise TTransportExceptionInterrupted.Create('in…

Large files files are truncated, but you can click here to view the full file