Client.php 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. <?php
  2. /**
  3. * Hoa
  4. *
  5. *
  6. * @license
  7. *
  8. * New BSD License
  9. *
  10. * Copyright © 2007-2017, Hoa community. All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or without
  13. * modification, are permitted provided that the following conditions are met:
  14. * * Redistributions of source code must retain the above copyright
  15. * notice, this list of conditions and the following disclaimer.
  16. * * Redistributions in binary form must reproduce the above copyright
  17. * notice, this list of conditions and the following disclaimer in the
  18. * documentation and/or other materials provided with the distribution.
  19. * * Neither the name of the Hoa nor the names of its contributors may be
  20. * used to endorse or promote products derived from this software without
  21. * specific prior written permission.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  24. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  25. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  26. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
  27. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  28. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  29. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  30. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  31. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  32. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33. * POSSIBILITY OF SUCH DAMAGE.
  34. */
  35. namespace Hoa\Websocket\Test\Unit;
  36. use Hoa\Event;
  37. use Hoa\Http;
  38. use Hoa\Test;
  39. use Hoa\Websocket;
  40. use Hoa\Websocket\Client as SUT;
  41. use Mock\Hoa\Socket;
  42. /**
  43. * Class \Hoa\Websocket\Test\Unit\Client.
  44. *
  45. * Test suite for the WebSocket client class.
  46. *
  47. * @copyright Copyright © 2007-2017 Hoa community
  48. * @license New BSD License
  49. */
  50. class Client extends Test\Unit\Suite
  51. {
  52. public function case_is_a_connection()
  53. {
  54. $this
  55. ->given($this->mockGenerator->orphanize('__construct'))
  56. ->when($result = new \Mock\Hoa\Websocket\Client())
  57. ->then
  58. ->object($result)
  59. ->isInstanceOf(Websocket\Connection::class);
  60. }
  61. public function case_constructor()
  62. {
  63. $this
  64. ->given(
  65. $socket = new Socket\Client('tcp://*:1234'),
  66. $endPoint = '/foobar',
  67. $response = new Http\Response()
  68. )
  69. ->when($result = new SUT($socket, $endPoint, $response))
  70. ->then
  71. ->object($result->getConnection())
  72. ->isIdenticalTo($socket)
  73. ->string($result->getEndPoint())
  74. ->isEqualTo($endPoint)
  75. ->object($result->getResponse())
  76. ->isIdenticalTo($response);
  77. }
  78. public function case_constructor_with_an_undefined_endpoint()
  79. {
  80. $this
  81. ->given(
  82. $socket = new Socket\Client('tcp://*:1234'),
  83. $endPoint = null,
  84. $response = new Http\Response()
  85. )
  86. ->when($result = new SUT($socket, $endPoint, $response))
  87. ->then
  88. ->object($result->getConnection())
  89. ->isIdenticalTo($socket)
  90. ->string($result->getEndPoint())
  91. ->isEqualTo('/')
  92. ->object($result->getResponse())
  93. ->isIdenticalTo($response);
  94. }
  95. public function case_constructor_with_a_socket_defined_endpoint()
  96. {
  97. $this
  98. ->given(
  99. $socket = new Websocket\Socket('tcp://*:1234', false, '/foobar'),
  100. $hoaSocket = new Socket\Client('tcp://*:1234'),
  101. $endPoint = null,
  102. $response = new Http\Response(),
  103. $this->calling($hoaSocket)->getSocket = $socket
  104. )
  105. ->when($result = new SUT($hoaSocket, $endPoint, $response))
  106. ->then
  107. ->object($result->getConnection())
  108. ->isIdenticalTo($hoaSocket)
  109. ->string($result->getEndPoint())
  110. ->isEqualTo('/foobar')
  111. ->object($result->getResponse())
  112. ->isIdenticalTo($response);
  113. }
  114. public function case_constructor_with_an_undefined_response()
  115. {
  116. $this
  117. ->given(
  118. $socket = new Socket\Client('tcp://*:1234'),
  119. $endPoint = '/foobar'
  120. )
  121. ->when($result = new SUT($socket, $endPoint))
  122. ->then
  123. ->object($result->getConnection())
  124. ->isIdenticalTo($socket)
  125. ->string($result->getEndPoint())
  126. ->isEqualTo('/foobar')
  127. ->object($result->getResponse())
  128. ->isInstanceOf(Http\Response::class);
  129. }
  130. public function case_connect()
  131. {
  132. $this
  133. ->given(
  134. $socket = new Socket\Client('tcp://*:1234'),
  135. $this->mockGenerator->makeVisible('doHandshake')->generate(SUT::class),
  136. $client = new \Mock\Hoa\Websocket\Client($socket),
  137. $this->calling($client)->doHandshake = function () use (&$called) {
  138. $called = true;
  139. return;
  140. }
  141. )
  142. ->when($result = $client->connect())
  143. ->then
  144. ->variable($result)
  145. ->isNull()
  146. ->boolean($called)
  147. ->isTrue();
  148. }
  149. public function case_receive_a_complete_message_and_is_not_disconnected()
  150. {
  151. $self = $this;
  152. $this
  153. ->given(
  154. $socket = new Socket\Client('tcp://*:1234'),
  155. $this->mockGenerator->orphanize('__construct'),
  156. $node = new \Mock\Hoa\Websocket\Node(),
  157. $this->mockGenerator->makeVisible('_run')->generate(SUT::class),
  158. $client = new \Mock\Hoa\Websocket\Client($socket),
  159. $this->calling($node)->isMessageComplete = true,
  160. $this->calling($socket)->getCurrentNode = $node,
  161. $this->calling($socket)->isDisconnected = false,
  162. $this->calling($client)->_run = function (Websocket\Node $_node) use (&$called, $self, $node) {
  163. $called = true;
  164. $self
  165. ->object($_node)
  166. ->isIdenticalTo($node);
  167. return;
  168. }
  169. )
  170. ->when($result = $client->receive())
  171. ->then
  172. ->variable($result)
  173. ->isNull()
  174. ->boolean($called)
  175. ->isTrue();
  176. }
  177. public function case_receive_an_incomplete_message_and_is_disconnected()
  178. {
  179. $self = $this;
  180. $this
  181. ->given(
  182. $socket = new Socket\Client('tcp://*:1234'),
  183. $this->mockGenerator->orphanize('__construct'),
  184. $node = new \Mock\Hoa\Websocket\Node(),
  185. $this->mockGenerator->makeVisible('_run')->generate(SUT::class),
  186. $client = new \Mock\Hoa\Websocket\Client($socket),
  187. $this->calling($node)->isMessageComplete = false,
  188. $this->calling($socket)->getCurrentNode = $node,
  189. $this->calling($socket)->isDisconnected = true,
  190. $this->calling($client)->_run = function (Websocket\Node $_node) use (&$called, $self, $node) {
  191. $called = true;
  192. $self
  193. ->object($_node)
  194. ->isIdenticalTo($node);
  195. return;
  196. }
  197. )
  198. ->when($result = $client->receive())
  199. ->then
  200. ->variable($result)
  201. ->isNull()
  202. ->boolean($called)
  203. ->isTrue();
  204. }
  205. public function case_do_handshake()
  206. {
  207. $self = $this;
  208. $this
  209. ->given(
  210. $this->mockGenerator->orphanize('__construct'),
  211. $sock = new \Mock\Hoa\Websocket\Socket(),
  212. $this->mockGenerator->orphanize('__construct'),
  213. $node = new \Mock\Hoa\Websocket\Node(),
  214. $socket = new Socket\Client('tcp://*:1234'),
  215. $endPoint = '/foobar',
  216. $response = new Http\Response(),
  217. $client = new \Mock\Hoa\Websocket\Client($socket, $endPoint, $response),
  218. $host = 'example.org',
  219. $client->setHost($host),
  220. $challenge = 'Y2FzZV9kb19oYW5kc2hhaA==',
  221. $this->calling($sock)->isSecured = false,
  222. $this->calling($socket)->getSocket = $sock,
  223. $this->calling($socket)->connect = function () use (&$calledA) {
  224. $calledA = true;
  225. return true;
  226. },
  227. $this->calling($socket)->setStreamBlocking = function ($_block) use (&$calledB, $self) {
  228. $calledB = true;
  229. $self
  230. ->boolean($_block)
  231. ->isTrue();
  232. return true;
  233. },
  234. $this->calling($socket)->writeAll = function ($_data) use (&$calledC, $self, $endPoint, $host, $challenge) {
  235. $calledC = true;
  236. $self
  237. ->string($_data)
  238. ->isEqualTo(
  239. 'GET ' . $endPoint . ' HTTP/1.1' . CRLF .
  240. 'Host: ' . $host . CRLF .
  241. 'User-Agent: Hoa' . CRLF .
  242. 'Upgrade: WebSocket' . CRLF .
  243. 'Connection: Upgrade' . CRLF .
  244. 'Pragma: no-cache' . CRLF .
  245. 'Cache-Control: no-cache' . CRLF .
  246. 'Sec-WebSocket-Key: ' . $challenge . CRLF .
  247. 'Sec-WebSocket-Version: 13' . CRLF . CRLF
  248. );
  249. return strlen($_data);
  250. },
  251. $this->calling($socket)->read = function ($_size) use (&$calledD, $self, $challenge) {
  252. $calledD = true;
  253. $self
  254. ->integer($_size)
  255. ->isEqualTo(2048);
  256. return
  257. 'HTTP/1.1 101 Switching Protocols' . CRLF .
  258. 'Upgrade: websocket' . CRLF .
  259. 'Connection: Upgrade' . CRLF .
  260. 'Sec-WebSocket-Accept: ' . base64_encode(sha1($challenge . Websocket\Protocol\Rfc6455::GUID, true)) . CRLF .
  261. 'Sec-WebSocket-Version: 13' . CRLF . CRLF;
  262. },
  263. $this->calling($socket)->getCurrentNode = $node,
  264. $this->calling($client)->getNewChallenge = $challenge,
  265. $client->on(
  266. 'open',
  267. function (Event\Bucket $bucket) use (&$calledE, $self) {
  268. $calledE = true;
  269. $self
  270. ->variable($bucket->getData())
  271. ->isNull();
  272. return;
  273. }
  274. )
  275. )
  276. ->when($result = $this->invoke($client)->doHandshake())
  277. ->then
  278. ->variable($result)
  279. ->isNull()
  280. ->boolean($calledA)
  281. ->isTrue()
  282. ->boolean($calledB)
  283. ->isTrue()
  284. ->boolean($calledC)
  285. ->isTrue()
  286. ->boolean($calledD)
  287. ->isTrue()
  288. ->boolean($calledE)
  289. ->isTrue()
  290. ->boolean($node->getHandshake())
  291. ->isTrue()
  292. ->object($node->getProtocolImplementation())
  293. ->isInstanceOf(Websocket\Protocol\Rfc6455::class)
  294. ->float($response->getHttpVersion())
  295. ->isEqualTo(1.1)
  296. ->array($response->getHeaders())
  297. ->isEqualTo([
  298. 'status' => $response::STATUS_SWITCHING_PROTOCOLS,
  299. 'upgrade' => 'websocket',
  300. 'connection' => 'Upgrade',
  301. 'sec-websocket-accept' => base64_encode(sha1($challenge . Websocket\Protocol\Rfc6455::GUID, true)),
  302. 'sec-websocket-version' => '13'
  303. ]);
  304. }
  305. public function case_do_handshake_with_a_secured_connection()
  306. {
  307. $self = $this;
  308. $this
  309. ->given(
  310. $this->mockGenerator->orphanize('__construct'),
  311. $sock = new \Mock\Hoa\Websocket\Socket(),
  312. $this->mockGenerator->orphanize('__construct'),
  313. $node = new \Mock\Hoa\Websocket\Node(),
  314. $socket = new Socket\Client('tcp://*:1234'),
  315. $endPoint = '/foobar',
  316. $response = new Http\Response(),
  317. $client = new \Mock\Hoa\Websocket\Client($socket, $endPoint, $response),
  318. $host = 'example.org',
  319. $client->setHost($host),
  320. $challenge = 'Y2FzZV9kb19oYW5kc2hhaA==',
  321. $this->calling($sock)->isSecured = true,
  322. $this->calling($socket)->getSocket = $sock,
  323. $this->calling($socket)->connect = function () use (&$calledA) {
  324. $calledA = true;
  325. return true;
  326. },
  327. $this->calling($socket)->enableEncryption = function ($_enable, $_type, $_sessionStream) use (&$calledB, $self, $socket) {
  328. $calledB = true;
  329. $self
  330. ->boolean($_enable)
  331. ->isTrue()
  332. ->integer($_type)
  333. ->isEqualTo($socket::ENCRYPTION_TLS)
  334. ->variable($_sessionStream)
  335. ->isNull();
  336. return true;
  337. },
  338. $this->calling($socket)->setStreamBlocking = function ($_block) use (&$calledC, $self) {
  339. $calledC = true;
  340. $self
  341. ->boolean($_block)
  342. ->isTrue();
  343. return true;
  344. },
  345. $this->calling($socket)->writeAll = function ($_data) use (&$calledD, $self, $endPoint, $host, $challenge) {
  346. $calledD = true;
  347. $self
  348. ->string($_data)
  349. ->isEqualTo(
  350. 'GET ' . $endPoint . ' HTTP/1.1' . CRLF .
  351. 'Host: ' . $host . CRLF .
  352. 'User-Agent: Hoa' . CRLF .
  353. 'Upgrade: WebSocket' . CRLF .
  354. 'Connection: Upgrade' . CRLF .
  355. 'Pragma: no-cache' . CRLF .
  356. 'Cache-Control: no-cache' . CRLF .
  357. 'Sec-WebSocket-Key: ' . $challenge . CRLF .
  358. 'Sec-WebSocket-Version: 13' . CRLF . CRLF
  359. );
  360. return strlen($_data);
  361. },
  362. $this->calling($socket)->read = function ($_size) use (&$calledE, $self, $challenge) {
  363. $calledE = true;
  364. $self
  365. ->integer($_size)
  366. ->isEqualTo(2048);
  367. return
  368. 'HTTP/1.1 101 Switching Protocols' . CRLF .
  369. 'Upgrade: websocket' . CRLF .
  370. 'Connection: Upgrade' . CRLF .
  371. 'Sec-WebSocket-Accept: ' . base64_encode(sha1($challenge . Websocket\Protocol\Rfc6455::GUID, true)) . CRLF .
  372. 'Sec-WebSocket-Version: 13' . CRLF . CRLF;
  373. },
  374. $this->calling($socket)->getCurrentNode = $node,
  375. $this->calling($client)->getNewChallenge = $challenge,
  376. $client->on(
  377. 'open',
  378. function (Event\Bucket $bucket) use (&$calledF, $self) {
  379. $calledF = true;
  380. $self
  381. ->variable($bucket->getData())
  382. ->isNull();
  383. return;
  384. }
  385. )
  386. )
  387. ->when($result = $this->invoke($client)->doHandshake())
  388. ->then
  389. ->variable($result)
  390. ->isNull()
  391. ->boolean($calledA)
  392. ->isTrue()
  393. ->boolean($calledB)
  394. ->isTrue()
  395. ->boolean($calledC)
  396. ->isTrue()
  397. ->boolean($calledD)
  398. ->isTrue()
  399. ->boolean($calledE)
  400. ->isTrue()
  401. ->boolean($calledF)
  402. ->isTrue()
  403. ->boolean($node->getHandshake())
  404. ->isTrue()
  405. ->object($node->getProtocolImplementation())
  406. ->isInstanceOf(Websocket\Protocol\Rfc6455::class)
  407. ->float($response->getHttpVersion())
  408. ->isEqualTo(1.1)
  409. ->array($response->getHeaders())
  410. ->isEqualTo([
  411. 'status' => $response::STATUS_SWITCHING_PROTOCOLS,
  412. 'upgrade' => 'websocket',
  413. 'connection' => 'Upgrade',
  414. 'sec-websocket-accept' => base64_encode(sha1($challenge . Websocket\Protocol\Rfc6455::GUID, true)),
  415. 'sec-websocket-version' => '13'
  416. ]);
  417. }
  418. public function case_do_handshake_with_no_host()
  419. {
  420. $self = $this;
  421. $this
  422. ->given(
  423. $this->mockGenerator->orphanize('__construct'),
  424. $sock = new \Mock\Hoa\Websocket\Socket(),
  425. $this->mockGenerator->orphanize('__construct'),
  426. $node = new \Mock\Hoa\Websocket\Node(),
  427. $socket = new Socket\Client('tcp://*:1234'),
  428. $endPoint = '/foobar',
  429. $response = new Http\Response(),
  430. $client = new SUT($socket, $endPoint, $response),
  431. $this->calling($sock)->isSecured = false,
  432. $this->calling($socket)->getSocket = $sock,
  433. $this->calling($socket)->connect = null,
  434. $this->calling($socket)->enableEncryption = null,
  435. $this->calling($socket)->setStreamBlocking = null
  436. )
  437. ->exception(function () use ($client) {
  438. $this->invoke($client)->doHandshake();
  439. })
  440. ->isInstanceOf(Websocket\Exception::class);
  441. }
  442. public function case_do_handshake_invalid_response_status()
  443. {
  444. return $this->_case_do_handshake_invalid_response(
  445. 'HTTP/1.1 404 Not Found' . CRLF . CRLF
  446. );
  447. }
  448. public function case_do_handshake_invalid_response_upgrade_header()
  449. {
  450. return $this->_case_do_handshake_invalid_response(
  451. 'HTTP/1.1 101 Switching Protocols' . CRLF .
  452. 'Upgrade: foobar' . CRLF . CRLF
  453. );
  454. }
  455. public function case_do_handshake_invalid_response_connection_header()
  456. {
  457. return $this->_case_do_handshake_invalid_response(
  458. 'HTTP/1.1 101 Switching Protocols' . CRLF .
  459. 'Upgrade: websocket' . CRLF .
  460. 'Connection: foobar' . CRLF . CRLF
  461. );
  462. }
  463. public function case_do_handshake_invalid_response_sec_websocket_accept_header()
  464. {
  465. return $this->_case_do_handshake_invalid_response(
  466. 'HTTP/1.1 101 Switching Protocols' . CRLF .
  467. 'Upgrade: websocket' . CRLF .
  468. 'Connection: foobar' . CRLF .
  469. 'Sec-WebSocket-Accept: ' . base64_encode(sha1('XXXXXXXXXXXXXXXXXXXXXX==' . Websocket\Protocol\Rfc6455::GUID, true)) . CRLF .
  470. 'Sec-WebSocket-Version: 13' . CRLF . CRLF
  471. );
  472. }
  473. protected function _case_do_handshake_invalid_response($response)
  474. {
  475. $self = $this;
  476. $this
  477. ->given(
  478. $this->mockGenerator->orphanize('__construct'),
  479. $sock = new \Mock\Hoa\Websocket\Socket(),
  480. $this->mockGenerator->orphanize('__construct'),
  481. $node = new \Mock\Hoa\Websocket\Node(),
  482. $socket = new Socket\Client('tcp://*:1234'),
  483. $endPoint = '/foobar',
  484. $client = new \Mock\Hoa\Websocket\Client($socket, $endPoint),
  485. $host = 'example.org',
  486. $client->setHost($host),
  487. $challenge = 'Y2FzZV9kb19oYW5kc2hhaA==',
  488. $this->calling($sock)->isSecured = true,
  489. $this->calling($socket)->getSocket = $sock,
  490. $this->calling($socket)->connect = true,
  491. $this->calling($socket)->enableEncryption = function ($_enable, $_type, $_sessionStream) use ($self, $socket) {
  492. $self
  493. ->boolean($_enable)
  494. ->isTrue()
  495. ->integer($_type)
  496. ->isEqualTo($socket::ENCRYPTION_TLS)
  497. ->variable($_sessionStream)
  498. ->isNull();
  499. return true;
  500. },
  501. $this->calling($socket)->setStreamBlocking = function ($_block) use ($self) {
  502. $self
  503. ->boolean($_block)
  504. ->isTrue();
  505. return true;
  506. },
  507. $this->calling($socket)->writeAll = function ($_data) use ($self, $endPoint, $host, $challenge) {
  508. $self
  509. ->string($_data)
  510. ->isEqualTo(
  511. 'GET ' . $endPoint . ' HTTP/1.1' . CRLF .
  512. 'Host: ' . $host . CRLF .
  513. 'User-Agent: Hoa' . CRLF .
  514. 'Upgrade: WebSocket' . CRLF .
  515. 'Connection: Upgrade' . CRLF .
  516. 'Pragma: no-cache' . CRLF .
  517. 'Cache-Control: no-cache' . CRLF .
  518. 'Sec-WebSocket-Key: ' . $challenge . CRLF .
  519. 'Sec-WebSocket-Version: 13' . CRLF . CRLF
  520. );
  521. return strlen($_data);
  522. },
  523. $this->calling($socket)->read = function ($_size) use ($self, $challenge, $response) {
  524. $self
  525. ->integer($_size)
  526. ->isEqualTo(2048);
  527. return $response;
  528. },
  529. $this->calling($client)->getNewChallenge = $challenge
  530. )
  531. ->exception(function () use ($client) {
  532. $this->invoke($client)->doHandshake();
  533. })
  534. ->isInstanceOf(Websocket\Exception\BadProtocol::class);
  535. }
  536. public function case_get_new_challenge()
  537. {
  538. $this
  539. ->given(
  540. $this->mockGenerator->orphanize('__construct'),
  541. $client = new \Mock\Hoa\Websocket\Client()
  542. )
  543. ->when(function () use ($client) {
  544. for ($i = 0; $i < 1000; ++$i) {
  545. $this
  546. ->string($client->getNewChallenge())
  547. ->matches('/^[A-Za-z0-9]{21}[AQgw]==$/');
  548. }
  549. });
  550. }
  551. public function case_close()
  552. {
  553. return $this->_case_close(true);
  554. }
  555. public function case_close_with_no_protocol_implementation()
  556. {
  557. return $this->_case_close(null);
  558. }
  559. protected function _case_close($protocolCalledValue)
  560. {
  561. $self = $this;
  562. $this
  563. ->given(
  564. $this->mockGenerator->orphanize('__construct'),
  565. $node = new \Mock\Hoa\Websocket\Node(),
  566. $socket = new Socket\Client('tcp://*:1234'),
  567. $this->mockGenerator->orphanize('__construct'),
  568. $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455(),
  569. $client = new SUT($socket),
  570. $code = Websocket\Connection::CLOSE_NORMAL,
  571. $reason = 'foobar',
  572. $mask = true,
  573. $this->calling($node)->getProtocolImplementation = $protocolCalledValue ? $protocol : null,
  574. $this->calling($protocol)->close = function ($_code, $_reason, $_mask) use (&$calledA, $self, $code, $reason, $mask) {
  575. $calledA = true;
  576. $self
  577. ->integer($_code)
  578. ->isEqualTo($code)
  579. ->string($reason)
  580. ->isEqualTo($reason)
  581. ->boolean($_mask)
  582. ->isEqualTo($mask);
  583. return;
  584. },
  585. $this->calling($socket)->getCurrentNode = $node,
  586. $this->calling($socket)->mute = function () use (&$calledB) {
  587. $calledB = true;
  588. return;
  589. },
  590. $this->calling($socket)->setStreamTimeout = function ($_second, $_millisecond) use (&$calledC, $self) {
  591. $calledC = true;
  592. $self
  593. ->integer($_second)
  594. ->isEqualTo(0)
  595. ->integer($_millisecond)
  596. ->isEqualTo(30000);
  597. return true;
  598. },
  599. $this->calling($socket)->read = function ($_size) use (&$calledD, $self) {
  600. $calledD = true;
  601. $self
  602. ->integer($_size)
  603. ->isEqualTo(1);
  604. return 'x';
  605. }
  606. )
  607. ->when($result = $client->close($code, $reason))
  608. ->then
  609. ->variable($result)
  610. ->isNull()
  611. ->variable($calledA)
  612. ->isEqualTo($protocolCalledValue)
  613. ->boolean($calledB)
  614. ->isTrue()
  615. ->boolean($calledC)
  616. ->isTrue()
  617. ->boolean($calledD)
  618. ->isTrue()
  619. ->boolean($socket->isDisconnected())
  620. ->isTrue();
  621. }
  622. public function case_set_end_point()
  623. {
  624. $this
  625. ->given(
  626. $socket = new Socket\Client('tcp://*:1234'),
  627. $endPoint = '/foobar',
  628. $client = new SUT($socket, $endPoint)
  629. )
  630. ->when($result = $this->invoke($client)->setEndPoint('/bazqux'))
  631. ->then
  632. ->string($result)
  633. ->isEqualTo($endPoint);
  634. }
  635. public function case_get_end_point()
  636. {
  637. $this
  638. ->given(
  639. $socket = new Socket\Client('tcp://*:1234'),
  640. $endPoint = '/bazqux',
  641. $client = new SUT($socket, '/foobar'),
  642. $this->invoke($client)->setEndPoint($endPoint)
  643. )
  644. ->when($result = $client->getEndPoint())
  645. ->then
  646. ->string($result)
  647. ->isEqualTo($endPoint);
  648. }
  649. public function case_set_response()
  650. {
  651. $this
  652. ->given(
  653. $socket = new Socket\Client('tcp://*:1234'),
  654. $endPoint = '/',
  655. $response = new Http\Response(),
  656. $client = new SUT($socket, $endPoint, $response)
  657. )
  658. ->when($result = $client->setResponse(new Http\Response()))
  659. ->then
  660. ->object($result)
  661. ->isIdenticalTo($response);
  662. }
  663. public function case_get_response()
  664. {
  665. $this
  666. ->given(
  667. $socket = new Socket\Client('tcp://*:1234'),
  668. $endPoint = '/',
  669. $response = new Http\Response(),
  670. $client = new SUT($socket, $endPoint, new Http\Response()),
  671. $this->invoke($client)->setResponse($response)
  672. )
  673. ->when($result = $client->getResponse())
  674. ->then
  675. ->object($result)
  676. ->isIdenticalTo($response);
  677. }
  678. public function case_set_host()
  679. {
  680. $this
  681. ->given(
  682. $socket = new Socket\Client('tcp://*:1234'),
  683. $client = new SUT($socket)
  684. )
  685. ->when($result = $client->setHost('example.org'))
  686. ->then
  687. ->variable($result)
  688. ->isNull();
  689. }
  690. public function case_get_host()
  691. {
  692. $this
  693. ->given(
  694. $socket = new Socket\Client('tcp://*:1234'),
  695. $client = new SUT($socket),
  696. $host = 'example.org',
  697. $client->setHost($host)
  698. )
  699. ->when($result = $client->getHost())
  700. ->then
  701. ->string($result)
  702. ->isEqualTo($host);
  703. }
  704. public function case_get_an_undefined_host()
  705. {
  706. unset($_SERVER);
  707. $this
  708. ->given(
  709. $socket = new Socket\Client('tcp://*:1234'),
  710. $client = new SUT($socket)
  711. )
  712. ->when($result = $client->getHost())
  713. ->then
  714. ->variable($result)
  715. ->isNull();
  716. }
  717. public function case_get_a_global_defined_host()
  718. {
  719. $this
  720. ->given(
  721. $socket = new Socket\Client('tcp://*:1234'),
  722. $host = 'example.org',
  723. $_SERVER['HTTP_HOST'] = $host,
  724. $client = new SUT($socket)
  725. )
  726. ->when($result = $client->getHost())
  727. ->then
  728. ->string($result)
  729. ->isEqualTo($host);
  730. }
  731. }