Skip to content

Commit 5873b89

Browse files
authored
Merge pull request #519 from clue-labs/psr7-request
Refactor `Request` and `ServerRequest` classes to build on top of new PSR-7 implementation
2 parents 4c23ea4 + 0638dcd commit 5873b89

File tree

6 files changed

+636
-39
lines changed

6 files changed

+636
-39
lines changed

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2642,8 +2642,7 @@ This is mostly used internally to represent each outgoing HTTP request
26422642
message for the HTTP client implementation. Likewise, you can also use this
26432643
class with other HTTP client implementations and for tests.
26442644

2645-
> Internally, this implementation builds on top of an existing outgoing
2646-
request message and only adds support for streaming. This base class is
2645+
> Internally, this implementation builds on top of a base class which is
26472646
considered an implementation detail that may change in the future.
26482647

26492648
#### ServerRequest
@@ -2662,8 +2661,7 @@ This is mostly used internally to represent each incoming request message.
26622661
Likewise, you can also use this class in test cases to test how your web
26632662
application reacts to certain HTTP requests.
26642663

2665-
> Internally, this implementation builds on top of an existing outgoing
2666-
request message and only adds required server methods. This base class is
2664+
> Internally, this implementation builds on top of a base class which is
26672665
considered an implementation detail that may change in the future.
26682666

26692667
#### ResponseException

src/Io/AbstractRequest.php

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<?php
2+
3+
namespace React\Http\Io;
4+
5+
use Psr\Http\Message\RequestInterface;
6+
use Psr\Http\Message\StreamInterface;
7+
use Psr\Http\Message\UriInterface;
8+
use RingCentral\Psr7\Uri;
9+
10+
/**
11+
* [Internal] Abstract HTTP request base class (PSR-7)
12+
*
13+
* @internal
14+
* @see RequestInterface
15+
*/
16+
abstract class AbstractRequest extends AbstractMessage implements RequestInterface
17+
{
18+
/** @var ?string */
19+
private $requestTarget;
20+
21+
/** @var string */
22+
private $method;
23+
24+
/** @var UriInterface */
25+
private $uri;
26+
27+
/**
28+
* @param string $method
29+
* @param string|UriInterface $uri
30+
* @param array<string,string|string[]> $headers
31+
* @param StreamInterface $body
32+
* @param string unknown $protocolVersion
33+
*/
34+
protected function __construct(
35+
$method,
36+
$uri,
37+
array $headers,
38+
StreamInterface $body,
39+
$protocolVersion
40+
) {
41+
if (\is_string($uri)) {
42+
$uri = new Uri($uri);
43+
} elseif (!$uri instanceof UriInterface) {
44+
throw new \InvalidArgumentException(
45+
'Argument #2 ($uri) expected string|Psr\Http\Message\UriInterface'
46+
);
47+
}
48+
49+
// assign default `Host` request header from URI unless already given explicitly
50+
$host = $uri->getHost();
51+
if ($host !== '') {
52+
foreach ($headers as $name => $value) {
53+
if (\strtolower($name) === 'host' && $value !== array()) {
54+
$host = '';
55+
break;
56+
}
57+
}
58+
if ($host !== '') {
59+
$port = $uri->getPort();
60+
if ($port !== null && (!($port === 80 && $uri->getScheme() === 'http') || !($port === 443 && $uri->getScheme() === 'https'))) {
61+
$host .= ':' . $port;
62+
}
63+
64+
$headers = array('Host' => $host) + $headers;
65+
}
66+
}
67+
68+
parent::__construct($protocolVersion, $headers, $body);
69+
70+
$this->method = $method;
71+
$this->uri = $uri;
72+
}
73+
74+
public function getRequestTarget()
75+
{
76+
if ($this->requestTarget !== null) {
77+
return $this->requestTarget;
78+
}
79+
80+
$target = $this->uri->getPath();
81+
if ($target === '') {
82+
$target = '/';
83+
}
84+
if (($query = $this->uri->getQuery()) !== '') {
85+
$target .= '?' . $query;
86+
}
87+
88+
return $target;
89+
}
90+
91+
public function withRequestTarget($requestTarget)
92+
{
93+
if ((string) $requestTarget === $this->requestTarget) {
94+
return $this;
95+
}
96+
97+
$request = clone $this;
98+
$request->requestTarget = (string) $requestTarget;
99+
100+
return $request;
101+
}
102+
103+
public function getMethod()
104+
{
105+
return $this->method;
106+
}
107+
108+
public function withMethod($method)
109+
{
110+
if ((string) $method === $this->method) {
111+
return $this;
112+
}
113+
114+
$request = clone $this;
115+
$request->method = (string) $method;
116+
117+
return $request;
118+
}
119+
120+
public function getUri()
121+
{
122+
return $this->uri;
123+
}
124+
125+
public function withUri(UriInterface $uri, $preserveHost = false)
126+
{
127+
if ($uri === $this->uri) {
128+
return $this;
129+
}
130+
131+
$request = clone $this;
132+
$request->uri = $uri;
133+
134+
$host = $uri->getHost();
135+
$port = $uri->getPort();
136+
if ($port !== null && $host !== '' && (!($port === 80 && $uri->getScheme() === 'http') || !($port === 443 && $uri->getScheme() === 'https'))) {
137+
$host .= ':' . $port;
138+
}
139+
140+
// update `Host` request header if URI contains a new host and `$preserveHost` is false
141+
if ($host !== '' && (!$preserveHost || $request->getHeaderLine('Host') === '')) {
142+
// first remove all headers before assigning `Host` header to ensure it always comes first
143+
foreach (\array_keys($request->getHeaders()) as $name) {
144+
$request = $request->withoutHeader($name);
145+
}
146+
147+
// add `Host` header first, then all other original headers
148+
$request = $request->withHeader('Host', $host);
149+
foreach ($this->withoutHeader('Host')->getHeaders() as $name => $value) {
150+
$request = $request->withHeader($name, $value);
151+
}
152+
}
153+
154+
return $request;
155+
}
156+
}

src/Message/Request.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
use Psr\Http\Message\RequestInterface;
66
use Psr\Http\Message\StreamInterface;
77
use Psr\Http\Message\UriInterface;
8+
use React\Http\Io\AbstractRequest;
89
use React\Http\Io\BufferedBody;
910
use React\Http\Io\ReadableBodyStream;
1011
use React\Stream\ReadableStreamInterface;
11-
use RingCentral\Psr7\Request as BaseRequest;
1212

1313
/**
1414
* Respresents an outgoing HTTP request message.
@@ -22,13 +22,12 @@
2222
* message for the HTTP client implementation. Likewise, you can also use this
2323
* class with other HTTP client implementations and for tests.
2424
*
25-
* > Internally, this implementation builds on top of an existing outgoing
26-
* request message and only adds support for streaming. This base class is
25+
* > Internally, this implementation builds on top of a base class which is
2726
* considered an implementation detail that may change in the future.
2827
*
2928
* @see RequestInterface
3029
*/
31-
final class Request extends BaseRequest implements RequestInterface
30+
final class Request extends AbstractRequest implements RequestInterface
3231
{
3332
/**
3433
* @param string $method HTTP method for the request.

src/Message/ServerRequest.php

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
use Psr\Http\Message\ServerRequestInterface;
66
use Psr\Http\Message\StreamInterface;
77
use Psr\Http\Message\UriInterface;
8+
use React\Http\Io\AbstractRequest;
89
use React\Http\Io\BufferedBody;
910
use React\Http\Io\HttpBodyStream;
1011
use React\Stream\ReadableStreamInterface;
11-
use RingCentral\Psr7\Request as BaseRequest;
1212

1313
/**
1414
* Respresents an incoming server request message.
@@ -24,13 +24,12 @@
2424
* Likewise, you can also use this class in test cases to test how your web
2525
* application reacts to certain HTTP requests.
2626
*
27-
* > Internally, this implementation builds on top of an existing outgoing
28-
* request message and only adds required server methods. This base class is
27+
* > Internally, this implementation builds on top of a base class which is
2928
* considered an implementation detail that may change in the future.
3029
*
3130
* @see ServerRequestInterface
3231
*/
33-
final class ServerRequest extends BaseRequest implements ServerRequestInterface
32+
final class ServerRequest extends AbstractRequest implements ServerRequestInterface
3433
{
3534
private $attributes = array();
3635

@@ -57,26 +56,22 @@ public function __construct(
5756
$version = '1.1',
5857
$serverParams = array()
5958
) {
60-
$stream = null;
6159
if (\is_string($body)) {
6260
$body = new BufferedBody($body);
6361
} elseif ($body instanceof ReadableStreamInterface && !$body instanceof StreamInterface) {
64-
$stream = $body;
65-
$body = null;
62+
$temp = new self($method, '', $headers);
63+
$size = (int) $temp->getHeaderLine('Content-Length');
64+
if (\strtolower($temp->getHeaderLine('Transfer-Encoding')) === 'chunked') {
65+
$size = null;
66+
}
67+
$body = new HttpBodyStream($body, $size);
6668
} elseif (!$body instanceof StreamInterface) {
6769
throw new \InvalidArgumentException('Invalid server request body given');
6870
}
6971

70-
$this->serverParams = $serverParams;
7172
parent::__construct($method, $url, $headers, $body, $version);
7273

73-
if ($stream !== null) {
74-
$size = (int) $this->getHeaderLine('Content-Length');
75-
if (\strtolower($this->getHeaderLine('Transfer-Encoding')) === 'chunked') {
76-
$size = null;
77-
}
78-
$this->stream = new HttpBodyStream($stream, $size);
79-
}
74+
$this->serverParams = $serverParams;
8075

8176
$query = $this->getUri()->getQuery();
8277
if ($query !== '') {

0 commit comments

Comments
 (0)