HttpProxyServerTest.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. <?php
  2. use LeProxy\LeProxy\ConnectorFactory;
  3. use LeProxy\LeProxy\HttpProxyServer;
  4. use React\Http\Io\HttpBodyStream;
  5. use React\Http\Io\ServerRequest;
  6. use React\Promise\Promise;
  7. use React\Stream\ThroughStream;
  8. class HttpProxyServerTest extends PHPUnit_Framework_TestCase
  9. {
  10. public function testCtor()
  11. {
  12. $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
  13. $socket = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
  14. $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
  15. $server = new HttpProxyServer($loop, $socket, $connector);
  16. }
  17. public function testRequestWithoutAuthenticationReturnsAuthenticationRequired()
  18. {
  19. $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
  20. $socket = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
  21. $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
  22. $server = new HttpProxyServer($loop, $socket, $connector);
  23. $server->setAuthArray(array('user' => 'pass'));
  24. $request = new ServerRequest('GET', '/');
  25. $response = $server->handleRequest($request);
  26. $this->assertEquals(407, $response->getStatusCode());
  27. }
  28. public function testRequestWithInvalidAuthenticationReturnsAuthenticationRequired()
  29. {
  30. $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
  31. $socket = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
  32. $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
  33. $server = new HttpProxyServer($loop, $socket, $connector);
  34. $server->setAuthArray(array('user' => 'pass'));
  35. $request = new ServerRequest('GET', '/', array('Proxy-Authorization' => 'Basic dXNlcg=='));
  36. $response = $server->handleRequest($request);
  37. $this->assertEquals(407, $response->getStatusCode());
  38. }
  39. public function testRequestWithValidAuthenticationReturnsSuccess()
  40. {
  41. $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
  42. $socket = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
  43. $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
  44. $server = new HttpProxyServer($loop, $socket, $connector);
  45. $server->setAuthArray(array('user' => 'pass'));
  46. $request = new ServerRequest('GET', '/', array('Proxy-Authorization' => 'Basic dXNlcjpwYXNz'));
  47. $response = $server->handleRequest($request);
  48. $this->assertEquals(405, $response->getStatusCode());
  49. }
  50. public function testRequestProtectedLocalhostReturnsSuccess()
  51. {
  52. $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
  53. $socket = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
  54. $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
  55. $server = new HttpProxyServer($loop, $socket, $connector);
  56. $server->allowUnprotected = false;
  57. $request = new ServerRequest('GET', '/', array(), null, '1.1', array('REMOTE_ADDR' => '127.0.0.1'));
  58. $response = $server->handleRequest($request);
  59. $this->assertEquals(405, $response->getStatusCode());
  60. }
  61. public function testRequestProtectedRemoteReturnsForbidden()
  62. {
  63. $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
  64. $socket = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
  65. $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
  66. $server = new HttpProxyServer($loop, $socket, $connector);
  67. $server->allowUnprotected = false;
  68. $request = new ServerRequest('GET', '/', array(), null, '1.1', array('REMOTE_ADDR' => '192.168.1.1'));
  69. $response = $server->handleRequest($request);
  70. $this->assertEquals(403, $response->getStatusCode());
  71. }
  72. public function testRequestUnprotectedRemoteReturnsSuccess()
  73. {
  74. $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
  75. $socket = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
  76. $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
  77. $server = new HttpProxyServer($loop, $socket, $connector);
  78. $server->allowUnprotected = true;
  79. $request = new ServerRequest('GET', '/', array(), null, '1.1', array('REMOTE_ADDR' => '192.168.1.1'));
  80. $response = $server->handleRequest($request);
  81. $this->assertEquals(405, $response->getStatusCode());
  82. }
  83. public function testRequestConnectCallsConnector()
  84. {
  85. $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
  86. $socket = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
  87. $promise = new Promise(function () { });
  88. $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
  89. $connector->expects($this->once())->method('connect')->with('example.com:80?source=http%3A%2F%2F192.168.1.1%3A5060')->willReturn($promise);
  90. $server = new HttpProxyServer($loop, $socket, $connector);
  91. $request = new ServerRequest('CONNECT', 'http://example.com', array(), null, '1.1', array('REMOTE_ADDR' => '192.168.1.1', 'REMOTE_PORT' => 5060));
  92. $request = $request->withRequestTarget('example.com:80');
  93. $response = $server->handleRequest($request);
  94. $this->assertInstanceOf('React\Promise\PromiseInterface', $response);
  95. }
  96. public function testRequestConnectCallsConnectorBlockedReturnsForbidden()
  97. {
  98. $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
  99. $socket = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
  100. $promise = \React\Promise\reject(new RuntimeException('', ConnectorFactory::CODE_BLOCKED));
  101. $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
  102. $connector->expects($this->once())->method('connect')->willReturn($promise);
  103. $server = new HttpProxyServer($loop, $socket, $connector);
  104. $request = new ServerRequest('CONNECT', 'http://example.com', array(), null, '1.1', array('REMOTE_ADDR' => '192.168.1.1', 'REMOTE_PORT' => 5060));
  105. $request = $request->withRequestTarget('example.com:80');
  106. $promise = $server->handleRequest($request);
  107. $response = null;
  108. $promise->then(function ($ret) use (&$response) {
  109. $response = $ret;
  110. });
  111. $this->assertNotNull($response);
  112. $this->assertEquals(403, $response->getStatusCode());
  113. }
  114. public function testRequestConnectCallsConnectorTimeoutReturnsGatewayTimeout()
  115. {
  116. $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
  117. $socket = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
  118. $promise = \React\Promise\reject(new RuntimeException('', SOCKET_ETIMEDOUT));
  119. $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
  120. $connector->expects($this->once())->method('connect')->willReturn($promise);
  121. $server = new HttpProxyServer($loop, $socket, $connector);
  122. $request = new ServerRequest('CONNECT', 'http://example.com', array(), null, '1.1', array('REMOTE_ADDR' => '192.168.1.1', 'REMOTE_PORT' => 5060));
  123. $request = $request->withRequestTarget('example.com:80');
  124. $promise = $server->handleRequest($request);
  125. $response = null;
  126. $promise->then(function ($ret) use (&$response) {
  127. $response = $ret;
  128. });
  129. $this->assertNotNull($response);
  130. $this->assertEquals(504, $response->getStatusCode());
  131. }
  132. public function testRequestAbsoluteCallsConnector()
  133. {
  134. $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
  135. $socket = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
  136. $promise = new Promise(function () { });
  137. $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
  138. $connector->expects($this->once())->method('connect')->with('example.com:80?source=http%3A%2F%2F192.168.1.1%3A5060')->willReturn($promise);
  139. $server = new HttpProxyServer($loop, $socket, $connector);
  140. $request = new ServerRequest('GET', 'http://example.com/path', array(), null, '1.1', array('REMOTE_ADDR' => '192.168.1.1', 'REMOTE_PORT' => 5060));
  141. $request = $request->withRequestTarget('http://example.com/path');
  142. $response = $server->handleRequest($request);
  143. $this->assertInstanceOf('React\Promise\PromiseInterface', $response);
  144. }
  145. public function testPlainRequestForwardsWithExplicitHeadersAsGiven()
  146. {
  147. $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
  148. $socket = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
  149. $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
  150. $outgoing = $this->getMockBuilder('React\HttpClient\Request')->disableOriginalConstructor()->getMock();
  151. $client = $this->getMockBuilder('React\HttpClient\Client')->disableOriginalConstructor()->getMock();
  152. $client->expects($this->once())
  153. ->method('request')
  154. ->with('GET', 'http://example.com/', array('Cookie' => array('name=value'), 'USER-AGENT' => array('TEST')))
  155. ->willReturn($outgoing);
  156. $server = new HttpProxyServer($loop, $socket, $connector);
  157. $ref = new ReflectionProperty($server, 'client');
  158. $ref->setAccessible(true);
  159. $ref->setValue($server, $client);
  160. $request = new ServerRequest('GET', 'http://example.com/', array('Cookie' => 'name=value', 'USER-AGENT' => 'TEST'));
  161. $request = $request->withRequestTarget((string)$request->getUri());
  162. $request = $request->withBody(new HttpBodyStream(new ThroughStream(), null));
  163. $promise = $server->handleRequest($request);
  164. $this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
  165. }
  166. public function testPlainRequestWithValidAuthenticationForwardsViaHttpClientWithoutAuthorizationHeader()
  167. {
  168. $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
  169. $socket = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
  170. $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
  171. $outgoing = $this->getMockBuilder('React\HttpClient\Request')->disableOriginalConstructor()->getMock();
  172. $client = $this->getMockBuilder('React\HttpClient\Client')->disableOriginalConstructor()->getMock();
  173. $client->expects($this->once())
  174. ->method('request')
  175. ->with('GET', 'http://example.com/', array('Cookie' => array('name=value'), 'User-Agent' => array()))
  176. ->willReturn($outgoing);
  177. $server = new HttpProxyServer($loop, $socket, $connector);
  178. $server->setAuthArray(array('user' => 'pass'));
  179. $ref = new ReflectionProperty($server, 'client');
  180. $ref->setAccessible(true);
  181. $ref->setValue($server, $client);
  182. $request = new ServerRequest('GET', 'http://example.com/', array('Proxy-Authorization' => 'Basic dXNlcjpwYXNz', 'Cookie' => 'name=value'));
  183. $request = $request->withRequestTarget((string)$request->getUri());
  184. $request = $request->withBody(new HttpBodyStream(new ThroughStream(), null));
  185. $promise = $server->handleRequest($request);
  186. $this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
  187. }
  188. }