fpwebsocket.pp 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875
  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. { IWSTransport }
  135. IWSTransport = Interface
  136. // Check if transport can read data
  137. Function CanRead(aTimeOut: Integer) : Boolean;
  138. // Read length of buffer bytes. Raise exception if no data read
  139. Procedure ReadBuffer (aBytes : TBytes);
  140. // Read at most aCount bytes into buffer. Return number of bytes actually read, set length of buffer to actually read
  141. function ReadBytes (var aBytes : TBytes; aCount : Integer) : Integer;
  142. // Write at most aCount bytes.
  143. function WriteBytes (aBytes : TBytes; aCount : Integer) : Integer;
  144. // Write complete buffer. Raise exception if not all bytes were written.
  145. Procedure WriteBuffer (aBytes : TBytes);
  146. function ReadLn : String;
  147. function PeerIP: string;
  148. function PeerPort: word;
  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 PeerPort: word; virtual;
  159. function ReadLn : String; virtual;
  160. function ReadBytes (var aBytes : TBytes; aCount : Integer) : Integer; virtual;
  161. Procedure ReadBuffer (aBytes : TBytes); virtual;
  162. function WriteBytes (aBytes : TBytes; aCount : Integer) : Integer; virtual;
  163. Procedure WriteBuffer (aBytes : TBytes);
  164. Property Socket : TSocketStream Read FSocket;
  165. end;
  166. TWSTransport = class(TObject, IWSTransport)
  167. Private
  168. FHelper : TWSSocketHelper;
  169. FStream : TSocketStream;
  170. function GetSocket: TSocketStream;
  171. Public
  172. Constructor Create(aStream : TSocketStream);
  173. Destructor Destroy; override;
  174. Procedure CloseSocket;
  175. Property Helper : TWSSocketHelper Read FHelper Implements IWSTransport;
  176. Property Socket : TSocketStream Read GetSocket;
  177. end;
  178. { TWSFramePayload }
  179. TWSFramePayload = record
  180. DataLength: QWord;
  181. // Data is unmasked
  182. Data: TBytes;
  183. MaskKey: dword;
  184. Masked: Boolean;
  185. Procedure ReadData(var Content : TBytes; aTransport : IWSTransport);
  186. Procedure Read(buffer: TBytes; aTransport : IWSTransport);
  187. class procedure DoMask(var aData: TBytes; Key: DWORD); static;
  188. class procedure CopyMasked(SrcData: TBytes; var DestData: TBytes; Key: DWORD; aOffset: Integer); static;
  189. class function CopyMasked(SrcData: TBytes; Key: DWORD) : TBytes; static;
  190. end;
  191. { TWSFrame }
  192. TWSFrame = Class
  193. private
  194. FFrameType: TFrameType;
  195. FFinalFrame: Boolean;
  196. FRSV: Byte;
  197. FPayload : TWSFramePayload;
  198. FReason: WORD;
  199. protected
  200. function Read(aTransport: IWSTransport): boolean;
  201. function GetAsBytes : TBytes; virtual;
  202. Public
  203. // Read a message from transport. Returns Nil if the connection was closed when reading.
  204. class function CreateFromStream(aTransport : IWSTransport): TWSFrame;
  205. public
  206. constructor Create(aType: TFrameType; aIsFinal: Boolean; APayload: TBytes; aMask : Integer = 0); overload; virtual;
  207. constructor Create(Const aMessage : UTF8String; aMask : Integer = 0); overload; virtual;
  208. constructor Create(aType: TFrameType; aIsFinal: Boolean = True; aMask: Integer = 0); overload; virtual;
  209. property Reserved : Byte read FRSV write FRSV;
  210. property FinalFrame: Boolean read FFinalFrame write FFinalFrame;
  211. property Payload : TWSFramePayload Read FPayload Write FPayLoad;
  212. property FrameType: TFrameType read FFrameType;
  213. property Reason: WORD read FReason;
  214. Property AsBytes : TBytes Read GetAsBytes;
  215. end;
  216. TWSFrameClass = Class of TWSFrame;
  217. { TWSMessage }
  218. TWSMessage = record
  219. private
  220. function GetAsString: UTF8String;
  221. function GetAsUnicodeString: UnicodeString;
  222. Public
  223. PayLoad : TBytes;
  224. Sequences : TFrameSequences;
  225. IsText : Boolean;
  226. // Use these only when IsText is true (PayLoad contains valid UTF-8).
  227. // You may use them when IsText is false, but only if you know there is valid UTF-8 in payload.
  228. // Return Payload as a UTF8 string
  229. Property AsString : UTF8String Read GetAsString;
  230. // Return Payload as a UTF8 string
  231. Property AsUTF8String : UTF8String Read GetAsString;
  232. // Return Payload (assumed to contain valid UTF8) as a UTF16 string
  233. Property AsUnicodeString : UnicodeString Read GetAsUnicodeString;
  234. end;
  235. TWSMessageEvent = procedure(Sender: TObject; const aMessage : TWSMessage) of object;
  236. TWSControlEvent = procedure(Sender: TObject; aType : TFrameType; const aData: TBytes) of object;
  237. TCloseState = (csNone,csSent,csReceived,csClosed);
  238. TCloseStates = Set of TCloseState;
  239. TWSOption = (woPongExplicit, // Send Pong explicitly, not implicitly.
  240. woCloseExplicit, // SeDo Close explicitly, not implicitly.
  241. woIndividualFrames, // Send frames one by one, do not concatenate.
  242. woSkipUpgradeCheck, // Skip handshake "Upgrade:" HTTP header cheack.
  243. woSkipVersionCheck, // Skip handshake "Sec-WebSocket-Version' HTTP header check.
  244. woSendErrClosesConn // Don't raise an exception when writing to a broken connection
  245. );
  246. TWSOptions = set of TWSOption;
  247. { TWSConnection }
  248. TWSConnection = class
  249. Private
  250. class var _ConnectionCount : {$IFDEF CPU64}QWord{$ELSE}Cardinal{$ENDIF};
  251. private
  252. FAutoDisconnect: Boolean;
  253. FConnectionID: String;
  254. FFreeUserData: Boolean;
  255. FOnDisconnect: TNotifyEvent;
  256. FOutgoingFrameMask: Integer;
  257. FOwner: TComponent;
  258. FUserData: TObject;
  259. FWebSocketVersion: Integer;
  260. FInitialOpcode : TFrameType;
  261. FMessageContent : TBytes;
  262. FHandshakeRequest: TWSHandShakeRequest;
  263. FOnMessageReceived: TWSMessageEvent;
  264. FOnControl: TWSControlEvent;
  265. FCloseState : TCloseState;
  266. FOptions: TWSOptions;
  267. Function GetPeerIP : String;
  268. Function GetPeerPort : word;
  269. protected
  270. procedure AllocateConnectionID; virtual;
  271. Procedure SetCloseState(aValue : TCloseState); virtual;
  272. Procedure DoDisconnect; virtual; abstract;
  273. // Read message from connection. Return False if connection was closed.
  274. function DoReadMessage: Boolean;
  275. procedure DispatchEvent(aInitialType : TFrameType; aFrame: TWSFrame; aMessageContent: TBytes);
  276. Procedure SetHandShakeRequest(aRequest : TWSHandShakeRequest);
  277. Function HandleIncoming(aFrame: TWSFrame) : Boolean; virtual;
  278. function GetHandshakeCompleted: Boolean; virtual; abstract;
  279. Function GetTransport : IWSTransport; virtual; abstract;
  280. property Owner : TComponent Read FOwner;
  281. function IsValidUTF8(aValue: TBytes): boolean;
  282. Public
  283. Type
  284. TConnectionIDAllocator = Procedure(out aID : String) of object;
  285. class var IDAllocator : TConnectionIDAllocator;
  286. Public
  287. Constructor Create(aOwner : TComponent; aOptions : TWSOptions); virtual;
  288. destructor Destroy; override;
  289. // Extract close data
  290. Class Function GetCloseData(aBytes : TBytes; Out aReason : String) : Word;
  291. // Send close with message data
  292. procedure Close(aData : TBytes = Nil); overload;
  293. procedure Close(aMessage : UTF8String); overload;
  294. procedure Close(aMessage : UTF8String; aReason: word); overload;
  295. // Check incoming message
  296. function CheckIncoming(aTimeout: Integer; DoRead : Boolean = True): TIncomingResult;
  297. // read & process incoming message. Return nil if connection was close.
  298. function ReadMessage: Boolean;
  299. // Disconnect
  300. Procedure Disconnect;
  301. // Descendents can override this to provide custom frames
  302. Function FrameClass : TWSFrameClass; virtual;
  303. // Send raw frame. No checking is done !
  304. procedure Send(aFrame : TWSFrame); virtual;
  305. // Send message
  306. procedure Send(const AMessage: UTF8string);
  307. // Send binary data
  308. procedure Send(const ABytes: TBytes);
  309. // Send control frame. ftPing,ftPong,ftClose
  310. procedure Send(aFrameType: TFrameType; aData : TBytes = Nil);
  311. // Disconnect when status is set to csClosed;
  312. Property AutoDisconnect : Boolean Read FAutoDisconnect Write FAutoDisconnect;
  313. // Close frame handling
  314. Property CloseState : TCloseState Read FCloseState;
  315. // Connection ID, allocated during create
  316. Property ConnectionID : String Read FConnectionID;
  317. // If set to true, the owner data is freed when the connection is freed.
  318. Property FreeUserData : Boolean Read FFreeUserData Write FFreeUserData;
  319. // Request headers during handshake
  320. property HandshakeRequest: TWSHandShakeRequest read FHandshakeRequest;
  321. // Has handshake been completed ?
  322. property HandshakeCompleted: Boolean read GetHandshakeCompleted;
  323. // Options passed by server
  324. Property Options : TWSOptions Read FOptions;
  325. // Mask to use when sending frames. Set to nonzero value to send masked frames.
  326. Property OutgoingFrameMask : Integer Read FOutgoingFrameMask Write FOutgoingFrameMask;
  327. // Peer IP address
  328. property PeerIP: string read GetPeerIP;
  329. // Peer IP port
  330. property PeerPort: word read GetPeerPort;
  331. // Transport in use by this connection
  332. property Transport: IWSTransport read GetTransport;
  333. // User data to associate with this connection.
  334. Property UserData : TObject Read FUserData Write FUserData;
  335. // Socket version to check for
  336. Property WebSocketVersion : Integer Read FWebSocketVersion Write FWebSocketVersion;
  337. // Called when text/binary data was received
  338. property OnMessageReceived: TWSMessageEvent read FOnMessageReceived write FOnMessageReceived;
  339. // Called when Ping, Pong, Close control messages come in.
  340. property OnControl: TWSControlEvent read FOnControl write FOnControl;
  341. // Called when disconnect is called.
  342. property OnDisconnect: TNotifyEvent read FOnDisconnect write FOnDisconnect;
  343. end;
  344. { TWSClientTransport }
  345. TWSClientTransport = Class(TWSTransport)
  346. end;
  347. { TWSClientConnection }
  348. TWSClientConnection = Class(TWSConnection)
  349. private
  350. FTransport : TWSClientTransport;
  351. FHandshakeResponse: TWSHandShakeResponse;
  352. Protected
  353. function GetTransport : IWSTransport ; override;
  354. public
  355. Constructor Create(aOwner: TComponent; aTransport : TWSClientTransport; aOptions : TWSOptions); reintroduce; overload;
  356. Destructor Destroy; override;
  357. //
  358. function GetHandshakeCompleted: Boolean; override;
  359. // Owned by connection
  360. Property ClientTransport : TWSClientTransport Read FTransport;
  361. //
  362. Property HandShakeResponse : TWSHandShakeResponse Read FHandshakeResponse Write FHandshakeResponse;
  363. End;
  364. { TWSServerTransport }
  365. TWSServerTransport = class(TWSTransport)
  366. end;
  367. { TWSServerConnection }
  368. TWSConnectionHandshakeEvent = function(aRequest : TWSHandShakeRequest; aResponse : TWSHandShakeResponse): boolean of object;
  369. TWSServerConnection = Class(TWSConnection)
  370. Private
  371. FExtraHeaders: TStrings;
  372. FHandshakeResponseSent: Boolean;
  373. FOnHandShake: TWSConnectionHandshakeEvent;
  374. FTransport : TWSServerTransport;
  375. Protected
  376. Procedure DoDisconnect; override;
  377. function GetTransport: IWSTransport; override;
  378. function DoPrepareHandshakeResponse(aRequest : TWSHandShakeRequest; aResponse : TWSHandShakeResponse): boolean; virtual;
  379. function GetHandshakeCompleted: Boolean; override;
  380. public
  381. // Transport is owned by connection
  382. constructor Create(aOwner : TComponent; aTransport : TWSServerTransport; aOptions : TWSOptions); overload;
  383. // disconnect
  384. destructor Destroy; override;
  385. // Do full circle.
  386. Procedure PerformHandshake; virtual;
  387. // Given a request, send response
  388. function DoHandshake(const aRequest : TWSHandShakeRequest): Boolean; virtual;
  389. // Has handshake been exchanged?
  390. property HandshakeResponseSent: Boolean read FHandshakeResponseSent;
  391. // Extra handshake headers
  392. Property ExtraHeaders : TStrings Read FExtraHeaders;
  393. // Owned by connection
  394. property ServerTransport : TWSServerTransport Read FTransport;
  395. // Called when exchanging handshake
  396. Property OnHandshake : TWSConnectionHandshakeEvent Read FOnHandShake write FOnHandshake;
  397. end;
  398. Type
  399. { TBytesHelper }
  400. TBytesHelper = Type helper for TBytes
  401. // No swapping of bytes
  402. Function ToDword(aOffset : Integer = 0) : DWORD;
  403. Function ToInt32(aOffset : Integer = 0) : LongInt;
  404. Function ToWord(aOffset : Integer = 0) : Word;
  405. Function ToQWord(aOffset : Integer = 0) : QWord;
  406. Procedure FromDword(const aData : DWORD; aOffset : Integer = 0);
  407. Procedure FromInt32(const aData : Longint; aOffset : Integer = 0);
  408. Procedure FromWord(const aData : Word; aOffset : Integer = 0);
  409. Procedure FromQWord(const aData : QWord; aOffset : Integer = 0);
  410. procedure Reverse(var Dest: TBytes; Offset: Integer; Size: Integer);
  411. Function Reverse(Offset: Integer; Size: Integer) : TBytes;
  412. Procedure Append(aData : TBytes);
  413. end;
  414. Resourcestring
  415. SErrNotSimpleOperation = 'Frame type %d is not a simple operation.';
  416. SErrCloseAlreadySent = 'Close message already sent, cannot send more data.';
  417. SErrHandshakeInComplete = 'Operation cannot be performed while the handshake is not completed';
  418. SErrConnectionActive = 'Operation cannot be performed while the websocket connection is active';
  419. SErrConnectionInActive = 'Operation cannot be performed while the websocket connection is not active';
  420. SErrServerActive = 'Operation cannot be performed while the websocket connection is active';
  421. SErrInvalidSizeFlag = 'Invalid size flag: %d';
  422. SErrInvalidFrameType = 'Invalid frame type flag: %d';
  423. SErrWriteReturnedError = 'Write operation returned error: (%d) %s';
  424. function DecodeBytesBase64(const s: string; Strict: boolean = false) : TBytes;
  425. function EncodeBytesBase64(const aBytes : TBytes) : String;
  426. implementation
  427. uses strutils, sha1,base64;
  428. { TFrameTypeHelper }
  429. function TFrameTypeHelper.GetAsFlag: Byte;
  430. Const
  431. Flags : Array[TFrameType] of byte = (FlagContinuation,FlagText,FlagBinary,FlagClose,FlagPing,FlagPong,FlagReserved);
  432. begin
  433. Result:=Flags[Self];
  434. end;
  435. procedure TFrameTypeHelper.SetAsFlag(AValue: Byte);
  436. begin
  437. case aValue of
  438. FlagContinuation : Self:=ftContinuation;
  439. FlagText : Self:=ftText;
  440. FlagBinary : Self:=ftBinary;
  441. FlagClose : Self:=ftClose;
  442. FlagPing : Self:=ftPing;
  443. FlagPong : Self:=ftPong;
  444. else
  445. Self:=ftFutureOpcodes;
  446. end;
  447. end;
  448. { TWSServerTransport }
  449. { TWSHandShakeResponse }
  450. constructor TWSHandShakeResponse.Create(const aResource: string; const aExtraHeaders: TStrings);
  451. begin
  452. inherited Create(aResource, aExtraHeaders);
  453. HTTPVersion:='1.1';
  454. StatusCode:=101;
  455. StatusText:='Switching Protocols';
  456. end;
  457. procedure TWSHandShakeResponse.ToStrings(aHandShake: TWSHandshakeRequest; aResponse: TStrings; AddStatusLine: Boolean);
  458. Function CalcKey : String;
  459. Var
  460. B : TBytes;
  461. hash : TSHA1Digest;
  462. K : string;
  463. begin
  464. // respond key
  465. b:=[];
  466. k:= Trim(aHandshake.Key) + SSecWebSocketGUID;
  467. hash:=sha1.SHA1String(k);
  468. SetLength(B,SizeOf(hash));
  469. Move(Hash,B[0],Length(B));
  470. Result:=EncodeBytesBase64(B);
  471. end;
  472. begin
  473. // Fill needed headers
  474. Upgrade:='websocket';
  475. Connection:='Upgrade';
  476. // Chrome doesn't like it if you send an empty protocol header.
  477. if (Protocol='') and (aHandshake.Protocol<>'') then
  478. Protocol:=aHandshake.Protocol;
  479. if Version='' then
  480. Version:=IntToStr(DefaultWebSocketVersion);
  481. if Accept='' then
  482. Accept:=CalcKey;
  483. if AddStatusLine then
  484. aResponse.Add('HTTP/%s %d %s',[HTTPVersion,StatusCode,StatusText]);
  485. aResponse.AddStrings(RawHeaders);
  486. end;
  487. { TWSTransport }
  488. function TWSTransport.GetSocket: TSocketStream;
  489. begin
  490. Result:=FHelper.Socket;
  491. end;
  492. constructor TWSTransport.Create(aStream : TSocketStream);
  493. begin
  494. FStream:=aStream;
  495. FHelper:=TWSSocketHelper.Create(FStream);
  496. end;
  497. destructor TWSTransport.Destroy;
  498. begin
  499. FreeAndNil(FHelper);
  500. FreeAndNil(FStream);
  501. inherited Destroy;
  502. end;
  503. procedure TWSTransport.CloseSocket;
  504. begin
  505. sockets.CloseSocket(FStream.Handle);
  506. end;
  507. { TWSTransport }
  508. constructor TWSSocketHelper.Create(aSocket: TSocketStream);
  509. begin
  510. FSocket:=aSocket;
  511. {$if defined(FreeBSD) or defined(Linux)}
  512. FSocket.ReadFlags:=MSG_NOSIGNAL;
  513. FSocket.WriteFlags:=MSG_NOSIGNAL;
  514. {$endif}
  515. end;
  516. function TWSSocketHelper.CanRead(aTimeOut: Integer): Boolean;
  517. begin
  518. Result:=FSocket.CanRead(aTimeout);
  519. end;
  520. function TWSSocketHelper.PeerIP: string;
  521. Function SocketAddrToString(ASocketAddr: TSockAddr): String;
  522. begin
  523. if ASocketAddr.sa_family = AF_INET then
  524. Result := NetAddrToStr(ASocketAddr.sin_addr)
  525. else // no ipv6 support yet
  526. Result := '';
  527. end;
  528. begin
  529. Result:= SocketAddrToString(FSocket.RemoteAddress);
  530. end;
  531. function TWSSocketHelper.PeerPort: word;
  532. Function SocketAddrToPort(ASocketAddr: TSockAddr): word;
  533. begin
  534. if ASocketAddr.sa_family = AF_INET then
  535. Result := ASocketAddr.sin_port
  536. else // no ipv6 support yet
  537. Result := 0;
  538. end;
  539. begin
  540. Result:=SocketAddrToPort(FSocket.RemoteAddress);
  541. end;
  542. function TWSSocketHelper.ReadLn: String;
  543. Var
  544. C : Byte;
  545. aSize : integer;
  546. begin
  547. // Preset
  548. Result:='';
  549. SetLength(Result,255);
  550. aSize:=0;
  551. C:=0;
  552. While (FSocket.Read(C,1)=1) and (C<>10) do
  553. begin
  554. Inc(aSize);
  555. if aSize>Length(Result) then
  556. SetLength(Result,Length(Result)+255);
  557. Result[aSize]:=AnsiChar(C);
  558. end;
  559. if (aSize>0) and (Result[aSize]=#13) then
  560. Dec(aSize);
  561. SetLength(Result,aSize);
  562. end;
  563. function TWSSocketHelper.ReadBytes(var aBytes: TBytes; aCount: Integer): Integer;
  564. var
  565. buf: TBytes;
  566. aPos, toRead: QWord;
  567. begin
  568. if aCount=0 then exit(0);
  569. aPos := 0;
  570. SetLength(aBytes, aCount);
  571. repeat
  572. SetLength(buf{%H-}, aCount);
  573. Result := FSocket.Read(buf[0], aCount - aPos);
  574. if Result <= 0 then
  575. break;
  576. SetLength(buf, Result);
  577. Move(buf[0], aBytes[aPos], Result);
  578. Inc(aPos, Result);
  579. ToRead := aCount - aPos;
  580. Result := aCount;
  581. until toRead <= 0;
  582. end;
  583. procedure TWSSocketHelper.ReadBuffer(aBytes: TBytes);
  584. begin
  585. if Length(ABytes)=0 then exit;
  586. FSocket.ReadBuffer(aBytes[0],Length(ABytes));
  587. end;
  588. function TWSSocketHelper.WriteBytes(aBytes: TBytes; aCount: Integer): Integer;
  589. begin
  590. if aCount=0 then exit(0);
  591. Result:=FSocket.Write(aBytes[0],aCount);
  592. end;
  593. procedure TWSSocketHelper.WriteBuffer(aBytes: TBytes);
  594. begin
  595. if Length(aBytes)=0 then exit;
  596. FSocket.WriteBuffer(aBytes[0],Length(aBytes));
  597. end;
  598. { TWSMessage }
  599. function TWSMessage.GetAsString: UTF8String;
  600. begin
  601. Result:=UTF8String(TEncoding.UTF8.GetString(Payload));
  602. end;
  603. function TWSMessage.GetAsUnicodeString: UnicodeString;
  604. begin
  605. Result:=UTF8Decode(asUTF8String);
  606. end;
  607. { TBytesHelper }
  608. function TBytesHelper.Reverse(Offset: Integer; Size: Integer): TBytes;
  609. begin
  610. Result:=[];
  611. Reverse(Result,Offset,Size);
  612. end;
  613. procedure TBytesHelper.Append(aData: TBytes);
  614. Var
  615. sLen,dLen : SizeInt;
  616. begin
  617. sLen:=Length(Self);
  618. dLen:=Length(aData);
  619. if dLen>0 then
  620. begin
  621. SetLength(Self,dLen+sLen);
  622. Move(aData[0],Self[sLen],dLen);
  623. end;
  624. end;
  625. procedure TBytesHelper.Reverse(var Dest: TBytes; Offset: Integer; Size: Integer);
  626. var
  627. I: Integer;
  628. begin
  629. SetLength(dest, Size);
  630. for I := 0 to Size - 1 do
  631. Dest[Size-1-I]:=Self[Offset+I];
  632. end;
  633. function TBytesHelper.ToInt32(aOffset: Integer = 0): LongInt;
  634. begin
  635. Result:=0;
  636. Move(Self[aOffSet],Result,SizeOf(LongInt));
  637. end;
  638. function TBytesHelper.ToDword(aOffset: Integer): DWORD;
  639. begin
  640. Result:=0;
  641. Move(Self[aOffSet],Result,SizeOf(DWORD));
  642. end;
  643. function TBytesHelper.ToWord(aOffset: Integer): Word;
  644. begin
  645. Result:=0;
  646. Move(Self[aOffSet],Result,SizeOf(Word));
  647. end;
  648. function TBytesHelper.ToQWord(aOffset: Integer): QWord;
  649. begin
  650. Result:=0;
  651. Move(Self[aOffSet],Result,SizeOf(QWord));
  652. end;
  653. procedure TBytesHelper.FromDword(const aData: DWORD; aOffset: Integer);
  654. begin
  655. Move(aData, Self[aOffSet],SizeOf(DWORD));
  656. end;
  657. procedure TBytesHelper.FromInt32(const aData: Longint; aOffset: Integer);
  658. begin
  659. Move(aData, Self[aOffSet],SizeOf(Longint));
  660. end;
  661. procedure TBytesHelper.FromWord(const aData: Word; aOffset: Integer = 0);
  662. begin
  663. Move(aData, Self[aOffSet],SizeOf(Word));
  664. end;
  665. procedure TBytesHelper.FromQWord(const aData: QWord; aOffset: Integer);
  666. begin
  667. Move(aData, Self[aOffSet],SizeOf(QWord));
  668. end;
  669. Function HToNx(Host: QWord) : QWord;
  670. begin
  671. {$ifdef FPC_BIG_ENDIAN}
  672. htonx:=host;
  673. {$else}
  674. htonx:=SwapEndian(host);
  675. {$endif}
  676. end;
  677. Function NToHx(Net: QWord) : QWord;
  678. begin
  679. {$ifdef FPC_BIG_ENDIAN}
  680. ntohx:=Net;
  681. {$else}
  682. ntohx:=SwapEndian(Net);
  683. {$endif}
  684. end;
  685. function EncodeBytesBase64(const aBytes : TBytes) : String;
  686. var
  687. OutStream : TStringStream;
  688. Encoder : TBase64EncodingStream;
  689. begin
  690. if Length(aBytes)=0 then
  691. Exit('');
  692. Encoder:=Nil;
  693. OutStream:=TStringStream.Create('');
  694. try
  695. Encoder:=TBase64EncodingStream.Create(OutStream);
  696. if Length(aBytes)>0 then
  697. Encoder.WriteBuffer(aBytes[0],Length(aBytes));
  698. Encoder.Flush;
  699. Result:=OutStream.DataString;
  700. finally
  701. Encoder.Free;
  702. OutStream.free;
  703. end;
  704. end;
  705. function DecodeBytesBase64(const s: string; Strict: boolean = false) : TBytes;
  706. Const
  707. StrictModes : Array[Boolean] of TBase64DecodingMode = (bdmMime,bdmStrict);
  708. var
  709. missing : Integer;
  710. SD : String;
  711. Instream,
  712. Outstream : TBytesStream;
  713. Decoder : TBase64DecodingStream;
  714. begin
  715. Result:=[];
  716. if Length(s)=0 then
  717. Exit;
  718. SD:=S;
  719. Missing:=Length(Sd) mod 4;
  720. if Missing>0 then
  721. SD:=SD+StringOfChar('=',Missing);
  722. Outstream:=Nil;
  723. Decoder:=Nil;
  724. Instream:=TStringStream.Create(SD);
  725. try
  726. Outstream:=TBytesStream.Create(Nil);
  727. Decoder:=TBase64DecodingStream.Create(Instream,StrictModes[Strict]);
  728. Outstream.CopyFrom(Decoder,Decoder.Size);
  729. Result:=Outstream.Bytes;
  730. finally
  731. Decoder.Free;
  732. Outstream.Free;
  733. Instream.Free;
  734. end;
  735. end;
  736. { TWSFramePayload }
  737. procedure TWSFramePayload.ReadData(var Content: TBytes; aTransport: IWSTransport);
  738. Const
  739. MaxBufSize = 32*1024;
  740. Var
  741. Buf : TBytes;
  742. aPos,toRead : QWord;
  743. aCount, FailCnt : Longint;
  744. begin
  745. Buf:=[];
  746. ToRead:=DataLength;
  747. aPos:=0;
  748. FailCnt:=0;
  749. Repeat
  750. aCount:=ToRead;
  751. if aCount>MaxBufSize then
  752. aCount:=MaxBufSize;
  753. SetLength(Buf,aCount);
  754. aCount := aTransport.ReadBytes(Buf,aCount);
  755. if aCount>0 then
  756. begin
  757. Move(Buf[0],Content[aPos],aCount);
  758. Inc(aPos,aCount);
  759. ToRead:=DataLength-aPos;
  760. FailCnt:=0;
  761. end
  762. else
  763. begin
  764. sleep(1);
  765. inc(FailCnt);
  766. if FailCnt>100 then
  767. raise Exception.Create('20230316102741 TWSFramePayload.ReadData');
  768. end;
  769. Until (ToRead<=0);
  770. end;
  771. procedure TWSFramePayload.Read(buffer: TBytes; aTransport: IWSTransport);
  772. Var
  773. LenFlag : Byte;
  774. paylen16 : Word;
  775. content: TBytes;
  776. begin
  777. content:=[];
  778. Masked := ((buffer[1] and FlagMasked) <> 0);
  779. LenFlag := buffer[1] and FlagLengthMask;
  780. Case LenFlag of
  781. FlagTwoBytes:
  782. begin
  783. aTransport.ReadBytes(Buffer,2);
  784. Paylen16:=Buffer.ToWord(0);
  785. DataLength := ntohs(PayLen16);
  786. end;
  787. FlagEightBytes:
  788. begin
  789. aTransport.ReadBytes(Buffer,8);
  790. DataLength:=Buffer.ToQWord(0);
  791. DataLength := ntohx(DataLength); // MVC : Needs fixing
  792. end
  793. else
  794. DataLength:=lenFlag;
  795. end;
  796. if Masked then
  797. begin
  798. // In some times, not 4 bytes are returned
  799. aTransport.ReadBytes(Buffer,4);
  800. MaskKey:=buffer.ToDword(0);
  801. end;
  802. SetLength(content, DataLength);
  803. if (DataLength>0) then
  804. begin
  805. ReadData(Content,aTransport);
  806. if Masked then
  807. DoMask(Content, MaskKey);
  808. Data := content;
  809. end;
  810. end;
  811. { TWSFrame }
  812. constructor TWSFrame.Create(aType: TFrameType; aIsFinal: Boolean; APayload: TBytes; aMask: Integer=0);
  813. begin
  814. Create(aType,aIsFinal,aMask);
  815. FPayload.Data := APayload;
  816. if Assigned(aPayload) then
  817. FPayload.DataLength := Cardinal(Length(aPayload));
  818. end;
  819. constructor TWSFrame.Create(aType: TFrameType; aIsFinal : Boolean = True; aMask: Integer=0);
  820. begin
  821. FReason:=CLOSE_NORMAL_CLOSURE;
  822. FPayload:=Default(TWSFramePayload);
  823. FPayload.MaskKey:=aMask;
  824. FPayload.Masked:=aMask<>0;
  825. FFrameType := aType;
  826. FFinalFrame := AIsFinal;
  827. end;
  828. constructor TWSFrame.Create(const aMessage: UTF8String; aMask: Integer=0);
  829. Var
  830. Data : TBytes;
  831. begin
  832. Data:=TEncoding.UTF8.GetBytes(UnicodeString(AMessage));
  833. Create(ftText,True,Data,aMask);
  834. end;
  835. class function TWSFrame.CreateFromStream(aTransport : IWSTransport): TWSFrame;
  836. begin
  837. Result:=TWSFrame.Create;
  838. try
  839. if not Result.Read(aTransport) then
  840. FreeAndNil(Result);
  841. except
  842. FreeAndNil(Result);
  843. Raise;
  844. end;
  845. end;
  846. function TWSFrame.Read(aTransport: IWSTransport): boolean;
  847. Var
  848. Buffer : Tbytes;
  849. B1 : Byte;
  850. begin
  851. Result:=False;
  852. Buffer:=Default(TBytes);
  853. SetLength(Buffer,2);
  854. if aTransport.ReadBytes(Buffer,2)=0 then
  855. Exit;
  856. if Length(Buffer)<2 then
  857. Raise EWebSocket.Create('Could not read frame header');
  858. B1:=buffer[0];
  859. FFinalFrame:=(B1 and FlagFinalFrame) = FlagFinalFrame;
  860. FRSV:=(B1 and %01110000) shr 4;
  861. FFrameType.AsFlag:=(B1 and $F);
  862. FPayload.Read(Buffer,aTransport);
  863. FReason:=CLOSE_NORMAL_CLOSURE;
  864. if FFrameType=ftClose then
  865. if FPayload.DataLength = 1 then
  866. FReason:=CLOSE_PROTOCOL_ERROR
  867. else
  868. if FPayload.DataLength>1 then
  869. begin
  870. FReason:=SwapEndian(FPayload.Data.ToWord(0));
  871. FPayload.DataLength := FPayload.DataLength - 2;
  872. if FPayload.DataLength > 0 then
  873. move(FPayload.Data[2], FPayload.Data[0], FPayload.DataLength);
  874. SetLength(FPayload.Data, FPayload.DataLength);
  875. end;
  876. Result:=True;
  877. end;
  878. function TWSFrame.GetAsBytes: TBytes;
  879. var
  880. LenByte,firstByte: Byte;
  881. buffer, LengthBytes: TBytes;
  882. aOffSet, I : Integer;
  883. pLen16 : Word;
  884. pLen64 : QWord;
  885. begin
  886. Result:=Nil;
  887. firstByte := FrameType.AsFlag;
  888. if FinalFrame then
  889. firstByte := firstByte or FlagFinalFrame;
  890. if FPayload.DataLength < FlagTwoBytes then
  891. begin
  892. aOffSet:=2;
  893. LenByte := Byte(FPayload.DataLength);
  894. LengthBytes:=[];
  895. end
  896. else if Payload.DataLength < (1 shl 16) then
  897. begin
  898. aOffset:=4;
  899. LenByte := FlagTwoBytes;
  900. plen16:=Payload.DataLength;
  901. SetLength(LengthBytes, SizeOf(Word));
  902. LengthBytes.FromWord(HToNs(pLen16));
  903. end
  904. else
  905. begin
  906. aOffset:=10;
  907. LenByte:=FlagEightBytes;
  908. plen64:=Payload.DataLength;
  909. SetLength(LengthBytes, Sizeof(UInt64));
  910. LengthBytes.FromQWord(HToNx(Plen64));
  911. end;
  912. Buffer:=[];
  913. if FPayload.Masked then
  914. begin
  915. lenByte:=Lenbyte or FlagMasked;
  916. aoffSet:=aOffSet+4;
  917. end;
  918. SetLength(buffer,aOffset+Int64(FPayload.DataLength));
  919. buffer[0] := firstByte;
  920. buffer[1] := LenByte;
  921. for I := 0 to Length(LengthBytes)-1 do
  922. buffer[2 + I] := LengthBytes[I];
  923. if Payload.Masked then
  924. begin
  925. Buffer.FromInt32(Payload.MaskKey,aOffSet-4);
  926. TWSFramePayload.CopyMasked(Payload.Data,Buffer,Payload.MaskKey,aOffset);
  927. end
  928. else
  929. if Payload.DataLength > 0 then
  930. move(Payload.Data[0], buffer[aOffset], Payload.DataLength);
  931. Result := Buffer;
  932. end;
  933. class procedure TWSFramePayload.DoMask(var aData: TBytes; Key: DWORD);
  934. begin
  935. CopyMasked(aData,aData,Key,0)
  936. end;
  937. class procedure TWSFramePayload.CopyMasked(SrcData: TBytes; var DestData: TBytes; Key: DWORD; aOffset: Integer);
  938. var
  939. currentMaskIndex: Longint;
  940. byteKeys: TBytes;
  941. I: Longint;
  942. begin
  943. CurrentMaskIndex := 0;
  944. byteKeys:=[];
  945. SetLength(byteKeys, SizeOf(Key));
  946. ByteKeys.FromDword(Key);
  947. for I := 0 to Length(SrcData) - 1 do
  948. begin
  949. DestData[I+aOffset] := SrcData[I] XOR byteKeys[currentMaskIndex];
  950. currentMaskIndex := (currentMaskIndex + 1) mod 4;
  951. end;
  952. end;
  953. class function TWSFramePayload.CopyMasked(SrcData: TBytes; Key: DWORD): TBytes;
  954. begin
  955. Result:=[];
  956. SetLength(Result,Length(SrcData));
  957. CopyMasked(SrcData,Result,Key,0)
  958. end;
  959. { TWSRequest }
  960. function TWSHeaders.GetS(aIdx: Integer): String;
  961. begin
  962. Result:=GetH(WSHeaderNames[aIdx]);
  963. end;
  964. procedure TWSHeaders.SetS(AIndex: Integer; const AValue: string);
  965. begin
  966. SetH(WSHeaderNames[aIndex],aValue);
  967. end;
  968. function TWSHeaders.GetH(const aName: string): String;
  969. begin
  970. Result:=Trim(FRawHeaders.Values[aName]);
  971. end;
  972. Procedure TWSHeaders.SetH(const aName,aValue: string);
  973. begin
  974. FRawHeaders.Values[aName]:=' '+aValue;
  975. end;
  976. constructor TWSHeaders.Create(Const aResource : String; const AHeaderList: TStrings);
  977. var
  978. I : Integer;
  979. N,V : String;
  980. begin
  981. FResource:=aResource;
  982. FRawHeaders:=TStringList.Create;
  983. FRawHeaders.NameValueSeparator:=':';
  984. if Assigned(aHeaderList) then
  985. for I:=0 to aHeaderList.Count-1 do
  986. begin
  987. aHeaderList.GetNameValue(I,N,V);
  988. if (N<>'') and (V<>'') then
  989. FRawHeaders.Add(N+': '+Trim(V));
  990. end;
  991. end;
  992. destructor TWSHeaders.Destroy;
  993. begin
  994. FreeAndNil(FRawHeaders);
  995. inherited;
  996. end;
  997. { TWSConnection }
  998. procedure TWSConnection.Send(aFrameType : TFrameType; aData : TBytes = Nil);
  999. Var
  1000. aFrame : TWSFrame;
  1001. begin
  1002. if not (aFrameType in [ftClose,ftPing,ftPong]) then
  1003. Raise EWebSocket.CreateFmt(SErrNotSimpleOperation,[Ord(aFrameType)]);
  1004. aFrame:=FrameClass.Create(aFrameType,True,aData, OutgoingFrameMask);
  1005. try
  1006. Send(aFrame);
  1007. finally
  1008. aFrame.Free;
  1009. end;
  1010. end;
  1011. procedure TWSConnection.SetHandShakeRequest(aRequest: TWSHandShakeRequest);
  1012. begin
  1013. FreeAndNil(FHandshakeRequest);
  1014. FHandShakeRequest:=aRequest;
  1015. end;
  1016. constructor TWSConnection.Create(aOwner : TComponent; aOptions: TWSOptions);
  1017. begin
  1018. FOwner:=aOwner;
  1019. Foptions:=aOptions;
  1020. FWebSocketVersion:=WebSocketVersion;
  1021. AllocateConnectionID;
  1022. end;
  1023. destructor TWSConnection.Destroy;
  1024. begin
  1025. FreeAndNil(FHandshakeRequest);
  1026. If FreeUserData then
  1027. FreeAndNil(FUserData);
  1028. inherited;
  1029. end;
  1030. class function TWSConnection.GetCloseData(aBytes: TBytes; out aReason: String): Word;
  1031. begin
  1032. Result:=0;
  1033. aReason:='';
  1034. if Length(aBytes)>1 then
  1035. Result:=NToHs(aBytes.ToWord(0));
  1036. if Length(aBytes)>2 then
  1037. aReason:=TEncoding.UTF8.GetAnsiString(aBytes,2,Length(aBytes)-2);
  1038. end;
  1039. function TWSConnection.GetPeerIP: String;
  1040. Var
  1041. S : IWSTransport;
  1042. begin
  1043. S:=Transport;
  1044. if Assigned(S) then
  1045. Result:=S.PeerIP
  1046. else
  1047. Result:=''
  1048. end;
  1049. function TWSConnection.GetPeerPort: word;
  1050. Var
  1051. S : IWSTransport;
  1052. begin
  1053. S:=Transport;
  1054. if Assigned(S) then
  1055. Result:=S.PeerPort
  1056. else
  1057. Result:=0
  1058. end;
  1059. procedure TWSConnection.AllocateConnectionID;
  1060. begin
  1061. if Assigned(IDAllocator) then
  1062. IDAllocator(FConnectionID);
  1063. if FConnectionID='' then
  1064. {$IFDEF CPU64}
  1065. FConnectionID:=IntToStr(InterlockedIncrement64(_ConnectionCount));
  1066. {$ELSE}
  1067. FConnectionID:=IntToStr(InterlockedIncrement(_ConnectionCount));
  1068. {$ENDIF}
  1069. end;
  1070. procedure TWSConnection.SetCloseState(aValue: TCloseState);
  1071. begin
  1072. FCloseState:=aValue;
  1073. if (FCloseState=csClosed) and AutoDisconnect then
  1074. Disconnect;
  1075. end;
  1076. function TWSConnection.ReadMessage: Boolean;
  1077. begin
  1078. Result:=DoReadMessage;
  1079. end;
  1080. procedure TWSConnection.DispatchEvent(aInitialType: TFrameType; aFrame: TWSFrame; aMessageContent: TBytes);
  1081. Var
  1082. msg: TWSMessage;
  1083. begin
  1084. Case aInitialType of
  1085. ftPing,
  1086. ftPong,
  1087. ftClose :
  1088. If Assigned(FOnControl) then
  1089. FOnControl(Self,aInitialType,aMessageContent);
  1090. ftBinary,
  1091. ftText :
  1092. begin
  1093. if Assigned(FOnMessageReceived) then
  1094. begin
  1095. Msg:=Default(TWSMessage);
  1096. Msg.IsText:=(aInitialType=ftText);
  1097. if aFrame.FrameType=ftBinary then
  1098. Msg.Sequences:=[fsFirst]
  1099. else
  1100. Msg.Sequences:=[fsContinuation];
  1101. if aFrame.FinalFrame then
  1102. Msg.Sequences:=Msg.Sequences+[fsLast];
  1103. Msg.PayLoad:=aMessageContent;
  1104. FOnMessageReceived(Self, Msg);
  1105. end;
  1106. end;
  1107. ftContinuation: ; // Cannot happen normally
  1108. end;
  1109. end;
  1110. function TWSConnection.HandleIncoming(aFrame: TWSFrame) : Boolean;
  1111. Procedure UpdateCloseState;
  1112. begin
  1113. if (FCloseState=csNone) then
  1114. FCloseState:=csReceived
  1115. else if (FCloseState=csSent) then
  1116. FCloseState:=csClosed;
  1117. end;
  1118. procedure ProtocolError(aCode: Word);
  1119. begin
  1120. Close('', aCode);
  1121. UpdateCloseState;
  1122. Result:=false;
  1123. end;
  1124. begin
  1125. Result:=true;
  1126. // check Reserved bits
  1127. if aFrame.Reserved<>0 then
  1128. begin
  1129. ProtocolError(CLOSE_PROTOCOL_ERROR);
  1130. Exit;
  1131. end;
  1132. // check Reserved opcode
  1133. if aFrame.FrameType = ftFutureOpcodes then
  1134. begin
  1135. ProtocolError(CLOSE_PROTOCOL_ERROR);
  1136. Exit;
  1137. end;
  1138. { If control frame it must be complete }
  1139. if ((aFrame.FrameType=ftPing) or
  1140. (aFrame.FrameType=ftPong) or
  1141. (aFrame.FrameType=ftClose))
  1142. and (not aFrame.FinalFrame) then
  1143. begin
  1144. ProtocolError(CLOSE_PROTOCOL_ERROR);
  1145. Exit;
  1146. end;
  1147. //
  1148. // here we handle payload.
  1149. // if aFrame.FrameType in [ftBinary,ftText] then
  1150. // begin
  1151. // FInitialOpcode:=aFrame.FrameType;
  1152. // FMessageContent:=aFrame.Payload.Data;
  1153. // end;
  1154. // Special handling
  1155. Case aFrame.FrameType of
  1156. ftContinuation:
  1157. begin
  1158. if FInitialOpcode=ftContinuation then
  1159. begin
  1160. ProtocolError(CLOSE_PROTOCOL_ERROR);
  1161. Exit;
  1162. end;
  1163. FMessageContent.Append(aFrame.Payload.Data);
  1164. if aFrame.FinalFrame then
  1165. begin
  1166. if FInitialOpcode = ftText then
  1167. if IsValidUTF8(FMessageContent) then
  1168. DispatchEvent(FInitialOpcode,aFrame,FMessageContent)
  1169. else
  1170. ProtocolError(CLOSE_INVALID_FRAME_PAYLOAD_DATA)
  1171. else
  1172. DispatchEvent(FInitialOpcode,aFrame,FMessageContent);
  1173. // reset initial opcode
  1174. FInitialOpcode:=ftContinuation;
  1175. end;
  1176. end;
  1177. ftPing:
  1178. begin
  1179. if aFrame.Payload.DataLength > 125 then
  1180. ProtocolError(CLOSE_PROTOCOL_ERROR)
  1181. else
  1182. if not (woPongExplicit in Options) then
  1183. begin
  1184. Send(ftPong,aFrame.Payload.Data);
  1185. DispatchEvent(ftPing,aFrame,aFrame.Payload.Data);
  1186. end;
  1187. end;
  1188. ftClose:
  1189. begin
  1190. // If our side sent the initial close, this is the reply, and we must disconnect (Result=false).
  1191. Result:=FCloseState=csNone;
  1192. if Result then
  1193. begin
  1194. if (aFrame.Payload.DataLength>123) then
  1195. begin
  1196. ProtocolError(CLOSE_PROTOCOL_ERROR);
  1197. exit;
  1198. end;
  1199. if not (woCloseExplicit in Options) then
  1200. begin
  1201. if (aFrame.Reason<CLOSE_NORMAL_CLOSURE) or
  1202. (aFrame.Reason=CLOSE_RESERVER) or
  1203. (aFrame.Reason=CLOSE_NO_STATUS_RCVD) or
  1204. (aFrame.Reason=CLOSE_ABNORMAL_CLOSURE) or
  1205. ((aFrame.Reason>CLOSE_TLS_HANDSHAKE) and (aFrame.Reason<3000)) then
  1206. begin
  1207. ProtocolError(CLOSE_PROTOCOL_ERROR);
  1208. exit;
  1209. end;
  1210. if IsValidUTF8(aFrame.Payload.Data) then
  1211. begin
  1212. DispatchEvent(ftClose,aFrame,aFrame.Payload.Data);
  1213. Close('', aFrame.Reason); // Will update state
  1214. UpdateCloseState;
  1215. Result:=False; // We can disconnect.
  1216. end
  1217. else
  1218. ProtocolError(CLOSE_PROTOCOL_ERROR);
  1219. end
  1220. else
  1221. UpdateCloseState
  1222. end
  1223. else
  1224. UpdateCloseState;
  1225. end;
  1226. ftBinary,ftText:
  1227. begin
  1228. if FInitialOpcode in [ftText, ftBinary] then
  1229. begin
  1230. ProtocolError(CLOSE_PROTOCOL_ERROR);
  1231. Exit;
  1232. end;
  1233. FInitialOpcode:=aFrame.FrameType;
  1234. FMessageContent:=aFrame.Payload.Data;
  1235. if aFrame.FinalFrame then
  1236. begin
  1237. if aFrame.FrameType = ftText then
  1238. if IsValidUTF8(aFrame.Payload.Data) then
  1239. DispatchEvent(FInitialOpcode,aFrame,aFrame.Payload.Data)
  1240. else
  1241. ProtocolError(CLOSE_INVALID_FRAME_PAYLOAD_DATA)
  1242. else
  1243. DispatchEvent(FInitialOpcode,aFrame,aFrame.Payload.Data);
  1244. FInitialOpcode:=ftContinuation;
  1245. end;
  1246. end;
  1247. else
  1248. ; // avoid Compiler warning
  1249. End;
  1250. end;
  1251. function TWSConnection.IsValidUTF8(aValue: TBytes): boolean;
  1252. var
  1253. i, len, n, j: integer;
  1254. c: ^byte;
  1255. begin
  1256. Result := true;
  1257. len := length(aValue);
  1258. if len = 0 then
  1259. exit;
  1260. Result := False;
  1261. i := 0;
  1262. c := @AValue[0];
  1263. while i < len do
  1264. begin
  1265. if (c^ <= $7f) then
  1266. n := 0
  1267. else if (c^ >= $c2) and (c^ <= $df) then
  1268. n := 1
  1269. else if (c^ = $e0) then
  1270. n := 2
  1271. else if (c^ >= $e1) and (c^ <= $ec) then
  1272. n := 2
  1273. else if (c^ = $ed) then
  1274. n := 2
  1275. else if (c^ >= $ee) and (c^ <= $ef) then
  1276. n := 2
  1277. else if (c^ = $f0) then
  1278. n := 3
  1279. else if (c^ >= $f1) and (c^ <= $f3) then
  1280. n := 3
  1281. else if (c^ = $f4) then
  1282. n := 3
  1283. else
  1284. exit;
  1285. j := 0;
  1286. Inc(i);
  1287. while j < n do
  1288. begin
  1289. if i >= len then
  1290. exit;
  1291. case c^ of
  1292. $c2..$df, $e1..$ec, $ee..$ef, $f1..$f3:
  1293. if not (((c + 1)^ >= $80) and ((c + 1)^ <= $bf)) then
  1294. exit;
  1295. $e0:
  1296. if not (((c + 1)^ >= $a0) and ((c + 1)^ <= $bf)) then
  1297. exit;
  1298. $ed:
  1299. if not (((c + 1)^ >= $80) and ((c + 1)^ <= $9f)) then
  1300. exit;
  1301. $f0:
  1302. if not (((c + 1)^ >= $90) and ((c + 1)^ <= $bf)) then
  1303. exit;
  1304. $f4:
  1305. if not (((c + 1)^ >= $80) and ((c + 1)^ <= $8f)) then
  1306. exit;
  1307. $80..$bf:
  1308. if not (((c + 1)^ >= $80) and ((c + 1)^ <= $bf)) then
  1309. exit;
  1310. end;
  1311. Inc(c);
  1312. Inc(i);
  1313. Inc(j);
  1314. end;
  1315. Inc(c);
  1316. end;
  1317. Result := True;
  1318. end;
  1319. function TWSConnection.FrameClass: TWSFrameClass;
  1320. begin
  1321. Result:=TWSFrame;
  1322. end;
  1323. procedure TWSConnection.Send(const AMessage: UTF8string);
  1324. var
  1325. aFrame: TWSFrame;
  1326. begin
  1327. aFrame:=FrameClass.Create(aMessage, OutgoingFrameMask);
  1328. try
  1329. Send(aFrame);
  1330. finally
  1331. aFrame.Free;
  1332. end;
  1333. end;
  1334. procedure TWSConnection.Send(const ABytes: TBytes);
  1335. var
  1336. aFrame: TWSFrame;
  1337. begin
  1338. aFrame:=FrameClass.Create(ftBinary,True,ABytes, OutgoingFrameMask);
  1339. try
  1340. Send(aFrame);
  1341. finally
  1342. aFrame.Free;
  1343. end;
  1344. end;
  1345. procedure TWSConnection.Close(aMessage: UTF8String);
  1346. begin
  1347. Close(aMessage, CLOSE_NORMAL_CLOSURE);
  1348. end;
  1349. procedure TWSConnection.Close(aMessage: UTF8String; aReason: word);
  1350. var
  1351. aData: TBytes;
  1352. aSize: Integer;
  1353. begin
  1354. aData := [];
  1355. // first two bytes is reason of close RFC 6455 section-5.5.1
  1356. aData := TEncoding.UTF8.GetAnsiBytes(aMessage);
  1357. aSize := Length(aData);
  1358. SetLength(aData, aSize + 2);
  1359. if aSize > 0 then
  1360. move(aData[0], aData[2], aSize);
  1361. aData[0] := (aReason and $FF00) shr 8;
  1362. aData[1] := aReason and $FF;
  1363. Close(aData);
  1364. end;
  1365. procedure TWSConnection.Disconnect;
  1366. begin
  1367. DoDisconnect;
  1368. if Assigned(FOnDisconnect) then
  1369. FOnDisconnect(Self);
  1370. end;
  1371. procedure TWSConnection.Close(aData: TBytes);
  1372. begin
  1373. Send(ftClose,aData);
  1374. end;
  1375. procedure TWSConnection.Send(aFrame: TWSFrame);
  1376. Var
  1377. Data : TBytes;
  1378. Res: Integer;
  1379. ErrMsg: UTF8String;
  1380. begin
  1381. if FCloseState=csClosed then
  1382. Raise EWebSocket.Create(SErrCloseAlreadySent);
  1383. Data:=aFrame.AsBytes;
  1384. Res := Transport.WriteBytes(Data,Length(Data));
  1385. if Res < 0 then
  1386. begin
  1387. FCloseState:=csClosed;
  1388. ErrMsg := Format(SErrWriteReturnedError, [GetLastOSError, SysErrorMessage(GetLastOSError)]);
  1389. if woSendErrClosesConn in Options then
  1390. begin
  1391. SetLength(Data, 0);
  1392. Data.Append(TEncoding.UTF8.GetBytes(UnicodeString(ErrMsg)));
  1393. DispatchEvent(ftClose, nil, Data);
  1394. end
  1395. else
  1396. Raise EWebSocket.Create(ErrMsg);
  1397. end;
  1398. if (aFrame.FrameType=ftClose) then
  1399. begin
  1400. if FCloseState=csNone then
  1401. FCloseState:=csSent
  1402. else if FCloseState=csReceived then
  1403. FCloseState:=csClosed;
  1404. end;
  1405. end;
  1406. function TWSConnection.DoReadMessage: Boolean;
  1407. Var
  1408. F : TWSFrame;
  1409. begin
  1410. Result:=False;
  1411. If not Transport.CanRead(0) then
  1412. Exit;
  1413. f:=FrameClass.CreateFromStream(Transport);
  1414. try
  1415. if Assigned(F) then
  1416. Result:=HandleIncoming(F)
  1417. finally
  1418. F.Free;
  1419. end;
  1420. end;
  1421. function TWSConnection.CheckIncoming(aTimeout: Integer; DoRead: Boolean = True): TIncomingResult;
  1422. begin
  1423. if not Transport.CanRead(aTimeOut) then
  1424. Result:=irNone
  1425. else if Not DoRead then
  1426. Result:=irWaiting
  1427. else if ReadMessage then
  1428. Result:=irOK
  1429. else
  1430. Result:=irClose;
  1431. end;
  1432. constructor TWSClientConnection.Create(aOwner: TComponent; aTransport: TWSClientTransport; aOptions : TWSOptions);
  1433. begin
  1434. Inherited Create(aOwner,aOptions);
  1435. FTransport:=aTransport;
  1436. end;
  1437. destructor TWSClientConnection.Destroy;
  1438. begin
  1439. FreeAndNil(FTransport);
  1440. inherited;
  1441. end;
  1442. function TWSClientConnection.GetHandshakeCompleted: Boolean;
  1443. begin
  1444. Result:=Assigned(FHandshakeResponse);
  1445. end;
  1446. function TWSClientConnection.GetTransport: IWSTransport;
  1447. begin
  1448. Result:=FTransport;
  1449. end;
  1450. { TWSHandShakeRequest }
  1451. Class Function TWSHandShakeRequest.GenerateKey : String;
  1452. Var
  1453. I : Integer;
  1454. B : TBytes;
  1455. begin
  1456. B:=[];
  1457. SetLength(B,16);
  1458. For I:=0 to 15 do
  1459. B[i]:=Random(256);
  1460. Result:=EncodeBytesBase64(B);
  1461. end;
  1462. constructor TWSHandShakeRequest.Create(const aResource: string; const aExtraHeaders: TStrings);
  1463. begin
  1464. Inherited Create(aResource,aExtraHeaders);
  1465. Version:=IntToStr(DefaultWebSocketVersion);
  1466. end;
  1467. procedure TWSHandShakeRequest.ToStrings(aHeaders: TStrings);
  1468. procedure Add(const AName, aValue, aDefault: String);
  1469. Var
  1470. V : String;
  1471. begin
  1472. V:=aValue;
  1473. if V='' then
  1474. V:=aDefault;
  1475. if V<>'' then
  1476. aHeaders.Add(aName+': '+V)
  1477. end;
  1478. Var
  1479. N,V : String;
  1480. I : Integer;
  1481. begin
  1482. aHeaders.Clear;
  1483. if Resource='' then
  1484. Resource:='/';
  1485. aHeaders.Add('GET ' + Resource + ' HTTP/1.1');
  1486. V:=Host;
  1487. if (V<>'') and (Port<>443) and (Port<>80) then
  1488. V:=V+':'+IntToStr(Port);
  1489. Add('Host',V,'');
  1490. Add('Upgrade',Upgrade,'websocket');
  1491. Add('Connection',Connection,'Upgrade');
  1492. Add('Origin',Origin,Host);
  1493. if Key='' then
  1494. Key:=GenerateKey;
  1495. Add('Sec-WebSocket-Key',Key,'');
  1496. Add('Sec-WebSocket-Protocol',Protocol,'');
  1497. Add('Sec-WebSocket-Version',Version,'');
  1498. For I:=0 to RawHeaders.Count-1 do
  1499. begin
  1500. RawHeaders.GetNameValue(I,N,V);
  1501. if (N<>'') and (V<>'') then
  1502. if (aHeaders.Values[N]='') then
  1503. Add(N,V,'')
  1504. end;
  1505. end;
  1506. { TWSServerConnection }
  1507. constructor TWSServerConnection.Create(aOwner : TComponent; aTransport : TWSServerTransport; aOptions : TWSOptions);
  1508. begin
  1509. Inherited Create(aOwner,aOptions);
  1510. FHandshakeResponseSent := False;
  1511. FTransport:=aTransport;
  1512. FExtraHeaders:=TStringList.Create;
  1513. FExtraHeaders.NameValueSeparator:=':';
  1514. end;
  1515. destructor TWSServerConnection.Destroy;
  1516. begin
  1517. if Assigned(FTransport) then
  1518. DisConnect;
  1519. FreeAndNil(FExtraHeaders);
  1520. inherited;
  1521. end;
  1522. procedure TWSServerConnection.PerformHandshake;
  1523. Var
  1524. Headers : TStrings;
  1525. aResource,Status,aLine : String;
  1526. HSR : TWSHandShakeRequest;
  1527. begin
  1528. Status:=Transport.ReadLn;
  1529. aResource:=ExtractWord(2,Status,[' ']);
  1530. HSR:=Nil;
  1531. Headers:=TStringList.Create;
  1532. try
  1533. Headers.NameValueSeparator:=':';
  1534. aLine:=Transport.ReadLn;
  1535. While aLine<>'' do
  1536. begin
  1537. Headers.Add(aLine);
  1538. aLine:=Transport.ReadLn;
  1539. end;
  1540. HSR:=TWSHandShakeRequest.Create(aResource,Headers);
  1541. FHandshakeResponseSent:=DoHandshake(HSR);
  1542. finally
  1543. HSR.Free;
  1544. Headers.Free;
  1545. end;
  1546. end;
  1547. function TWSServerConnection.GetHandshakeCompleted: Boolean;
  1548. begin
  1549. Result:=HandshakeResponseSent;
  1550. end;
  1551. procedure TWSServerConnection.DoDisconnect;
  1552. begin
  1553. if Assigned(FTransport) then
  1554. FTransport.CloseSocket;
  1555. FreeAndNil(FTransPort);
  1556. end;
  1557. function TWSServerConnection.GetTransport: IWSTransport;
  1558. begin
  1559. Result:=FTransport;
  1560. end;
  1561. function TWSServerConnection.DoPrepareHandshakeResponse(
  1562. aRequest: TWSHandShakeRequest; aResponse: TWSHandShakeResponse): boolean;
  1563. begin
  1564. If Assigned(OnHandshake) then
  1565. Result:=OnHandShake(aRequest,aResponse)
  1566. else
  1567. Result:=true;
  1568. end;
  1569. function TWSServerConnection.DoHandshake(const aRequest : TWSHandShakeRequest) : Boolean;
  1570. var
  1571. aLine,Reply : string;
  1572. aResp : TWSHandShakeResponse;
  1573. H : TStrings;
  1574. B : TBytes;
  1575. begin
  1576. Result:=False;
  1577. H:=Nil;
  1578. aResp:=TWSHandShakeResponse.Create('',FExtraHeaders);
  1579. try
  1580. try
  1581. DoPrepareHandshakeResponse(aRequest,aResp);
  1582. H:=TStringList.Create;
  1583. aResp.ToStrings(aRequest,H,True);
  1584. Reply:='';
  1585. For aLine in H do
  1586. Reply:=Reply+aLine+#13#10;
  1587. Reply:=Reply+#13#10;
  1588. B:=TEncoding.UTF8.GetAnsiBytes(Reply);
  1589. Transport.WriteBytes(B,Length(B));
  1590. Result:=True;
  1591. FHandshakeResponseSent:=True;
  1592. except
  1593. on E: Exception do
  1594. begin
  1595. // Close the connection if the handshake failed
  1596. Disconnect;
  1597. end;
  1598. end;
  1599. finally
  1600. H.Free;
  1601. aResp.Free;
  1602. end;
  1603. end;
  1604. end.