Merge pull request #4 from Yvi71/master

openssl support. tested. worked.
This commit is contained in:
André Mussche 2016-11-10 12:55:31 +01:00 committed by GitHub
commit f3268769c3
5 changed files with 104 additions and 28 deletions

View file

@ -625,7 +625,12 @@ begin
//ws://host:port/<resourcename> //ws://host:port/<resourcename>
//about resourcename, see: http://dev.w3.org/html5/websockets/ "Parsing WebSocket URLs" //about resourcename, see: http://dev.w3.org/html5/websockets/ "Parsing WebSocket URLs"
//sURL := Format('ws://%s:%d/%s', [Host, Port, WSResourceName]); //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]); sURL := Format('http://%s:%d/%s', [Host, Port, WSResourceName]);
{$ENDIF}
ReadTimeout := Max(5 * 1000, ReadTimeout); ReadTimeout := Max(5 * 1000, ReadTimeout);
{ voorbeeld: { voorbeeld:

View file

@ -11,6 +11,9 @@ uses
Classes, SysUtils, Classes, SysUtils,
IdIOHandlerStack, IdGlobal, IdException, IdBuffer, IdIOHandlerStack, IdGlobal, IdException, IdBuffer,
SyncObjs, SyncObjs,
{$IFNDEF WS_NO_SSL}
IdSSLOpenSSL,
{$ENDIF}
Generics.Collections; Generics.Collections;
type type
@ -22,11 +25,14 @@ type
TIdIOHandlerWebsocket = class; TIdIOHandlerWebsocket = class;
EIdWebSocketHandleError = class(EIdSocketHandleError); EIdWebSocketHandleError = class(EIdSocketHandleError);
{$if CompilerVersion >= 26} //XE5 {.$if CompilerVersion >= 26} //XE5
TIdTextEncoding = IIdTextEncoding; //TIdTextEncoding = IIdTextEncoding;
{$ifend} {.$ifend}
{$IFDEF WS_NO_SSL}
TIdIOHandlerWebsocket = class(TIdIOHandlerStack) TIdIOHandlerWebsocket = class(TIdIOHandlerStack)
{$ELSE}
TIdIOHandlerWebsocket = class(TIdSSLIOHandlerSocketOpenSSL)
{$ENDIF}
private private
FIsServerSide: Boolean; FIsServerSide: Boolean;
FBusyUpgrading: Boolean; FBusyUpgrading: Boolean;
@ -55,12 +61,15 @@ type
function ReadFrame(out aFIN, aRSV1, aRSV2, aRSV3: boolean; out aDataCode: TWSDataCode; out aData: TIdBytes): Integer; function ReadFrame(out aFIN, aRSV1, aRSV2, aRSV3: boolean; out aDataCode: TWSDataCode; out aData: TIdBytes): Integer;
function ReadMessage(var aBuffer: TIdBytes; out aDataCode: TWSDataCode): Integer; function ReadMessage(var aBuffer: TIdBytes; out aDataCode: TWSDataCode): Integer;
{$if CompilerVersion >= 26} //XE5 {.$if CompilerVersion >= 26} //XE5
function UTF8Encoding: IIdTextEncoding; //function UTF8Encoding: IIdTextEncoding;
{$else} {.$else}
function UTF8Encoding: TEncoding; function UTF8Encoding: TEncoding;
{$ifend} {.$ifend}
public public
{$IFNDEF WS_NO_SSL}
procedure ClearSSLOptions;
{$ENDIF}
function WriteData(aData: TIdBytes; aType: TWSDataCode; function WriteData(aData: TIdBytes; aType: TWSDataCode;
aFIN: boolean = true; aRSV1: boolean = false; aRSV2: boolean = false; aRSV3: boolean = false): integer; aFIN: boolean = true; aRSV1: boolean = false; aRSV2: boolean = false; aRSV3: boolean = false): integer;
property BusyUpgrading : Boolean read FBusyUpgrading write FBusyUpgrading; property BusyUpgrading : Boolean read FBusyUpgrading write FBusyUpgrading;
@ -258,6 +267,14 @@ begin
FPendingWriteCount := 0; FPendingWriteCount := 0;
end; end;
{$IFNDEF WS_NO_SSL}
procedure TIdIOHandlerWebsocket.ClearSSLOptions;
begin
self.fxSSLOptions.Free;
self.fxSSLOptions := nil;
end;
{$ENDIF}
procedure TIdIOHandlerWebsocket.Close; procedure TIdIOHandlerWebsocket.Close;
var var
iaWriteBuffer: TIdBytes; iaWriteBuffer: TIdBytes;
@ -556,8 +573,6 @@ end;
function TIdIOHandlerWebsocket.WriteDataToTarget(const ABuffer: TIdBytes; function TIdIOHandlerWebsocket.WriteDataToTarget(const ABuffer: TIdBytes;
const AOffset, ALength: Integer): Integer; const AOffset, ALength: Integer): Integer;
var
data: TIdBytes;
begin begin
if UseSingleWriteThread and IsWebsocket and (GetCurrentThreadId <> TIdWebsocketWriteThread.Instance.ThreadID) then if UseSingleWriteThread and IsWebsocket and (GetCurrentThreadId <> TIdWebsocketWriteThread.Instance.ThreadID) then
Assert(False, 'Write done in different thread than TIdWebsocketWriteThread!'); Assert(False, 'Write done in different thread than TIdWebsocketWriteThread!');
@ -576,19 +591,17 @@ begin
end end
else else
begin begin
data := ToBytes(ABuffer, ALength, AOffset);
{$IFDEF DEBUG_WS} {$IFDEF DEBUG_WS}
if Debughook > 0 then if Debughook > 0 then
OutputDebugString(PChar(Format('Send (ws, TID:%d, P:%d): %s', OutputDebugString(PChar(Format('Send (ws, TID:%d, P:%d): %s',
[getcurrentthreadid, Self.Binding.PeerPort, BytesToStringRaw(data)]))); [getcurrentthreadid, Self.Binding.PeerPort, BytesToStringRaw(ABuffer)])));
{$ENDIF} {$ENDIF}
try try
if FWriteTextToTarget then 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) webBit1 in ClientExtensionBits, webBit2 in ClientExtensionBits, webBit3 in ClientExtensionBits)
else 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); webBit1 in ClientExtensionBits, webBit2 in ClientExtensionBits, webBit3 in ClientExtensionBits);
except except
FClosedGracefully := True; FClosedGracefully := True;
@ -831,17 +844,17 @@ begin
FLock.Leave; FLock.Leave;
end; end;
{$if CompilerVersion >= 26} //XE5 {.$if CompilerVersion >= 26} //XE5
function TIdIOHandlerWebsocket.UTF8Encoding: IIdTextEncoding; //function TIdIOHandlerWebsocket.UTF8Encoding: IIdTextEncoding;
begin //begin
Result := IndyTextEncoding_UTF8; // Result := IndyTextEncoding_UTF8;
end; //end;
{$else} {.$else}
function TIdIOHandlerWebsocket.UTF8Encoding: TEncoding; function TIdIOHandlerWebsocket.UTF8Encoding: TEncoding;
begin begin
Result := TIdTextEncoding.UTF8; Result := TIdTextEncoding.UTF8;
end; end;
{$ifend} {.$ifend}
function TIdIOHandlerWebsocket.ReadFrame(out aFIN, aRSV1, aRSV2, aRSV3: boolean; function TIdIOHandlerWebsocket.ReadFrame(out aFIN, aRSV1, aRSV2, aRSV3: boolean;
out aDataCode: TWSDataCode; out aData: TIdBytes): Integer; out aDataCode: TWSDataCode; out aData: TIdBytes): Integer;
@ -1135,11 +1148,11 @@ begin
AppendBytes(bData, aData); //important: send all at once! AppendBytes(bData, aData); //important: send all at once!
ioffset := 0; ioffset := 0;
iDataLength := Length(bData);
repeat repeat
//Result := Binding.Send(bData, ioffset); result := inherited WriteDataToTarget(bdata,iOffset, (iDataLength-ioffset));
Result := inherited WriteDataToTarget(bdata, iOffset, (Length(bData) - ioffset)); //ssl compatible?
Inc(ioffset, Result); Inc(ioffset, Result);
until ioffset >= Length(bData); until ioffset >= iDataLength;
// if debughook > 0 then // if debughook > 0 then
// OutputDebugString(PChar(Format('Written (TID:%d, P:%d): %s', // OutputDebugString(PChar(Format('Written (TID:%d, P:%d): %s',

View file

@ -5,10 +5,18 @@ interface
uses uses
Classes, Classes,
IdServerIOHandlerStack, IdIOHandlerStack, IdGlobal, IdIOHandler, IdYarn, IdThread, IdSocketHandle, IdServerIOHandlerStack, IdIOHandlerStack, IdGlobal, IdIOHandler, IdYarn, IdThread, IdSocketHandle,
{$IFNDEF WS_NO_SSL}
IdSSLOpenSSL,
sysutils,
{$ENDIF}
IdIOHandlerWebsocket; IdIOHandlerWebsocket;
type type
{$IFDEF WS_NO_SSL}
TIdServerIOHandlerWebsocket = class(TIdServerIOHandlerStack) TIdServerIOHandlerWebsocket = class(TIdServerIOHandlerStack)
{$ELSE}
TIdServerIOHandlerWebsocket = class(TIdServerIOHandlersslOpenSSL)
{$ENDIF}
protected protected
procedure InitComponent; override; procedure InitComponent; override;
public public
@ -23,8 +31,40 @@ implementation
function TIdServerIOHandlerWebsocket.Accept(ASocket: TIdSocketHandle; function TIdServerIOHandlerWebsocket.Accept(ASocket: TIdSocketHandle;
AListenerThread: TIdThread; AYarn: TIdYarn): TIdIOHandler; AListenerThread: TIdThread; AYarn: TIdYarn): TIdIOHandler;
{$IFNDEF WS_NO_SSL}
var
LIO: TIdIOHandlerWebsocket;
{$ENDIF}
begin begin
{$IFDEF WS_NO_SSL}
Result := inherited Accept(ASocket, AListenerThread, AYarn); 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 if Result <> nil then
begin begin
(Result as TIdIOHandlerWebsocket).IsServerSide := True; //server must not mask, only client (Result as TIdIOHandlerWebsocket).IsServerSide := True; //server must not mask, only client
@ -35,7 +75,9 @@ end;
procedure TIdServerIOHandlerWebsocket.InitComponent; procedure TIdServerIOHandlerWebsocket.InitComponent;
begin begin
inherited InitComponent; inherited InitComponent;
{$IFDEF WS_NO_SSL}
IOHandlerSocketClass := TIdIOHandlerWebsocket; IOHandlerSocketClass := TIdIOHandlerWebsocket;
{$ENDIF}
end; end;
function TIdServerIOHandlerWebsocket.MakeClientIOHandler( function TIdServerIOHandlerWebsocket.MakeClientIOHandler(

View file

@ -137,7 +137,9 @@ begin
aSocketIOHandler.WritePing(context); aSocketIOHandler.WritePing(context);
end end
else else
begin
context.IOHandler.WriteData(nil, wdcPing); context.IOHandler.WriteData(nil, wdcPing);
end;
end; end;
end; end;
@ -325,13 +327,22 @@ begin
hash.Free; hash.Free;
end; end;
AResponseInfo.CustomHeaders.Values['Sec-WebSocket-Accept'] := sValue; 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? //send same protocol back?
AResponseInfo.CustomHeaders.Values['Sec-WebSocket-Protocol'] := context.WebSocketProtocol; AResponseInfo.CustomHeaders.Values['Sec-WebSocket-Protocol'] := context.WebSocketProtocol;
//we do not support extensions yet (gzip deflate compression etc) //we do not support extensions yet (gzip deflate compression etc)
//AResponseInfo.CustomHeaders.Values['Sec-WebSocket-Extensions'] := context.WebSocketExtensions; //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 //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 //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 //send response back
context.IOHandler.InputBuffer.Clear; context.IOHandler.InputBuffer.Clear;

View file

@ -4,7 +4,7 @@ interface
uses uses
IdServerWebsocketHandling, IdServerSocketIOHandling, IdServerWebsocketContext, IdServerWebsocketHandling, IdServerSocketIOHandling, IdServerWebsocketContext,
IdHTTPServer, IdContext, IdCustomHTTPServer, Classes, IdIOHandlerWebsocket; IdHTTPServer, IdContext, IdCustomHTTPServer, Classes, IdIOHandlerWebsocket, IdServerIOHandler;
type type
TWebsocketMessageText = procedure(const AContext: TIdServerWSContext; const aText: string) of object; TWebsocketMessageText = procedure(const AContext: TIdServerWSContext; const aText: string) of object;
@ -43,7 +43,12 @@ type
implementation implementation
uses uses
IdServerIOHandlerWebsocket, IdStreamVCL, IdGlobal, Windows, IdWinsock2; IdServerIOHandlerWebsocket, IdStreamVCL, IdGlobal, Windows,
{$IFNDEF WS_NO_SSL}
idIOHandler,
idssl,
{$ENDIF}
IdWinsock2;
{ TIdWebsocketServer } { TIdWebsocketServer }