websocket2.pas 63 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978
  1. {==============================================================================|
  2. | Project : Bauglir Internet Library |
  3. |==============================================================================|
  4. | Content: Generic connection and server |
  5. |==============================================================================|
  6. | Copyright (c)2011-2012, Bronislav Klucka |
  7. | All rights reserved. |
  8. | Source code is licenced under original 4-clause BSD licence: |
  9. | http://licence.bauglir.com/bsd4.php |
  10. | |
  11. | |
  12. | Project download homepage: |
  13. | http://code.google.com/p/bauglir-websocket/ |
  14. | Project homepage: |
  15. | http://www.webnt.eu/index.php |
  16. | WebSocket RFC: |
  17. | http://tools.ietf.org/html/rfc6455 |
  18. | |
  19. | |
  20. |==============================================================================|
  21. | Requirements: Ararat Synapse (http://www.ararat.cz/synapse/) |
  22. |==============================================================================}
  23. {
  24. 2.0.4
  25. 1/ change: send generic frame SendData public on WSConnection
  26. 2/ pascal bugfix: closing connection issues (e.g. infinite sleep)
  27. 3/ add: server CloseAllConnections
  28. 4/ change: default client version 13 (RFC)
  29. 5/ pascal change: CanReceiveOrSend public
  30. 6/ pascal bugfix: events missing on erratic traffic
  31. 7/ add: make Handschake public property
  32. @todo
  33. * move writing to separate thread
  34. * test for simultaneous i/o operations
  35. http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17
  36. http://tools.ietf.org/html/rfc6455
  37. http://dev.w3.org/html5/websockets/#refsFILEAPI
  38. }
  39. unit WebSocket2;
  40. {$IFDEF FPC}
  41. {$MODE DELPHI}
  42. {$ENDIF}
  43. {$H+}
  44. interface
  45. uses
  46. {$IFDEF UNIX}
  47. cthreads,
  48. {$ENDIF}
  49. Classes, SysUtils, blcksock, syncobjs,
  50. CustomServer2;
  51. const
  52. {:Constants section defining what kind of data are sent from one pont to another}
  53. {:Continuation frame }
  54. wsCodeContinuation = $0;
  55. {:Text frame }
  56. wsCodeText = $1;
  57. {:Binary frame }
  58. wsCodeBinary = $2;
  59. {:Close frame }
  60. wsCodeClose = $8;
  61. {:Ping frame }
  62. wsCodePing = $9;
  63. {:Frame frame }
  64. wsCodePong = $A;
  65. {:Constants section defining close codes}
  66. {:Normal valid closure, connection purpose was fulfilled}
  67. wsCloseNormal = 1000;
  68. {:Endpoint is going away (like server shutdown) }
  69. wsCloseShutdown = 1001;
  70. {:Protocol error }
  71. wsCloseErrorProtocol = 1002;
  72. {:Unknown frame data type or data type application cannot handle }
  73. wsCloseErrorData = 1003;
  74. {:Reserved }
  75. wsCloseReserved1 = 1004;
  76. {:Close received by peer but without any close code. This close code MUST NOT be sent by application. }
  77. wsCloseNoStatus = 1005;
  78. {:Abnotmal connection shutdown close code. This close code MUST NOT be sent by application. }
  79. wsCloseErrorClose = 1006;
  80. {:Received text data are not valid UTF-8. }
  81. wsCloseErrorUTF8 = 1007;
  82. {:Endpoint is terminating the connection because it has received a message that violates its policy. Generic error. }
  83. wsCloseErrorPolicy = 1008;
  84. {:Too large message received }
  85. wsCloseTooLargeMessage = 1009;
  86. {:Client is terminating the connection because it has expected the server to negotiate one or more extension, but the server didn't return them in the response message of the WebSocket handshake }
  87. wsCloseClientExtensionError= 1010;
  88. {:Server is terminating the connection because it encountered an unexpected condition that prevented it from fulfilling the request }
  89. wsCloseErrorServerRequest = 1011;
  90. {:Connection was closed due to a failure to perform a TLS handshake. This close code MUST NOT be sent by application. }
  91. wsCloseErrorTLS = 1015;
  92. type
  93. TWebSocketCustomConnection = class;
  94. {:Event procedural type to hook OnOpen events on connection
  95. }
  96. TWebSocketConnectionEvent = procedure (aSender: TWebSocketCustomConnection) of object;
  97. {:Event procedural type to hook OnPing, OnPong events on connection
  98. TWebSocketConnectionPingPongEvent = procedure (aSender: TWebSocketCustomConnection; aData: string) of object;
  99. }
  100. {:Event procedural type to hook OnClose event on connection
  101. }
  102. TWebSocketConnectionClose = procedure (aSender: TWebSocketCustomConnection; aCloseCode: integer; aCloseReason: string; aClosedByPeer: boolean) of object;
  103. {:Event procedural type to hook OnRead on OnWrite event on connection
  104. }
  105. TWebSocketConnectionData = procedure (aSender: TWebSocketCustomConnection; aFinal, aRes1, aRes2, aRes3: boolean; aCode: integer; aData: TMemoryStream) of object;
  106. {:Event procedural type to hook OnReadFull
  107. }
  108. TWebSocketConnectionDataFull = procedure (aSender: TWebSocketCustomConnection; aCode: integer; aData: TMemoryStream) of object;
  109. {:abstract(WebSocket connection)
  110. class is parent class for server and client connection
  111. }
  112. TWebSocketCustomConnection = class(TCustomConnection)
  113. private
  114. protected
  115. fOnRead: TWebSocketConnectionData;
  116. fOnReadFull: TWebSocketConnectionDataFull;
  117. fOnWrite: TWebSocketConnectionData;
  118. fOnClose: TWebSocketConnectionClose;
  119. fOnOpen: TWebSocketConnectionEvent;
  120. //fOnPing: TWebSocketConnectionPingPongEvent;
  121. //fOnPong: TWebSocketConnectionPingPongEvent;
  122. fCookie: string;
  123. fVersion: integer;
  124. fProtocol: string;
  125. fResourceName: string;
  126. fOrigin: string;
  127. fExtension: string;
  128. fPort: string;
  129. fHost: string;
  130. fHeaders: TStringList;
  131. fClosedByMe: boolean;
  132. fClosedByPeer: boolean;
  133. fMasking: boolean;
  134. fRequireMasking: boolean;
  135. fHandshake: boolean;
  136. fCloseCode: integer;
  137. fCloseReason: string;
  138. fClosingByPeer: boolean;
  139. fReadFinal: boolean;
  140. fReadRes1: boolean;
  141. fReadRes2: boolean;
  142. fReadRes3: boolean;
  143. fReadCode: integer;
  144. fReadStream: TMemoryStream;
  145. fWriteFinal: boolean;
  146. fWriteRes1: boolean;
  147. fWriteRes2: boolean;
  148. fWriteRes3: boolean;
  149. fWriteCode: integer;
  150. fWriteStream: TMemoryStream;
  151. fSendCriticalSection: TCriticalSection;
  152. fFullDataProcess: boolean;
  153. fFullDataStream: TMemoryStream;
  154. function GetClosed: boolean;
  155. function GetClosing: boolean;
  156. procedure ExecuteConnection; override;
  157. function ReadData(var aFinal, aRes1, aRes2, aRes3: boolean; var aCode: integer; aData: TMemoryStream): integer; virtual;
  158. function ValidConnection: boolean;
  159. procedure DoSyncClose;
  160. procedure DoSyncOpen;
  161. //procedure DoSyncPing;
  162. //procedure DoSyncPong;
  163. procedure DoSyncRead;
  164. procedure DoSyncReadFull;
  165. procedure DoSyncWrite;
  166. procedure SyncClose;
  167. procedure SyncOpen;
  168. //procedure SyncPing;
  169. //procedure SyncPong;
  170. procedure SyncRead;
  171. procedure SyncReadFull;
  172. procedure SyncWrite;
  173. {:Overload this function to process connection close (not at socket level, but as an actual WebSocket frame)
  174. aCloseCode represents close code (see wsClose constants)
  175. aCloseReason represents textual information transfered with frame (there is no specified format or meaning)
  176. aClosedByPeer whether connection has been closed by this connection object or by peer endpoint
  177. }
  178. procedure ProcessClose(aCloseCode: integer; aCloseReason: string; aClosedByPeer: boolean); virtual;
  179. {:Overload this function to process data as soon as they are read before other Process<data> function is called
  180. this function should be used by extensions to modify incomming data before the are process based on code
  181. }
  182. procedure ProcessData(var aFinal: boolean; var aRes1: boolean; var aRes2: boolean; var aRes3: boolean; var aCode: integer; aData: TMemoryStream); virtual;
  183. {:Overload this function to process ping frame)
  184. aData represents textual information transfered with frame (there is no specified format or meaning)
  185. }
  186. procedure ProcessPing(aData: string); virtual;
  187. {:Overload this function to process pong frame)
  188. aData represents textual information transfered with frame (there is no specified format or meaning)
  189. }
  190. procedure ProcessPong(aData: string); virtual;
  191. {:Overload this function to process binary frame)
  192. aFinal whether frame is final frame or continuing
  193. aRes1 whether 1st extension bit is set up
  194. aRes2 whether 2nd extension bit is set up
  195. aRes3 whether 3rd extension bit is set up
  196. aData data stream
  197. second version is for contuniation frames
  198. }
  199. procedure ProcessStream(aFinal, aRes1, aRes2, aRes3: boolean; aData: TMemoryStream); virtual;
  200. procedure ProcessStreamContinuation(aFinal, aRes1, aRes2, aRes3: boolean; aData: TMemoryStream); virtual;
  201. procedure ProcessStreamFull(aData: TMemoryStream); virtual;
  202. {:Overload this function to process text frame)
  203. aFinal whether frame is final frame or continuing
  204. aRes1 whether 1st extension bit is set up
  205. aRes2 whether 2nd extension bit is set up
  206. aRes3 whether 3rd extension bit is set up
  207. aData textual data
  208. second version is for contuniation frames
  209. }
  210. procedure ProcessText(aFinal, aRes1, aRes2, aRes3: boolean; aData: string); virtual;
  211. procedure ProcessTextContinuation(aFinal, aRes1, aRes2, aRes3: boolean; aData: string); virtual;
  212. procedure ProcessTextFull(aData: string); virtual;
  213. published
  214. public
  215. constructor Create(aSocket: TTCPCustomConnectionSocket); override;
  216. destructor Destroy; override;
  217. {:
  218. Whether connection is in active state (not closed, closing, socket, exists, i/o threads not terminated..)
  219. }
  220. function CanReceiveOrSend: boolean;
  221. {:Procedure to close connection
  222. aCloseCode represents close code (see wsClose constants)
  223. aCloseReason represents textual information transfered with frame (there is no specified format or meaning) the string can only be 123 bytes length
  224. }
  225. procedure Close(aCode: integer; aCloseReason: string); virtual; abstract;
  226. {:Send binary frame
  227. aData data stream
  228. aFinal whether frame is final frame or continuing
  229. aRes1 1st extension bit
  230. aRes2 2nd extension bit
  231. aRes3 3rd extension bit
  232. }
  233. procedure SendBinary(aData: TStream; aFinal: boolean = true; aRes1: boolean = false; aRes2: boolean = false; aRes3: boolean = false);
  234. {:Send binary continuation frame
  235. aData data stream
  236. aFinal whether frame is final frame or continuing
  237. aRes1 1st extension bit
  238. aRes2 2nd extension bit
  239. aRes3 3rd extension bit
  240. }
  241. procedure SendBinaryContinuation(aData: TStream; aFinal: boolean = true; aRes1: boolean = false; aRes2: boolean = false; aRes3: boolean = false);
  242. {:Send generic frame
  243. aFinal whether frame is final frame or continuing
  244. aRes1 1st extension bit
  245. aRes2 2nd extension bit
  246. aRes3 3rd extension bit
  247. aCode frame code
  248. aData data stream or string
  249. }
  250. function SendData(aFinal, aRes1, aRes2, aRes3: boolean; aCode: integer; aData: TStream): integer; overload; virtual;
  251. function SendData(aFinal, aRes1, aRes2, aRes3: boolean; aCode: integer; aData: string): integer; overload; virtual;
  252. {:Send textual frame
  253. aData data string (MUST be UTF-8)
  254. aFinal whether frame is final frame or continuing
  255. aRes1 1st extension bit
  256. aRes2 2nd extension bit
  257. aRes3 3rd extension bit
  258. }
  259. procedure SendText(aData: string; aFinal: boolean = true; aRes1: boolean = false; aRes2: boolean = false; aRes3: boolean = false); virtual;
  260. {:Send textual continuation frame
  261. aData data string (MUST be UTF-8)
  262. aFinal whether frame is final frame or continuing
  263. aRes1 1st extension bit
  264. aRes2 2nd extension bit
  265. aRes3 3rd extension bit
  266. }
  267. procedure SendTextContinuation(aData: string; aFinal: boolean = true; aRes1: boolean = false; aRes2: boolean = false; aRes3: boolean = false);
  268. {:Send Ping
  269. aData ping informations
  270. }
  271. procedure Ping(aData: string);
  272. {:Send Pong
  273. aData pong informations
  274. }
  275. procedure Pong(aData: string);
  276. {:Temination procedure
  277. This method should be called instead of Terminate to terminate thread,
  278. it internally calls Terminate, but can be overloaded,
  279. and can be used for data clean up
  280. }
  281. procedure TerminateThread; override;
  282. {: Whether connection has been closed
  283. (either socket has been closed or thread has been terminated or WebSocket has been closed by this and peer connection)
  284. }
  285. property Closed: boolean read GetClosed;
  286. {: Whether WebSocket has been closed by this and peer connection }
  287. property Closing: boolean read GetClosing;
  288. {: WebSocket connection cookies
  289. Property is regular unparsed Cookie header string
  290. e.g. cookie1=value1;cookie2=value2
  291. empty string represents that no cookies are present
  292. }
  293. property Cookie: string read fCookie;
  294. {: WebSocket connection extensions
  295. Property is regular unparsed Sec-WebSocket-Extensions header string
  296. e.g. foo, bar; baz=2
  297. On both client and server connection this value represents the extension(s) selected by server to be used
  298. as a result of extension negotioation
  299. value - represents that no extension was negotiated and no header will be sent to client
  300. it is the default value
  301. }
  302. property Extension: string read fExtension;
  303. {:Whether to register for full data processing
  304. (callink @link(ProcessFullText), @link(ProcessFullStream) @link(OnFullRead)
  305. those methods/events are called if FullDataProcess is @true and whole message is read (after final frame)
  306. }
  307. property FullDataProcess: boolean read fFullDataProcess write fFullDataProcess;
  308. {:
  309. Whether WebSocket handshake was succecfull (and connection is afer WS handshake)
  310. }
  311. property Handshake: boolean read fHandshake;
  312. {: WebSocket connection host
  313. Property is regular unparsed Host header string
  314. e.g. server.example.com
  315. }
  316. property Host: string read fHost;
  317. {: WebSocket connection origin
  318. Property is regular unparsed Sec-WebSocket-Origin header string
  319. e.g. http://example.com
  320. }
  321. property Origin: string read fOrigin;
  322. {: WebSocket connection protocol
  323. Property is regular unparsed Sec-WebSocket-Protocol header string
  324. e.g. chat, superchat
  325. On both client and server connection this value represents the protocol(s) selected by server to be used
  326. as a result of protocol negotioation
  327. value - represents that no protocol was negotiated and no header will be sent to client
  328. it is the default value
  329. }
  330. property Protocol: string read fProtocol;
  331. {: Connection port }
  332. property Port: string read fPort;
  333. {: Connection resource
  334. e.g. /path1/path2/path3/file.ext
  335. }
  336. property ResourceName: string read fResourceName;
  337. {: WebSocket version (either 7 or 8 or 13)}
  338. property Version: integer read fVersion;
  339. {: WebSocket Close frame event }
  340. property OnClose: TWebSocketConnectionClose read fOnClose write fOnClose;
  341. {: WebSocket connection successfully }
  342. property OnOpen: TWebSocketConnectionEvent read fOnOpen write fOnOpen;
  343. { : WebSocket ping
  344. property OnPing: TWebSocketConnectionPingPongEvent read fOnPing write fOnPing;
  345. }
  346. { : WebSocket pong
  347. property OnPong: TWebSocketConnectionPingPongEvent read fOnPong write fOnPong;
  348. }
  349. {: WebSocket frame read }
  350. property OnRead: TWebSocketConnectionData read fOnRead write fOnRead;
  351. {: WebSocket read full data}
  352. property OnReadFull: TWebSocketConnectionDataFull read fOnReadFull write fOnReadFull;
  353. {: WebSocket frame written }
  354. property OnWrite: TWebSocketConnectionData read fOnWrite write fOnWrite;
  355. end;
  356. {: Class of WebSocket connections }
  357. TWebSocketCustomConnections = class of TWebSocketCustomConnection;
  358. {: WebSocket server connection automatically created by server on incoming connection }
  359. TWebSocketServerConnection = class(TWebSocketCustomConnection)
  360. public
  361. constructor Create(aSocket: TTCPCustomConnectionSocket); override;
  362. procedure Close(aCode: integer; aCloseReason: string); override;
  363. procedure TerminateThread; override;
  364. {: List of all headers
  365. keys are lowercased header name
  366. e.g host, connection, sec-websocket-key
  367. }
  368. property Header: TStringList read fHeaders;
  369. end;
  370. {: Class of WebSocket server connections }
  371. TWebSocketServerConnections = class of TWebSocketServerConnection;
  372. {: WebSocket client connection, this object shoud be created to establish client to server connection }
  373. TWebSocketClientConnection = class(TWebSocketCustomConnection)
  374. protected
  375. function BeforeExecuteConnection: boolean; override;
  376. public
  377. {: construstor to create connection,
  378. parameters has the same meaning as corresponging connection properties (see 2 differences below) and
  379. should be formated according to headers values
  380. aProtocol and aExtension in constructor represents protocol(s) and extension(s)
  381. client is trying to negotiate, obejst properties then represents
  382. protocol(s) and extension(s) the server is supporting (the negotiation result)
  383. Version must be >= 8
  384. }
  385. constructor Create(aHost, aPort, aResourceName: string;
  386. aOrigin: string = '-'; aProtocol: string = '-'; aExtension: string = '-';
  387. aCookie: string = '-'; aVersion: integer = 13); reintroduce; virtual;
  388. procedure Close(aCode: integer; aCloseReason: string); override;
  389. procedure Execute; override;
  390. end;
  391. TWebSocketServer = class;
  392. {:Event procedural type to hook OnReceiveConnection events on server
  393. every time new server connection is about to be created (client is connecting to server)
  394. this event is called
  395. properties are representing connection properties as defined in @link(TWebSocketServerConnection)
  396. Protocol and Extension represents corresponding headers sent by client, as their out value
  397. server must define what kind of protocol(s) and extension(s) server is supporting, if event
  398. is not implemented, both values are considered as - (no value at all)
  399. HttpResult represents the HTTP result to be send in response, if connection is about to be
  400. accepted, the value MUST BE 101, any other value meand that the client will be informed about the
  401. result (using the HTTP code meaning) and connection will be closed, if event is not implemented
  402. 101 is used as a default value
  403. }
  404. TWebSocketServerReceiveConnection = procedure (
  405. Server: TWebSocketServer; Socket: TTCPCustomConnectionSocket;
  406. Header: TStringList;
  407. ResourceName, Host, Port, Origin, Cookie: string;
  408. HttpResult: integer;
  409. Protocol, Extensions: string
  410. ) of object;
  411. TWebSocketServer = class(TCustomServer)
  412. protected
  413. {CreateServerConnection sync variables}
  414. fncSocket: TTCPCustomConnectionSocket;
  415. fncResourceName: string;
  416. fncHost: string;
  417. fncPort: string;
  418. fncOrigin: string;
  419. fncProtocol: string;
  420. fncExtensions: string;
  421. fncCookie: string;
  422. fncHeaders: string;
  423. fncResultHttp: integer;
  424. fOnReceiveConnection: TWebSocketServerReceiveConnection; protected
  425. function CreateServerConnection(aSocket: TTCPCustomConnectionSocket): TCustomConnection; override;
  426. procedure DoSyncReceiveConnection;
  427. procedure SyncReceiveConnection;
  428. property Terminated;
  429. {:This function defines what kind of TWebSocketServerConnection implementation should be used as
  430. a connection object.
  431. The servers default return value is TWebSocketServerConnection.
  432. If new connection class based on TWebSocketServerConnection is implemented,
  433. new server should be implemented as well with this method overloaded
  434. properties are representing connection properties as defined in @link(TWebSocketServerConnection)
  435. Protocol and Extension represents corresponding headers sent by client, as their out value
  436. server must define what kind of protocol(s) and extension(s) server is supporting, if event
  437. is not implemented, both values are cosidered as - (no value at all)
  438. HttpResult represents the HTTP result to be send in response, if connection is about to be
  439. accepted, the value MUST BE 101, any other value meand that the client will be informed about the
  440. result (using the HTTP code meaning) and connection will be closed, if event is not implemented
  441. 101 is used as a default value
  442. }
  443. function GetWebSocketConnectionClass(
  444. Socket: TTCPCustomConnectionSocket;
  445. Header: TStringList;
  446. ResourceName, Host, Port, Origin, Cookie: string;
  447. out HttpResult: integer;
  448. var Protocol, Extensions: string
  449. ): TWebSocketServerConnections; virtual;
  450. public
  451. {: WebSocket connection received }
  452. property OnReceiveConnection: TWebSocketServerReceiveConnection read fOnReceiveConnection write fOnReceiveConnection;
  453. {: close all connections
  454. for parameters see connection Close method
  455. }
  456. procedure CloseAllConnections(aCloseCode: integer; aReason: string);
  457. {:Temination procedure
  458. This method should be called instead of Terminate to terminate thread,
  459. it internally calls Terminate, but can be overloaded,
  460. and can be used for data clean up
  461. }
  462. procedure TerminateThread; override;
  463. {: Method to send binary data to all connected clients
  464. see @link(TWebSocketServerConnection.SendBinary) for parameters description
  465. }
  466. procedure BroadcastBinary(aData: TStream; aFinal: boolean = true; aRes1: boolean = false; aRes2: boolean = false; aRes3: boolean = false);
  467. {: Method to send text data to all connected clients
  468. see @link(TWebSocketServerConnection.SendText) for parameters description
  469. }
  470. procedure BroadcastText(aData: string; aFinal: boolean = true; aRes1: boolean = false; aRes2: boolean = false; aRes3: boolean = false);
  471. end;
  472. implementation
  473. uses Math, synautil, synacode, synsock {$IFDEF Win32}, Windows{$ENDIF Win32},
  474. BClasses, synachar;
  475. {$IFDEF Win32} {$O-} {$ENDIF Win32}
  476. function httpCode(code: integer): string;
  477. begin
  478. case (code) of
  479. 100: result := 'Continue';
  480. 101: result := 'Switching Protocols';
  481. 200: result := 'OK';
  482. 201: result := 'Created';
  483. 202: result := 'Accepted';
  484. 203: result := 'Non-Authoritative Information';
  485. 204: result := 'No Content';
  486. 205: result := 'Reset Content';
  487. 206: result := 'Partial Content';
  488. 300: result := 'Multiple Choices';
  489. 301: result := 'Moved Permanently';
  490. 302: result := 'Found';
  491. 303: result := 'See Other';
  492. 304: result := 'Not Modified';
  493. 305: result := 'Use Proxy';
  494. 307: result := 'Temporary Redirect';
  495. 400: result := 'Bad Request';
  496. 401: result := 'Unauthorized';
  497. 402: result := 'Payment Required';
  498. 403: result := 'Forbidden';
  499. 404: result := 'Not Found';
  500. 405: result := 'Method Not Allowed';
  501. 406: result := 'Not Acceptable';
  502. 407: result := 'Proxy Authentication Required';
  503. 408: result := 'Request Time-out';
  504. 409: result := 'Conflict';
  505. 410: result := 'Gone';
  506. 411: result := 'Length Required';
  507. 412: result := 'Precondition Failed';
  508. 413: result := 'Request Entity Too Large';
  509. 414: result := 'Request-URI Too Large';
  510. 415: result := 'Unsupported Media Type';
  511. 416: result := 'Requested range not satisfiable';
  512. 417: result := 'Expectation Failed';
  513. 500: result := 'Internal Server Error';
  514. 501: result := 'Not Implemented';
  515. 502: result := 'Bad Gateway';
  516. 503: result := 'Service Unavailable';
  517. 504: result := 'Gateway Time-out';
  518. else result := 'unknown code: $code';
  519. end;
  520. end;
  521. function ReadHttpHeaders(aSocket: TTCPCustomConnectionSocket; var aGet: string; aHeaders: TStrings): boolean;
  522. var s, name: string;
  523. begin
  524. aGet := '';
  525. aHeaders.Clear;
  526. result := true;
  527. repeat
  528. aSocket.MaxLineLength := 1024 * 1024; // not to attack memory on server
  529. s := aSocket.RecvString(30 * 1000); // not to hang up connection
  530. if (aSocket.LastError <> 0) then
  531. begin
  532. result := false;
  533. break;
  534. end;
  535. if (s = '') then
  536. break;
  537. if (aGet = '') then
  538. aGet := s
  539. else
  540. begin
  541. name := LowerCase(trim(SeparateLeft(s, ':')));
  542. if (aHeaders.Values[name] = '') then
  543. aHeaders.Values[name] := trim(SeparateRight(s, ':'))
  544. else
  545. aHeaders.Values[name] := aHeaders.Values[name] + ',' + trim(SeparateRight(s, ':'));
  546. end;
  547. until {IsTerminated} false;
  548. aSocket.MaxLineLength := 0;
  549. end;
  550. procedure ODS(aStr: string); overload;
  551. begin
  552. {$IFDEF Win32}
  553. OutputDebugString(pChar(FormatDateTime('yyyy-mm-dd hh:nn:ss', now) + ': ' + aStr));
  554. {$ENDIF Win32}
  555. end;
  556. procedure ODS(aStr: string; aData: array of const); overload;
  557. begin
  558. {$IFDEF Win32}
  559. ODS(Format(aStr, aData));
  560. {$ENDIF Win32}
  561. end;
  562. { TWebSocketServer }
  563. procedure TWebSocketServer.BroadcastBinary(aData: TStream; aFinal: boolean = true; aRes1: boolean = false; aRes2: boolean = false; aRes3: boolean = false);
  564. var i: integer;
  565. begin
  566. LockTermination;
  567. for i := 0 to fConnections.Count - 1 do
  568. begin
  569. if (not TWebSocketServerConnection(fConnections[i]).IsTerminated) then
  570. TWebSocketServerConnection(fConnections[i]).SendBinary(aData, aFinal, aRes1, aRes2, aRes3);
  571. end;
  572. UnLockTermination;
  573. end;
  574. procedure TWebSocketServer.BroadcastText(aData: string; aFinal: boolean = true; aRes1: boolean = false; aRes2: boolean = false; aRes3: boolean = false);
  575. var i: integer;
  576. begin
  577. LockTermination;
  578. for i := 0 to fConnections.Count - 1 do
  579. begin
  580. if (not TWebSocketServerConnection(fConnections[i]).IsTerminated) then
  581. TWebSocketServerConnection(fConnections[i]).SendText(aData, aFinal, aRes1, aRes2, aRes3);
  582. end;
  583. UnLockTermination;
  584. end;
  585. procedure TWebSocketServer.CloseAllConnections(aCloseCode: integer; aReason: string);
  586. var i: integer;
  587. begin
  588. LockTermination;
  589. //for i := 0 to fConnections.Count - 1 do
  590. for i := fConnections.Count - 1 downto 0 do
  591. begin
  592. if (not TWebSocketServerConnection(fConnections[i]).IsTerminated) then
  593. TWebSocketServerConnection(fConnections[i]).Close(aCloseCode, aReason);// SendBinary(aData, aFinal, aRes1, aRes2, aRes3);
  594. end;
  595. UnLockTermination;
  596. end;
  597. function TWebSocketServer.CreateServerConnection(aSocket: TTCPCustomConnectionSocket): TCustomConnection;
  598. var headers, hrs: TStringList;
  599. get: string;
  600. s{, resName, host, port}, key, version{, origin, protocol, extensions, cookie}: string;
  601. iversion, vv: integer;
  602. res: boolean;
  603. r : TWebSocketServerConnections;
  604. begin
  605. fncSocket := aSocket;
  606. result := inherited CreateServerConnection(aSocket);
  607. headers := TStringList.Create;
  608. try
  609. res := ReadHttpHeaders(aSocket, get, headers);
  610. if (res) then
  611. begin
  612. res := false;
  613. try
  614. //CHECK HTTP GET
  615. if ((Pos('GET ', Uppercase(get)) <> 0) and (Pos(' HTTP/1.1', Uppercase(get)) <> 0)) then
  616. begin
  617. fncResourceName := SeparateRight(get, ' ');
  618. fncResourceName := SeparateLeft(fncResourceName, ' ');
  619. end
  620. else exit;
  621. fncResourceName := trim(fncResourceName);
  622. {
  623. : string;
  624. : string;
  625. : string;
  626. : string;
  627. : string;
  628. : string;
  629. : string;
  630. fncHeaders: string;
  631. }
  632. //CHECK HOST AND PORT
  633. s := headers.Values['host'];
  634. if (s <> '') then
  635. begin
  636. fncHost := trim(s);
  637. fncPort := SeparateRight(fncHost, ':');
  638. fncHost := SeparateLeft(fncHost, ':');
  639. end;
  640. fncHost := trim(fncHost);
  641. fncPort := trim(fncPort);
  642. if (fncHost = '') then exit;
  643. //if (fncPort <> '') and (fncPort <> self.port) then exit;
  644. {
  645. if (self.host <> '0.0.0.0') and (self.Host <> '127.0.0.1') and
  646. (self.host <> 'localhost') and (fncHost <> self.host) then exit;
  647. }
  648. //WEBSOCKET KEY
  649. s := headers.Values['sec-websocket-key'];
  650. if (s <> '') then
  651. begin
  652. if (Length(DecodeBase64(s)) = 16) then
  653. begin
  654. key := s;
  655. end;
  656. end;
  657. if (key = '') then exit;
  658. key := trim(key);
  659. //WEBSOCKET VERSION
  660. s := headers.Values['sec-websocket-version'];
  661. if (s <> '') then
  662. begin
  663. vv := StrToIntDef(s, -1);
  664. if ((vv >= 7) and (vv <= 13)) then
  665. begin
  666. version := s;
  667. end;
  668. end;
  669. if (version = '') then exit;
  670. version := trim(version);
  671. iversion := StrToIntDef(version, 13);
  672. if (LowerCase(headers.Values['upgrade']) <> LowerCase('websocket')) or (pos('upgrade', LowerCase(headers.Values['connection'])) = 0) then
  673. exit;
  674. //COOKIES
  675. fncProtocol := '-';
  676. fncExtensions := '-';
  677. fncCookie := '-';
  678. fncOrigin := '-';
  679. if (iversion < 13) then
  680. begin
  681. if (headers.IndexOfName('sec-websocket-origin') > -1) then
  682. fncOrigin := trim(headers.Values['sec-websocket-origin']);
  683. end
  684. else begin
  685. if (headers.IndexOfName('origin') > -1) then
  686. fncOrigin := trim(headers.Values['origin']);
  687. end;
  688. if (headers.IndexOfName('sec-websocket-protocol') > -1) then
  689. fncProtocol := trim(headers.Values['sec-websocket-protocol']);
  690. if (headers.IndexOfName('sec-websocket-extensions') > -1) then
  691. fncExtensions := trim(headers.Values['sec-websocket-extensions']);
  692. if (headers.IndexOfName('cookie') > -1) then
  693. fncCookie := trim(headers.Values['cookie']);
  694. fncHeaders := trim(headers.text);
  695. {
  696. ODS(get);
  697. ODS(fncHeaders);
  698. ODS('ResourceName: %s', [fncResourceName]);
  699. ODS('Host: %s', [fncHost]);
  700. ODS('Post: %s', [fncPort]);
  701. ODS('Key: %s', [key]);
  702. ODS('Version: %s', [version]);
  703. ODS('Origin: %s', [fncOrigin]);
  704. ODS('Protocol: %s', [fncProtocol]);
  705. ODS('Extensions: %s', [fncExtensions]);
  706. ODS('Cookie: %s', [fncCookie]);
  707. {}
  708. res := true;
  709. finally
  710. if (res) then
  711. begin
  712. fncResultHttp := 101;
  713. hrs := TStringList.Create;
  714. hrs.Assign(headers);
  715. r := GetWebSocketConnectionClass(
  716. fncSocket,
  717. hrs,
  718. fncResourceName, fncHost, fncPort, fncOrigin, fncCookie,
  719. fncResultHttp, fncProtocol, fncExtensions
  720. );
  721. if (assigned(r)) then
  722. begin
  723. DoSyncReceiveConnection;
  724. if (fncResultHttp <> 101) then //HTTP ERROR FALLBACK
  725. begin
  726. aSocket.SendString(Format('HTTP/1.1 %d %s'+#13#10, [fncResultHttp, httpCode(fncResultHttp)]));
  727. aSocket.SendString(Format('%d %s'+#13#10#13#10, [fncResultHttp, httpCode(fncResultHttp)]));
  728. end
  729. else
  730. begin
  731. key := EncodeBase64(SHA1(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'));
  732. s := 'HTTP/1.1 101 Switching Protocols' + #13#10;
  733. s := s + 'Upgrade: websocket' + #13#10;
  734. s := s + 'Connection: Upgrade' + #13#10;
  735. s := s + 'Sec-WebSocket-Accept: ' + key + #13#10;
  736. if (fncProtocol <> '-') then
  737. begin
  738. s := s + 'Sec-WebSocket-Protocol: ' + fncProtocol + #13#10;
  739. end;
  740. if (fncExtensions <> '-') then
  741. begin
  742. s := s + 'Sec-WebSocket-Extensions: ' + fncExtensions + #13#10;
  743. end;
  744. s := s + #13#10;
  745. aSocket.SendString(s);
  746. if (aSocket.LastError = 0) then
  747. begin
  748. result := r.Create(aSocket);
  749. TWebSocketCustomConnection(result).fCookie := fncCookie;
  750. TWebSocketCustomConnection(result).fVersion := StrToInt(version);
  751. TWebSocketCustomConnection(result).fProtocol := fncProtocol;
  752. TWebSocketCustomConnection(result).fResourceName := fncResourceName;
  753. TWebSocketCustomConnection(result).fOrigin := fncOrigin;
  754. TWebSocketCustomConnection(result).fExtension := fncExtensions;
  755. TWebSocketCustomConnection(result).fPort := fncPort;
  756. TWebSocketCustomConnection(result).fHost := fncHost;
  757. TWebSocketCustomConnection(result).fHeaders.Assign(headers);
  758. TWebSocketCustomConnection(result).fHandshake := true;
  759. end;
  760. end;
  761. end;
  762. hrs.Free;
  763. end;
  764. end;
  765. end;
  766. finally
  767. headers.Free;
  768. end;
  769. end;
  770. procedure TWebSocketServer.DoSyncReceiveConnection;
  771. begin
  772. if (assigned(fOnReceiveConnection)) then
  773. Synchronize(SyncReceiveConnection)
  774. end;
  775. function TWebSocketServer.GetWebSocketConnectionClass( Socket: TTCPCustomConnectionSocket;
  776. Header: TStringList;
  777. ResourceName, Host, Port, Origin, Cookie: string;
  778. out HttpResult: integer;
  779. var Protocol, Extensions: string
  780. ): TWebSocketServerConnections;
  781. begin
  782. result := TWebSocketServerConnection;
  783. end;
  784. procedure TWebSocketServer.SyncReceiveConnection;
  785. var h: TStringList;
  786. begin
  787. if (assigned(fOnReceiveConnection)) then
  788. begin
  789. h := TStringList.Create;
  790. h.Text := fncHeaders;
  791. fOnReceiveConnection(
  792. self, fncSocket,
  793. h,
  794. fncResourceName, fncHost, fncPort, fncOrigin, fncCookie,
  795. fncResultHttp, fncProtocol, fncExtensions
  796. );
  797. h.Free;
  798. end;
  799. end;
  800. procedure TWebSocketServer.TerminateThread;
  801. begin
  802. if (terminated) then exit;
  803. fOnReceiveConnection := nil;
  804. inherited;
  805. end;
  806. { TWebSocketCustomConnection }
  807. function TWebSocketCustomConnection.CanReceiveOrSend: boolean;
  808. begin
  809. result := ValidConnection and not (fClosedByMe or fClosedByPeer) and fHandshake;
  810. end;
  811. constructor TWebSocketCustomConnection.Create(aSocket: TTCPCustomConnectionSocket);
  812. begin
  813. fHeaders := TStringList.Create;
  814. fCookie := '';
  815. fVersion := 0;
  816. fProtocol := '-';
  817. fResourceName := '';
  818. fOrigin := '';
  819. fExtension := '-';
  820. fPort := '';
  821. fHost := '';
  822. fClosedByMe := false;
  823. fClosedByPeer := false;
  824. fMasking := false;
  825. fClosingByPeer := false;
  826. fRequireMasking := false;
  827. fReadFinal := false;
  828. fReadRes1 := false;
  829. fReadRes2 := false;
  830. fReadRes3 := false;
  831. fReadCode := 0;
  832. fReadStream := TMemoryStream.Create;
  833. fWriteFinal := false;
  834. fWriteRes1 := false;
  835. fWriteRes2 := false;
  836. fWriteRes3 := false;
  837. fWriteCode := 0;
  838. fWriteStream := TMemoryStream.Create;
  839. fFullDataProcess := false;
  840. fFullDataStream := TMemoryStream.Create;
  841. fSendCriticalSection := TCriticalSection.Create;
  842. fHandshake := false;
  843. inherited;
  844. end;
  845. destructor TWebSocketCustomConnection.Destroy;
  846. begin
  847. fSendCriticalSection.Free;
  848. fFullDataStream.Free;
  849. fWriteStream.Free;
  850. fReadStream.Free;
  851. fHeaders.Free;
  852. inherited;
  853. end;
  854. procedure TWebSocketCustomConnection.DoSyncClose;
  855. begin
  856. if (assigned(fOnClose)) then
  857. Synchronize(SyncClose);
  858. end;
  859. procedure TWebSocketCustomConnection.DoSyncOpen;
  860. begin
  861. if (assigned(fOnOpen)) then
  862. Synchronize(SyncOpen);
  863. end;
  864. {
  865. procedure TWebSocketCustomConnection.DoSyncPing;
  866. begin
  867. end;
  868. procedure TWebSocketCustomConnection.DoSyncPong;
  869. begin
  870. end;
  871. }
  872. procedure TWebSocketCustomConnection.DoSyncRead;
  873. begin
  874. fReadStream.Position := 0;
  875. if (assigned(fOnRead)) then
  876. Synchronize(SyncRead);
  877. end;
  878. procedure TWebSocketCustomConnection.DoSyncReadFull;
  879. begin
  880. fFullDataStream.Position := 0;
  881. if (assigned(fOnReadFull)) then
  882. Synchronize(SyncReadFull);
  883. end;
  884. procedure TWebSocketCustomConnection.DoSyncWrite;
  885. begin
  886. if (assigned(fOnWrite)) then
  887. Synchronize(SyncWrite);
  888. end;
  889. procedure TWebSocketCustomConnection.ExecuteConnection;
  890. var
  891. result: integer;
  892. //Data: string;
  893. closeCode: integer;
  894. closeResult: string;
  895. s: string;
  896. lastDataCode, lastDataCode2: integer;
  897. //Data: TStringStream;
  898. begin
  899. DoSyncOpen;
  900. try
  901. //while(not IsTerminated) or fClosed do
  902. lastDataCode := -1;
  903. lastDataCode2 := -1;
  904. while CanReceiveOrSend do
  905. begin
  906. //OutputDebugString(pChar(Format('execute %d', [fIndex])));
  907. result := ReadData(fReadFinal, fReadRes1, fReadRes2, fReadRes3, fReadCode, fReadStream);
  908. if (CanReceiveOrSend) then
  909. begin
  910. if (result = 0) then // no socket error occured
  911. begin
  912. fReadStream.Position := 0;
  913. ProcessData(fReadFinal, fReadRes1, fReadRes2, fReadRes3, fReadCode, fReadStream);
  914. fReadStream.Position := 0;
  915. if (fReadCode in [wsCodeText, wsCodeBinary]) and fFullDataProcess then
  916. begin
  917. fFullDataStream.Size := 0;
  918. fFullDataStream.Position := 0;
  919. end;
  920. if (fReadCode in [wsCodeContinuation, wsCodeText, wsCodeBinary]) and fFullDataProcess then
  921. begin
  922. fReadStream.Position := 0;
  923. fFullDataStream.CopyFrom(fReadStream, fReadStream.Size);
  924. fReadStream.Position := 0;
  925. end;
  926. //if (fReadFinal) then //final frame
  927. begin
  928. case fReadCode of
  929. wsCodeContinuation: begin
  930. if (lastDataCode = wsCodeText) then
  931. begin
  932. s := ReadStrFromStream(fReadStream, fReadStream.size);
  933. ProcessTextContinuation(fReadFinal, fReadRes1, fReadRes2, fReadRes3, s);
  934. DoSyncRead;
  935. end
  936. else if (lastDataCode = wsCodeBinary) then
  937. begin
  938. ProcessStreamContinuation(fReadFinal, fReadRes1, fReadRes2, fReadRes3, fReadStream);
  939. DoSyncRead;
  940. end
  941. else Close(wsCloseErrorProtocol, 'Unknown continuaton');
  942. if (fReadFinal) then lastDataCode := -1;
  943. end;
  944. wsCodeText: begin // text, binary frame
  945. s := ReadStrFromStream(fReadStream, fReadStream.size);
  946. ProcessText(fReadFinal, fReadRes1, fReadRes2, fReadRes3, s);
  947. DoSyncRead;
  948. if (not fReadFinal) then lastDataCode := wsCodeText
  949. else lastDataCode := -1;
  950. lastDataCode2 := wsCodeText;
  951. end;
  952. wsCodeBinary: begin // text, binary frame
  953. ProcessStream(fReadFinal, fReadRes1, fReadRes2, fReadRes3, fReadStream);
  954. DoSyncRead;
  955. if (not fReadFinal) then lastDataCode := wsCodeBinary
  956. else lastDataCode := -1;
  957. lastDataCode2 := wsCodeBinary;
  958. end;
  959. wsCodeClose: begin //connection close
  960. closeCode := wsCloseNoStatus;
  961. closeResult := ReadStrFromStream(fReadStream, fReadStream.size);
  962. if (length(closeResult) > 1) then
  963. begin
  964. closeCode := ord(closeResult[1])*256 + ord(closeResult[2]);
  965. delete(closeResult, 1, 2);
  966. end;
  967. fClosedByPeer := true;
  968. //OutputDebugString(pChar(Format('closing1 %d', [fIndex])));
  969. ProcessClose(closeCode, closeResult, true);
  970. //OutputDebugString(pChar(Format('closing2 %d', [fIndex])));
  971. TerminateThread;
  972. //OutputDebugString(pChar(Format('closing3 %d', [fIndex])));
  973. fSendCriticalSection.Enter;
  974. end;
  975. wsCodePing: begin // ping
  976. ProcessPing(ReadStrFromStream(fReadStream, fReadStream.size));
  977. DoSyncRead;
  978. end;
  979. wsCodePong: begin // pong
  980. ProcessPong(ReadStrFromStream(fReadStream, fReadStream.size));
  981. DoSyncRead;
  982. end
  983. else begin //ERROR
  984. Close(wsCloseErrorData, Format('Unknown data type: %d', [fReadCode]));
  985. end;
  986. end;
  987. end;
  988. if (fReadCode in [wsCodeContinuation, wsCodeText, wsCodeBinary]) and fFullDataProcess and fReadFinal then
  989. begin
  990. fFullDataStream.Position := 0;
  991. if (lastDataCode2 = wsCodeText) then
  992. begin
  993. s := ReadStrFromStream(fFullDataStream, fFullDataStream.size);
  994. ProcessTextFull(s);
  995. end
  996. else if (lastDataCode2 = wsCodeBinary) then ProcessStreamFull(fFullDataStream);
  997. SyncReadFull;
  998. end;
  999. end
  1000. else
  1001. TerminateThread;
  1002. end;
  1003. end;
  1004. finally
  1005. {$IFDEF UNIX} sleep(2000); {$ENDIF UNIX}
  1006. end;
  1007. while not terminated do sleep(500);
  1008. //OutputDebugString(pChar(Format('terminating %d', [fIndex])));
  1009. fSendCriticalSection.Enter;
  1010. end;
  1011. function TWebSocketCustomConnection.GetClosed: boolean;
  1012. begin
  1013. result := not CanReceiveOrSend;
  1014. end;
  1015. function TWebSocketCustomConnection.GetClosing: boolean;
  1016. begin
  1017. result := (fClosedByMe or fClosedByPeer);
  1018. end;
  1019. procedure TWebSocketCustomConnection.Ping(aData: string);
  1020. begin
  1021. if (CanReceiveOrSend) then
  1022. begin
  1023. SendData(true, false, false, false, wsCodePing, aData);
  1024. end;
  1025. end;
  1026. procedure TWebSocketCustomConnection.Pong(aData: string);
  1027. begin
  1028. if (CanReceiveOrSend) then
  1029. begin
  1030. SendData(true, false, false, false, wsCodePong, aData);
  1031. end;
  1032. end;
  1033. procedure TWebSocketCustomConnection.ProcessClose(aCloseCode: integer; aCloseReason: string; aClosedByPeer: boolean);
  1034. begin
  1035. fCloseCode := aCloseCode;
  1036. fCloseReason := aCloseReason;
  1037. fClosingByPeer := aClosedByPeer;
  1038. DoSyncClose;
  1039. end;
  1040. procedure TWebSocketCustomConnection.ProcessData(var aFinal, aRes1, aRes2,
  1041. aRes3: boolean; var aCode: integer; aData: TMemoryStream);
  1042. begin
  1043. end;
  1044. procedure TWebSocketCustomConnection.ProcessPing(aData: string);
  1045. begin
  1046. Pong(aData);
  1047. end;
  1048. procedure TWebSocketCustomConnection.ProcessPong(aData: string);
  1049. begin
  1050. end;
  1051. procedure TWebSocketCustomConnection.ProcessStream(aFinal, aRes1, aRes2,
  1052. aRes3: boolean; aData: TMemoryStream);
  1053. begin
  1054. end;
  1055. procedure TWebSocketCustomConnection.ProcessStreamContinuation(aFinal,
  1056. aRes1, aRes2, aRes3: boolean; aData: TMemoryStream);
  1057. begin
  1058. end;
  1059. procedure TWebSocketCustomConnection.ProcessStreamFull(
  1060. aData: TMemoryStream);
  1061. begin
  1062. end;
  1063. procedure TWebSocketCustomConnection.ProcessText(aFinal, aRes1, aRes2,
  1064. aRes3: boolean; aData: string);
  1065. begin
  1066. end;
  1067. procedure TWebSocketCustomConnection.ProcessTextContinuation(aFinal, aRes1,
  1068. aRes2, aRes3: boolean; aData: string);
  1069. begin
  1070. end;
  1071. procedure TWebSocketCustomConnection.ProcessTextFull(aData: string);
  1072. begin
  1073. end;
  1074. function GetByte(aSocket: TTCPCustomConnectionSocket; var aByte: Byte; var aTimeout: integer): integer;
  1075. begin
  1076. aByte := aSocket.RecvByte(aTimeout);
  1077. result := aSocket.LastError;
  1078. end;
  1079. function hexToStr(aDec: integer; aLength: integer): string;
  1080. var tmp: string;
  1081. i: integer;
  1082. begin
  1083. tmp := IntToHex(aDec, aLength);
  1084. result := '';
  1085. for i := 1 to (Length(tmp)+1) div 2 do
  1086. begin
  1087. result := result + ansichar(StrToInt('$'+Copy(tmp, i * 2 - 1, 2)));
  1088. end;
  1089. end;
  1090. function StrToHexstr2(str: string): string;
  1091. var i: integer;
  1092. begin
  1093. result := '';
  1094. for i := 1 to Length(str) do result := result + IntToHex(ord(str[i]), 2) + ' ';
  1095. end;
  1096. function TWebSocketCustomConnection.ReadData(var aFinal, aRes1, aRes2, aRes3: boolean;
  1097. var aCode: integer; aData: TMemoryStream): integer;
  1098. var timeout: integer;
  1099. b: byte;
  1100. mask: boolean;
  1101. len, i: int64;
  1102. mBytes: array[0..3] of byte;
  1103. ms: TMemoryStream;
  1104. begin
  1105. result := 0;
  1106. len := 0;
  1107. //aCode := 0;
  1108. repeat
  1109. timeout := 10 * 1000;
  1110. if CanReceiveOrSend then
  1111. begin
  1112. //OutputDebugString(pChar(Format('%d', [Index])));
  1113. if (fSocket.CanReadEx(1000)) then
  1114. begin
  1115. if CanReceiveOrSend then
  1116. begin
  1117. b := fSocket.RecvByte(1000);
  1118. if (fSocket.LastError = 0) then
  1119. begin
  1120. try
  1121. try
  1122. // BASIC INFORMATIONS
  1123. aFinal := (b and $80) = $80;
  1124. aRes1 := (b and $40) = $40;
  1125. aRes2 := (b and $20) = $20;
  1126. aRes3 := (b and $10) = $10;
  1127. aCode := b and $F;
  1128. // MASK AND LENGTH
  1129. mask := false;
  1130. result := GetByte(fSocket, b, timeout);
  1131. if (result = 0) then
  1132. begin
  1133. mask := (b and $80) = $80;
  1134. len := (b and $7F);
  1135. if (len = 126) then
  1136. begin
  1137. result := GetByte(fSocket, b, timeout);
  1138. if (result = 0) then
  1139. begin
  1140. len := b * $100; // 00 00
  1141. result := GetByte(fSocket, b, timeout);
  1142. if (result = 0) then
  1143. begin
  1144. len := len + b;
  1145. end;
  1146. end;
  1147. end
  1148. else if (len = 127) then //00 00 00 00 00 00 00 00
  1149. begin
  1150. //TODO nesting og get byte should be different
  1151. result := GetByte(fSocket, b, timeout);
  1152. if (result = 0) then
  1153. begin
  1154. len := b * $100000000000000;
  1155. if (result = 0) then
  1156. begin
  1157. result := GetByte(fSocket, b, timeout);
  1158. len := len + b * $1000000000000;
  1159. end;
  1160. if (result = 0) then
  1161. begin
  1162. result := GetByte(fSocket, b, timeout);
  1163. len := len + b * $10000000000;
  1164. end;
  1165. if (result = 0) then
  1166. begin
  1167. result := GetByte(fSocket, b, timeout);
  1168. len := len + b * $100000000;
  1169. end;
  1170. if (result = 0) then
  1171. begin
  1172. result := GetByte(fSocket, b, timeout);
  1173. len := len + b * $1000000;
  1174. end;
  1175. if (result = 0) then
  1176. begin
  1177. result := GetByte(fSocket, b, timeout);
  1178. len := len + b * $10000;
  1179. end;
  1180. if (result = 0) then
  1181. begin
  1182. result := GetByte(fSocket, b, timeout);
  1183. len := len + b * $100;
  1184. end;
  1185. if (result = 0) then
  1186. begin
  1187. result := GetByte(fSocket, b, timeout);
  1188. len := len + b;
  1189. end;
  1190. end;
  1191. end;
  1192. end;
  1193. if (result = 0) and (fRequireMasking) and (not mask) then
  1194. begin
  1195. // TODO some protocol error
  1196. raise Exception.Create('mask');
  1197. end;
  1198. // MASKING KEY
  1199. if (mask) and (result = 0) then
  1200. begin
  1201. result := GetByte(fSocket, mBytes[0], timeout);
  1202. if (result = 0) then result := GetByte(fSocket, mBytes[1], timeout);
  1203. if (result = 0) then result := GetByte(fSocket, mBytes[2], timeout);
  1204. if (result = 0) then result := GetByte(fSocket, mBytes[3], timeout);
  1205. end;
  1206. // READ DATA
  1207. if (result = 0) then
  1208. begin
  1209. aData.Clear;
  1210. ms := TMemoryStream.Create;
  1211. try
  1212. timeout := 1000 * 60 * 60 * 2; //(len div (1024 * 1024)) * 1000 * 60;
  1213. if (mask) then fSocket.RecvStreamSize(ms, timeout, len)
  1214. else fSocket.RecvStreamSize(aData, timeout, len);
  1215. ms.Position := 0;
  1216. aData.Position := 0;
  1217. result := fSocket.LastError;
  1218. if (result = 0) then
  1219. begin
  1220. if (mask) then
  1221. begin
  1222. i := 0;
  1223. while i < len do
  1224. begin
  1225. ms.ReadBuffer(b, sizeOf(b));
  1226. b := b xor mBytes[i mod 4];
  1227. aData.WriteBuffer(b, SizeOf(b));
  1228. inc(i);
  1229. end;
  1230. end;
  1231. end;
  1232. finally
  1233. ms.free;
  1234. end;
  1235. aData.Position := 0;
  1236. break;
  1237. end;
  1238. except
  1239. result := -1;
  1240. end;
  1241. finally
  1242. end;
  1243. end
  1244. else
  1245. begin
  1246. result := -1;
  1247. end;
  1248. end
  1249. else
  1250. begin
  1251. result := -1;
  1252. end;
  1253. end
  1254. else
  1255. begin
  1256. // if (fSocket.CanRead(0)) then
  1257. // ODS(StrToHexstr2(fSocket.RecvBufferStr(10, 1000)));
  1258. if (fSocket.LastError <> WSAETIMEDOUT) and (fSocket.LastError <> 0) then
  1259. begin
  1260. //if (fSocket.LastError = WS then
  1261. result := -1;
  1262. end;
  1263. end;
  1264. end
  1265. else
  1266. begin
  1267. result := -1;
  1268. end;
  1269. if (result <> 0) then
  1270. begin
  1271. if (not Terminated) then
  1272. begin
  1273. if (fSocket.LastError = WSAECONNRESET) then
  1274. begin
  1275. result := 0;
  1276. aCode := wsCodeClose;
  1277. aFinal := true;
  1278. aRes1 := false;
  1279. aRes2 := false;
  1280. aRes3 := false;
  1281. aData.Size := 0;
  1282. WriteStrToStream(aData, ansichar(wsCloseErrorClose div 256) + ansichar(wsCloseErrorClose mod 256));
  1283. aData.Position := 0;
  1284. end
  1285. else
  1286. begin
  1287. if (not fClosedByMe) then
  1288. begin
  1289. Close(wsCloseErrorProtocol, '');
  1290. TerminateThread;
  1291. end;
  1292. end;
  1293. end;
  1294. break;
  1295. end
  1296. until false;
  1297. end;
  1298. function TWebSocketCustomConnection.SendData(aFinal, aRes1, aRes2, aRes3: boolean; aCode: integer; aData: TStream): integer;
  1299. var b: byte;
  1300. s: ansistring;
  1301. mBytes: array[0..3] of byte;
  1302. ms: TMemoryStream;
  1303. i, len: int64;
  1304. begin
  1305. result := 0;
  1306. if (CanReceiveOrSend) or ((aCode = wsCodeClose) and (not fClosedByPeer)) then
  1307. begin
  1308. fSendCriticalSection.Enter;
  1309. try
  1310. s := '';
  1311. // BASIC INFORMATIONS
  1312. b := IfThen(aFinal, 1, 0) * $80;
  1313. b := b + IfThen(aRes1, 1, 0) * $40;
  1314. b := b + IfThen(aRes2, 1, 0) * $20;
  1315. b := b + IfThen(aRes3, 1, 0) * $10;
  1316. b := b + aCode;
  1317. s := s + ansichar(b);
  1318. // MASK AND LENGTH
  1319. b := IfThen(fMasking, 1, 0) * $80;
  1320. if (aData.Size < 126) then
  1321. b := b + aData.Size
  1322. else if (aData.Size < 65536) then
  1323. b := b + 126
  1324. else
  1325. b := b + 127;
  1326. s := s + ansichar(b);
  1327. if (aData.Size >= 126) then
  1328. begin
  1329. if (aData.Size < 65536) then
  1330. begin
  1331. s := s + hexToStr(aData.Size, 4);
  1332. end
  1333. else
  1334. begin
  1335. s := s + hexToStr(aData.Size, 16);
  1336. end;
  1337. end;
  1338. // MASKING KEY
  1339. if (fMasking) then
  1340. begin
  1341. mBytes[0] := Random(256);
  1342. mBytes[1] := Random(256);
  1343. mBytes[2] := Random(256);
  1344. mBytes[3] := Random(256);
  1345. s := s + ansichar(mBytes[0]);
  1346. s := s + ansichar(mBytes[1]);
  1347. s := s + ansichar(mBytes[2]);
  1348. s := s + ansichar(mBytes[3]);
  1349. end;
  1350. fSocket.SendString(s);
  1351. result := fSocket.LastError;
  1352. if (result = 0) then
  1353. begin
  1354. aData.Position := 0;
  1355. ms := TMemoryStream.Create;
  1356. try
  1357. if (not fMasking) then
  1358. begin
  1359. fSocket.SendStreamRaw(aData);
  1360. end
  1361. else
  1362. begin
  1363. i := 0;
  1364. len := aData.Size;
  1365. while i < len do
  1366. begin
  1367. aData.ReadBuffer(b, sizeOf(b));
  1368. b := b xor mBytes[i mod 4];
  1369. ms.WriteBuffer(b, SizeOf(b));
  1370. inc(i);
  1371. end;
  1372. ms.Position := 0;
  1373. fSocket.SendStreamRaw(ms);
  1374. end;
  1375. result := fSocket.LastError;
  1376. if (result = 0) then
  1377. begin
  1378. fWriteFinal := aFinal;
  1379. fWriteRes1 := aRes1;
  1380. fWriteRes2 := aRes2;
  1381. fWriteRes3 := aRes3;
  1382. fWriteCode := aCode;
  1383. aData.Position := 0;
  1384. fWriteStream.Clear;
  1385. fWriteStream.LoadFromStream(aData);
  1386. DoSyncWrite;
  1387. end;
  1388. finally
  1389. ms.Free;
  1390. end;
  1391. end;
  1392. finally
  1393. if (aCode <> wsCodeClose) then
  1394. while not fSocket.CanWrite(10) do sleep(10);
  1395. fSendCriticalSection.Leave;
  1396. end;
  1397. end;
  1398. end;
  1399. function TWebSocketCustomConnection.SendData(aFinal, aRes1, aRes2, aRes3: boolean; aCode: integer; aData: string): integer;
  1400. var ms : TMemoryStream;
  1401. begin
  1402. ms := TMemoryStream.Create;
  1403. try
  1404. WriteStrToStream(ms, aData);
  1405. result := SendData(aFinal, aRes1, aRes2, aRes3, aCode, ms);
  1406. finally
  1407. ms.Free;
  1408. end;
  1409. end;
  1410. procedure TWebSocketCustomConnection.SendBinary(aData: TStream; aFinal: boolean = true; aRes1: boolean = false; aRes2: boolean = false; aRes3: boolean = false);
  1411. begin
  1412. SendData(aFinal, aRes1, aRes2, aRes3, wsCodeBinary, aData);
  1413. end;
  1414. procedure TWebSocketCustomConnection.SendBinaryContinuation(aData: TStream; aFinal, aRes1, aRes2, aRes3: boolean);
  1415. begin
  1416. SendData(aFinal, aRes1, aRes2, aRes3, wsCodeContinuation, aData);
  1417. end;
  1418. procedure TWebSocketCustomConnection.SendText(aData: string; aFinal: boolean = true; aRes1: boolean = false; aRes2: boolean = false; aRes3: boolean = false);
  1419. begin
  1420. SendData(aFinal, aRes1, aRes2, aRes3, wsCodeText, aData);
  1421. end;
  1422. procedure TWebSocketCustomConnection.SendTextContinuation(aData: string; aFinal, aRes1, aRes2, aRes3: boolean);
  1423. begin
  1424. SendData(aFinal, aRes1, aRes2, aRes3, wsCodeContinuation, aData);
  1425. end;
  1426. {
  1427. procedure TWebSocketCustomConnection.SendStream(aFinal, aRes1, aRes2, aRes3: boolean; aData: TStream);
  1428. begin
  1429. if (CanReceiveOrSend) then
  1430. begin
  1431. SendData(aFinal, aRes1, aRes2, aRes3, wsCodeBinary, aData);
  1432. end;
  1433. end;
  1434. }
  1435. {
  1436. procedure TWebSocketCustomConnection.SendStream(aData: TStream);
  1437. begin
  1438. //SendStream(aFinal, false, false, false, aData);
  1439. end;
  1440. }
  1441. {
  1442. procedure TWebSocketCustomConnection.SendText(aFinal, aRes1, aRes2, aRes3: boolean; aData: string);
  1443. //var tmp: string;
  1444. begin
  1445. if (CanReceiveOrSend) then
  1446. begin
  1447. SendData(aFinal, false, false, false, wsCodeText, aData);
  1448. end;
  1449. end;
  1450. }
  1451. {
  1452. procedure TWebSocketCustomConnection.SendText(aData: string);
  1453. begin
  1454. //SendText(true, false, false, false, aData);
  1455. //SendData(true, false, false
  1456. end;
  1457. }
  1458. procedure TWebSocketCustomConnection.SyncClose;
  1459. begin
  1460. if (assigned(fOnClose)) then
  1461. fOnClose(self, fCloseCode, fCloseReason, fClosingByPeer);
  1462. end;
  1463. procedure TWebSocketCustomConnection.SyncOpen;
  1464. begin
  1465. if (assigned(fOnOpen)) then
  1466. fOnOpen(self);
  1467. end;
  1468. {
  1469. procedure TWebSocketCustomConnection.SyncPing;
  1470. begin
  1471. end;
  1472. procedure TWebSocketCustomConnection.SyncPong;
  1473. begin
  1474. end;
  1475. }
  1476. procedure TWebSocketCustomConnection.SyncRead;
  1477. begin
  1478. fReadStream.Position := 0;
  1479. if (assigned(fOnRead)) then
  1480. fOnRead(self, fReadFinal, fReadRes1, fReadRes2, fReadRes3, fReadCode, fReadStream);
  1481. end;
  1482. procedure TWebSocketCustomConnection.SyncReadFull;
  1483. begin
  1484. fFullDataStream.Position := 0;
  1485. if (assigned(fOnReadFull)) then
  1486. fOnReadFull(self, fReadCode, fFullDataStream);
  1487. end;
  1488. procedure TWebSocketCustomConnection.SyncWrite;
  1489. begin
  1490. fWriteStream.Position := 0;
  1491. if (assigned(fOnWrite)) then
  1492. fOnWrite(self, fWriteFinal, fWriteRes1, fWriteRes2, fWriteRes3, fWriteCode, fWriteStream);
  1493. end;
  1494. procedure TWebSocketCustomConnection.TerminateThread;
  1495. begin
  1496. if (Terminated) then exit;
  1497. if (not Closed) then
  1498. DoSyncClose;
  1499. Socket.OnSyncStatus := nil;
  1500. Socket.OnStatus := nil;
  1501. fOnRead := nil;
  1502. fOnReadFull := nil;
  1503. fOnWrite := nil;
  1504. fOnClose := nil;
  1505. fOnOpen := nil;
  1506. {
  1507. if not Closing then
  1508. begin
  1509. SendData(true, false, false, false, wsCodeClose, '1001');
  1510. end;
  1511. }
  1512. inherited;
  1513. end;
  1514. function TWebSocketCustomConnection.ValidConnection: boolean;
  1515. begin
  1516. result := (not IsTerminated) and (Socket.Socket <> INVALID_SOCKET);
  1517. end;
  1518. { TWebSocketServerConnection }
  1519. procedure TWebSocketServerConnection.Close(aCode: integer; aCloseReason: string);
  1520. begin
  1521. if (Socket.Socket <> INVALID_SOCKET) and (not fClosedByMe) then
  1522. begin
  1523. fClosedByMe := true;
  1524. if (not fClosedByPeer) then
  1525. begin
  1526. SendData(true, false, false, false, wsCodeClose, hexToStr(aCode, 4) + copy(aCloseReason, 1, 123));
  1527. //Sleep(2000);
  1528. ProcessClose(aCode, aCloseReason, false);
  1529. end;
  1530. TerminateThread;
  1531. end;
  1532. end;
  1533. constructor TWebSocketServerConnection.Create(aSocket: TTCPCustomConnectionSocket);
  1534. begin
  1535. inherited;
  1536. fRequireMasking := true;
  1537. end;
  1538. procedure TWebSocketServerConnection.TerminateThread;
  1539. begin
  1540. if (Terminated) then exit;
  1541. //if (not TWebSocketServer(fParent).Terminated) and (not fClosedByMe) then DoSyncClose;
  1542. fOnClose := nil;
  1543. inherited;
  1544. end;
  1545. { TWebSocketClientConnection }
  1546. function TWebSocketClientConnection.BeforeExecuteConnection: boolean;
  1547. var key, s, get: string;
  1548. i: integer;
  1549. headers: TStringList;
  1550. begin
  1551. Result := not IsTerminated;
  1552. if (Result) then
  1553. begin
  1554. s := Format('GET %s HTTP/1.1' + #13#10, [fResourceName]);
  1555. s := s + Format('Upgrade: websocket' + #13#10, []);
  1556. s := s + Format('Connection: Upgrade' + #13#10, []);
  1557. s := s + Format('Host: %s:%s' + #13#10, [fHost, fPort]);
  1558. for I := 1 to 16 do key := key + ansichar(Random(85) + 32);
  1559. key := EncodeBase64(key);
  1560. s := s + Format('Sec-WebSocket-Key: %s' + #13#10, [(key)]);
  1561. s := s + Format('Sec-WebSocket-Version: %d' + #13#10, [fVersion]);
  1562. //TODO extensions
  1563. if (fProtocol <> '-') then
  1564. s := s + Format('Sec-WebSocket-Protocol: %s' + #13#10, [fProtocol]);
  1565. if (fOrigin <> '-') then
  1566. begin
  1567. if (fVersion < 13) then
  1568. s := s + Format('Sec-WebSocket-Origin: %s' + #13#10, [fOrigin])
  1569. else
  1570. s := s + Format('Origin: %s' + #13#10, [fOrigin]);
  1571. end;
  1572. if (fCookie <> '-') then
  1573. s := s + Format('Cookie: %s' + #13#10, [(fCookie)]);
  1574. if (fExtension <> '-') then
  1575. s := s + Format('Sec-WebSocket-Extensions: %s' + #13#10, [fExtension]);
  1576. s := s + #13#10;
  1577. fSocket.SendString(s);
  1578. Result := (not IsTerminated) and (fSocket.LastError = 0);
  1579. if (result) then
  1580. begin
  1581. headers := TStringList.Create;
  1582. try
  1583. result := ReadHttpHeaders(fSocket, get, headers);
  1584. if (result) then result := pos(LowerCase('HTTP/1.1 101'), LowerCase(get)) = 1;
  1585. if (result) then result := (LowerCase(headers.Values['upgrade']) = LowerCase('websocket')) and (LowerCase(headers.Values['connection']) = 'upgrade');
  1586. fProtocol := '-';
  1587. fExtension := '-';
  1588. if (headers.IndexOfName('sec-websocket-protocol') > -1) then
  1589. fProtocol := trim(headers.Values['sec-websocket-protocol']);
  1590. if (headers.IndexOfName('sec-websocket-extensions') > -1) then
  1591. fExtension := trim(headers.Values['sec-websocket-extensions']);
  1592. if (result) then result := (headers.Values['sec-websocket-accept'] = EncodeBase64(SHA1(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
  1593. finally
  1594. headers.Free;
  1595. end;
  1596. end;
  1597. end;
  1598. if (result) then fHandshake := true;
  1599. end;
  1600. procedure TWebSocketClientConnection.Close(aCode: integer; aCloseReason: string);
  1601. begin
  1602. if ValidConnection and (not fClosedByMe) then
  1603. begin
  1604. fClosedByMe := true;
  1605. if (not fClosedByPeer) then
  1606. begin
  1607. SendData(true, false, false, false, wsCodeClose, hexToStr(aCode, 4) + copy(aCloseReason, 1, 123));
  1608. //Sleep(2000);
  1609. ProcessClose(aCode, aCloseReason, false);
  1610. end;
  1611. TerminateThread;
  1612. end;
  1613. end;
  1614. constructor TWebSocketClientConnection.Create(aHost, aPort,
  1615. aResourceName, aOrigin, aProtocol: string; aExtension: string; aCookie: string; aVersion: integer);
  1616. begin
  1617. fSocket := TTCPCustomConnectionSocket.Create;
  1618. inherited Create(fSocket);
  1619. fOrigin := aOrigin;
  1620. fHost := aHost;
  1621. fPort := aPort;
  1622. fResourceName := aResourceName;
  1623. fProtocol := aProtocol;
  1624. fVersion := aVersion;
  1625. fMasking := true;
  1626. fCookie := aCookie;
  1627. fExtension := aExtension;
  1628. end;
  1629. {
  1630. procedure TWebSocketClientConnection.DoConnect;
  1631. begin
  1632. if (assigned(fOnConnect)) then
  1633. Synchronize(SyncConnect);
  1634. end;
  1635. procedure TWebSocketClientConnection.DoDisconnect;
  1636. begin
  1637. if (assigned(fOnDisConnect)) then
  1638. Synchronize(SyncDisconnect);
  1639. end;
  1640. }
  1641. procedure TWebSocketClientConnection.Execute;
  1642. begin
  1643. if (not IsTerminated) and (fVersion >= 8) then
  1644. begin
  1645. fSocket.Connect(fHost, fPort);
  1646. if (SSL) then
  1647. fSocket.SSLDoConnect;
  1648. if (fSocket.LastError = 0) then
  1649. begin
  1650. //DoConnect;
  1651. inherited Execute;
  1652. //DoDisconnect;
  1653. end
  1654. else TerminateThread;
  1655. end;
  1656. end;
  1657. {
  1658. procedure TWebSocketClientConnection.SyncConnect;
  1659. begin
  1660. fOnConnect(self);
  1661. end;
  1662. procedure TWebSocketClientConnection.SyncDisconnect;
  1663. begin
  1664. fOnDisConnect(self);
  1665. end;
  1666. }
  1667. initialization
  1668. Randomize;
  1669. {
  1670. GET / HTTP/1.1
  1671. Upgrade: websocket
  1672. Connection: Upgrade
  1673. Host: 81.0.231.149:81
  1674. Sec-WebSocket-Origin: http://html5.bauglir.dev
  1675. Sec-WebSocket-Key: Q9ceXTuzjdF2o23CRYvnuA==
  1676. Sec-WebSocket-Version: 8
  1677. GET / HTTP/1.1
  1678. Host: 81.0.231.149:81
  1679. User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0
  1680. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  1681. Accept-Language: sk,cs;q=0.8,en-us;q=0.5,en;q=0.3
  1682. Accept-Encoding: gzip, deflate
  1683. Accept-Charset: ISO-8859-2,utf-8;q=0.7,*;q=0.7
  1684. Connection: keep-alive, Upgrade
  1685. Sec-WebSocket-Version: 7
  1686. Sec-WebSocket-Origin: http://html5.bauglir.dev
  1687. Sec-WebSocket-Key: HgBKcPfdBSzjCYxGnWCO3g==
  1688. Pragma: no-cache
  1689. Cache-Control: no-cache
  1690. Upgrade: websocket
  1691. Cookie: __utma=72544661.1949147240.1313811966.1313811966.1313811966.1; __utmb=72544661.3.10.1313811966; __utmc=72544661; __utmz=72544661.1313811966.1.1.utmcsr=localhost|utmccn=(referral)|utmcmd=referral|utmcct=/websocket/index.php
  1692. 1300}
  1693. end.