fpwebsocket.pp 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648
  1. {
  2. $Id: header,v 1.1 2000/07/13 06:33:45 michael Exp $
  3. This file is part of the Free Component Library (FCL)
  4. Copyright (c) 2021 - by the Free Pascal development team
  5. Abstract websocket protocol implementation - objects only
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. unit fpwebsocket;
  13. {$mode objfpc}
  14. {$h+}
  15. {$modeswitch advancedrecords}
  16. {$modeswitch typehelpers}
  17. interface
  18. uses
  19. Classes, SysUtils, sockets, ssockets;
  20. Const
  21. SSecWebSocketGUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
  22. SSecWebsocketProtocol = 'Sec-WebSocket-Protocol';
  23. SSecWebsocketVersion = 'Sec-WebSocket-Version';
  24. SSecWebsocketExtensions = 'Sec-WebSocket-Extensions';
  25. SSecWebsocketKey = 'Sec-WebSocket-Key';
  26. SSecWebsocketAccept = 'Sec-WebSocket-Accept';
  27. MinFrameSize = 4;
  28. DefaultWebSocketVersion = 13;
  29. // Opcodes
  30. FlagReserved = $F;
  31. FlagContinuation = $0;
  32. FlagText = $1;
  33. FlagBinary = $2;
  34. FlagClose = $8;
  35. FlagPing = $9;
  36. FlagPong = $A;
  37. // For SVR etc.
  38. FlagTwoBytes = 126;
  39. FlagEightBytes = 127;
  40. FlagFinalFrame : Byte = $80;
  41. FlagMasked : Byte = $80;
  42. FlagLengthMask : Byte = $7F;
  43. FlagRES1 = $40;
  44. FlagRES2 = $20;
  45. FlagRES3 = $10;
  46. CLOSE_NORMAL_CLOSURE = 1000;
  47. CLOSE_GOING_AWAY = 1001;
  48. CLOSE_PROTOCOL_ERROR = 1002;
  49. CLOSE_UNSUPORTED_DATA = 1003;
  50. CLOSE_RESERVER = 1004;
  51. CLOSE_NO_STATUS_RCVD = 1005;
  52. CLOSE_ABNORMAL_CLOSURE = 1006;
  53. CLOSE_INVALID_FRAME_PAYLOAD_DATA = 1007;
  54. CLOSE_POLICY_VIOLATION = 1008;
  55. CLOSE_MESSAGE_TOO_BIG = 1009;
  56. CLOSE_MANDRATORY_EXT = 1010;
  57. CLOSE_INTERNAL_SERVER_ERROR = 1011;
  58. CLOSE_TLS_HANDSHAKE = 1015;
  59. type
  60. EWebSocket = Class(Exception);
  61. EWSHandShake = class(EWebSocket);
  62. TFrameType = (ftContinuation,ftText,ftBinary,ftClose,ftPing,ftPong,ftFutureOpcodes);
  63. TFrameTypes = Set of TFrameType;
  64. TFrameSequence = (fsFirst,fsContinuation,fsLast);
  65. TFrameSequences = Set of TFrameSequence;
  66. TIncomingResult = (irNone, // No data waiting
  67. irWaiting, // Data waiting
  68. irOK, // Data was waiting and handled
  69. irClose // Data was waiting, handled, and we must disconnect (CloseState=csClosed)
  70. );
  71. { TFrameTypeHelper }
  72. TFrameTypeHelper = Type helper for TFrametype
  73. private
  74. function GetAsFlag: Byte;
  75. procedure SetAsFlag(AValue: Byte);
  76. Public
  77. Property asFlag : Byte Read GetAsFlag Write SetAsFlag;
  78. end;
  79. { TWSHeaders }
  80. TWSHeaders = class
  81. private
  82. FRawHeaders: TStrings;
  83. FResource: String;
  84. Protected
  85. Function GetS(aIdx : Integer) : String;
  86. procedure SetS(AIndex: Integer; const AValue: string);
  87. Function GetH(const aName : string) : String;
  88. procedure SetH(const aName, aValue: string);
  89. Public
  90. Const
  91. WSHeaderNames : Array[0..8] of string
  92. = ('Host','Origin','Connection','Upgrade',SSecWebSocketProtocol,
  93. SSecWebSocketVersion,SSecWebSocketExtensions,SSecWebSocketKey,
  94. SSecWebSocketAccept);
  95. public
  96. constructor Create(const aResource : String; const AHeaderList: TStrings); virtual;
  97. Destructor Destroy; override;
  98. Property RawHeaders : TStrings Read FRawHeaders;
  99. property Resource : String Read FResource Write FResource;
  100. property Host: string Index 0 read GetS Write SetS;
  101. property Origin: string Index 1 read GetS Write SetS;
  102. property Connection: string Index 2 read GetS Write SetS;
  103. property Upgrade: string Index 3 read GetS Write SetS;
  104. property Protocol: string Index 4 read GetS Write SetS;
  105. property Version: string Index 5 read GetS Write SetS;
  106. property Extensions : String Index 6 read GetS Write SetS;
  107. property Key: string Index 7 read GetS Write SetS;
  108. end;
  109. { TWSHandShakeRequest }
  110. TWSHandShakeRequest = Class(TWSHeaders)
  111. private
  112. FPort: Word;
  113. Public
  114. Constructor Create(const aResource : string; const aExtraHeaders : TStrings); override;
  115. class function GenerateKey: String; static;
  116. Procedure ToStrings(aHeaders : TStrings);
  117. Property Port : Word Read FPort Write FPort;
  118. End;
  119. { TWSHandShakeResponse }
  120. TWSHandShakeResponse = Class (TWSHeaders)
  121. private
  122. FHTTPVersion: String;
  123. FStatusCode: Integer;
  124. FStatusText: String;
  125. Public
  126. Constructor Create(const aResource : string; const aExtraHeaders : TStrings); override;
  127. Procedure ToStrings(aHandShake : TWSHandshakeRequest; aResponse : TStrings; AddStatusLine : Boolean);
  128. Property HTTPVersion : String Read FHTTPVersion Write FHTTPVersion;
  129. Property StatusCode : Integer Read FStatusCode Write FStatusCode;
  130. Property StatusText : String Read FStatusText Write FSTatusText;
  131. property Accept : String Index 8 read GetS Write SetS;
  132. End;
  133. {$INTERFACES CORBA}
  134. { TWSTransport }
  135. { IWSTransport }
  136. IWSTransport = Interface
  137. // Check if transport can read data
  138. Function CanRead(aTimeOut: Integer) : Boolean;
  139. // Read length of buffer bytes. Raise exception if no data read
  140. Procedure ReadBuffer (aBytes : TBytes);
  141. // Read at most aCount bytes into buffer. Return number of bytes actually read, set length of buffer to actually read
  142. function ReadBytes (var aBytes : TBytes; aCount : Integer) : Integer;
  143. // Write at most aCount bytes.
  144. function WriteBytes (aBytes : TBytes; aCount : Integer) : Integer;
  145. // Write complete buffer. Raise exception if not all bytes were written.
  146. Procedure WriteBuffer (aBytes : TBytes);
  147. function ReadLn : String;
  148. function PeerIP: string;
  149. end;
  150. { TWSSocketHelper }
  151. TWSSocketHelper = Class (TObject,IWSTransport)
  152. Private
  153. FSocket : TSocketStream;
  154. Public
  155. Constructor Create (aSocket : TSocketStream);
  156. Function CanRead(aTimeOut: Integer) : Boolean;
  157. function PeerIP: string; virtual;
  158. function ReadLn : String; virtual;
  159. function ReadBytes (var aBytes : TBytes; aCount : Integer) : Integer; virtual;
  160. Procedure ReadBuffer (aBytes : TBytes); virtual;
  161. function WriteBytes (aBytes : TBytes; aCount : Integer) : Integer; virtual;
  162. Procedure WriteBuffer (aBytes : TBytes);
  163. Property Socket : TSocketStream Read FSocket;
  164. end;
  165. TWSTransport = class(TObject, IWSTransport)
  166. Private
  167. FHelper : TWSSocketHelper;
  168. FStream : TSocketStream;
  169. function GetSocket: TSocketStream;
  170. Public
  171. Constructor Create(aStream : TSocketStream);
  172. Destructor Destroy; override;
  173. Procedure CloseSocket;
  174. Property Helper : TWSSocketHelper Read FHelper Implements IWSTransport;
  175. Property Socket : TSocketStream Read GetSocket;
  176. end;
  177. { TWSFramePayload }
  178. TWSFramePayload = record
  179. DataLength: QWord;
  180. // Data is unmasked
  181. Data: TBytes;
  182. MaskKey: dword;
  183. Masked: Boolean;
  184. Procedure ReadData(var Content : TBytes; aTransport : IWSTransport);
  185. Procedure Read(buffer: TBytes; aTransport : IWSTransport);
  186. class procedure DoMask(var aData: TBytes; Key: DWORD); static;
  187. class procedure CopyMasked(SrcData: TBytes; var DestData: TBytes; Key: DWORD; aOffset: Integer); static;
  188. class function CopyMasked(SrcData: TBytes; Key: DWORD) : TBytes; static;
  189. end;
  190. { TWSFrame }
  191. TWSFrame = Class
  192. private
  193. FFrameType: TFrameType;
  194. FFinalFrame: Boolean;
  195. FRSV: Byte;
  196. FPayload : TWSFramePayload;
  197. protected
  198. function Read(aTransport: IWSTransport): boolean;
  199. function GetAsBytes : TBytes; virtual;
  200. Public
  201. // Read a message from transport. Returns Nil if the connection was closed when reading.
  202. class function CreateFromStream(aTransport : IWSTransport): TWSFrame;
  203. public
  204. constructor Create(aType: TFrameType; aIsFinal: Boolean; APayload: TBytes; aMask : Integer = 0); overload; virtual;
  205. constructor Create(Const aMessage : UTF8String; aMask : Integer = 0); overload; virtual;
  206. constructor Create(aType: TFrameType; aIsFinal: Boolean = True; aMask: Integer = 0); overload; virtual;
  207. property Reserved : Byte read FRSV write FRSV;
  208. property FinalFrame: Boolean read FFinalFrame write FFinalFrame;
  209. property Payload : TWSFramePayload Read FPayload Write FPayLoad;
  210. property FrameType: TFrameType read FFrameType;
  211. Property AsBytes : TBytes Read GetAsBytes;
  212. end;
  213. TWSFrameClass = Class of TWSFrame;
  214. { TWSMessage }
  215. TWSMessage = record
  216. private
  217. function GetAsString: UTF8String;
  218. function GetAsUnicodeString: UnicodeString;
  219. Public
  220. PayLoad : TBytes;
  221. Sequences : TFrameSequences;
  222. IsText : Boolean;
  223. Property AsString : UTF8String Read GetAsString;
  224. Property AsUTF8String : UTF8String Read GetAsString;
  225. Property AsUnicodeString : UnicodeString Read GetAsUnicodeString;
  226. end;
  227. TWSMessageEvent = procedure(Sender: TObject; const aMessage : TWSMessage) of object;
  228. TWSControlEvent = procedure(Sender: TObject; aType : TFrameType; const aData: TBytes) of object;
  229. TCloseState = (csNone,csSent,csReceived,csClosed);
  230. TCloseStates = Set of TCloseState;
  231. TWSOption = (woPongExplicit, // Send Pong explicitly, not implicitly.
  232. woCloseExplicit, // SeDo Close explicitly, not implicitly.
  233. woIndividualFrames, // Send frames one by one, do not concatenate.
  234. woSkipUpgradeCheck, // Skip handshake "Upgrade:" HTTP header cheack.
  235. woSkipVersionCheck // Skip handshake "Sec-WebSocket-Version' HTTP header check.
  236. );
  237. TWSOptions = set of TWSOption;
  238. { TWSConnection }
  239. TWSConnection = class
  240. Private
  241. class var _ConnectionCount : {$IFDEF CPU64}QWord{$ELSE}Cardinal{$ENDIF};
  242. private
  243. FAutoDisconnect: Boolean;
  244. FConnectionID: String;
  245. FFreeUserData: Boolean;
  246. FOnDisconnect: TNotifyEvent;
  247. FOutgoingFrameMask: Integer;
  248. FOwner: TComponent;
  249. FUserData: TObject;
  250. FWebSocketVersion: Integer;
  251. FInitialOpcode : TFrameType;
  252. FMessageContent : TBytes;
  253. FHandshakeRequest: TWSHandShakeRequest;
  254. FOnMessageReceived: TWSMessageEvent;
  255. FOnControl: TWSControlEvent;
  256. FCloseState : TCloseState;
  257. FOptions: TWSOptions;
  258. Function GetPeerIP : String;
  259. protected
  260. procedure AllocateConnectionID; virtual;
  261. Procedure SetCloseState(aValue : TCloseState); virtual;
  262. Procedure DoDisconnect; virtual; abstract;
  263. // Read message from connection. Return False if connection was closed.
  264. function DoReadMessage: Boolean;
  265. procedure DispatchEvent(aInitialType : TFrameType; aFrame: TWSFrame);
  266. Procedure SetHandShakeRequest(aRequest : TWSHandShakeRequest);
  267. Function HandleIncoming(aFrame: TWSFrame) : Boolean; virtual;
  268. function GetHandshakeCompleted: Boolean; virtual; abstract;
  269. Function GetTransport : IWSTransport; virtual; abstract;
  270. property Owner : TComponent Read FOwner;
  271. Public
  272. Type
  273. TConnectionIDAllocator = Procedure(out aID : String) of object;
  274. class var IDAllocator : TConnectionIDAllocator;
  275. Public
  276. Constructor Create(aOwner : TComponent; aOptions : TWSOptions); virtual;
  277. destructor Destroy; override;
  278. // Extract close data
  279. Class Function GetCloseData(aBytes : TBytes; Out aReason : String) : Word;
  280. // Send close with message data
  281. procedure Close(aData : TBytes = Nil); overload;
  282. procedure Close(aMessage : UTF8String); overload;
  283. procedure Close(aMessage : UTF8String; aReason: word); overload;
  284. // Check incoming message
  285. function CheckIncoming(aTimeout: Integer; DoRead : Boolean = True): TIncomingResult;
  286. // read & process incoming message. Return nil if connection was close.
  287. function ReadMessage: Boolean;
  288. // Disconnect
  289. Procedure Disconnect;
  290. // Descendents can override this to provide custom frames
  291. Function FrameClass : TWSFrameClass; virtual;
  292. // Send raw frame. No checking is done !
  293. procedure Send(aFrame : TWSFrame); virtual;
  294. // Send message
  295. procedure Send(const AMessage: UTF8string);
  296. // Send binary data
  297. procedure Send(const ABytes: TBytes);
  298. // Send control frame. ftPing,ftPong,ftClose
  299. procedure Send(aFrameType: TFrameType; aData : TBytes = Nil);
  300. // Disconnect when status is set to csClosed;
  301. Property AutoDisconnect : Boolean Read FAutoDisconnect Write FAutoDisconnect;
  302. // Close frame handling
  303. Property CloseState : TCloseState Read FCloseState;
  304. // Connection ID, allocated during create
  305. Property ConnectionID : String Read FConnectionID;
  306. // If set to true, the owner data is freed when the connection is freed.
  307. Property FreeUserData : Boolean Read FFreeUserData Write FFreeUserData;
  308. // Request headers during handshake
  309. property HandshakeRequest: TWSHandShakeRequest read FHandshakeRequest;
  310. // Has handshake been completed ?
  311. property HandshakeCompleted: Boolean read GetHandshakeCompleted;
  312. // Options passed by server
  313. Property Options : TWSOptions Read FOptions;
  314. // Mask to use when sending frames. Set to nonzero value to send masked frames.
  315. Property OutgoingFrameMask : Integer Read FOutgoingFrameMask Write FOutgoingFrameMask;
  316. // Peer IP address
  317. property PeerIP: string read GetPeerIP;
  318. // Transport in use by this connection
  319. property Transport: IWSTransport read GetTransport;
  320. // User data to associate with this connection.
  321. Property UserData : TObject Read FUserData Write FUserData;
  322. // Socket version to check for
  323. Property WebSocketVersion : Integer Read FWebSocketVersion Write FWebSocketVersion;
  324. // Called when text/binary data was received
  325. property OnMessageReceived: TWSMessageEvent read FOnMessageReceived write FOnMessageReceived;
  326. // Called when Ping, Pong, Close control messages come in.
  327. property OnControl: TWSControlEvent read FOnControl write FOnControl;
  328. // Called when disconnect is called.
  329. property OnDisconnect: TNotifyEvent read FOnDisconnect write FOnDisconnect;
  330. end;
  331. { TWSClientTransport }
  332. TWSClientTransport = Class(TWSTransport)
  333. end;
  334. { TWSClientConnection }
  335. TWSClientConnection = Class(TWSConnection)
  336. private
  337. FTransport : TWSClientTransport;
  338. FHandshakeResponse: TWSHandShakeResponse;
  339. Protected
  340. function GetTransport : IWSTransport ; override;
  341. public
  342. Constructor Create(aOwner: TComponent; aTransport : TWSClientTransport; aOptions : TWSOptions); reintroduce; overload;
  343. Destructor Destroy; override;
  344. //
  345. function GetHandshakeCompleted: Boolean; override;
  346. // Owned by connection
  347. Property ClientTransport : TWSClientTransport Read FTransport;
  348. //
  349. Property HandShakeResponse : TWSHandShakeResponse Read FHandshakeResponse Write FHandshakeResponse;
  350. End;
  351. { TWSServerTransport }
  352. TWSServerTransport = class(TWSTransport)
  353. end;
  354. { TWSServerConnection }
  355. TWSConnectionHandshakeEvent = procedure (aRequest : TWSHandShakeRequest; aResponse : TWSHandShakeResponse) of object;
  356. TWSServerConnection = Class(TWSConnection)
  357. Private
  358. FExtraHeaders: TStrings;
  359. FHandshakeResponseSent: Boolean;
  360. FOnHandShake: TWSConnectionHandshakeEvent;
  361. FTransport : TWSServerTransport;
  362. Protected
  363. Procedure DoDisconnect; override;
  364. function GetTransport: IWSTransport; override;
  365. procedure DoPrepareHandshakeResponse(aRequest : TWSHandShakeRequest; aResponse : TWSHandShakeResponse); virtual;
  366. function GetHandshakeCompleted: Boolean; override;
  367. public
  368. // Transport is owned by connection
  369. constructor Create(aOwner : TComponent; aTransport : TWSServerTransport; aOptions : TWSOptions); overload;
  370. // disconnect
  371. destructor Destroy; override;
  372. // Do full circle.
  373. Procedure PerformHandshake; virtual;
  374. // Given a request, send response
  375. function DoHandshake(const aRequest : TWSHandShakeRequest): Boolean;
  376. // Has handshake been exchanged?
  377. property HandshakeResponseSent: Boolean read FHandshakeResponseSent;
  378. // Extra handshake headers
  379. Property ExtraHeaders : TStrings Read FExtraHeaders;
  380. // Owned by connection
  381. property ServerTransport : TWSServerTransport Read FTransport;
  382. // Called when exchanging handshake
  383. Property OnHandshake : TWSConnectionHandshakeEvent Read FOnHandShake write FOnHandshake;
  384. end;
  385. Type
  386. { TBytesHelper }
  387. TBytesHelper = Type helper for TBytes
  388. // No swapping of bytes
  389. Function ToDword(aOffset : Integer = 0) : DWORD;
  390. Function ToInt32(aOffset : Integer = 0) : LongInt;
  391. Function ToWord(aOffset : Integer = 0) : Word;
  392. Function ToQWord(aOffset : Integer = 0) : QWord;
  393. Procedure FromDword(const aData : DWORD; aOffset : Integer = 0);
  394. Procedure FromInt32(const aData : Longint; aOffset : Integer = 0);
  395. Procedure FromWord(const aData : Word; aOffset : Integer = 0);
  396. Procedure FromQWord(const aData : QWord; aOffset : Integer = 0);
  397. procedure Reverse(var Dest: TBytes; Offset: Integer; Size: Integer);
  398. Function Reverse(Offset: Integer; Size: Integer) : TBytes;
  399. Procedure Append(aData : TBytes);
  400. end;
  401. Resourcestring
  402. SErrNotSimpleOperation = 'Frame type %d is not a simple operation.';
  403. SErrCloseAlreadySent = 'Close message already sent, cannot send more data.';
  404. SErrHandshakeInComplete = 'Operation cannot be performed while the handshake is not completed';
  405. SErrConnectionActive = 'Operation cannot be performed while the websocket connection is active';
  406. SErrConnectionInActive = 'Operation cannot be performed while the websocket connection is not active';
  407. SErrServerActive = 'Operation cannot be performed while the websocket connection is active';
  408. SErrInvalidSizeFlag = 'Invalid size flag: %d';
  409. SErrInvalidFrameType = 'Invalid frame type flag: %d';
  410. function DecodeBytesBase64(const s: string; Strict: boolean = false) : TBytes;
  411. function EncodeBytesBase64(const aBytes : TBytes) : String;
  412. implementation
  413. uses strutils, sha1,base64;
  414. { TFrameTypeHelper }
  415. function TFrameTypeHelper.GetAsFlag: Byte;
  416. Const
  417. Flags : Array[TFrameType] of byte = (FlagContinuation,FlagText,FlagBinary,FlagClose,FlagPing,FlagPong,FlagReserved);
  418. begin
  419. Result:=Flags[Self];
  420. end;
  421. procedure TFrameTypeHelper.SetAsFlag(AValue: Byte);
  422. begin
  423. case aValue of
  424. FlagContinuation : Self:=ftContinuation;
  425. FlagText : Self:=ftText;
  426. FlagBinary : Self:=ftBinary;
  427. FlagClose : Self:=ftClose;
  428. FlagPing : Self:=ftPing;
  429. FlagPong : Self:=ftPong;
  430. else
  431. Self:=ftFutureOpcodes;
  432. //Raise EConvertError.CreateFmt(SErrInvalidFrameType,[aValue]);
  433. end;
  434. end;
  435. { TWSServerTransport }
  436. { TWSHandShakeResponse }
  437. constructor TWSHandShakeResponse.Create(const aResource: string; const aExtraHeaders: TStrings);
  438. begin
  439. inherited Create(aResource, aExtraHeaders);
  440. HTTPVersion:='1.1';
  441. StatusCode:=101;
  442. StatusText:='Switching Protocols';
  443. end;
  444. procedure TWSHandShakeResponse.ToStrings(aHandShake: TWSHandshakeRequest; aResponse: TStrings; AddStatusLine: Boolean);
  445. Function CalcKey : String;
  446. Var
  447. B : TBytes;
  448. hash : TSHA1Digest;
  449. K : string;
  450. begin
  451. // respond key
  452. b:=[];
  453. k:= Trim(aHandshake.Key) + SSecWebSocketGUID;
  454. hash:=sha1.SHA1String(k);
  455. SetLength(B,SizeOf(hash));
  456. Move(Hash,B[0],Length(B));
  457. Result:=EncodeBytesBase64(B);
  458. end;
  459. begin
  460. // Fill needed headers
  461. Upgrade:='websocket';
  462. Connection:='Upgrade';
  463. // Chrome doesn't like it if you send an empty protocol header.
  464. if (Protocol='') and (aHandshake.Protocol<>'') then
  465. Protocol:=aHandshake.Protocol;
  466. if Version='' then
  467. Version:=IntToStr(DefaultWebSocketVersion);
  468. if Accept='' then
  469. Accept:=CalcKey;
  470. if AddStatusLine then
  471. aResponse.Add('HTTP/%s %d %s',[HTTPVersion,StatusCode,StatusText]);
  472. aResponse.AddStrings(RawHeaders);
  473. end;
  474. { TWSTransport }
  475. function TWSTransport.GetSocket: TSocketStream;
  476. begin
  477. Result:=FHelper.Socket
  478. end;
  479. constructor TWSTransport.Create(aStream : TSocketStream);
  480. begin
  481. FStream:=aStream;
  482. FHelper:=TWSSocketHelper.Create(FStream);
  483. end;
  484. destructor TWSTransport.Destroy;
  485. begin
  486. FreeAndNil(FHelper);
  487. FreeAndNil(FStream);
  488. inherited Destroy;
  489. end;
  490. procedure TWSTransport.CloseSocket;
  491. begin
  492. sockets.CloseSocket(FStream.Handle);
  493. end;
  494. { TWSTransport }
  495. constructor TWSSocketHelper.Create(aSocket: TSocketStream);
  496. begin
  497. FSocket:=aSocket;
  498. {$if defined(FreeBSD) or defined(Linux)}
  499. FSocket.ReadFlags:=MSG_NOSIGNAL;
  500. FSocket.WriteFlags:=MSG_NOSIGNAL;
  501. {$endif}
  502. end;
  503. function TWSSocketHelper.CanRead(aTimeOut: Integer): Boolean;
  504. begin
  505. Result:=FSocket.CanRead(aTimeout);
  506. end;
  507. function TWSSocketHelper.PeerIP: string;
  508. Function SocketAddrToString(ASocketAddr: TSockAddr): String;
  509. begin
  510. if ASocketAddr.sa_family = AF_INET then
  511. Result := NetAddrToStr(ASocketAddr.sin_addr)
  512. else // no ipv6 support yet
  513. Result := '';
  514. end;
  515. begin
  516. Result:= SocketAddrToString(FSocket.RemoteAddress);
  517. end;
  518. function TWSSocketHelper.ReadLn: String;
  519. Var
  520. C : Byte;
  521. aSize : integer;
  522. begin
  523. // Preset
  524. Result:='';
  525. SetLength(Result,255);
  526. aSize:=0;
  527. C:=0;
  528. While (FSocket.Read(C,1)=1) and (C<>10) do
  529. begin
  530. Inc(aSize);
  531. if aSize>Length(Result) then
  532. SetLength(Result,Length(Result)+255);
  533. Result[aSize]:=AnsiChar(C);
  534. end;
  535. if (aSize>0) and (Result[aSize]=#13) then
  536. Dec(aSize);
  537. SetLength(Result,aSize);
  538. end;
  539. function TWSSocketHelper.ReadBytes(var aBytes: TBytes; aCount: Integer): Integer;
  540. var
  541. buf: TBytes;
  542. aPos, toRead: QWord;
  543. begin
  544. aPos := 0;
  545. SetLength(aBytes, aCount);
  546. repeat
  547. SetLength(buf, aCount);
  548. Result := FSocket.ReadData(buf, aCount - aPos);
  549. if Result = -1 then
  550. break;
  551. SetLength(buf, Result);
  552. Move(buf[0], aBytes[aPos], Result);
  553. Inc(aPos, Result);
  554. ToRead := aCount - aPos;
  555. Result := aCount;
  556. until toRead <= 0;
  557. end;
  558. procedure TWSSocketHelper.ReadBuffer(aBytes: TBytes);
  559. begin
  560. FSocket.ReadBuffer(aBytes,Length(ABytes));
  561. end;
  562. function TWSSocketHelper.WriteBytes(aBytes: TBytes; aCount: Integer): Integer;
  563. begin
  564. Result:=FSocket.WriteData(aBytes,aCount);
  565. end;
  566. procedure TWSSocketHelper.WriteBuffer(aBytes: TBytes);
  567. begin
  568. FSocket.WriteBuffer(aBytes,0,Length(aBytes));
  569. end;
  570. { TWSMessage }
  571. function TWSMessage.GetAsString: UTF8String;
  572. begin
  573. Result:=TEncoding.UTF8.GetAnsiString(Payload);
  574. end;
  575. function TWSMessage.GetAsUnicodeString: UnicodeString;
  576. begin
  577. Result:=UTF8Decode(asUTF8String);
  578. end;
  579. { TBytesHelper }
  580. function TBytesHelper.Reverse(Offset: Integer; Size: Integer): TBytes;
  581. begin
  582. Result:=[];
  583. Reverse(Result,Offset,Size);
  584. end;
  585. procedure TBytesHelper.Append(aData: TBytes);
  586. Var
  587. sLen,dLen : SizeInt;
  588. begin
  589. sLen:=Length(Self);
  590. dLen:=Length(aData);
  591. if dLen>0 then
  592. begin
  593. SetLength(Self,dLen+sLen);
  594. Move(aData[0],Self[sLen],dLen);
  595. end;
  596. end;
  597. procedure TBytesHelper.Reverse(var Dest: TBytes; Offset: Integer; Size: Integer);
  598. var
  599. I: Integer;
  600. begin
  601. SetLength(dest, Size);
  602. for I := 0 to Size - 1 do
  603. Dest[Size-1-I]:=Self[Offset+I];
  604. end;
  605. function TBytesHelper.ToInt32(aOffset: Integer = 0): LongInt;
  606. begin
  607. Result:=0;
  608. Move(Self[aOffSet],Result,SizeOf(LongInt));
  609. end;
  610. function TBytesHelper.ToDword(aOffset: Integer): DWORD;
  611. begin
  612. Result:=0;
  613. Move(Self[aOffSet],Result,SizeOf(DWORD));
  614. end;
  615. function TBytesHelper.ToWord(aOffset: Integer): Word;
  616. begin
  617. Result:=0;
  618. Move(Self[aOffSet],Result,SizeOf(Word));
  619. end;
  620. function TBytesHelper.ToQWord(aOffset: Integer): QWord;
  621. begin
  622. Result:=0;
  623. Move(Self[aOffSet],Result,SizeOf(QWord));
  624. end;
  625. procedure TBytesHelper.FromDword(const aData: DWORD; aOffset: Integer);
  626. begin
  627. Move(aData, Self[aOffSet],SizeOf(DWORD));
  628. end;
  629. procedure TBytesHelper.FromInt32(const aData: Longint; aOffset: Integer);
  630. begin
  631. Move(aData, Self[aOffSet],SizeOf(Longint));
  632. end;
  633. procedure TBytesHelper.FromWord(const aData: Word; aOffset: Integer = 0);
  634. begin
  635. Move(aData, Self[aOffSet],SizeOf(Word));
  636. end;
  637. procedure TBytesHelper.FromQWord(const aData: QWord; aOffset: Integer);
  638. begin
  639. Move(aData, Self[aOffSet],SizeOf(QWord));
  640. end;
  641. Function HToNx(Host: QWord) : QWord;
  642. begin
  643. {$ifdef FPC_BIG_ENDIAN}
  644. htonx:=host;
  645. {$else}
  646. htonx:=SwapEndian(host);
  647. {$endif}
  648. end;
  649. Function NToHx(Net: QWord) : QWord;
  650. begin
  651. {$ifdef FPC_BIG_ENDIAN}
  652. ntohx:=Net;
  653. {$else}
  654. ntohx:=SwapEndian(Net);
  655. {$endif}
  656. end;
  657. function EncodeBytesBase64(const aBytes : TBytes) : String;
  658. var
  659. OutStream : TStringStream;
  660. Encoder : TBase64EncodingStream;
  661. begin
  662. if Length(aBytes)=0 then
  663. Exit('');
  664. Encoder:=Nil;
  665. OutStream:=TStringStream.Create('');
  666. try
  667. Encoder:=TBase64EncodingStream.create(OutStream);
  668. Encoder.WriteBuffer(aBytes,0,Length(aBytes));
  669. Encoder.Flush;
  670. Result:=OutStream.DataString;
  671. finally
  672. Encoder.Free;
  673. OutStream.free;
  674. end;
  675. end;
  676. function DecodeBytesBase64(const s: string; Strict: boolean = false) : TBytes;
  677. Const
  678. StrictModes : Array[Boolean] of TBase64DecodingMode = (bdmMime,bdmStrict);
  679. var
  680. missing : Integer;
  681. SD : String;
  682. Instream,
  683. Outstream : TBytesStream;
  684. Decoder : TBase64DecodingStream;
  685. begin
  686. Result:=[];
  687. if Length(s)=0 then
  688. Exit;
  689. SD:=S;
  690. Missing:=Length(Sd) mod 4;
  691. if Missing>0 then
  692. SD:=SD+StringOfChar('=',Missing);
  693. Outstream:=Nil;
  694. Decoder:=Nil;
  695. Instream:=TStringStream.Create(SD);
  696. try
  697. Outstream:=TBytesStream.Create(Nil);
  698. Decoder:=TBase64DecodingStream.Create(Instream,StrictModes[Strict]);
  699. Outstream.CopyFrom(Decoder,Decoder.Size);
  700. Result:=Outstream.Bytes;
  701. finally
  702. Decoder.Free;
  703. Outstream.Free;
  704. Instream.Free;
  705. end;
  706. end;
  707. { TWSFramePayload }
  708. procedure TWSFramePayload.ReadData(var Content: TBytes; aTransport: IWSTransport);
  709. Const
  710. MaxBufSize = 32*1024;
  711. Var
  712. Buf : TBytes;
  713. aPos,toRead : QWord;
  714. aCount : Longint;
  715. begin
  716. Buf:=[];
  717. ToRead:=DataLength;
  718. aPos:=0;
  719. Repeat
  720. aCount:=ToRead;
  721. if aCount>MaxBufSize then
  722. aCount:=MaxBufSize;
  723. SetLength(Buf,aCount);
  724. aCount := aTransport.ReadBytes(Buf,aCount);
  725. Move(Buf[0],Content[aPos],aCount);
  726. Inc(aPos,aCount);
  727. ToRead:=DataLength-aPos;
  728. Until (ToRead<=0);
  729. end;
  730. procedure TWSFramePayload.Read(buffer: TBytes; aTransport: IWSTransport);
  731. Var
  732. LenFlag : Byte;
  733. paylen16 : Word;
  734. content: TBytes;
  735. begin
  736. content:=[];
  737. Masked := ((buffer[1] and FlagMasked) <> 0);
  738. LenFlag := buffer[1] and FlagLengthMask;
  739. Case LenFlag of
  740. FlagTwoBytes:
  741. begin
  742. aTransport.ReadBytes(Buffer,2);
  743. Paylen16:=Buffer.ToWord(0);
  744. DataLength := ntohs(PayLen16);
  745. end;
  746. FlagEightBytes:
  747. begin
  748. aTransport.ReadBytes(Buffer,8);
  749. DataLength:=Buffer.ToQWord(0);
  750. DataLength := ntohx(DataLength); // MVC : Needs fixing
  751. end
  752. else
  753. DataLength:=lenFlag;
  754. end;
  755. if Masked then
  756. begin
  757. // In some times, not 4 bytes are returned
  758. aTransport.ReadBytes(Buffer,4);
  759. MaskKey:=buffer.ToDword(0);
  760. end;
  761. SetLength(content, DataLength);
  762. if (DataLength>0) then
  763. begin
  764. ReadData(Content,aTransport);
  765. if Masked then
  766. DoMask(Content, MaskKey);
  767. Data := content;
  768. end;
  769. end;
  770. { TWSFrame }
  771. constructor TWSFrame.Create(aType: TFrameType; aIsFinal: Boolean; APayload: TBytes; aMask: Integer=0);
  772. begin
  773. Create(aType,aIsFinal,aMask);
  774. FPayload.Data := APayload;
  775. if Assigned(aPayload) then
  776. FPayload.DataLength := Cardinal(Length(aPayload));
  777. end;
  778. constructor TWSFrame.Create(aType: TFrameType; aIsFinal : Boolean = True; aMask: Integer=0);
  779. begin
  780. FPayload:=Default(TWSFramePayload);
  781. FPayload.MaskKey:=aMask;
  782. FPayload.Masked:=aMask<>0;
  783. FFrameType := aType;
  784. FFinalFrame := AIsFinal;
  785. end;
  786. constructor TWSFrame.Create(const aMessage: UTF8String; aMask: Integer=0);
  787. Var
  788. Data : TBytes;
  789. begin
  790. Data:=TEncoding.UTF8.GetAnsiBytes(AMessage);
  791. Create(ftText,True,Data,aMask);
  792. end;
  793. class function TWSFrame.CreateFromStream(aTransport : IWSTransport): TWSFrame;
  794. begin
  795. Result:=TWSFrame.Create;
  796. try
  797. if not Result.Read(aTransport) then
  798. FreeAndNil(Result);
  799. except
  800. FreeAndNil(Result);
  801. Raise;
  802. end;
  803. end;
  804. function TWSFrame.Read(aTransport: IWSTransport): boolean;
  805. Var
  806. Buffer : Tbytes;
  807. B1 : Byte;
  808. begin
  809. Result:=False;
  810. Buffer:=Default(TBytes);
  811. SetLength(Buffer,2);
  812. if aTransport.ReadBytes(Buffer,2)=0 then
  813. Exit;
  814. if Length(Buffer)<2 then
  815. Raise EWebSocket.Create('Could not read frame header');
  816. B1:=buffer[0];
  817. FFinalFrame:=(B1 and FlagFinalFrame) = FlagFinalFrame;
  818. FRSV:=(B1 and %01110000) shr 4;
  819. FFrameType.AsFlag:=(B1 and $F);
  820. FPayload.Read(Buffer,aTransport);
  821. Result:=True;
  822. end;
  823. function TWSFrame.GetAsBytes: TBytes;
  824. var
  825. LenByte,firstByte: Byte;
  826. buffer, LengthBytes: TBytes;
  827. aOffSet, I : Integer;
  828. pLen16 : Word;
  829. pLen64 : QWord;
  830. begin
  831. Result:=Nil;
  832. firstByte := FrameType.AsFlag;
  833. if FinalFrame then
  834. firstByte := firstByte or FlagFinalFrame;
  835. if FPayload.DataLength < FlagTwoBytes then
  836. begin
  837. aOffSet:=2;
  838. LenByte := Byte(FPayload.DataLength);
  839. LengthBytes:=[];
  840. end
  841. else if Payload.DataLength < (1 shl 16) then
  842. begin
  843. aOffset:=4;
  844. LenByte := FlagTwoBytes;
  845. plen16:=Payload.DataLength;
  846. SetLength(LengthBytes, SizeOf(Word));
  847. LengthBytes.FromWord(HToNs(pLen16));
  848. end
  849. else
  850. begin
  851. aOffset:=10;
  852. LenByte:=FlagEightBytes;
  853. plen64:=Payload.DataLength;
  854. SetLength(LengthBytes, Sizeof(UInt64));
  855. LengthBytes.FromQWord(HToNx(Plen64));
  856. end;
  857. Buffer:=[];
  858. if FPayload.Masked then
  859. begin
  860. lenByte:=Lenbyte or FlagMasked;
  861. aoffSet:=aOffSet+4;
  862. end;
  863. SetLength(buffer,aOffset+Int64(FPayload.DataLength));
  864. buffer[0] := firstByte;
  865. buffer[1] := LenByte;
  866. for I := 0 to Length(LengthBytes)-1 do
  867. buffer[2 + I] := LengthBytes[I];
  868. if Payload.Masked then
  869. begin
  870. Buffer.FromInt32(Payload.MaskKey,aOffSet-4);
  871. TWSFramePayload.CopyMasked(Payload.Data,Buffer,Payload.MaskKey,aOffset);
  872. end
  873. else
  874. if Payload.DataLength > 0 then
  875. move(Payload.Data[0], buffer[aOffset], Payload.DataLength);
  876. Result := Buffer;
  877. end;
  878. class procedure TWSFramePayload.DoMask(var aData: TBytes; Key: DWORD);
  879. begin
  880. CopyMasked(aData,aData,Key,0)
  881. end;
  882. class procedure TWSFramePayload.CopyMasked(SrcData: TBytes; var DestData: TBytes; Key: DWORD; aOffset: Integer);
  883. var
  884. currentMaskIndex: Longint;
  885. byteKeys: TBytes;
  886. I: Longint;
  887. begin
  888. CurrentMaskIndex := 0;
  889. byteKeys:=[];
  890. SetLength(byteKeys, SizeOf(Key));
  891. ByteKeys.FromDword(Key);
  892. for I := 0 to Length(SrcData) - 1 do
  893. begin
  894. DestData[I+aOffset] := SrcData[I] XOR byteKeys[currentMaskIndex];
  895. currentMaskIndex := (currentMaskIndex + 1) mod 4;
  896. end;
  897. end;
  898. class function TWSFramePayload.CopyMasked(SrcData: TBytes; Key: DWORD): TBytes;
  899. begin
  900. Result:=[];
  901. SetLength(Result,Length(SrcData));
  902. CopyMasked(SrcData,Result,Key,0)
  903. end;
  904. { TWSRequest }
  905. function TWSHeaders.GetS(aIdx: Integer): String;
  906. begin
  907. Result:=GetH(WSHeaderNames[aIdx]);
  908. end;
  909. procedure TWSHeaders.SetS(AIndex: Integer; const AValue: string);
  910. begin
  911. SetH(WSHeaderNames[aIndex],aValue);
  912. end;
  913. function TWSHeaders.GetH(const aName: string): String;
  914. begin
  915. Result:=Trim(FRawHeaders.Values[aName]);
  916. end;
  917. Procedure TWSHeaders.SetH(const aName,aValue: string);
  918. begin
  919. FRawHeaders.Values[aName]:=' '+aValue;
  920. end;
  921. constructor TWSHeaders.Create(Const aResource : String; const AHeaderList: TStrings);
  922. var
  923. I : Integer;
  924. N,V : String;
  925. begin
  926. FResource:=aResource;
  927. FRawHeaders:=TStringList.Create;
  928. FRawHeaders.NameValueSeparator:=':';
  929. if Assigned(aHeaderList) then
  930. for I:=0 to aHeaderList.Count-1 do
  931. begin
  932. aHeaderList.GetNameValue(I,N,V);
  933. if (N<>'') and (V<>'') then
  934. FRawHeaders.Add(N+': '+Trim(V));
  935. end;
  936. end;
  937. destructor TWSHeaders.Destroy;
  938. begin
  939. FreeAndNil(FRawHeaders);
  940. inherited;
  941. end;
  942. { TWSConnection }
  943. procedure TWSConnection.Send(aFrameType : TFrameType; aData : TBytes = Nil);
  944. Var
  945. aFrame : TWSFrame;
  946. begin
  947. if not (aFrameType in [ftClose,ftPing,ftPong]) then
  948. Raise EWebSocket.CreateFmt(SErrNotSimpleOperation,[Ord(aFrameType)]);
  949. aFrame:=FrameClass.Create(aFrameType,True,aData);
  950. try
  951. Send(aFrame);
  952. finally
  953. aFrame.Free;
  954. end;
  955. end;
  956. procedure TWSConnection.SetHandShakeRequest(aRequest: TWSHandshakeRequest);
  957. begin
  958. FreeAndNil(FHandshakeRequest);
  959. FHandShakeRequest:=aRequest;
  960. end;
  961. constructor TWSConnection.Create(aOwner : TComponent; aOptions: TWSOptions);
  962. begin
  963. FOwner:=aOwner;
  964. Foptions:=aOptions;
  965. FWebSocketVersion:=WebSocketVersion;
  966. AllocateConnectionID;
  967. end;
  968. destructor TWSConnection.Destroy;
  969. begin
  970. FreeAndNil(FHandshakeRequest);
  971. If FreeUserData then
  972. FreeAndNil(FUserData);
  973. inherited;
  974. end;
  975. class function TWSConnection.GetCloseData(aBytes: TBytes; out aReason: String): Word;
  976. begin
  977. Result:=0;
  978. aReason:='';
  979. if Length(aBytes)>1 then
  980. Result:=NToHs(aBytes.ToWord(0));
  981. if Length(aBytes)>2 then
  982. aReason:=TEncoding.UTF8.GetAnsiString(aBytes,2,Length(aBytes)-2);
  983. end;
  984. function TWSConnection.GetPeerIP: String;
  985. Var
  986. S : IWSTransport;
  987. begin
  988. S:=Transport;
  989. if Assigned(S) then
  990. Result:=S.PeerIP
  991. else
  992. Result:=''
  993. end;
  994. procedure TWSConnection.AllocateConnectionID;
  995. begin
  996. if Assigned(IDAllocator) then
  997. IDAllocator(FConnectionID);
  998. if FConnectionID='' then
  999. {$IFDEF CPU64}
  1000. FConnectionID:=IntToStr(InterlockedIncrement64(_ConnectionCount));
  1001. {$ELSE}
  1002. FConnectionID:=IntToStr(InterlockedIncrement(_ConnectionCount));
  1003. {$ENDIF}
  1004. end;
  1005. procedure TWSConnection.SetCloseState(aValue: TCloseState);
  1006. begin
  1007. FCloseState:=aValue;
  1008. if (FCloseState=csClosed) and autoDisconnect then
  1009. Disconnect;
  1010. end;
  1011. function TWSConnection.ReadMessage: Boolean;
  1012. begin
  1013. Result:=DoReadMessage;
  1014. end;
  1015. procedure TWSConnection.DispatchEvent(aInitialType : TFrameType; aFrame : TWSFrame);
  1016. Var
  1017. msg: TWSMessage;
  1018. begin
  1019. Case aInitialType of
  1020. ftPing,
  1021. ftPong,
  1022. ftClose :
  1023. begin
  1024. If Assigned(FOnControl) then
  1025. FOnControl(Self,aInitialType,FMessageContent);
  1026. FMessageContent:=[];
  1027. end;
  1028. ftBinary,
  1029. ftText :
  1030. begin
  1031. if Assigned(FOnMessageReceived) then
  1032. begin
  1033. Msg:=Default(TWSMessage);
  1034. Msg.IsText:=(aInitialType=ftText);
  1035. if aFrame.FrameType=ftBinary then
  1036. Msg.Sequences:=[fsFirst]
  1037. else
  1038. Msg.Sequences:=[fsContinuation];
  1039. if aFrame.FinalFrame then
  1040. Msg.Sequences:=Msg.Sequences+[fsLast];
  1041. Msg.PayLoad:=FMessageContent;
  1042. FOnMessageReceived(Self, Msg);
  1043. end;
  1044. FMessageContent:=[];
  1045. end;
  1046. ftContinuation: ; // Cannot happen normally
  1047. end;
  1048. end;
  1049. Function TWSConnection.HandleIncoming(aFrame : TWSFrame) : Boolean;
  1050. Procedure UpdateCloseState;
  1051. begin
  1052. if (FCloseState=csNone) then
  1053. FCloseState:=csReceived
  1054. else if (FCloseState=csSent) then
  1055. FCloseState:=csClosed;
  1056. end;
  1057. begin
  1058. Result:=True;
  1059. // check Reserved bits
  1060. if aFrame.Reserved<>0 then
  1061. begin
  1062. Close('', CLOSE_PROTOCOL_ERROR);
  1063. UpdateCloseState;
  1064. Result:=false;
  1065. Exit;
  1066. end;
  1067. // check Reserved opcode
  1068. if aFrame.FrameType = ftFutureOpcodes then
  1069. begin
  1070. Close('', CLOSE_PROTOCOL_ERROR);
  1071. UpdateCloseState;
  1072. Result:=false;
  1073. Exit;
  1074. end;
  1075. { If control frame it must be complete }
  1076. if ((aFrame.FrameType=ftPing) or
  1077. (aFrame.FrameType=ftPong) or
  1078. (aFrame.FrameType=ftClose) or
  1079. (aFrame.FrameType=ftContinuation))
  1080. and (not aFrame.FinalFrame) then
  1081. begin
  1082. Close('', CLOSE_PROTOCOL_ERROR);
  1083. UpdateCloseState;
  1084. Result:=false;
  1085. Exit;
  1086. end;
  1087. // here we handle payload.
  1088. if aFrame.FrameType<>ftContinuation then
  1089. FInitialOpcode:=aFrame.FrameType;
  1090. if aFrame.FrameType in [ftPong,ftBinary,ftText,ftPing] then
  1091. FMessageContent:=aFrame.Payload.Data;
  1092. // Special handling
  1093. Case aFrame.FrameType of
  1094. ftContinuation:
  1095. FMessageContent.Append(aFrame.Payload.Data);
  1096. ftPing:
  1097. begin
  1098. if aFrame.Payload.DataLength > 125 then
  1099. Close('', CLOSE_PROTOCOL_ERROR)
  1100. else
  1101. if not (woPongExplicit in Options) then
  1102. Send(ftPong,aFrame.Payload.Data);
  1103. end;
  1104. ftClose:
  1105. begin
  1106. // If our side sent the initial close, this is the reply, and we must disconnect (Result=false).
  1107. Result:=FCloseState=csNone;
  1108. if Result then
  1109. begin
  1110. FMessageContent:=aFrame.Payload.Data;
  1111. if not (woCloseExplicit in Options) then
  1112. begin
  1113. Close('', CLOSE_NORMAL_CLOSURE); // Will update state
  1114. Result:=False; // We can disconnect.
  1115. end
  1116. else
  1117. UpdateCloseState
  1118. end
  1119. else
  1120. UpdateCloseState;
  1121. end;
  1122. else
  1123. ; // avoid Compiler warning
  1124. End;
  1125. if (aFrame.FinalFrame) or (woIndividualFrames in Options) then
  1126. DispatchEvent(FInitialOpcode,aFrame);
  1127. end;
  1128. function TWSConnection.FrameClass: TWSFrameClass;
  1129. begin
  1130. Result:=TWSFrame;
  1131. end;
  1132. procedure TWSConnection.Send(const AMessage: UTF8String);
  1133. var
  1134. aFrame: TWSFrame;
  1135. begin
  1136. aFrame:=FrameClass.Create(aMessage);
  1137. try
  1138. Send(aFrame);
  1139. finally
  1140. aFrame.Free;
  1141. end;
  1142. end;
  1143. procedure TWSConnection.Send(const ABytes: TBytes);
  1144. var
  1145. aFrame: TWSFrame;
  1146. begin
  1147. aFrame:=FrameClass.Create(ftBinary,True,ABytes);
  1148. try
  1149. Send(aFrame);
  1150. finally
  1151. aFrame.Free;
  1152. end;
  1153. end;
  1154. procedure TWSConnection.Close(aMessage: UTF8String);
  1155. begin
  1156. Close(aMessage, CLOSE_NORMAL_CLOSURE);
  1157. end;
  1158. procedure TWSConnection.Close(aMessage: UTF8String; aReason: word);
  1159. var
  1160. aData: TBytes;
  1161. aSize: Integer;
  1162. begin
  1163. // first two bytes is reason of close RFC 6455 section-5.5.1
  1164. aData := TEncoding.UTF8.GetAnsiBytes(aMessage);
  1165. aSize := Length(aData);
  1166. SetLength(aData, aSize + 2);
  1167. if aSize > 0 then
  1168. move(aData[0], aData[2], aSize);
  1169. aData[0] := (aReason and $FF00) shr 8;
  1170. aData[1] := aReason and $FF;
  1171. Close(aData);
  1172. end;
  1173. procedure TWSConnection.Disconnect;
  1174. begin
  1175. DoDisconnect;
  1176. end;
  1177. procedure TWSConnection.Close(aData: TBytes);
  1178. begin
  1179. Send(ftClose,aData);
  1180. end;
  1181. procedure TWSConnection.Send(aFrame: TWSFrame);
  1182. Var
  1183. Data : TBytes;
  1184. begin
  1185. if FCloseState=csClosed then
  1186. Raise EWebSocket.Create(SErrCloseAlreadySent);
  1187. Data:=aFrame.AsBytes;
  1188. Transport.WriteBytes(Data,Length(Data));
  1189. if (aFrame.FrameType=ftClose) then
  1190. begin
  1191. if FCloseState=csNone then
  1192. FCloseState:=csSent
  1193. else if FCloseState=csReceived then
  1194. FCloseState:=csClosed;
  1195. end;
  1196. end;
  1197. Function TWSConnection.DoReadMessage : Boolean ;
  1198. Var
  1199. F : TWSFrame;
  1200. begin
  1201. Result:=False;
  1202. If not Transport.CanRead(0) then
  1203. Exit;
  1204. f:=FrameClass.CreateFromStream(Transport);
  1205. try
  1206. if Assigned(F) then
  1207. Result:=HandleIncoming(F)
  1208. finally
  1209. F.Free;
  1210. end;
  1211. end;
  1212. function TWSConnection.CheckIncoming(aTimeout: Integer; DoRead: Boolean = True): TIncomingResult;
  1213. begin
  1214. if not Transport.CanRead(aTimeOut) then
  1215. Result:=irNone
  1216. else if Not DoRead then
  1217. Result:=irWaiting
  1218. else if ReadMessage then
  1219. Result:=irOK
  1220. else
  1221. Result:=irClose
  1222. end;
  1223. constructor TWSClientConnection.Create(aOwner: TComponent; aTransport: TWSClientTransport; aOptions : TWSOptions);
  1224. begin
  1225. Inherited Create(aOwner,aOptions);
  1226. FTransport:=aTransport;
  1227. end;
  1228. destructor TWSClientConnection.Destroy;
  1229. begin
  1230. FreeAndNil(FTransport);
  1231. inherited;
  1232. end;
  1233. function TWSClientConnection.GetHandshakeCompleted: Boolean;
  1234. begin
  1235. Result:=Assigned(FHandshakeResponse);
  1236. end;
  1237. function TWSClientConnection.GetTransport: IWSTransport;
  1238. begin
  1239. Result:=FTransport;
  1240. end;
  1241. { TWSHandShakeRequest }
  1242. Class Function TWSHandShakeRequest.GenerateKey : String;
  1243. Var
  1244. I : Integer;
  1245. B : TBytes;
  1246. begin
  1247. B:=[];
  1248. SetLength(B,16);
  1249. For I:=0 to 15 do
  1250. B[i]:=Random(256);
  1251. Result:=EncodeBytesBase64(B);
  1252. end;
  1253. constructor TWSHandShakeRequest.Create(const aResource: string; const aExtraHeaders: TStrings);
  1254. begin
  1255. Inherited Create(aResource,aExtraHeaders);
  1256. Version:=IntToStr(DefaultWebSocketVersion);
  1257. end;
  1258. procedure TWSHandShakeRequest.ToStrings(aHeaders: TStrings);
  1259. procedure Add(const AName, aValue, aDefault: String);
  1260. Var
  1261. V : String;
  1262. begin
  1263. V:=aValue;
  1264. if V='' then
  1265. V:=aDefault;
  1266. if V<>'' then
  1267. aHeaders.Add(aName+': '+V)
  1268. end;
  1269. Var
  1270. N,V : String;
  1271. I : Integer;
  1272. begin
  1273. aHeaders.Clear;
  1274. if Resource='' then
  1275. Resource:='/';
  1276. aHeaders.Add('GET ' + Resource + ' HTTP/1.1');
  1277. V:=Host;
  1278. if (V<>'') and (Port<>443) and (Port<>80) then
  1279. V:=V+':'+IntToStr(Port);
  1280. Add('Host',V,'');
  1281. Add('Upgrade',Upgrade,'websocket');
  1282. Add('Connection',Connection,'Upgrade');
  1283. Add('Origin',Origin,Host);
  1284. if Key='' then
  1285. Key:=GenerateKey;
  1286. Add('Sec-WebSocket-Key',Key,'');
  1287. Add('Sec-WebSocket-Protocol',Protocol,'');
  1288. Add('Sec-WebSocket-Version',Version,'');
  1289. For I:=0 to RawHeaders.Count-1 do
  1290. begin
  1291. RawHeaders.GetNameValue(I,N,V);
  1292. if (N<>'') and (V<>'') then
  1293. if (aHeaders.Values[N]='') then
  1294. Add(N,V,'')
  1295. end;
  1296. end;
  1297. { TWSServerConnection }
  1298. constructor TWSServerConnection.Create(aOwner : TComponent; aTransport : TWSServerTransport; aOptions : TWSOptions);
  1299. begin
  1300. Inherited Create(aOwner,aOptions);
  1301. FHandshakeResponseSent := False;
  1302. FTransport:=aTransport;
  1303. FExtraHeaders:=TStringList.Create;
  1304. FExtraHeaders.NameValueSeparator:=':';
  1305. end;
  1306. destructor TWSServerConnection.Destroy;
  1307. begin
  1308. DisConnect;
  1309. inherited;
  1310. end;
  1311. procedure TWSServerConnection.PerformHandshake;
  1312. Var
  1313. Headers : TStrings;
  1314. aResource,Status,aLine : String;
  1315. HSR : TWSHandShakeRequest;
  1316. begin
  1317. Status:=Transport.ReadLn;
  1318. aResource:=ExtractWord(2,Status,[' ']);
  1319. HSR:=Nil;
  1320. Headers:=TStringList.Create;
  1321. try
  1322. Headers.NameValueSeparator:=':';
  1323. aLine:=Transport.ReadLn;
  1324. While aLine<>'' do
  1325. begin
  1326. Headers.Add(aLine);
  1327. aLine:=Transport.ReadLn;
  1328. end;
  1329. HSR:=TWSHandShakeRequest.Create(aResource,Headers);
  1330. FHandshakeResponseSent:=DoHandshake(HSR);
  1331. finally
  1332. HSR.Free;
  1333. Headers.Free;
  1334. end;
  1335. end;
  1336. function TWSServerConnection.GetHandshakeCompleted: Boolean;
  1337. begin
  1338. Result:=HandshakeResponseSent;
  1339. end;
  1340. procedure TWSServerConnection.DoDisconnect;
  1341. begin
  1342. if Assigned(FTransport) then
  1343. FTransport.CloseSocket;
  1344. FreeAndNil(FTransPort);
  1345. end;
  1346. function TWSServerConnection.GetTransport: IWSTransport;
  1347. begin
  1348. Result:=FTransport;
  1349. end;
  1350. procedure TWSServerConnection.DoPrepareHandshakeResponse(aRequest: TWSHandShakeRequest; aResponse: TWSHandShakeResponse);
  1351. begin
  1352. If Assigned(OnHandshake) then
  1353. OnHandShake(aRequest,aResponse);
  1354. end;
  1355. function TWSServerConnection.DoHandshake(const aRequest : TWSHandShakeRequest) : Boolean;
  1356. var
  1357. aLine,Reply : string;
  1358. aResp : TWSHandShakeResponse;
  1359. H : TStrings;
  1360. B : TBytes;
  1361. begin
  1362. Result:=False;
  1363. H:=Nil;
  1364. aResp:=TWSHandShakeResponse.Create('',FExtraHeaders);
  1365. try
  1366. DoPrepareHandshakeResponse(aRequest,aResp);
  1367. try
  1368. H:=TStringList.Create;
  1369. aResp.ToStrings(aRequest,H,True);
  1370. Reply:='';
  1371. For aLine in H do
  1372. Reply:=Reply+aLine+#13#10;
  1373. Reply:=Reply+#13#10;
  1374. B:=TEncoding.UTF8.GetAnsiBytes(Reply);
  1375. Transport.WriteBytes(B,Length(B));
  1376. Result:=True;
  1377. FHandshakeResponseSent:=True;
  1378. except
  1379. on E: Exception do
  1380. begin
  1381. // Close the connection if the handshake failed
  1382. Disconnect;
  1383. end;
  1384. end;
  1385. finally
  1386. H.Free;
  1387. aResp.Free;
  1388. end;
  1389. end;
  1390. end.