Skip to content

Commit c22fde6

Browse files
authored
Merge pull request #404 from clue-labs/require-host
Require Host request header for HTTP/1.1 requests
2 parents c919cb7 + 6bcbe54 commit c22fde6

File tree

4 files changed

+96
-66
lines changed

4 files changed

+96
-66
lines changed

src/Io/RequestHeaderParser.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ public function parseRequest($headers, $remoteSocketUri, $localSocketUri)
168168
}
169169

170170
// default host if unset comes from local socket address or defaults to localhost
171+
$hasHost = $host !== null;
171172
if ($host === null) {
172173
$host = isset($localParts['host'], $localParts['port']) ? $localParts['host'] . ':' . $localParts['port'] : '127.0.0.1';
173174
}
@@ -230,8 +231,8 @@ public function parseRequest($headers, $remoteSocketUri, $localSocketUri)
230231
$request = $request->withRequestTarget($start['target']);
231232
}
232233

233-
// Optional Host header value MUST be valid (host and optional port)
234-
if ($request->hasHeader('Host')) {
234+
if ($hasHost) {
235+
// Optional Host request header value MUST be valid (host and optional port)
235236
$parts = \parse_url('http://' . $request->getHeaderLine('Host'));
236237

237238
// make sure value contains valid host component (IP or hostname)
@@ -244,6 +245,12 @@ public function parseRequest($headers, $remoteSocketUri, $localSocketUri)
244245
if ($parts === false || $parts) {
245246
throw new \InvalidArgumentException('Invalid Host header value');
246247
}
248+
} elseif (!$hasHost && $start['version'] === '1.1' && $start['method'] !== 'CONNECT') {
249+
// require Host request header for HTTP/1.1 (except for CONNECT method)
250+
throw new \InvalidArgumentException('Missing required Host request header');
251+
} elseif (!$hasHost) {
252+
// remove default Host request header for HTTP/1.0 when not explicitly given
253+
$request = $request->withoutHeader('Host');
247254
}
248255

249256
// ensure message boundaries are valid according to Content-Length and Transfer-Encoding request headers
@@ -266,9 +273,6 @@ public function parseRequest($headers, $remoteSocketUri, $localSocketUri)
266273
}
267274
}
268275

269-
// always sanitize Host header because it contains critical routing information
270-
$request = $request->withUri($request->getUri()->withUserInfo('u')->withUserInfo(''));
271-
272276
return $request;
273277
}
274278
}

tests/Io/RequestHeaderParserTest.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ public function testHeaderEventWithShouldApplyDefaultAddressFromLocalConnectionA
257257
$connection->emit('data', array("GET /foo HTTP/1.0\r\n\r\n"));
258258

259259
$this->assertEquals('http://127.1.1.1:8000/foo', $request->getUri());
260-
$this->assertEquals('127.1.1.1:8000', $request->getHeaderLine('Host'));
260+
$this->assertFalse($request->hasHeader('Host'));
261261
}
262262

263263
public function testHeaderEventViaHttpsShouldApplyHttpsSchemeFromLocalTlsConnectionAddress()
@@ -550,7 +550,7 @@ public function testInvalidContentLengthRequestHeaderWillEmitError()
550550
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(null)->getMock();
551551
$parser->handle($connection);
552552

553-
$connection->emit('data', array("GET / HTTP/1.1\r\nContent-Length: foo\r\n\r\n"));
553+
$connection->emit('data', array("GET / HTTP/1.1\r\nHost: localhost\r\nContent-Length: foo\r\n\r\n"));
554554

555555
$this->assertInstanceOf('InvalidArgumentException', $error);
556556
$this->assertSame(400, $error->getCode());
@@ -570,7 +570,7 @@ public function testInvalidRequestWithMultipleContentLengthRequestHeadersWillEmi
570570
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(null)->getMock();
571571
$parser->handle($connection);
572572

573-
$connection->emit('data', array("GET / HTTP/1.1\r\nContent-Length: 4\r\nContent-Length: 5\r\n\r\n"));
573+
$connection->emit('data', array("GET / HTTP/1.1\r\nHost: localhost\r\nContent-Length: 4\r\nContent-Length: 5\r\n\r\n"));
574574

575575
$this->assertInstanceOf('InvalidArgumentException', $error);
576576
$this->assertSame(400, $error->getCode());
@@ -590,7 +590,7 @@ public function testInvalidTransferEncodingRequestHeaderWillEmitError()
590590
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(null)->getMock();
591591
$parser->handle($connection);
592592

593-
$connection->emit('data', array("GET / HTTP/1.1\r\nTransfer-Encoding: foo\r\n\r\n"));
593+
$connection->emit('data', array("GET / HTTP/1.1\r\nHost: localhost\r\nTransfer-Encoding: foo\r\n\r\n"));
594594

595595
$this->assertInstanceOf('InvalidArgumentException', $error);
596596
$this->assertSame(501, $error->getCode());
@@ -610,7 +610,7 @@ public function testInvalidRequestWithBothTransferEncodingAndContentLengthWillEm
610610
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(null)->getMock();
611611
$parser->handle($connection);
612612

613-
$connection->emit('data', array("GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\nContent-Length: 0\r\n\r\n"));
613+
$connection->emit('data', array("GET / HTTP/1.1\r\nHost: localhost\r\nTransfer-Encoding: chunked\r\nContent-Length: 0\r\n\r\n"));
614614

615615
$this->assertInstanceOf('InvalidArgumentException', $error);
616616
$this->assertSame(400, $error->getCode());
@@ -762,7 +762,7 @@ public function testQueryParmetersWillBeSet()
762762
private function createGetRequest()
763763
{
764764
$data = "GET / HTTP/1.1\r\n";
765-
$data .= "Host: example.com:80\r\n";
765+
$data .= "Host: example.com\r\n";
766766
$data .= "Connection: close\r\n";
767767
$data .= "\r\n";
768768

@@ -772,7 +772,7 @@ private function createGetRequest()
772772
private function createAdvancedPostRequest()
773773
{
774774
$data = "POST /foo?bar=baz HTTP/1.1\r\n";
775-
$data .= "Host: example.com:80\r\n";
775+
$data .= "Host: example.com\r\n";
776776
$data .= "User-Agent: react/alpha\r\n";
777777
$data .= "Connection: close\r\n";
778778
$data .= "\r\n";

0 commit comments

Comments
 (0)