Skip to content

Commit

Permalink
Add server-side parameters to implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
legionth committed Apr 24, 2017
1 parent a3b1a84 commit c9983de
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 17 deletions.
35 changes: 29 additions & 6 deletions src/RequestHeaderParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ class RequestHeaderParser extends EventEmitter
private $buffer = '';
private $maxSize = 4096;

private $uri;
private $localSocketUri;
private $remoteSocketUri;

public function __construct($localSocketUri = '')
public function __construct($localSocketUri = null, $remoteSocketUri = null)
{
$this->uri = $localSocketUri;
$this->localSocketUri = $localSocketUri;
$this->remoteSocketUri = $remoteSocketUri;
}

public function feed($data)
Expand Down Expand Up @@ -85,13 +87,34 @@ private function parseRequest($data)

// create new obj implementing ServerRequestInterface by preserving all
// previous properties and restoring original request-target
$serverParams = array(
'REQUEST_TIME' => time(),
'REQUEST_TIME_FLOAT' => microtime(true)
);

if ($this->remoteSocketUri !== null) {
$remoteAddress = parse_url($this->remoteSocketUri);
$serverParams['REMOTE_ADDR'] = $remoteAddress['host'];
$serverParams['REMOTE_PORT'] = $remoteAddress['port'];
}

if ($this->localSocketUri !== null) {
$localAddress = parse_url($this->localSocketUri);
$serverParams['SERVER_ADDR'] = $localAddress['host'];
$serverParams['SERVER_PORT'] = $localAddress['port'];
if (isset($localAddress['scheme']) && $localAddress['scheme'] === 'https') {
$serverParams['HTTPS'] = 'on';
}
}

$target = $request->getRequestTarget();
$request = new ServerRequest(
$request->getMethod(),
$request->getUri(),
$request->getHeaders(),
$request->getBody(),
$request->getProtocolVersion()
$request->getProtocolVersion(),
$serverParams
);
$request = $request->withRequestTarget($target);

Expand Down Expand Up @@ -144,7 +167,7 @@ private function parseRequest($data)

// set URI components from socket address if not already filled via Host header
if ($request->getUri()->getHost() === '') {
$parts = parse_url($this->uri);
$parts = parse_url($this->localSocketUri);

$request = $request->withUri(
$request->getUri()->withScheme('http')->withHost($parts['host'])->withPort($parts['port']),
Expand All @@ -162,7 +185,7 @@ private function parseRequest($data)
}

// Update request URI to "https" scheme if the connection is encrypted
$parts = parse_url($this->uri);
$parts = parse_url($this->localSocketUri);
if (isset($parts['scheme']) && $parts['scheme'] === 'https') {
// The request URI may omit default ports here, so try to parse port
// from Host header field (if possible)
Expand Down
9 changes: 2 additions & 7 deletions src/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ public function handleConnection(ConnectionInterface $conn)
{
$that = $this;
$parser = new RequestHeaderParser(
($this->isConnectionEncrypted($conn) ? 'https://' : 'http://') . $conn->getLocalAddress()
($this->isConnectionEncrypted($conn) ? 'https://' : 'http://') . $conn->getLocalAddress(),
'tcp://' . $conn->getRemoteAddress()
);

$listener = array($parser, 'feed');
Expand Down Expand Up @@ -227,12 +228,6 @@ public function handleRequest(ConnectionInterface $conn, ServerRequestInterface
$conn->write("HTTP/1.1 100 Continue\r\n\r\n");
}

// attach remote ip to the request as metadata
$request->remoteAddress = trim(
parse_url('tcp://' . $conn->getRemoteAddress(), PHP_URL_HOST),
'[]'
);

$callback = $this->callback;
$promise = new Promise(function ($resolve, $reject) use ($callback, $request) {
$resolve($callback($request));
Expand Down
21 changes: 21 additions & 0 deletions src/ServerRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,27 @@ class ServerRequest extends Request implements ServerRequestInterface
private $queryParams = array();
private $parsedBody = null;

/**
* @param null|string $method HTTP method for the request.
* @param null|string|UriInterface $uri URI for the request.
* @param array $headers Headers for the message.
* @param string|resource|StreamInterface $body Message body.
* @param string $protocolVersion HTTP protocol version.
*
* @throws InvalidArgumentException for an invalid URI
*/
public function __construct(
$method,
$uri,
array $headers = array(),
$body = null,
$protocolVersion = '1.1',
$serverParams = array()
) {
$this->serverParams = $serverParams;
parent::__construct($method, $uri, $headers, $body, $protocolVersion);
}

public function getServerParams()
{
return $this->serverParams;
Expand Down
77 changes: 77 additions & 0 deletions tests/RequestHeaderParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,83 @@ public function testInvalidHttpVersion()
$this->assertSame('Received request with invalid protocol version', $error->getMessage());
}

public function testServerParamsWillBeSetOnHttpsRequest()
{
$request = null;

$parser = new RequestHeaderParser(
'https://127.1.1.1:8000',
'https://192.168.1.1:8001'
);

$parser->on('headers', function ($parsedRequest) use (&$request) {
$request = $parsedRequest;
});

$parser->feed("GET /foo HTTP/1.0\r\nHost: example.com\r\n\r\n");
$serverParams = $request->getServerParams();

$this->assertEquals('on', $serverParams['HTTPS']);
$this->assertNotEmpty($serverParams['REQUEST_TIME']);
$this->assertNotEmpty($serverParams['REQUEST_TIME_FLOAT']);

$this->assertEquals('127.1.1.1', $serverParams['SERVER_ADDR']);
$this->assertEquals('8000', $serverParams['SERVER_PORT']);

$this->assertEquals('192.168.1.1', $serverParams['REMOTE_ADDR']);
$this->assertEquals('8001', $serverParams['REMOTE_PORT']);
}

public function testServerParamsWillBeSetOnHttpRequest()
{
$request = null;

$parser = new RequestHeaderParser(
'http://127.1.1.1:8000',
'http://192.168.1.1:8001'
);

$parser->on('headers', function ($parsedRequest) use (&$request) {
$request = $parsedRequest;
});

$parser->feed("GET /foo HTTP/1.0\r\nHost: example.com\r\n\r\n");
$serverParams = $request->getServerParams();

$this->assertArrayNotHasKey('HTTPS', $serverParams);
$this->assertNotEmpty($serverParams['REQUEST_TIME']);
$this->assertNotEmpty($serverParams['REQUEST_TIME_FLOAT']);

$this->assertEquals('127.1.1.1', $serverParams['SERVER_ADDR']);
$this->assertEquals('8000', $serverParams['SERVER_PORT']);

$this->assertEquals('192.168.1.1', $serverParams['REMOTE_ADDR']);
$this->assertEquals('8001', $serverParams['REMOTE_PORT']);
}

public function testServerParamsWontBeSetOnMissingUrls()
{
$request = null;

$parser = new RequestHeaderParser();

$parser->on('headers', function ($parsedRequest) use (&$request) {
$request = $parsedRequest;
});

$parser->feed("GET /foo HTTP/1.0\r\nHost: example.com\r\n\r\n");
$serverParams = $request->getServerParams();

$this->assertNotEmpty($serverParams['REQUEST_TIME']);
$this->assertNotEmpty($serverParams['REQUEST_TIME_FLOAT']);

$this->assertArrayNotHasKey('SERVER_ADDR', $serverParams);
$this->assertArrayNotHasKey('SERVER_PORT', $serverParams);

$this->assertArrayNotHasKey('REMOTE_ADDR', $serverParams);
$this->assertArrayNotHasKey('REMOTE_PORT', $serverParams);
}

private function createGetRequest()
{
$data = "GET / HTTP/1.1\r\n";
Expand Down
45 changes: 41 additions & 4 deletions tests/ServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,27 +69,30 @@ public function testRequestEvent()
$server = new Server($this->socket, function (ServerRequestInterface $request) use (&$i, &$requestAssertion) {
$i++;
$requestAssertion = $request;

return \React\Promise\resolve(new Response());
});

$this->connection
->expects($this->once())
->expects($this->any())
->method('getRemoteAddress')
->willReturn('127.0.0.1');
->willReturn('127.0.0.1:8080');

$this->socket->emit('connection', array($this->connection));

$data = $this->createGetRequest();
$this->connection->emit('data', array($data));

$serverParams = $requestAssertion->getServerParams();

$this->assertSame(1, $i);
$this->assertInstanceOf('RingCentral\Psr7\Request', $requestAssertion);
$this->assertSame('GET', $requestAssertion->getMethod());
$this->assertSame('/', $requestAssertion->getRequestTarget());
$this->assertSame('/', $requestAssertion->getUri()->getPath());
$this->assertSame('http://example.com/', (string)$requestAssertion->getUri());
$this->assertSame('example.com', $requestAssertion->getHeaderLine('Host'));
$this->assertSame('127.0.0.1', $requestAssertion->remoteAddress);
$this->assertSame('127.0.0.1', $serverParams['REMOTE_ADDR']);
}

public function testRequestGetWithHostAndCustomPort()
Expand Down Expand Up @@ -288,7 +291,7 @@ public function testRequestWithoutHostEventUsesSocketAddress()
});

$this->connection
->expects($this->once())
->expects($this->any())
->method('getLocalAddress')
->willReturn('127.0.0.1:80');

Expand Down Expand Up @@ -2332,6 +2335,40 @@ function ($data) use (&$buffer) {
$this->assertInstanceOf('RuntimeException', $exception);
}

public function testServerRequestParams()
{
$requestValidation = null;
$server = new Server($this->socket, function (ServerRequestInterface $request) use (&$requestValidation) {
$requestValidation = $request;
return new Response();
});

$this->connection
->expects($this->any())
->method('getRemoteAddress')
->willReturn('192.168.1.2:80');

$this->connection
->expects($this->any())
->method('getLocalAddress')
->willReturn('127.0.0.1:8080');

$this->socket->emit('connection', array($this->connection));

$data = $this->createGetRequest();

$this->connection->emit('data', array($data));

$serverParams = $requestValidation->getServerParams();

$this->assertEquals('127.0.0.1', $serverParams['SERVER_ADDR']);
$this->assertEquals('8080', $serverParams['SERVER_PORT']);
$this->assertEquals('192.168.1.2', $serverParams['REMOTE_ADDR']);
$this->assertEquals('80', $serverParams['REMOTE_PORT']);
$this->assertNotNull($serverParams['REQUEST_TIME']);
$this->assertNotNull($serverParams['REQUEST_TIME_FLOAT']);
}

private function createGetRequest()
{
$data = "GET / HTTP/1.1\r\n";
Expand Down

0 comments on commit c9983de

Please sign in to comment.