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
10 * http://www.apache.org/licenses/LICENSE-2.0
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
22 {$I Thrift.Defines.inc}
23 {$I-} // prevent annoying errors with default log delegate and no console
26 {$IFNDEF OLD_SOCKETS} // not for OLD_SOCKETS
29 Winapi.Windows, Winapi.Winsock2;
32 AI_PASSIVE = $00000001; // Socket address will be used in bind() call
33 AI_CANONNAME = $00000002; // Return canonical name in first ai_canonname
34 AI_NUMERICHOST = $00000004; // Nodename must be a numeric address string
35 AI_NUMERICSERV = $00000008; // Servicename must be a numeric port number
37 AI_ALL = $00000100; // Query both IP6 and IP4 with AI_V4MAPPED
38 AI_ADDRCONFIG = $00000400; // Resolution only if global address configured
39 AI_V4MAPPED = $00000800; // On v6 failure, query v4 and convert to V4MAPPED format
41 AI_NON_AUTHORITATIVE = $00004000; // LUP_NON_AUTHORITATIVE
42 AI_SECURE = $00008000; // LUP_SECURE
43 AI_RETURN_PREFERRED_NAMES = $00010000; // LUP_RETURN_PREFERRED_NAMES
45 AI_FQDN = $00020000; // Return the FQDN in ai_canonname
46 AI_FILESERVER = $00040000; // Resolving fileserver name resolution
49 PAddrInfoA = ^TAddrInfoA;
55 ai_addrlen: NativeUInt;
56 ai_canonname: PAnsiChar;
61 PAddrInfoW = ^TAddrInfoW;
67 ai_addrlen: NativeUInt;
73 TAddressFamily = USHORT;
77 0: (_Byte: array[0..15] of UCHAR);
78 1: (_Word: array[0..7] of USHORT);
85 function GetBitField(Loc: Integer): Integer; inline;
86 procedure SetBitField(Loc: Integer; const aValue: Integer); inline;
88 property Zone: Integer index $0028 read GetBitField write SetBitField;
89 property Level: Integer index $2804 read GetBitField write SetBitField;
93 sin6_family: TAddressFamily;
98 0: (sin6_scope_id: ULONG);
99 1: (sin6_scope_struct: TScopeId);
101 PSockAddrIn6 = ^TSockAddrIn6;
104 NI_NOFQDN = $01; // Only return nodename portion for local hosts
105 NI_NUMERICHOST = $02; // Return numeric form of the host's address
106 NI_NAMEREQD = $04; // Error if the host's name not in DNS
107 NI_NUMERICSERV = $08; // Return numeric form of the service (port #)
108 NI_DGRAM = $10; // Service is a datagram service
110 NI_MAXHOST = 1025; // Max size of a fully-qualified domain name
111 NI_MAXSERV = 32; // Max size of a service name
113 function getaddrinfo(pNodeName, pServiceName: PAnsiChar; const pHints: TAddrInfoA; var ppResult: PAddrInfoA): Integer; stdcall;
114 function GetAddrInfoW(pNodeName, pServiceName: PWideChar; const pHints: TAddrInfoW; var ppResult: PAddrInfoW): Integer; stdcall;
115 procedure freeaddrinfo(pAddrInfo: PAddrInfoA); stdcall;
116 procedure FreeAddrInfoW(pAddrInfo: PAddrInfoW); stdcall;
117 function getnameinfo(const pSockaddr: TSockAddr; SockaddrLength: Integer; pNodeBuffer: PAnsiChar; NodeBufferSize: DWORD; pServiceBuffer: PAnsiChar;
118 ServiceBufferSize: DWORD; Flags: Integer): Integer; stdcall;
119 function GetNameInfoW(const pSockaddr: TSockAddr; SockaddrLength: Integer; pNodeBuffer: PWideChar; NodeBufferSize: DWORD; pServiceBuffer: PWideChar;
120 ServiceBufferSize: DWORD; Flags: Integer): Integer; stdcall;
123 TSmartPointerDestroyer<T> = reference to procedure(Value: T);
125 ISmartPointer<T> = reference to function: T;
127 TSmartPointer<T> = class(TInterfacedObject, ISmartPointer<T>)
130 FDestroyer: TSmartPointerDestroyer<T>;
132 constructor Create(AValue: T; ADestroyer: TSmartPointerDestroyer<T>);
133 destructor Destroy; override;
137 TBaseSocket = class abstract
139 TLogDelegate = reference to procedure( const str: string);
142 FSocket: Winapi.Winsock2.TSocket;
144 FRecvTimeout: Longword;
146 FLogDelegate: TLogDelegate;
147 class constructor Create;
148 class destructor Destroy;
149 class procedure DefaultLogDelegate(const Str: string);
151 IGetAddrInfoWrapper = interface
152 function Init: Integer;
153 function GetRes: PAddrInfoW;
154 property Res: PAddrInfoW read GetRes;
156 TGetAddrInfoWrapper = class(TInterfacedObject, IGetAddrInfoWrapper)
163 constructor Create(ANode, AService: string; AHints: PAddrInfoW);
164 destructor Destroy; override;
165 function Init: Integer;
166 function GetRes: PAddrInfoW;
167 property Res: PAddrInfoW read GetRes;
170 procedure CommonInit; virtual;
171 function CreateSocket(AAddress: string; APort: Integer): IGetAddrInfoWrapper;
172 procedure SetRecvTimeout(ARecvTimeout: Longword); virtual;
173 procedure SetSendTimeout(ASendTimeout: Longword); virtual;
174 procedure SetKeepAlive(AKeepAlive: Boolean); virtual;
175 procedure SetSocket(ASocket: Winapi.Winsock2.TSocket);
176 property LogDelegate: TLogDelegate read FLogDelegate;
179 // Constructs a new socket. Note that this does NOT actually connect the
182 constructor Create(ALogDelegate: TLogDelegate = nil); overload;
183 constructor Create(APort: Integer; ALogDelegate: TLogDelegate = nil); overload;
186 // Destroys the socket object, closing it if necessary.
188 destructor Destroy; override;
191 // Shuts down communications on the socket
193 procedure Close; virtual;
195 // The port that the socket is connected to
196 property Port: Integer read FPort write FPort;
198 // The receive timeout
199 property RecvTimeout: Longword read FRecvTimeout write SetRecvTimeout;
202 property SendTimeout: Longword read FSendTimeout write SetSendTimeout;
205 property KeepAlive: Boolean read FKeepAlive write SetKeepAlive;
207 // The underlying socket descriptor
208 property Socket: Winapi.Winsock2.TSocket read FSocket write SetSocket;
211 TSocket = class(TBaseSocket)
213 TCachedPeerAddr = record
215 0: (ipv4: TSockAddrIn);
216 1: (ipv6: TSockAddrIn6);
221 FPeerAddress: string;
223 FInterruptListener: ISmartPointer<Winapi.Winsock2.TSocket>;
224 FConnTimeout: Longword;
228 FMaxRecvRetries: Longword;
229 FCachedPeerAddr: TCachedPeerAddr;
230 procedure InitPeerInfo;
231 procedure OpenConnection(Res: TBaseSocket.IGetAddrInfoWrapper);
233 procedure SetGenericTimeout(S: Winapi.Winsock2.TSocket; Timeout: Longword; OptName: Integer);
234 function GetIsOpen: Boolean;
235 procedure SetNoDelay(ANoDelay: Boolean);
236 function GetSocketInfo: string;
237 function GetPeerHost: string;
238 function GetPeerAddress: string;
239 function GetPeerPort: Integer;
240 function GetOrigin: string;
242 procedure CommonInit; override;
243 procedure SetRecvTimeout(ARecvTimeout: Longword); override;
244 procedure SetSendTimeout(ASendTimeout: Longword); override;
245 procedure SetKeepAlive(AKeepAlive: Boolean); override;
248 // Constructs a new socket. Note that this does NOT actually connect the
251 constructor Create(ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
254 // Constructs a new socket. Note that this does NOT actually connect the
257 // @param host An IP address or hostname to connect to
258 // @param port The port to connect on
260 constructor Create(AHost: string; APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
263 // Constructor to create socket from socket descriptor.
265 constructor Create(ASocket: Winapi.Winsock2.TSocket; ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
268 // Constructor to create socket from socket descriptor that
269 // can be interrupted safely.
271 constructor Create(ASocket: Winapi.Winsock2.TSocket; AInterruptListener: ISmartPointer<Winapi.Winsock2.TSocket>;
272 ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
275 // Creates and opens the socket
277 // @throws ETransportationException If the socket could not connect
282 // Shuts down communications on the socket
284 procedure Close; override;
287 // Reads from the underlying socket.
288 // \returns the number of bytes read or 0 indicates EOF
289 // \throws TTransportException of types:
290 // Interrupted means the socket was interrupted
291 // out of a blocking call
292 // NotOpen means the socket has been closed
293 // TimedOut means the receive timeout expired
294 // Unknown means something unexpected happened
296 function Read(var Buf; Len: Integer): Integer;
299 // Writes to the underlying socket. Loops until done or fail.
301 procedure Write(const Buf; Len: Integer);
304 // Writes to the underlying socket. Does single send() and returns result.
306 function WritePartial(const Buf; Len: Integer): Integer;
309 // Returns a cached copy of the peer address.
311 function GetCachedAddress(out Len: Integer): PSockAddr;
314 // Set a cache of the peer address (used when trivially available: e.g.
315 // accept() or connect()). Only caches IPV4 and IPV6; unset for others.
317 procedure SetCachedAddress(const Addr: TSockAddr; Len: Integer);
320 // Controls whether the linger option is set on the socket.
322 // @param on Whether SO_LINGER is on
323 // @param linger If linger is active, the number of seconds to linger for
325 procedure SetLinger(LingerOn: Boolean; LingerVal: Integer);
328 // Calls select() on the socket to see if there is more data available.
330 function Peek: Boolean;
332 // Whether the socket is alive
333 property IsOpen: Boolean read GetIsOpen;
335 // The host that the socket is connected to
336 property Host: string read FHost write FHost;
338 // Whether to enable or disable Nagle's algorithm
339 property NoDelay: Boolean read FNoDelay write SetNoDelay;
342 property ConnTimeout: Longword read FConnTimeout write FConnTimeout;
344 // The max number of recv retries in the case of a WSAEWOULDBLOCK
345 property MaxRecvRetries: Longword read FMaxRecvRetries write FMaxRecvRetries;
347 // Socket information formatted as a string <Host: x Port: x>
348 property SocketInfo: string read GetSocketInfo;
350 // The DNS name of the host to which the socket is connected
351 property PeerHost: string read GetPeerHost;
353 // The address of the host to which the socket is connected
354 property PeerAddress: string read GetPeerAddress;
356 // The port of the host to which the socket is connected
357 property PeerPort: Integer read GetPeerPort;
359 // The origin the socket is connected to
360 property Origin: string read GetOrigin;
363 TServerSocketFunc = reference to procedure(sock: Winapi.Winsock2.TSocket);
365 TServerSocket = class(TBaseSocket)
372 FTcpRecvBuffer: Integer;
373 FAcceptTimeout: Longword;
375 FInterruptableChildren: Boolean;
376 FInterruptSockWriter, // is notified on Interrupt()
377 FInterruptSockReader, // is used in select with FSocket for interruptability
378 FChildInterruptSockWriter: Winapi.Winsock2.TSocket; // is notified on InterruptChildren()
379 FChildInterruptSockReader: ISmartPointer<Winapi.Winsock2.TSocket>; // if FnterruptableChildren this is shared with child TSockets
381 FAcceptCallback: TServerSocketFunc;
382 function CreateSocketObj(Client: Winapi.Winsock2.TSocket): TSocket;
383 procedure Notify(NotifySocket: Winapi.Winsock2.TSocket);
384 procedure SetInterruptableChildren(AValue: Boolean);
386 procedure CommonInit; override;
388 DEFAULT_BACKLOG = 1024;
393 // @param port Port number to bind to
395 constructor Create(APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
400 // @param port Port number to bind to
401 // @param sendTimeout Socket send timeout
402 // @param recvTimeout Socket receive timeout
404 constructor Create(APort: Integer; ASendTimeout, ARecvTimeout: Longword; ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
409 // @param address Address to bind to
410 // @param port Port number to bind to
412 constructor Create(AAddress: string; APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate = nil); overload;
415 function Accept: TSocket;
417 procedure InterruptChildren;
418 procedure Close; override;
420 property AcceptBacklog: Integer read FAcceptBacklog write FAcceptBacklog;
421 property AcceptTimeout: Longword read FAcceptTimeout write FAcceptTimeout;
422 property RetryLimit: Integer read FRetryLimit write FRetryLimit;
423 property RetryDelay: Integer read FRetryDelay write FRetryDelay;
424 property TcpSendBuffer: Integer read FTcpSendBuffer write FTcpSendBuffer;
425 property TcpRecvBuffer: Integer read FTcpRecvBuffer write FTcpRecvBuffer;
427 // When enabled (the default), new children TSockets will be constructed so
428 // they can be interrupted by TServerTransport.InterruptChildren().
429 // This is more expensive in terms of system calls (poll + recv) however
430 // ensures a connected client cannot interfere with TServer.Stop().
432 // When disabled, TSocket children do not incur an additional poll() call.
433 // Server-side reads are more efficient, however a client can interfere with
434 // the server's ability to shutdown properly by staying connected.
436 // Must be called before listen(); mode cannot be switched after that.
437 // \throws EPropertyError if listen() has been called
438 property InterruptableChildren: Boolean read FInterruptableChildren write SetInterruptableChildren;
440 // listenCallback gets called just before listen, and after all Thrift
441 // setsockopt calls have been made. If you have custom setsockopt
442 // things that need to happen on the listening socket, this is the place to do it.
443 property ListenCallback: TServerSocketFunc read FListenCallback write FListenCallback;
445 // acceptCallback gets called after each accept call, on the newly created socket.
446 // It is called after all Thrift setsockopt calls have been made. If you have
447 // custom setsockopt things that need to happen on the accepted
448 // socket, this is the place to do it.
449 property AcceptCallback: TServerSocketFunc read FAcceptCallback write FAcceptCallback;
452 {$ENDIF} // not for OLD_SOCKETS
454 {$IFNDEF OLD_SOCKETS} // not for OLD_SOCKETS
457 System.SysUtils, System.Math, System.DateUtils, Thrift.Transport;
459 constructor TBaseSocket.TGetAddrInfoWrapper.Create(ANode, AService: string; AHints: PAddrInfoW);
463 FService := AService;
468 destructor TBaseSocket.TGetAddrInfoWrapper.Destroy;
470 if Assigned(FRes) then
475 function TBaseSocket.TGetAddrInfoWrapper.Init: Integer;
478 Exit(GetAddrInfoW(@FNode[1], @FService[1], FHints^, FRes));
482 function TBaseSocket.TGetAddrInfoWrapper.GetRes: PAddrInfoW;
487 procedure DestroyerOfFineSockets(ssock: Winapi.Winsock2.TSocket);
492 function TScopeId.GetBitField(Loc: Integer): Integer;
494 Result := (Value shr (Loc shr 8)) and ((1 shl (Loc and $FF)) - 1);
497 procedure TScopeId.SetBitField(Loc: Integer; const aValue: Integer);
499 Value := (Value and ULONG((not ((1 shl (Loc and $FF)) - 1)))) or ULONG(aValue shl (Loc shr 8));
502 function getaddrinfo; external 'ws2_32.dll' name 'getaddrinfo';
503 function GetAddrInfoW; external 'ws2_32.dll' name 'GetAddrInfoW';
504 procedure freeaddrinfo; external 'ws2_32.dll' name 'freeaddrinfo';
505 procedure FreeAddrInfoW; external 'ws2_32.dll' name 'FreeAddrInfoW';
506 function getnameinfo; external 'ws2_32.dll' name 'getnameinfo';
507 function GetNameInfoW; external 'ws2_32.dll' name 'GetNameInfoW';
509 constructor TSmartPointer<T>.Create(AValue: T; ADestroyer: TSmartPointerDestroyer<T>);
513 FDestroyer := ADestroyer;
516 destructor TSmartPointer<T>.Destroy;
518 if Assigned(FDestroyer) then FDestroyer(FValue);
522 function TSmartPointer<T>.Invoke: T;
527 class constructor TBaseSocket.Create;
534 FillChar(Data, SizeOf(Data), 0);
535 Error := WSAStartup(Version, Data);
537 raise Exception.Create('Failed to initialize Winsock.');
540 class destructor TBaseSocket.Destroy;
545 class procedure TBaseSocket.DefaultLogDelegate(const Str: string);
549 OutStr := Format('Thrift: %s %s', [DateTimeToStr(Now, TFormatSettings.Create), Str]);
552 if IoResult <> 0 then OutputDebugString(PChar(OutStr));
554 OutputDebugString(PChar(OutStr));
558 procedure TBaseSocket.CommonInit;
560 FSocket := INVALID_SOCKET;
565 FLogDelegate := DefaultLogDelegate;
568 function TBaseSocket.CreateSocket(AAddress: string; APort: Integer): IGetAddrInfoWrapper;
572 ThePort: array[0..5] of Char;
575 FillChar(Hints, SizeOf(Hints), 0);
576 Hints.ai_family := PF_UNSPEC;
577 Hints.ai_socktype := SOCK_STREAM;
578 Hints.ai_flags := AI_PASSIVE or AI_ADDRCONFIG;
579 StrFmt(ThePort, '%d', [FPort]);
581 Result := TGetAddrInfoWrapper.Create(AAddress, ThePort, @Hints);
582 Error := Result.Init;
583 if Error <> 0 then begin
584 LogDelegate(Format('GetAddrInfoW %d: %s', [Error, SysErrorMessage(Error)]));
586 raise TTransportExceptionNotOpen.Create('Could not resolve host for server socket.');
589 // Pick the ipv6 address first since ipv4 addresses can be mapped
592 while Assigned(Res) do begin
593 if (Res^.ai_family = AF_INET6) or (not Assigned(Res^.ai_next)) then
598 FSocket := Winapi.Winsock2.socket(Res^.ai_family, Res^.ai_socktype, Res^.ai_protocol);
599 if FSocket = INVALID_SOCKET then begin
600 Error := WSAGetLastError;
601 LogDelegate(Format('TBaseSocket.CreateSocket() socket() %s', [SysErrorMessage(Error)]));
603 raise TTransportExceptionNotOpen.Create(Format('socket(): %s', [SysErrorMessage(Error)]));
607 procedure TBaseSocket.SetRecvTimeout(ARecvTimeout: Longword);
609 FRecvTimeout := ARecvTimeout;
612 procedure TBaseSocket.SetSendTimeout(ASendTimeout: Longword);
614 FSendTimeout := ASendTimeout;
617 procedure TBaseSocket.SetKeepAlive(AKeepAlive: Boolean);
619 FKeepAlive := AKeepAlive;
622 procedure TBaseSocket.SetSocket(ASocket: Winapi.Winsock2.TSocket);
624 if FSocket <> INVALID_SOCKET then
629 constructor TBaseSocket.Create(ALogDelegate: TLogDelegate);
633 if Assigned(ALogDelegate) then FLogDelegate := ALogDelegate;
636 constructor TBaseSocket.Create(APort: Integer; ALogDelegate: TLogDelegate);
641 if Assigned(ALogDelegate) then FLogDelegate := ALogDelegate;
644 destructor TBaseSocket.Destroy;
650 procedure TBaseSocket.Close;
652 if FSocket <> INVALID_SOCKET then begin
653 shutdown(FSocket, SD_BOTH);
654 closesocket(FSocket);
656 FSocket := INVALID_SOCKET;
659 procedure TSocket.InitPeerInfo;
661 FCachedPeerAddr.ipv4.sin_family := AF_UNSPEC;
667 procedure TSocket.CommonInit;
669 inherited CommonInit;
671 FInterruptListener := nil;
676 FMaxRecvRetries := 5;
680 procedure TSocket.OpenConnection(Res: TBaseSocket.IGetAddrInfoWrapper);
695 if SendTimeout > 0 then SetSendTimeout(SendTimeout);
696 if RecvTimeout > 0 then SetRecvTimeout(RecvTimeout);
697 if KeepAlive then SetKeepAlive(KeepAlive);
698 SetLinger(FLingerOn, FLingerVal);
699 SetNoDelay(FNoDelay);
701 // Set the socket to be non blocking for connect if a timeout exists
703 if FConnTimeout > 0 then begin
705 if ioctlsocket(Socket, Integer(FIONBIO), One) = SOCKET_ERROR then begin
706 ErrnoCopy := WSAGetLastError;
707 LogDelegate(Format('TSocket.OpenConnection() ioctlsocket() %s %s', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
708 raise TTransportExceptionNotOpen.Create(Format('ioctlsocket() failed: %s', [SysErrorMessage(ErrnoCopy)]));
712 if ioctlsocket(Socket, Integer(FIONBIO), Zero) = SOCKET_ERROR then begin
713 ErrnoCopy := WSAGetLastError;
714 LogDelegate(Format('TSocket.OpenConnection() ioctlsocket() %s %s', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
715 raise TTransportExceptionNotOpen.Create(Format('ioctlsocket() failed: %s', [SysErrorMessage(ErrnoCopy)]));
719 Ret := connect(Socket, Res.Res^.ai_addr^, Res.Res^.ai_addrlen);
720 if Ret = 0 then goto Done;
722 ErrnoCopy := WSAGetLastError;
723 if (ErrnoCopy <> WSAEINPROGRESS) and (ErrnoCopy <> WSAEWOULDBLOCK) then begin
724 LogDelegate(Format('TSocket.OpenConnection() connect() ', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
725 raise TTransportExceptionNotOpen.Create(Format('connect() failed: %s', [SysErrorMessage(ErrnoCopy)]));
729 _FD_SET(Socket, Fds);
730 if FConnTimeout > 0 then begin
731 TVal.tv_sec := FConnTimeout div 1000;
732 TVal.tv_usec := (FConnTimeout mod 1000) * 1000;
737 Ret := select(1, nil, @Fds, nil, PTVal);
739 if Ret > 0 then begin
740 // Ensure the socket is connected and that there are no errors set
742 Ret2 := getsockopt(Socket, SOL_SOCKET, SO_ERROR, @Val, Lon);
743 if Ret2 = SOCKET_ERROR then begin
744 ErrnoCopy := WSAGetLastError;
745 LogDelegate(Format('TSocket.OpenConnection() getsockopt() ', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
746 raise TTransportExceptionNotOpen.Create(Format('getsockopt(): %s', [SysErrorMessage(ErrnoCopy)]));
748 // no errors on socket, go to town
749 if Val = 0 then goto Done;
750 LogDelegate(Format('TSocket.OpenConnection() error on socket (after select()) ', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
751 raise TTransportExceptionNotOpen.Create(Format('socket OpenConnection() error: %s', [SysErrorMessage(Val)]));
753 else if Ret = 0 then begin
755 LogDelegate(Format('TSocket.OpenConnection() timed out ', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
756 raise TTransportExceptionNotOpen.Create('OpenConnection() timed out');
760 ErrnoCopy := WSAGetLastError;
761 LogDelegate(Format('TSocket.OpenConnection() select() ', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
762 raise TTransportExceptionNotOpen.Create(Format('select() failed: %s', [SysErrorMessage(ErrnoCopy)]));
766 // Set socket back to normal mode (blocking)
767 ioctlsocket(Socket, Integer(FIONBIO), Zero);
768 SetCachedAddress(Res.Res^.ai_addr^, Res.Res^.ai_addrlen);
771 procedure TSocket.LocalOpen;
773 Res: TBaseSocket.IGetAddrInfoWrapper;
777 // Validate port number
778 if (Port < 0) or (Port > $FFFF) then
779 raise TTransportExceptionBadArgs.Create('Specified port is invalid');
781 Res := CreateSocket(Host, Port);
786 procedure TSocket.SetGenericTimeout(S: Winapi.Winsock2.TSocket; Timeout: Longword; OptName: Integer);
790 if S = INVALID_SOCKET then
795 if setsockopt(S, SOL_SOCKET, OptName, @Time, SizeOf(Time)) = SOCKET_ERROR then
796 LogDelegate(Format('SetGenericTimeout() setsockopt() %s', [SysErrorMessage(WSAGetLastError)]));
799 function TSocket.GetIsOpen: Boolean;
801 Result := Socket <> INVALID_SOCKET;
804 procedure TSocket.SetNoDelay(ANoDelay: Boolean);
808 FNoDelay := ANoDelay;
809 if Socket = INVALID_SOCKET then
812 V := IfThen(FNoDelay, 1, 0);
813 if setsockopt(Socket, IPPROTO_TCP, TCP_NODELAY, @V, SizeOf(V)) = SOCKET_ERROR then
814 LogDelegate(Format('TSocket.SetNoDelay() setsockopt() %s %s', [SocketInfo, SysErrorMessage(WSAGetLastError)]));
817 function TSocket.GetSocketInfo: string;
819 if (FHost = '') or (Port = 0) then
820 Result := '<Host: ' + GetPeerAddress + ' Port: ' + GetPeerPort.ToString + '>'
822 Result := '<Host: ' + FHost + ' Port: ' + Port.ToString + '>';
825 function TSocket.GetPeerHost: string;
827 Addr: TSockAddrStorage;
830 ClientHost: array[0..NI_MAXHOST-1] of Char;
831 ClientService: array[0..NI_MAXSERV-1] of Char;
833 if FPeerHost = '' then begin
834 if Socket = INVALID_SOCKET then
837 AddrPtr := GetCachedAddress(AddrLen);
838 if AddrPtr = nil then begin
839 AddrLen := SizeOf(Addr);
840 if getpeername(Socket, PSockAddr(@Addr)^, AddrLen) <> 0 then
842 AddrPtr := PSockAddr(@Addr);
843 SetCachedAddress(AddrPtr^, AddrLen);
846 GetNameInfoW(AddrPtr^, AddrLen, ClientHost, NI_MAXHOST, ClientService, NI_MAXSERV, 0);
847 FPeerHost := ClientHost;
852 function TSocket.GetPeerAddress: string;
854 Addr: TSockAddrStorage;
857 ClientHost: array[0..NI_MAXHOST-1] of Char;
858 ClientService: array[0..NI_MAXSERV-1] of Char;
860 if FPeerAddress = '' then begin
861 if Socket = INVALID_SOCKET then
864 AddrPtr := GetCachedAddress(AddrLen);
865 if AddrPtr = nil then begin
866 AddrLen := SizeOf(Addr);
867 if getpeername(Socket, PSockAddr(@Addr)^, AddrLen) <> 0 then
869 AddrPtr := PSockAddr(@Addr);
870 SetCachedAddress(AddrPtr^, AddrLen);
873 GetNameInfoW(AddrPtr^, AddrLen, ClientHost, NI_MAXHOST, ClientService, NI_MAXSERV, NI_NUMERICHOST or NI_NUMERICSERV);
874 FPeerAddress := ClientHost;
875 TryStrToInt(ClientService, FPeerPort);
877 Result := FPeerAddress
880 function TSocket.GetPeerPort: Integer;
886 function TSocket.GetOrigin: string;
888 Result := GetPeerHost + ':' + GetPeerPort.ToString;
891 procedure TSocket.SetRecvTimeout(ARecvTimeout: Longword);
893 inherited SetRecvTimeout(ARecvTimeout);
894 SetGenericTimeout(Socket, ARecvTimeout, SO_RCVTIMEO);
897 procedure TSocket.SetSendTimeout(ASendTimeout: Longword);
899 inherited SetSendTimeout(ASendTimeout);
900 SetGenericTimeout(Socket, ASendTimeout, SO_SNDTIMEO);
903 procedure TSocket.SetKeepAlive(AKeepAlive: Boolean);
907 inherited SetKeepAlive(AKeepAlive);
909 Value := IfThen(KeepAlive, 1, 0);
910 if setsockopt(Socket, SOL_SOCKET, SO_KEEPALIVE, @Value, SizeOf(Value)) = SOCKET_ERROR then
911 LogDelegate(Format('TSocket.SetKeepAlive() setsockopt() %s %s', [SocketInfo, SysErrorMessage(WSAGetLastError)]));
914 constructor TSocket.Create(ALogDelegate: TBaseSocket.TLogDelegate = nil);
916 // Not needed, but just a placeholder
917 inherited Create(ALogDelegate);
920 constructor TSocket.Create(AHost: string; APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate);
922 inherited Create(APort, ALogDelegate);
926 constructor TSocket.Create(ASocket: Winapi.Winsock2.TSocket; ALogDelegate: TBaseSocket.TLogDelegate);
928 inherited Create(ALogDelegate);
932 constructor TSocket.Create(ASocket: Winapi.Winsock2.TSocket; AInterruptListener: ISmartPointer<Winapi.Winsock2.TSocket>;
933 ALogDelegate: TBaseSocket.TLogDelegate);
935 inherited Create(ALogDelegate);
937 FInterruptListener := AInterruptListener;
940 procedure TSocket.Open;
946 procedure TSocket.Close;
952 function TSocket.Read(var Buf; Len: Integer): Integer;
967 if Socket = INVALID_SOCKET then
968 raise TTransportExceptionNotOpen.Create('Called read on non-open socket');
972 // THRIFT_EAGAIN can be signalled both when a timeout has occurred and when
973 // the system is out of resources (an awesome undocumented feature).
974 // The following is an approximation of the time interval under which
975 // THRIFT_EAGAIN is taken to indicate an out of resources error.
976 EAgainThreshold := 0;
977 if RecvTimeout <> 0 then
978 // if a readTimeout is specified along with a max number of recv retries, then
979 // the threshold will ensure that the read timeout is not exceeded even in the
980 // case of resource errors
981 EAgainThreshold := RecvTimeout div IfThen(FMaxRecvRetries > 0, FMaxRecvRetries, 2);
984 // Read from the socket
985 if RecvTimeout > 0 then
988 // if there is no read timeout we don't need the TOD to determine whether
989 // an THRIFT_EAGAIN is due to a timeout or an out-of-resource condition.
992 if Assigned(FInterruptListener) then begin
994 _FD_SET(Socket, Fds);
995 _FD_SET(FInterruptListener, Fds);
996 if RecvTimeout > 0 then begin
997 TVal.tv_sec := RecvTimeout div 1000;
998 TVal.tv_usec := (RecvTimeout mod 1000) * 1000;
1004 Ret := select(2, @Fds, nil, nil, PTVal);
1005 ErrnoCopy := WSAGetLastError;
1006 if Ret < 0 then begin
1008 if (ErrnoCopy = WSAEINTR) and (Retries < FMaxRecvRetries) then begin
1012 LogDelegate(Format('TSocket.Read() select() %s', [SysErrorMessage(ErrnoCopy)]));
1013 raise TTransportExceptionUnknown.Create(Format('Unknown: %s', [SysErrorMessage(ErrnoCopy)]));
1015 else if Ret > 0 then begin
1016 // Check the interruptListener
1017 if FD_ISSET(FInterruptListener, Fds) then
1018 raise TTransportExceptionInterrupted.Create('Interrupted');
1021 raise TTransportExceptionTimedOut.Create('WSAEWOULDBLOCK (timed out)');
1023 // falling through means there is something to recv and it cannot block
1026 Got := recv(Socket, Buf, Len, 0);
1027 ErrnoCopy := WSAGetLastError;
1028 // Check for error on read
1029 if Got < 0 then begin
1030 if ErrnoCopy = WSAEWOULDBLOCK then begin
1031 // if no timeout we can assume that resource exhaustion has occurred.
1032 if RecvTimeout = 0 then
1033 raise TTransportExceptionTimedOut.Create('WSAEWOULDBLOCK (unavailable resources)');
1034 // check if this is the lack of resources or timeout case
1035 ReadElapsed := MilliSecondsBetween(Now, Start);
1036 if (EAgainThreshold = 0) or (ReadElapsed < EAgainThreshold) then begin
1037 if Retries < FMaxRecvRetries then begin
1043 raise TTransportExceptionTimedOut.Create('WSAEWOULDBLOCK (unavailable resources)');
1046 // infer that timeout has been hit
1047 raise TTransportExceptionTimedOut.Create('WSAEWOULDBLOCK (timed out)');
1050 // If interrupted, try again
1051 if (ErrnoCopy = WSAEINTR) and (Retries < FMaxRecvRetries) then begin
1056 if ErrnoCopy = WSAECONNRESET then
1059 // This ish isn't open
1060 if ErrnoCopy = WSAENOTCONN then
1061 raise TTransportExceptionNotOpen.Create('WSAENOTCONN');
1064 if ErrnoCopy = WSAETIMEDOUT then
1065 raise TTransportExceptionNotOpen.Create('WSAETIMEDOUT');
1067 // Now it's not a try again case, but a real probblez
1068 LogDelegate(Format('TSocket.Read() recv() %s %s', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
1070 // Some other error, whatevz
1071 raise TTransportExceptionUnknown.Create(Format('Unknown: %s', [SysErrorMessage(ErrnoCopy)]));
1077 procedure TSocket.Write(const Buf; Len: Integer);
1082 while Sent < Len do begin
1083 B := WritePartial((PByte(@Buf) + Sent)^, Len - Sent);
1085 // This should only happen if the timeout set with SO_SNDTIMEO expired.
1086 // Raise an exception.
1087 raise TTransportExceptionTimedOut.Create('send timeout expired');
1092 function TSocket.WritePartial(const Buf; Len: Integer): Integer;
1097 if Socket = INVALID_SOCKET then
1098 raise TTransportExceptionNotOpen.Create('Called write on non-open socket');
1100 B := send(Socket, Buf, Len, 0);
1103 // Fail on a send error
1104 ErrnoCopy := WSAGetLastError;
1105 if ErrnoCopy = WSAEWOULDBLOCK then
1108 LogDelegate(Format('TSocket.WritePartial() send() %s %s', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
1110 if (ErrnoCopy = WSAECONNRESET) or (ErrnoCopy = WSAENOTCONN) then begin
1112 raise TTransportExceptionNotOpen.Create(Format('write() send(): %s', [SysErrorMessage(ErrnoCopy)]));
1115 raise TTransportExceptionUnknown.Create(Format('write() send(): %s', [SysErrorMessage(ErrnoCopy)]));
1118 // Fail on blocked send
1120 raise TTransportExceptionNotOpen.Create('Socket send returned 0.');
1125 function TSocket.GetCachedAddress(out Len: Integer): PSockAddr;
1127 case FCachedPeerAddr.ipv4.sin_family of
1129 Len := SizeOf(TSockAddrIn);
1130 Result := PSockAddr(@FCachedPeerAddr.ipv4);
1133 Len := SizeOf(TSockAddrIn6);
1134 Result := PSockAddr(@FCachedPeerAddr.ipv6);
1142 procedure TSocket.SetCachedAddress(const Addr: TSockAddr; Len: Integer);
1144 case Addr.sa_family of
1145 AF_INET: if Len = SizeOf(TSockAddrIn) then FCachedPeerAddr.ipv4 := PSockAddrIn(@Addr)^;
1146 AF_INET6: if Len = SizeOf(TSockAddrIn6) then FCachedPeerAddr.ipv6 := PSockAddrIn6(@Addr)^;
1153 procedure TSocket.SetLinger(LingerOn: Boolean; LingerVal: Integer);
1157 FLingerOn := LingerOn;
1158 FLingerVal := LingerVal;
1159 if Socket = INVALID_SOCKET then
1162 L.l_onoff := IfThen(FLingerOn, 1, 0);
1163 L.l_linger := LingerVal;
1165 if setsockopt(Socket, SOL_SOCKET, SO_LINGER, @L, SizeOf(L)) = SOCKET_ERROR then
1166 LogDelegate(Format('TSocket.SetLinger() setsockopt() %s %s', [SocketInfo, SysErrorMessage(WSAGetLastError)]));
1169 function TSocket.Peek: Boolean;
1179 if not IsOpen then Exit(False);
1181 if Assigned(FInterruptListener) then begin
1185 _FD_SET(Socket, Fds);
1186 _FD_SET(FInterruptListener, Fds);
1187 if RecvTimeout > 0 then begin
1188 TVal.tv_sec := RecvTimeout div 1000;
1189 TVal.tv_usec := (RecvTimeout mod 1000) * 1000;
1195 Ret := select(2, @Fds, nil, nil, PTVal);
1196 ErrnoCopy := WSAGetLastError;
1197 if Ret < 0 then begin
1199 if (ErrnoCopy = WSAEINTR) and (Retries < FMaxRecvRetries) then begin
1203 LogDelegate(Format('TSocket.Peek() select() %s', [SysErrorMessage(ErrnoCopy)]));
1204 raise TTransportExceptionUnknown.Create(Format('Unknown: %s', [SysErrorMessage(ErrnoCopy)]));
1206 else if Ret > 0 then begin
1207 // Check the interruptListener
1208 if FD_ISSET(FInterruptListener, Fds) then
1210 // There must be data or a disconnection, fall through to the PEEK
1219 // Check to see if data is available or if the remote side closed
1220 Ret := recv(Socket, Buf, 1, MSG_PEEK);
1221 if Ret = SOCKET_ERROR then begin
1222 ErrnoCopy := WSAGetLastError;
1223 if ErrnoCopy = WSAECONNRESET then begin
1227 LogDelegate(Format('TSocket.Peek() recv() %s %s', [SocketInfo, SysErrorMessage(ErrnoCopy)]));
1228 raise TTransportExceptionUnknown.Create(Format('recv(): %s', [SysErrorMessage(ErrnoCopy)]));
1233 function TServerSocket.CreateSocketObj(Client: Winapi.Winsock2.TSocket): TSocket;
1235 if FInterruptableChildren then
1236 Result := TSocket.Create(Client, FChildInterruptSockReader)
1238 Result := TSocket.Create(Client);
1241 procedure TServerSocket.Notify(NotifySocket: Winapi.Winsock2.TSocket);
1245 if NotifySocket <> INVALID_SOCKET then begin
1247 if send(NotifySocket, Byt, SizeOf(Byt), 0) = SOCKET_ERROR then
1248 LogDelegate(Format('TServerSocket.Notify() send() %s', [SysErrorMessage(WSAGetLastError)]));
1252 procedure TServerSocket.SetInterruptableChildren(AValue: Boolean);
1255 raise Exception.Create('InterruptableChildren cannot be set after listen()');
1256 FInterruptableChildren := AValue;
1259 procedure TServerSocket.CommonInit;
1261 inherited CommonInit;
1262 FInterruptableChildren := True;
1263 FAcceptBacklog := DEFAULT_BACKLOG;
1264 FAcceptTimeout := 0;
1267 FTcpSendBuffer := 0;
1268 FTcpRecvBuffer := 0;
1269 FListening := False;
1270 FInterruptSockWriter := INVALID_SOCKET;
1271 FInterruptSockReader := INVALID_SOCKET;
1272 FChildInterruptSockWriter := INVALID_SOCKET;
1275 constructor TServerSocket.Create(APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate = nil);
1277 // Unnecessary, but here for documentation purposes
1278 inherited Create(APort, ALogDelegate);
1281 constructor TServerSocket.Create(APort: Integer; ASendTimeout, ARecvTimeout: Longword; ALogDelegate: TBaseSocket.TLogDelegate);
1283 inherited Create(APort, ALogDelegate);
1284 SendTimeout := ASendTimeout;
1285 RecvTimeout := ARecvTimeout;
1288 constructor TServerSocket.Create(AAddress: string; APort: Integer; ALogDelegate: TBaseSocket.TLogDelegate);
1290 inherited Create(APort, ALogDelegate);
1291 FAddress := AAddress;
1294 procedure TServerSocket.Listen;
1296 function CreateSocketPair(var Reader, Writer: Winapi.Winsock2.TSocket): Integer;
1302 0: (inaddr: TSockAddrIn);
1303 1: (addr: TSockAddr);
1307 listener: Winapi.Winsock2.TSocket;
1313 addrlen := SizeOf(a.inaddr);
1317 listener := Winapi.Winsock2.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1318 if listener = INVALID_SOCKET then
1321 FillChar(a, SizeOf(a), 0);
1322 a.inaddr.sin_family := AF_INET;
1323 a.inaddr.sin_addr.s_addr := htonl(INADDR_LOOPBACK);
1324 a.inaddr.sin_port := 0;
1325 Reader := INVALID_SOCKET;
1326 Writer := INVALID_SOCKET;
1328 // ignore errors coming out of this setsockopt. This is because
1329 // SO_EXCLUSIVEADDRUSE requires admin privileges on WinXP, but we don't
1330 // want to force socket pairs to be an admin.
1331 setsockopt(listener, SOL_SOCKET, Integer(SO_EXCLUSIVEADDRUSE), @reuse, SizeOf(reuse));
1332 if bind(listener, a.addr, SizeOf(a.inaddr)) = SOCKET_ERROR then
1335 if getsockname(listener, a.addr, addrlen) = SOCKET_ERROR then
1338 if Winapi.Winsock2.listen(listener, 1) = SOCKET_ERROR then
1341 Reader := WSASocket(AF_INET, SOCK_STREAM, 0, nil, 0, flags);
1342 if Reader = INVALID_SOCKET then
1345 if connect(Reader, a.addr, SizeOf(a.inaddr)) = SOCKET_ERROR then
1348 Writer := Winapi.Winsock2.accept(listener, nil, nil);
1349 if Writer = INVALID_SOCKET then
1352 closesocket(listener);
1356 e := WSAGetLastError;
1357 closesocket(listener);
1358 closesocket(Reader);
1359 closesocket(Writer);
1361 Result := SOCKET_ERROR;
1366 TempIntWriter: Winapi.Winsock2.TSocket;
1371 AddrInfo: IGetAddrInfoWrapper;
1372 SA: TSockAddrStorage;
1375 // Create the socket pair used to interrupt
1376 if CreateSocketPair(TempIntReader, TempIntWriter) = SOCKET_ERROR then begin
1377 LogDelegate(Format('TServerSocket.Listen() CreateSocketPair() Interrupt %s', [SysErrorMessage(WSAGetLastError)]));
1378 FInterruptSockReader := INVALID_SOCKET;
1379 FInterruptSockWriter := INVALID_SOCKET;
1382 FInterruptSockReader := TempIntReader;
1383 FInterruptSockWriter := TempIntWriter;
1386 // Create the socket pair used to interrupt all clients
1387 if CreateSocketPair(TempIntReader, TempIntWriter) = SOCKET_ERROR then begin
1388 LogDelegate(Format('TServerSocket.Listen() CreateSocketPair() ChildInterrupt %s', [SysErrorMessage(WSAGetLastError)]));
1389 FChildInterruptSockReader := TSmartPointer<Winapi.Winsock2.TSocket>.Create(INVALID_SOCKET, nil);
1390 FChildInterruptSockWriter := INVALID_SOCKET;
1393 FChildInterruptSockReader := TSmartPointer<Winapi.Winsock2.TSocket>.Create(TempIntReader, DestroyerOfFineSockets);
1394 FChildInterruptSockWriter := TempIntWriter;
1397 if (Port < 0) or (Port > $FFFF) then
1398 raise TTransportExceptionBadArgs.Create('Specified port is invalid');
1400 AddrInfo := CreateSocket(FAddress, Port);
1402 // Set SO_EXCLUSIVEADDRUSE to prevent 2MSL delay on accept
1404 setsockopt(Socket, SOL_SOCKET, Integer(SO_EXCLUSIVEADDRUSE), @one, SizeOf(One));
1405 // ignore errors coming out of this setsockopt on Windows. This is because
1406 // SO_EXCLUSIVEADDRUSE requires admin privileges on WinXP, but we don't
1407 // want to force servers to be an admin.
1409 // Set TCP buffer sizes
1410 if FTcpSendBuffer > 0 then begin
1411 if setsockopt(Socket, SOL_SOCKET, SO_SNDBUF, @FTcpSendBuffer, SizeOf(FTcpSendBuffer)) = SOCKET_ERROR then begin
1412 ErrnoCopy := WSAGetLastError;
1413 LogDelegate(Format('TServerSocket.Listen() setsockopt() SO_SNDBUF %s', [SysErrorMessage(ErrnoCopy)]));
1414 raise TTransportExceptionNotOpen.Create(Format('Could not set SO_SNDBUF: %s', [SysErrorMessage(ErrnoCopy)]));
1418 if FTcpRecvBuffer > 0 then begin
1419 if setsockopt(Socket, SOL_SOCKET, SO_RCVBUF, @FTcpRecvBuffer, SizeOf(FTcpRecvBuffer)) = SOCKET_ERROR then begin
1420 ErrnoCopy := WSAGetLastError;
1421 LogDelegate(Format('TServerSocket.Listen() setsockopt() SO_RCVBUF %s', [SysErrorMessage(ErrnoCopy)]));
1422 raise TTransportExceptionNotOpen.Create(Format('Could not set SO_RCVBUF: %s', [SysErrorMessage(ErrnoCopy)]));
1426 // Turn linger off, don't want to block on calls to close
1429 if setsockopt(Socket, SOL_SOCKET, SO_LINGER, @Ling, SizeOf(Ling)) = SOCKET_ERROR then begin
1430 ErrnoCopy := WSAGetLastError;
1431 LogDelegate(Format('TServerSocket.Listen() setsockopt() SO_LINGER %s', [SysErrorMessage(ErrnoCopy)]));
1432 raise TTransportExceptionNotOpen.Create(Format('Could not set SO_LINGER: %s', [SysErrorMessage(ErrnoCopy)]));
1435 // TCP Nodelay, speed over bandwidth
1436 if setsockopt(Socket, IPPROTO_TCP, TCP_NODELAY, @One, SizeOf(One)) = SOCKET_ERROR then begin
1437 ErrnoCopy := WSAGetLastError;
1438 LogDelegate(Format('TServerSocket.Listen() setsockopt() TCP_NODELAY %s', [SysErrorMessage(ErrnoCopy)]));
1439 raise TTransportExceptionNotOpen.Create(Format('Could not set TCP_NODELAY: %s', [SysErrorMessage(ErrnoCopy)]));
1442 // Set NONBLOCK on the accept socket
1443 if ioctlsocket(Socket, Integer(FIONBIO), One) = SOCKET_ERROR then begin
1444 ErrnoCopy := WSAGetLastError;
1445 LogDelegate(Format('TServerSocket.Listen() ioctlsocket() FIONBIO %s', [SysErrorMessage(ErrnoCopy)]));
1446 raise TTransportExceptionNotOpen.Create(Format('ioctlsocket() FIONBIO: %s', [SysErrorMessage(ErrnoCopy)]));
1449 // prepare the port information
1450 // we may want to try to bind more than once, since THRIFT_NO_SOCKET_CACHING doesn't
1451 // always seem to work. The client can configure the retry variables.
1454 if bind(Socket, AddrInfo.Res^.ai_addr^, AddrInfo.Res^.ai_addrlen) = 0 then
1457 if Retries > FRetryLimit then
1459 Sleep(FRetryDelay * 1000);
1462 // retrieve bind info
1463 if (Port = 0) and (Retries < FRetryLimit) then begin
1465 FillChar(SA, Len, 0);
1466 if getsockname(Socket, PSockAddr(@SA)^, Len) = SOCKET_ERROR then
1467 LogDelegate(Format('TServerSocket.Listen() getsockname() %s', [SysErrorMessage(WSAGetLastError)]))
1469 if SA.ss_family = AF_INET6 then
1470 Port := ntohs(PSockAddrIn6(@SA)^.sin6_port)
1472 Port := ntohs(PSockAddrIn(@SA)^.sin_port);
1476 // throw an error if we failed to bind properly
1477 if (Retries > FRetryLimit) then begin
1478 LogDelegate(Format('TServerSocket.Listen() BIND %d', [Port]));
1480 raise TTransportExceptionNotOpen.Create(Format('Could not bind: %s', [SysErrorMessage(WSAGetLastError)]));
1483 if Assigned(FListenCallback) then
1484 FListenCallback(Socket);
1487 if Winapi.Winsock2.listen(Socket, FAcceptBacklog) = SOCKET_ERROR then begin
1488 ErrnoCopy := WSAGetLastError;
1489 LogDelegate(Format('TServerSocket.Listen() listen() %s', [SysErrorMessage(ErrnoCopy)]));
1490 raise TTransportExceptionNotOpen.Create(Format('Could not listen: %s', [SysErrorMessage(ErrnoCopy)]));
1493 // The socket is now listening!
1496 function TServerSocket.Accept: TSocket;
1500 NumEInters: Integer;
1505 ClientAddress: TSockAddrStorage;
1507 ClientSocket: Winapi.Winsock2.TSocket;
1517 _FD_SET(Socket, Fds);
1518 _FD_SET(FInterruptSockReader, Fds);
1519 if FAcceptTimeout > 0 then begin
1520 TVal.tv_sec := FAcceptTimeout div 1000;
1521 TVal.tv_usec := (FAcceptTimeout mod 1000) * 1000;
1527 // TODO: if WSAEINTR is received, we'll restart the timeout.
1528 // To be accurate, we need to fix this in the future.
1529 Ret := select(2, @Fds, nil, nil, PTVal);
1531 if Ret < 0 then begin
1533 if (WSAGetLastError = WSAEINTR) and (NumEInters < MaxEInters) then begin
1534 // THRIFT_EINTR needs to be handled manually and we can tolerate
1539 ErrnoCopy := WSAGetLastError;
1540 LogDelegate(Format('TServerSocket.Accept() select() %s', [SysErrorMessage(ErrnoCopy)]));
1541 raise TTransportExceptionUnknown.Create(Format('Unknown: %s', [SysErrorMessage(ErrnoCopy)]));
1543 else if Ret > 0 then begin
1544 // Check for an interrupt signal
1545 if (FInterruptSockReader <> INVALID_SOCKET) and FD_ISSET(FInterruptSockReader, Fds) then begin
1546 if recv(FInterruptSockReader, Buf, SizeOf(Buf), 0) = SOCKET_ERROR then
1547 LogDelegate(Format('TServerSocket.Accept() recv() interrupt %s', [SysErrorMessage(WSAGetLastError)]));
1548 raise TTransportExceptionInterrupted.Create('interrupted');
1551 // Check for the actual server socket being ready
1552 if FD_ISSET(Socket, Fds) then
1556 LogDelegate('TServerSocket.Accept() select() 0');
1557 raise TTransportExceptionUnknown.Create('unknown error');
1561 Size := SizeOf(ClientAddress);
1562 ClientSocket := Winapi.Winsock2.accept(Socket, @ClientAddress, @Size);
1563 if ClientSocket = INVALID_SOCKET then begin
1564 ErrnoCopy := WSAGetLastError;
1565 LogDelegate(Format('TServerSocket.Accept() accept() %s', [SysErrorMessage(ErrnoCopy)]));
1566 raise TTransportExceptionUnknown.Create(Format('accept(): %s', [SysErrorMessage(ErrnoCopy)]));
1569 // Make sure client socket is blocking
1571 if ioctlsocket(ClientSocket, Integer(FIONBIO), Zero) = SOCKET_ERROR then begin
1572 ErrnoCopy := WSAGetLastError;
1573 closesocket(ClientSocket);
1574 LogDelegate(Format('TServerSocket.Accept() ioctlsocket() FIONBIO %s', [SysErrorMessage(ErrnoCopy)]));
1575 raise TTransportExceptionUnknown.Create(Format('ioctlsocket(): %s', [SysErrorMessage(ErrnoCopy)]));
1578 Client := CreateSocketObj(ClientSocket);
1579 if SendTimeout > 0 then
1580 Client.SendTimeout := SendTimeout;
1581 if RecvTimeout > 0 then
1582 Client.RecvTimeout := RecvTimeout;
1584 Client.KeepAlive := KeepAlive;
1585 Client.SetCachedAddress(PSockAddr(@ClientAddress)^, Size);
1587 if Assigned(FAcceptCallback) then
1588 FAcceptCallback(ClientSocket);
1593 procedure TServerSocket.Interrupt;
1595 Notify(FInterruptSockWriter);
1598 procedure TServerSocket.InterruptChildren;
1600 Notify(FChildInterruptSockWriter);
1603 procedure TServerSocket.Close;
1606 if FInterruptSockWriter <> INVALID_SOCKET then
1607 closesocket(FInterruptSockWriter);
1608 if FInterruptSockReader <> INVALID_SOCKET then
1609 closesocket(FInterruptSockReader);
1610 if FChildInterruptSockWriter <> INVALID_SOCKET then
1611 closesocket(FChildInterruptSockWriter);
1612 FChildInterruptSockReader := TSmartPointer<Winapi.Winsock2.TSocket>.Create(INVALID_SOCKET, nil);
1613 FListening := False;
1616 {$ENDIF} // not for OLD_SOCKETS