From 61cefd77e0dd508ba8a2f347948408044fbaf663 Mon Sep 17 00:00:00 2001 From: yvi71 Date: Fri, 13 Nov 2015 01:59:22 +0100 Subject: [PATCH 1/2] added support for openssl - and therewith dependency to openssl units. Disable ssl support (and additional dependencies) set the compiler swith WS_NO_SSL --- IdHTTPWebsocketClient.pas | 5 +++ IdIOHandlerWebsocket.pas | 63 ++++++++++++++++++++-------------- IdServerIOHandlerWebsocket.pas | 41 ++++++++++++++++++++++ IdServerWebsocketHandling.pas | 13 ++++++- IdWebsocketServer.pas | 9 +++-- 5 files changed, 103 insertions(+), 28 deletions(-) diff --git a/IdHTTPWebsocketClient.pas b/IdHTTPWebsocketClient.pas index 83ba9ed..70e5435 100644 --- a/IdHTTPWebsocketClient.pas +++ b/IdHTTPWebsocketClient.pas @@ -625,7 +625,12 @@ begin //ws://host:port/ //about resourcename, see: http://dev.w3.org/html5/websockets/ "Parsing WebSocket URLs" //sURL := Format('ws://%s:%d/%s', [Host, Port, WSResourceName]); + sURL := Format('https://%s:%d/%s', [Host, Port, WSResourceName]); +{$IFDEF WS_NO_SSL} + //TODO: depend protocol on usessl - param passing in here sURL := Format('http://%s:%d/%s', [Host, Port, WSResourceName]); +{$ENDIF} + ReadTimeout := Max(5 * 1000, ReadTimeout); { voorbeeld: diff --git a/IdIOHandlerWebsocket.pas b/IdIOHandlerWebsocket.pas index 33b6d21..642684c 100644 --- a/IdIOHandlerWebsocket.pas +++ b/IdIOHandlerWebsocket.pas @@ -11,6 +11,9 @@ uses Classes, SysUtils, IdIOHandlerStack, IdGlobal, IdException, IdBuffer, SyncObjs, +{$IFNDEF WS_NO_SSL} + IdSSLOpenSSL, +{$ENDIF} Generics.Collections; type @@ -22,11 +25,14 @@ type TIdIOHandlerWebsocket = class; EIdWebSocketHandleError = class(EIdSocketHandleError); - {$if CompilerVersion >= 26} //XE5 - TIdTextEncoding = IIdTextEncoding; - {$ifend} - + {.$if CompilerVersion >= 26} //XE5 + //TIdTextEncoding = IIdTextEncoding; + {.$ifend} +{$IFDEF WS_NO_SSL} TIdIOHandlerWebsocket = class(TIdIOHandlerStack) +{ELSE} + TIdIOHandlerWebsocketSSL = class(TIdSSLIOHandlerSocketOpenSSL) +{$ENDIF} private FIsServerSide: Boolean; FBusyUpgrading: Boolean; @@ -55,12 +61,15 @@ type function ReadFrame(out aFIN, aRSV1, aRSV2, aRSV3: boolean; out aDataCode: TWSDataCode; out aData: TIdBytes): Integer; function ReadMessage(var aBuffer: TIdBytes; out aDataCode: TWSDataCode): Integer; - {$if CompilerVersion >= 26} //XE5 - function UTF8Encoding: IIdTextEncoding; - {$else} + {.$if CompilerVersion >= 26} //XE5 + //function UTF8Encoding: IIdTextEncoding; + {.$else} function UTF8Encoding: TEncoding; - {$ifend} + {.$ifend} public +{$IFNDEF WS_NO_SSL} + procedure ClearSSLOptions; +{$ENDIF} function WriteData(aData: TIdBytes; aType: TWSDataCode; aFIN: boolean = true; aRSV1: boolean = false; aRSV2: boolean = false; aRSV3: boolean = false): integer; property BusyUpgrading : Boolean read FBusyUpgrading write FBusyUpgrading; @@ -258,6 +267,14 @@ begin FPendingWriteCount := 0; end; +{$IFNDEF WS_NO_SSL} +procedure TIdIOHandlerWebsocketSSL.ClearSSLOptions; +begin + self.fxSSLOptions.Free; + self.fxSSLOptions := nil; +end; +{$ENDIF + procedure TIdIOHandlerWebsocket.Close; var iaWriteBuffer: TIdBytes; @@ -556,8 +573,6 @@ end; function TIdIOHandlerWebsocket.WriteDataToTarget(const ABuffer: TIdBytes; const AOffset, ALength: Integer): Integer; -var - data: TIdBytes; begin if UseSingleWriteThread and IsWebsocket and (GetCurrentThreadId <> TIdWebsocketWriteThread.Instance.ThreadID) then Assert(False, 'Write done in different thread than TIdWebsocketWriteThread!'); @@ -576,19 +591,17 @@ begin end else begin - data := ToBytes(ABuffer, ALength, AOffset); {$IFDEF DEBUG_WS} if Debughook > 0 then OutputDebugString(PChar(Format('Send (ws, TID:%d, P:%d): %s', - [getcurrentthreadid, Self.Binding.PeerPort, BytesToStringRaw(data)]))); - + [getcurrentthreadid, Self.Binding.PeerPort, BytesToStringRaw(ABuffer)]))); {$ENDIF} try if FWriteTextToTarget then - Result := WriteData(data, wdcText, True{send all at once}, + Result := WriteData(ABuffer, wdcText, True{send all at once}, webBit1 in ClientExtensionBits, webBit2 in ClientExtensionBits, webBit3 in ClientExtensionBits) else - Result := WriteData(data, wdcBinary, True{send all at once}, + Result := WriteData(ABuffer, wdcBinary, True{send all at once}, webBit1 in ClientExtensionBits, webBit2 in ClientExtensionBits, webBit3 in ClientExtensionBits); except FClosedGracefully := True; @@ -831,17 +844,17 @@ begin FLock.Leave; end; -{$if CompilerVersion >= 26} //XE5 -function TIdIOHandlerWebsocket.UTF8Encoding: IIdTextEncoding; -begin - Result := IndyTextEncoding_UTF8; -end; -{$else} +{.$if CompilerVersion >= 26} //XE5 +//function TIdIOHandlerWebsocket.UTF8Encoding: IIdTextEncoding; +//begin +// Result := IndyTextEncoding_UTF8; +//end; +{.$else} function TIdIOHandlerWebsocket.UTF8Encoding: TEncoding; begin Result := TIdTextEncoding.UTF8; end; -{$ifend} +{.$ifend} function TIdIOHandlerWebsocket.ReadFrame(out aFIN, aRSV1, aRSV2, aRSV3: boolean; out aDataCode: TWSDataCode; out aData: TIdBytes): Integer; @@ -1135,11 +1148,11 @@ begin AppendBytes(bData, aData); //important: send all at once! ioffset := 0; + iDataLength := Length(bData); repeat - //Result := Binding.Send(bData, ioffset); - Result := inherited WriteDataToTarget(bdata, iOffset, (Length(bData) - ioffset)); //ssl compatible? + result := inherited WriteDataToTarget(bdata,iOffset, (iDataLength-ioffset)); Inc(ioffset, Result); - until ioffset >= Length(bData); + until ioffset >= iDataLenght; // if debughook > 0 then // OutputDebugString(PChar(Format('Written (TID:%d, P:%d): %s', diff --git a/IdServerIOHandlerWebsocket.pas b/IdServerIOHandlerWebsocket.pas index 80e1061..f863847 100644 --- a/IdServerIOHandlerWebsocket.pas +++ b/IdServerIOHandlerWebsocket.pas @@ -5,10 +5,18 @@ interface uses Classes, IdServerIOHandlerStack, IdIOHandlerStack, IdGlobal, IdIOHandler, IdYarn, IdThread, IdSocketHandle, +{$IFNDEF WS_NO_SSL} + IdSSLOpenSSL, + sysutils, +{$ENDIF} IdIOHandlerWebsocket; type +{$IFNDEF WS_NO_SSL} TIdServerIOHandlerWebsocket = class(TIdServerIOHandlerStack) +{$ELSE} + TIdServerIOHandlerWebsocket = class(TIdServerIOHandlersslOpenSSL) +{$ENDIF} protected procedure InitComponent; override; public @@ -23,8 +31,40 @@ implementation function TIdServerIOHandlerWebsocket.Accept(ASocket: TIdSocketHandle; AListenerThread: TIdThread; AYarn: TIdYarn): TIdIOHandler; +{$IFNDEF WS_NO_SSL} +var + LIO: TIdIOHandlerWebsocketSSL; +{$ENDIF} begin +{$IFDEF WS_NO_SSL} Result := inherited Accept(ASocket, AListenerThread, AYarn); +{$ELSE} + Assert(ASocket<>nil); + Assert(fSSLContext<>nil); + LIO := TIdIOHandlerWebsocket.Create(nil); + try + LIO.PassThrough := True; + LIO.Open; + if LIO.Binding.Accept(ASocket.Handle) then + begin + //we need to pass the SSLOptions for the socket from the server + LIO.ClearSSLOptions; + LIO.IsPeer := True; + LIO.SSLOptions := SSLOptions; + LIO.SSLSocket := TIdSSLSocket.Create(Self); + LIO.SSLContext := fSSLContext; + + end + else + begin + FreeAndNil(LIO); + end; + except + LIO.Free; + raise; + end; + Result := LIO; +{$ENDIF} if Result <> nil then begin (Result as TIdIOHandlerWebsocket).IsServerSide := True; //server must not mask, only client @@ -35,6 +75,7 @@ end; procedure TIdServerIOHandlerWebsocket.InitComponent; begin inherited InitComponent; +//TODO: Check if this is necessary for SSL IOHandlerSocketClass := TIdIOHandlerWebsocket; end; diff --git a/IdServerWebsocketHandling.pas b/IdServerWebsocketHandling.pas index f1f6f52..28ebd68 100644 --- a/IdServerWebsocketHandling.pas +++ b/IdServerWebsocketHandling.pas @@ -137,7 +137,9 @@ begin aSocketIOHandler.WritePing(context); end else + begin context.IOHandler.WriteData(nil, wdcPing); + end; end; end; @@ -325,13 +327,22 @@ begin hash.Free; end; AResponseInfo.CustomHeaders.Values['Sec-WebSocket-Accept'] := sValue; - +{$IFNDEF WS_NO_SSL} + //keep alive the ssl connection + AResponseInfo.CustomHeaders.Values['Keep-alive'] := 'true'; +{$ENDIF} + //send same protocol back? AResponseInfo.CustomHeaders.Values['Sec-WebSocket-Protocol'] := context.WebSocketProtocol; //we do not support extensions yet (gzip deflate compression etc) //AResponseInfo.CustomHeaders.Values['Sec-WebSocket-Extensions'] := context.WebSocketExtensions; //http://www.lenholgate.com/blog/2011/07/websockets---the-deflate-stream-extension-is-broken-and-badly-designed.html //but is could be done using idZlib.pas and DecompressGZipStream etc +{$IFNDEF WS_NO_SSL} + //YD: TODO: Check if this is really necessary + AResponseInfo.CustomHeaders.Values['sec-websocket-extensions'] := ''; + context.WebSocketExtensions := ''; +{$ENDIF} //send response back context.IOHandler.InputBuffer.Clear; diff --git a/IdWebsocketServer.pas b/IdWebsocketServer.pas index 4062c8c..980d0ab 100644 --- a/IdWebsocketServer.pas +++ b/IdWebsocketServer.pas @@ -4,7 +4,7 @@ interface uses IdServerWebsocketHandling, IdServerSocketIOHandling, IdServerWebsocketContext, - IdHTTPServer, IdContext, IdCustomHTTPServer, Classes, IdIOHandlerWebsocket; + IdHTTPServer, IdContext, IdCustomHTTPServer, Classes, IdIOHandlerWebsocket, IdServerIOHandler; type TWebsocketMessageText = procedure(const AContext: TIdServerWSContext; const aText: string) of object; @@ -43,7 +43,12 @@ type implementation uses - IdServerIOHandlerWebsocket, IdStreamVCL, IdGlobal, Windows, IdWinsock2; + IdServerIOHandlerWebsocket, IdStreamVCL, IdGlobal, Windows, +{$IFNDEF WS_NO_SSL} + idIOHandler, + idssl, +{$ENDIF} + IdWinsock2; { TIdWebsocketServer } From 803bce2cf31ee6031e0e0c97f2eb91ac07ded4fe Mon Sep 17 00:00:00 2001 From: Yvi71 Date: Fri, 13 Nov 2015 11:09:24 +0100 Subject: [PATCH 2/2] Fixed some issues with the compiler switch and other things i messed up last night. Fixed some issues with the compiler switch and other things i messed up last night --- IdIOHandlerWebsocket.pas | 10 +++++----- IdServerIOHandlerWebsocket.pas | 7 ++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/IdIOHandlerWebsocket.pas b/IdIOHandlerWebsocket.pas index 642684c..9219ece 100644 --- a/IdIOHandlerWebsocket.pas +++ b/IdIOHandlerWebsocket.pas @@ -30,8 +30,8 @@ type {.$ifend} {$IFDEF WS_NO_SSL} TIdIOHandlerWebsocket = class(TIdIOHandlerStack) -{ELSE} - TIdIOHandlerWebsocketSSL = class(TIdSSLIOHandlerSocketOpenSSL) +{$ELSE} + TIdIOHandlerWebsocket = class(TIdSSLIOHandlerSocketOpenSSL) {$ENDIF} private FIsServerSide: Boolean; @@ -268,12 +268,12 @@ begin end; {$IFNDEF WS_NO_SSL} -procedure TIdIOHandlerWebsocketSSL.ClearSSLOptions; +procedure TIdIOHandlerWebsocket.ClearSSLOptions; begin self.fxSSLOptions.Free; self.fxSSLOptions := nil; end; -{$ENDIF +{$ENDIF} procedure TIdIOHandlerWebsocket.Close; var @@ -1152,7 +1152,7 @@ begin repeat result := inherited WriteDataToTarget(bdata,iOffset, (iDataLength-ioffset)); Inc(ioffset, Result); - until ioffset >= iDataLenght; + until ioffset >= iDataLength; // if debughook > 0 then // OutputDebugString(PChar(Format('Written (TID:%d, P:%d): %s', diff --git a/IdServerIOHandlerWebsocket.pas b/IdServerIOHandlerWebsocket.pas index f863847..ed63e4d 100644 --- a/IdServerIOHandlerWebsocket.pas +++ b/IdServerIOHandlerWebsocket.pas @@ -12,7 +12,7 @@ uses IdIOHandlerWebsocket; type -{$IFNDEF WS_NO_SSL} +{$IFDEF WS_NO_SSL} TIdServerIOHandlerWebsocket = class(TIdServerIOHandlerStack) {$ELSE} TIdServerIOHandlerWebsocket = class(TIdServerIOHandlersslOpenSSL) @@ -33,7 +33,7 @@ function TIdServerIOHandlerWebsocket.Accept(ASocket: TIdSocketHandle; AListenerThread: TIdThread; AYarn: TIdYarn): TIdIOHandler; {$IFNDEF WS_NO_SSL} var - LIO: TIdIOHandlerWebsocketSSL; + LIO: TIdIOHandlerWebsocket; {$ENDIF} begin {$IFDEF WS_NO_SSL} @@ -75,8 +75,9 @@ end; procedure TIdServerIOHandlerWebsocket.InitComponent; begin inherited InitComponent; -//TODO: Check if this is necessary for SSL +{$IFDEF WS_NO_SSL} IOHandlerSocketClass := TIdIOHandlerWebsocket; +{$ENDIF} end; function TIdServerIOHandlerWebsocket.MakeClientIOHandler(