@@ -128,39 +128,6 @@ public function handle(ConnectionInterface $conn)
128128 */
129129 public function parseRequest ($ headers , ConnectionInterface $ connection )
130130 {
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-
164131 // reuse same connection params for all server params for this connection
165132 $ cid = \PHP_VERSION_ID < 70200 ? \spl_object_hash ($ connection ) : \spl_object_id ($ connection );
166133 if (isset ($ this ->connectionParams [$ cid ])) {
@@ -207,101 +174,6 @@ public function parseRequest($headers, ConnectionInterface $connection)
207174 $ serverParams ['REQUEST_TIME ' ] = (int ) ($ now = $ this ->clock ->now ());
208175 $ serverParams ['REQUEST_TIME_FLOAT ' ] = $ now ;
209176
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 );
306178 }
307179}
0 commit comments