From f79c3cdb8a57184b38072ae5187352ba36bc76a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Mussche?= Date: Thu, 3 Jul 2014 11:39:18 +0200 Subject: [PATCH] write timeout + error handling (try except for callbacks) --- IdHTTPWebsocketClient.pas | 18 ++++++++++++++++ IdServerSocketIOHandling.pas | 40 +++++++++++++++++++++--------------- IdSocketIOHandling.pas | 7 +++++++ IdWebsocketServer.pas | 17 ++++++++++++++- 4 files changed, 65 insertions(+), 17 deletions(-) diff --git a/IdHTTPWebsocketClient.pas b/IdHTTPWebsocketClient.pas index 26f4098..8d86901 100644 --- a/IdHTTPWebsocketClient.pas +++ b/IdHTTPWebsocketClient.pas @@ -38,10 +38,12 @@ type FOnData: TWebsocketMsgBin; FOnTextData: TWebsocketMsgText; FNoAsyncRead: Boolean; + FWriteTimeout: Integer; function GetIOHandlerWS: TIdIOHandlerWebsocket; procedure SetIOHandlerWS(const Value: TIdIOHandlerWebsocket); procedure SetOnData(const Value: TWebsocketMsgBin); procedure SetOnTextData(const Value: TWebsocketMsgText); + procedure SetWriteTimeout(const Value: Integer); protected FSocketIOCompatible: Boolean; FSocketIOHandshakeResponse: string; @@ -94,6 +96,8 @@ type property Host; property Port; property WSResourceName: string read FWSResourceName write FWSResourceName; + + property WriteTimeout: Integer read FWriteTimeout write SetWriteTimeout default 2000; end; // on error @@ -231,6 +235,8 @@ begin // FHeartBeat := TTimer.Create(nil); // FHeartBeat.Enabled := False; // FHeartBeat.OnTimer := HeartBeatTimer; + + FWriteTimeout := 2 * 1000; end; procedure TIdHTTPWebsocketClient.AsyncDispatchEvent(const aEvent: TStream); @@ -752,6 +758,11 @@ begin if not Self.NoAsyncRead then TIdWebsocketMultiReadThread.Instance.AddClient(Self); end; + + //default 2s write timeout + //http://msdn.microsoft.com/en-us/library/windows/desktop/ms740532(v=vs.85).aspx + if Connected then + Self.IOHandler.Binding.SetSockOpt(SOL_SOCKET, SO_SNDTIMEO, Self.WriteTimeout); end; procedure TIdHTTPWebsocketClient.Lock; @@ -899,6 +910,13 @@ begin // TIdWebsocketMultiReadThread.Instance.AddClient(Self); end; +procedure TIdHTTPWebsocketClient.SetWriteTimeout(const Value: Integer); +begin + FWriteTimeout := Value; + if Connected then + Self.IOHandler.Binding.SetSockOpt(SOL_SOCKET, SO_SNDTIMEO, Self.WriteTimeout); +end; + { TIdHTTPSocketIOClient } (* diff --git a/IdServerSocketIOHandling.pas b/IdServerSocketIOHandling.pas index fb5a944..f913199 100644 --- a/IdServerSocketIOHandling.pas +++ b/IdServerSocketIOHandling.pas @@ -88,28 +88,36 @@ begin begin if context.IsDisconnected then Continue; - if not Assigned(aCallback) then - WriteSocketIOEvent(context, ''{no room}, aEventName, jsonarray, nil, nil) - else - WriteSocketIOEventRef(context, ''{no room}, aEventName, jsonarray, - procedure(const aData: string) - begin - aCallback(context, SO(aData), nil); - end, aOnError); + try + if not Assigned(aCallback) then + WriteSocketIOEvent(context, ''{no room}, aEventName, jsonarray, nil, nil) + else + WriteSocketIOEventRef(context, ''{no room}, aEventName, jsonarray, + procedure(const aData: string) + begin + aCallback(context, SO(aData), nil); + end, aOnError); + except + //try to send to others + end; Inc(Result); end; for context in FConnectionsGUID.Values do begin if context.IsDisconnected then Continue; - if not Assigned(aCallback) then - WriteSocketIOEvent(context, ''{no room}, aEventName, jsonarray, nil, nil) - else - WriteSocketIOEventRef(context, ''{no room}, aEventName, jsonarray, - procedure(const aData: string) - begin - aCallback(context, SO(aData), nil); - end, aOnError); + try + if not Assigned(aCallback) then + WriteSocketIOEvent(context, ''{no room}, aEventName, jsonarray, nil, nil) + else + WriteSocketIOEventRef(context, ''{no room}, aEventName, jsonarray, + procedure(const aData: string) + begin + aCallback(context, SO(aData), nil); + end, aOnError); + except + //try to send to others + end; Inc(Result); end; finally diff --git a/IdSocketIOHandling.pas b/IdSocketIOHandling.pas index ef033c8..4aaecda 100644 --- a/IdSocketIOHandling.pas +++ b/IdSocketIOHandling.pas @@ -601,6 +601,8 @@ procedure TIdBaseSocketIOHandling.ProcessSocketIORequest( begin Result := ''; ilength := strmRequest.Size - strmRequest.Position; + if ilength <= 0 then + Exit; SetLength(utf8, ilength); strmRequest.Read(utf8[0], ilength); Result := TEncoding.UTF8.GetString(utf8); @@ -1365,6 +1367,11 @@ begin FEvent := TEvent.Create; FQueue.Add(aData); + + //max 1000 items in queue (otherwise infinite mem leak possible?) + while FQueue.Count > 1000 do + FQueue.Delete(0); + FEvent.SetEvent; end; diff --git a/IdWebsocketServer.pas b/IdWebsocketServer.pas index 16236f9..4062c8c 100644 --- a/IdWebsocketServer.pas +++ b/IdWebsocketServer.pas @@ -15,7 +15,9 @@ type FSocketIO: TIdServerSocketIOHandling_Ext; FOnMessageText: TWebsocketMessageText; FOnMessageBin: TWebsocketMessageBin; + FWriteTimeout: Integer; function GetSocketIO: TIdServerSocketIOHandling; + procedure SetWriteTimeout(const Value: Integer); protected procedure DoCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); override; @@ -34,12 +36,14 @@ type property OnMessageBin : TWebsocketMessageBin read FOnMessageBin write FOnMessageBin; property SocketIO: TIdServerSocketIOHandling read GetSocketIO; + published + property WriteTimeout: Integer read FWriteTimeout write SetWriteTimeout default 2000; end; implementation uses - IdServerIOHandlerWebsocket, IdStreamVCL, IdGlobal, Windows; + IdServerIOHandlerWebsocket, IdStreamVCL, IdGlobal, Windows, IdWinsock2; { TIdWebsocketServer } @@ -52,12 +56,18 @@ begin ContextClass := TIdServerWSContext; if IOHandler = nil then IOHandler := TIdServerIOHandlerWebsocket.Create(Self); + + FWriteTimeout := 2 * 1000; //2s end; procedure TIdWebsocketServer.ContextCreated(AContext: TIdContext); begin inherited ContextCreated(AContext); (AContext as TIdServerWSContext).OnCustomChannelExecute := Self.WebsocketChannelRequest; + + //default 2s write timeout + //http://msdn.microsoft.com/en-us/library/windows/desktop/ms740532(v=vs.85).aspx + AContext.Connection.Socket.Binding.SetSockOpt(SOL_SOCKET, SO_SNDTIMEO, Self.WriteTimeout); end; procedure TIdWebsocketServer.ContextDisconnected(AContext: TIdContext); @@ -109,6 +119,11 @@ begin end; end; +procedure TIdWebsocketServer.SetWriteTimeout(const Value: Integer); +begin + FWriteTimeout := Value; +end; + procedure TIdWebsocketServer.WebsocketChannelRequest( const AContext: TIdServerWSContext; aType: TWSDataType; const aStrmRequest, aStrmResponse: TMemoryStream);