@@ -27,6 +27,9 @@ class RequestHeaderParser extends EventEmitter
27
27
/** @var Clock */
28
28
private $ clock ;
29
29
30
+ /** @var array<string|int,array<string,string>> */
31
+ private $ connectionParams = array ();
32
+
30
33
public function __construct (Clock $ clock )
31
34
{
32
35
$ this ->clock = $ clock ;
@@ -66,8 +69,7 @@ public function handle(ConnectionInterface $conn)
66
69
try {
67
70
$ request = $ that ->parseRequest (
68
71
(string )\substr ($ buffer , 0 , $ endOfHeader + 2 ),
69
- $ conn ->getRemoteAddress (),
70
- $ conn ->getLocalAddress ()
72
+ $ conn
71
73
);
72
74
} catch (Exception $ exception ) {
73
75
$ buffer = '' ;
@@ -119,13 +121,12 @@ public function handle(ConnectionInterface $conn)
119
121
120
122
/**
121
123
* @param string $headers buffer string containing request headers only
122
- * @param ?string $remoteSocketUri
123
- * @param ?string $localSocketUri
124
+ * @param ConnectionInterface $connection
124
125
* @return ServerRequestInterface
125
126
* @throws \InvalidArgumentException
126
127
* @internal
127
128
*/
128
- public function parseRequest ($ headers , $ remoteSocketUri , $ localSocketUri )
129
+ public function parseRequest ($ headers , ConnectionInterface $ connection )
129
130
{
130
131
// additional, stricter safe-guard for request line
131
132
// because request parser doesn't properly cope with invalid ones
@@ -160,26 +161,59 @@ public function parseRequest($headers, $remoteSocketUri, $localSocketUri)
160
161
}
161
162
}
162
163
164
+ // reuse same connection params for all server params for this connection
165
+ $ cid = \PHP_VERSION_ID < 70200 ? \spl_object_hash ($ connection ) : \spl_object_id ($ connection );
166
+ if (isset ($ this ->connectionParams [$ cid ])) {
167
+ $ serverParams = $ this ->connectionParams [$ cid ];
168
+ } else {
169
+ // assign new server params for new connection
170
+ $ serverParams = array ();
171
+
172
+ // scheme is `http` unless TLS is used
173
+ $ localSocketUri = $ connection ->getLocalAddress ();
174
+ $ localParts = $ localSocketUri === null ? array () : \parse_url ($ localSocketUri );
175
+ if (isset ($ localParts ['scheme ' ]) && $ localParts ['scheme ' ] === 'tls ' ) {
176
+ $ serverParams ['HTTPS ' ] = 'on ' ;
177
+ }
178
+
179
+ // apply SERVER_ADDR and SERVER_PORT if server address is known
180
+ // address should always be known, even for Unix domain sockets (UDS)
181
+ // but skip UDS as it doesn't have a concept of host/port.
182
+ if ($ localSocketUri !== null && isset ($ localParts ['host ' ], $ localParts ['port ' ])) {
183
+ $ serverParams ['SERVER_ADDR ' ] = $ localParts ['host ' ];
184
+ $ serverParams ['SERVER_PORT ' ] = $ localParts ['port ' ];
185
+ }
186
+
187
+ // apply REMOTE_ADDR and REMOTE_PORT if source address is known
188
+ // address should always be known, unless this is over Unix domain sockets (UDS)
189
+ $ remoteSocketUri = $ connection ->getRemoteAddress ();
190
+ if ($ remoteSocketUri !== null ) {
191
+ $ remoteAddress = \parse_url ($ remoteSocketUri );
192
+ $ serverParams ['REMOTE_ADDR ' ] = $ remoteAddress ['host ' ];
193
+ $ serverParams ['REMOTE_PORT ' ] = $ remoteAddress ['port ' ];
194
+ }
195
+
196
+ // remember server params for all requests from this connection, reset on connection close
197
+ $ this ->connectionParams [$ cid ] = $ serverParams ;
198
+ $ params =& $ this ->connectionParams ;
199
+ $ connection ->on ('close ' , function () use (&$ params , $ cid ) {
200
+ assert (\is_array ($ params ));
201
+ unset($ params [$ cid ]);
202
+ });
203
+ }
204
+
163
205
// create new obj implementing ServerRequestInterface by preserving all
164
206
// previous properties and restoring original request-target
165
- $ serverParams = array (
166
- 'REQUEST_TIME ' => (int ) ($ now = $ this ->clock ->now ()),
167
- 'REQUEST_TIME_FLOAT ' => $ now
168
- );
207
+ $ serverParams ['REQUEST_TIME ' ] = (int ) ($ now = $ this ->clock ->now ());
208
+ $ serverParams ['REQUEST_TIME_FLOAT ' ] = $ now ;
169
209
170
210
// scheme is `http` unless TLS is used
171
- $ localParts = $ localSocketUri === null ? array () : \parse_url ($ localSocketUri );
172
- if (isset ($ localParts ['scheme ' ]) && $ localParts ['scheme ' ] === 'tls ' ) {
173
- $ scheme = 'https:// ' ;
174
- $ serverParams ['HTTPS ' ] = 'on ' ;
175
- } else {
176
- $ scheme = 'http:// ' ;
177
- }
211
+ $ scheme = isset ($ serverParams ['HTTPS ' ]) ? 'https:// ' : 'http:// ' ;
178
212
179
213
// default host if unset comes from local socket address or defaults to localhost
180
214
$ hasHost = $ host !== null ;
181
215
if ($ host === null ) {
182
- $ host = isset ($ localParts [ ' host ' ], $ localParts [ ' port ' ]) ? $ localParts [ ' host ' ] . ': ' . $ localParts [ ' port ' ] : '127.0.0.1 ' ;
216
+ $ host = isset ($ serverParams [ ' SERVER_ADDR ' ], $ serverParams [ ' SERVER_PORT ' ]) ? $ serverParams [ ' SERVER_ADDR ' ] . ': ' . $ serverParams [ ' SERVER_PORT ' ] : '127.0.0.1 ' ;
183
217
}
184
218
185
219
if ($ start ['method ' ] === 'OPTIONS ' && $ start ['target ' ] === '* ' ) {
@@ -210,22 +244,6 @@ public function parseRequest($headers, $remoteSocketUri, $localSocketUri)
210
244
}
211
245
}
212
246
213
- // apply REMOTE_ADDR and REMOTE_PORT if source address is known
214
- // address should always be known, unless this is over Unix domain sockets (UDS)
215
- if ($ remoteSocketUri !== null ) {
216
- $ remoteAddress = \parse_url ($ remoteSocketUri );
217
- $ serverParams ['REMOTE_ADDR ' ] = $ remoteAddress ['host ' ];
218
- $ serverParams ['REMOTE_PORT ' ] = $ remoteAddress ['port ' ];
219
- }
220
-
221
- // apply SERVER_ADDR and SERVER_PORT if server address is known
222
- // address should always be known, even for Unix domain sockets (UDS)
223
- // but skip UDS as it doesn't have a concept of host/port.
224
- if ($ localSocketUri !== null && isset ($ localParts ['host ' ], $ localParts ['port ' ])) {
225
- $ serverParams ['SERVER_ADDR ' ] = $ localParts ['host ' ];
226
- $ serverParams ['SERVER_PORT ' ] = $ localParts ['port ' ];
227
- }
228
-
229
247
$ request = new ServerRequest (
230
248
$ start ['method ' ],
231
249
$ uri ,
0 commit comments