@@ -128,39 +128,6 @@ public function handle(ConnectionInterface $conn)
128
128
*/
129
129
public function parseRequest ($ headers , ConnectionInterface $ connection )
130
130
{
131
- // additional, stricter safe-guard for request line
132
- // because request parser doesn't properly cope with invalid ones
133
- $ start = array ();
134
- if (!\preg_match ('#^(?<method>[^ ]+) (?<target>[^ ]+) HTTP/(?<version>\d\.\d)#m ' , $ headers , $ start )) {
135
- throw new \InvalidArgumentException ('Unable to parse invalid request-line ' );
136
- }
137
-
138
- // only support HTTP/1.1 and HTTP/1.0 requests
139
- if ($ start ['version ' ] !== '1.1 ' && $ start ['version ' ] !== '1.0 ' ) {
140
- throw new \InvalidArgumentException ('Received request with invalid protocol version ' , Response::STATUS_VERSION_NOT_SUPPORTED );
141
- }
142
-
143
- // match all request header fields into array, thanks to @kelunik for checking the HTTP specs and coming up with this regex
144
- $ matches = array ();
145
- $ n = \preg_match_all ('/^([^()<>@,;: \\\"\/\[\]?={}\x01-\x20\x7F]++):[\x20\x09]*+((?:[\x20\x09]*+[\x21-\x7E\x80-\xFF]++)*+)[\x20\x09]*+[\r]?+\n/m ' , $ headers , $ matches , \PREG_SET_ORDER );
146
-
147
- // check number of valid header fields matches number of lines + request line
148
- if (\substr_count ($ headers , "\n" ) !== $ n + 1 ) {
149
- throw new \InvalidArgumentException ('Unable to parse invalid request header fields ' );
150
- }
151
-
152
- // format all header fields into associative array
153
- $ host = null ;
154
- $ fields = array ();
155
- foreach ($ matches as $ match ) {
156
- $ fields [$ match [1 ]][] = $ match [2 ];
157
-
158
- // match `Host` request header
159
- if ($ host === null && \strtolower ($ match [1 ]) === 'host ' ) {
160
- $ host = $ match [2 ];
161
- }
162
- }
163
-
164
131
// reuse same connection params for all server params for this connection
165
132
$ cid = \PHP_VERSION_ID < 70200 ? \spl_object_hash ($ connection ) : \spl_object_id ($ connection );
166
133
if (isset ($ this ->connectionParams [$ cid ])) {
@@ -207,101 +174,6 @@ public function parseRequest($headers, ConnectionInterface $connection)
207
174
$ serverParams ['REQUEST_TIME ' ] = (int ) ($ now = $ this ->clock ->now ());
208
175
$ serverParams ['REQUEST_TIME_FLOAT ' ] = $ now ;
209
176
210
- // scheme is `http` unless TLS is used
211
- $ scheme = isset ($ serverParams ['HTTPS ' ]) ? 'https:// ' : 'http:// ' ;
212
-
213
- // default host if unset comes from local socket address or defaults to localhost
214
- $ hasHost = $ host !== null ;
215
- if ($ host === null ) {
216
- $ host = isset ($ serverParams ['SERVER_ADDR ' ], $ serverParams ['SERVER_PORT ' ]) ? $ serverParams ['SERVER_ADDR ' ] . ': ' . $ serverParams ['SERVER_PORT ' ] : '127.0.0.1 ' ;
217
- }
218
-
219
- if ($ start ['method ' ] === 'OPTIONS ' && $ start ['target ' ] === '* ' ) {
220
- // support asterisk-form for `OPTIONS *` request line only
221
- $ uri = $ scheme . $ host ;
222
- } elseif ($ start ['method ' ] === 'CONNECT ' ) {
223
- $ parts = \parse_url ('tcp:// ' . $ start ['target ' ]);
224
-
225
- // check this is a valid authority-form request-target (host:port)
226
- if (!isset ($ parts ['scheme ' ], $ parts ['host ' ], $ parts ['port ' ]) || \count ($ parts ) !== 3 ) {
227
- throw new \InvalidArgumentException ('CONNECT method MUST use authority-form request target ' );
228
- }
229
- $ uri = $ scheme . $ start ['target ' ];
230
- } else {
231
- // support absolute-form or origin-form for proxy requests
232
- if ($ start ['target ' ][0 ] === '/ ' ) {
233
- $ uri = $ scheme . $ host . $ start ['target ' ];
234
- } else {
235
- // ensure absolute-form request-target contains a valid URI
236
- $ parts = \parse_url ($ start ['target ' ]);
237
-
238
- // make sure value contains valid host component (IP or hostname), but no fragment
239
- if (!isset ($ parts ['scheme ' ], $ parts ['host ' ]) || $ parts ['scheme ' ] !== 'http ' || isset ($ parts ['fragment ' ])) {
240
- throw new \InvalidArgumentException ('Invalid absolute-form request-target ' );
241
- }
242
-
243
- $ uri = $ start ['target ' ];
244
- }
245
- }
246
-
247
- $ request = new ServerRequest (
248
- $ start ['method ' ],
249
- $ uri ,
250
- $ fields ,
251
- '' ,
252
- $ start ['version ' ],
253
- $ serverParams
254
- );
255
-
256
- // only assign request target if it is not in origin-form (happy path for most normal requests)
257
- if ($ start ['target ' ][0 ] !== '/ ' ) {
258
- $ request = $ request ->withRequestTarget ($ start ['target ' ]);
259
- }
260
-
261
- if ($ hasHost ) {
262
- // Optional Host request header value MUST be valid (host and optional port)
263
- $ parts = \parse_url ('http:// ' . $ request ->getHeaderLine ('Host ' ));
264
-
265
- // make sure value contains valid host component (IP or hostname)
266
- if (!$ parts || !isset ($ parts ['scheme ' ], $ parts ['host ' ])) {
267
- $ parts = false ;
268
- }
269
-
270
- // make sure value does not contain any other URI component
271
- if (\is_array ($ parts )) {
272
- unset($ parts ['scheme ' ], $ parts ['host ' ], $ parts ['port ' ]);
273
- }
274
- if ($ parts === false || $ parts ) {
275
- throw new \InvalidArgumentException ('Invalid Host header value ' );
276
- }
277
- } elseif (!$ hasHost && $ start ['version ' ] === '1.1 ' && $ start ['method ' ] !== 'CONNECT ' ) {
278
- // require Host request header for HTTP/1.1 (except for CONNECT method)
279
- throw new \InvalidArgumentException ('Missing required Host request header ' );
280
- } elseif (!$ hasHost ) {
281
- // remove default Host request header for HTTP/1.0 when not explicitly given
282
- $ request = $ request ->withoutHeader ('Host ' );
283
- }
284
-
285
- // ensure message boundaries are valid according to Content-Length and Transfer-Encoding request headers
286
- if ($ request ->hasHeader ('Transfer-Encoding ' )) {
287
- if (\strtolower ($ request ->getHeaderLine ('Transfer-Encoding ' )) !== 'chunked ' ) {
288
- throw new \InvalidArgumentException ('Only chunked-encoding is allowed for Transfer-Encoding ' , Response::STATUS_NOT_IMPLEMENTED );
289
- }
290
-
291
- // Transfer-Encoding: chunked and Content-Length header MUST NOT be used at the same time
292
- // as per https://tools.ietf.org/html/rfc7230#section-3.3.3
293
- if ($ request ->hasHeader ('Content-Length ' )) {
294
- throw new \InvalidArgumentException ('Using both `Transfer-Encoding: chunked` and `Content-Length` is not allowed ' , Response::STATUS_BAD_REQUEST );
295
- }
296
- } elseif ($ request ->hasHeader ('Content-Length ' )) {
297
- $ string = $ request ->getHeaderLine ('Content-Length ' );
298
-
299
- if ((string )(int )$ string !== $ string ) {
300
- // Content-Length value is not an integer or not a single integer
301
- throw new \InvalidArgumentException ('The value of `Content-Length` is not valid ' , Response::STATUS_BAD_REQUEST );
302
- }
303
- }
304
-
305
- return $ request ;
177
+ return ServerRequest::parseMessage ($ headers , $ serverParams );
306
178
}
307
179
}
0 commit comments