Skip to content

Commit f5f3cc0

Browse files
committed
Simplify assigning response headers by avoiding duplicate assignments
1 parent 5ea0858 commit f5f3cc0

1 file changed

Lines changed: 37 additions & 33 deletions

File tree

src/StreamingServer.php

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -332,57 +332,61 @@ public function handleResponse(ConnectionInterface $connection, ServerRequestInt
332332
return;
333333
}
334334

335-
$response = $response->withProtocolVersion($request->getProtocolVersion());
335+
$code = $response->getStatusCode();
336+
$method = $request->getMethod();
337+
338+
// assign HTTP protocol version from request automatically
339+
$version = $request->getProtocolVersion();
340+
$response = $response->withProtocolVersion($version);
336341

337-
// assign default "X-Powered-By" header as first for history reasons
342+
// assign default "X-Powered-By" header automatically
338343
if (!$response->hasHeader('X-Powered-By')) {
339344
$response = $response->withHeader('X-Powered-By', 'React/alpha');
340-
}
341-
342-
if ($response->hasHeader('X-Powered-By') && $response->getHeaderLine('X-Powered-By') === ''){
345+
} elseif ($response->getHeaderLine('X-Powered-By') === ''){
343346
$response = $response->withoutHeader('X-Powered-By');
344347
}
345348

346-
$response = $response->withoutHeader('Transfer-Encoding');
347-
348-
// assign date header if no 'date' is given, use the current time where this code is running
349+
// assign default "Date" header from current time automatically
349350
if (!$response->hasHeader('Date')) {
350351
// IMF-fixdate = day-name "," SP date1 SP time-of-day SP GMT
351352
$response = $response->withHeader('Date', gmdate('D, d M Y H:i:s') . ' GMT');
352-
}
353-
354-
if ($response->hasHeader('Date') && $response->getHeaderLine('Date') === ''){
353+
} elseif ($response->getHeaderLine('Date') === ''){
355354
$response = $response->withoutHeader('Date');
356355
}
357356

358-
if (!$body instanceof HttpBodyStream) {
359-
$response = $response->withHeader('Content-Length', (string)$body->getSize());
360-
} elseif (!$response->hasHeader('Content-Length') && $request->getProtocolVersion() === '1.1') {
357+
// assign "Content-Length" and "Transfer-Encoding" headers automatically
358+
$chunked = false;
359+
if (($method === 'CONNECT' && $code >= 200 && $code < 300) || ($code >= 100 && $code < 200) || $code === 204) {
360+
// 2xx response to CONNECT and 1xx and 204 MUST NOT include Content-Length or Transfer-Encoding header
361+
$response = $response->withoutHeader('Content-Length')->withoutHeader('Transfer-Encoding');
362+
} elseif (!$body instanceof HttpBodyStream) {
363+
// assign Content-Length header when using a "normal" buffered body string
364+
$response = $response->withHeader('Content-Length', (string)$body->getSize())->withoutHeader('Transfer-Encoding');
365+
} elseif (!$response->hasHeader('Content-Length') && $version === '1.1') {
361366
// assign chunked transfer-encoding if no 'content-length' is given for HTTP/1.1 responses
362367
$response = $response->withHeader('Transfer-Encoding', 'chunked');
368+
$chunked = true;
369+
} else {
370+
// remove any Transfer-Encoding headers unless automatically enabled above
371+
$response = $response->withoutHeader('Transfer-Encoding');
363372
}
364373

365-
// HTTP/1.1 assumes persistent connection support by default
366-
// we do not support persistent connections, so let the client know
367-
if ($request->getProtocolVersion() === '1.1') {
368-
$response = $response->withHeader('Connection', 'close');
369-
}
370-
// 2xx response to CONNECT and 1xx and 204 MUST NOT include Content-Length or Transfer-Encoding header
371-
$code = $response->getStatusCode();
372-
if (($request->getMethod() === 'CONNECT' && $code >= 200 && $code < 300) || ($code >= 100 && $code < 200) || $code === 204) {
373-
$response = $response->withoutHeader('Content-Length')->withoutHeader('Transfer-Encoding');
374-
}
375-
376-
// 101 (Switching Protocols) response uses Connection: upgrade header
377-
// persistent connections are currently not supported, so do not use
378-
// this for any other replies in order to preserve "Connection: close"
374+
// assign "Connection" header automatically
379375
if ($code === 101) {
376+
// 101 (Switching Protocols) response uses Connection: upgrade header
380377
$response = $response->withHeader('Connection', 'upgrade');
378+
} elseif ($version === '1.1') {
379+
// HTTP/1.1 assumes persistent connection support by default
380+
// we do not support persistent connections, so let the client know
381+
$response = $response->withHeader('Connection', 'close');
382+
} else {
383+
// remove any Connection headers unless automatically enabled above
384+
$response = $response->withoutHeader('Connection');
381385
}
382386

383387
// 101 (Switching Protocols) response (for Upgrade request) forwards upgraded data through duplex stream
384388
// 2xx (Successful) response to CONNECT forwards tunneled application data through duplex stream
385-
if (($code === 101 || ($request->getMethod() === 'CONNECT' && $code >= 200 && $code < 300)) && $body instanceof HttpBodyStream && $body->input instanceof WritableStreamInterface) {
389+
if (($code === 101 || ($method === 'CONNECT' && $code >= 200 && $code < 300)) && $body instanceof HttpBodyStream && $body->input instanceof WritableStreamInterface) {
386390
if ($request->getBody()->isReadable()) {
387391
// request is still streaming => wait for request close before forwarding following data from connection
388392
$request->getBody()->on('close', function () use ($connection, $body) {
@@ -399,7 +403,7 @@ public function handleResponse(ConnectionInterface $connection, ServerRequestInt
399403
}
400404

401405
// build HTTP response header by appending status line and header fields
402-
$headers = "HTTP/" . $response->getProtocolVersion() . " " . $response->getStatusCode() . " " . $response->getReasonPhrase() . "\r\n";
406+
$headers = "HTTP/" . $version . " " . $code . " " . $response->getReasonPhrase() . "\r\n";
403407
foreach ($response->getHeaders() as $name => $values) {
404408
foreach ($values as $value) {
405409
$headers .= $name . ": " . $value . "\r\n";
@@ -408,14 +412,14 @@ public function handleResponse(ConnectionInterface $connection, ServerRequestInt
408412

409413
// response to HEAD and 1xx, 204 and 304 responses MUST NOT include a body
410414
// exclude status 101 (Switching Protocols) here for Upgrade request handling above
411-
if ($request->getMethod() === 'HEAD' || $code === 100 || ($code > 101 && $code < 200) || $code === 204 || $code === 304) {
415+
if ($method === 'HEAD' || $code === 100 || ($code > 101 && $code < 200) || $code === 204 || $code === 304) {
412416
$body = '';
413417
}
414418

415419
// this is a non-streaming response body or the body stream already closed?
416420
if (!$body instanceof ReadableStreamInterface || !$body->isReadable()) {
417421
// add final chunk if a streaming body is already closed and uses `Transfer-Encoding: chunked`
418-
if ($body instanceof ReadableStreamInterface && $response->getHeaderLine('Transfer-Encoding') === 'chunked') {
422+
if ($body instanceof ReadableStreamInterface && $chunked) {
419423
$body = "0\r\n\r\n";
420424
}
421425

@@ -427,7 +431,7 @@ public function handleResponse(ConnectionInterface $connection, ServerRequestInt
427431

428432
$connection->write($headers . "\r\n");
429433

430-
if ($response->getHeaderLine('Transfer-Encoding') === 'chunked') {
434+
if ($chunked) {
431435
$body = new ChunkedEncoder($body);
432436
}
433437

0 commit comments

Comments
 (0)