-
Notifications
You must be signed in to change notification settings - Fork 71
Asynchronously handle streamed responses #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,9 +78,9 @@ public function onRequest(ReactRequest $request, HttpResponse $response) | |
|
||
$syRequest = $this->mapRequest($request); | ||
|
||
//start buffering the output, so cgi is not sending any http headers | ||
//this is necessary because it would break session handling since | ||
//headers_sent() returns true if any unbuffered output reaches cgi stdout. | ||
// start buffering the output, so cgi is not sending any http headers | ||
// this is necessary because it would break session handling since | ||
// headers_sent() returns true if any unbuffered output reaches cgi stdout. | ||
ob_start(); | ||
|
||
try { | ||
|
@@ -92,9 +92,15 @@ public function onRequest(ReactRequest $request, HttpResponse $response) | |
} catch (\Exception $exception) { | ||
$response->writeHead(500); // internal server error | ||
$response->end(); | ||
|
||
// end buffering if we need to throw | ||
@ob_end_clean(); | ||
throw $exception; | ||
} | ||
|
||
// should not receive output from application->handle() | ||
@ob_end_clean(); | ||
|
||
$this->mapResponse($response, $syResponse); | ||
|
||
if ($this->application instanceof TerminableInterface) { | ||
|
@@ -170,15 +176,10 @@ protected function mapRequest(ReactRequest $reactRequest) | |
*/ | ||
protected function mapResponse(HttpResponse $reactResponse, SymfonyResponse $syResponse) | ||
{ | ||
//end active session | ||
// end active session | ||
if (PHP_SESSION_ACTIVE === session_status()) { | ||
session_write_close(); | ||
session_unset(); //reset $_SESSION | ||
} | ||
|
||
$content = $syResponse->getContent(); | ||
if ($syResponse instanceof SymfonyStreamedResponse) { | ||
$syResponse->sendContent(); | ||
session_unset(); // reset $_SESSION | ||
} | ||
|
||
$nativeHeaders = []; | ||
|
@@ -200,8 +201,8 @@ protected function mapResponse(HttpResponse $reactResponse, SymfonyResponse $syR | |
} | ||
} | ||
|
||
//after reading all headers we need to reset it, so next request | ||
//operates on a clean header. | ||
// after reading all headers we need to reset it, so next request | ||
// operates on a clean header. | ||
header_remove(); | ||
|
||
$headers = array_merge($nativeHeaders, $syResponse->headers->allPreserveCase()); | ||
|
@@ -238,14 +239,33 @@ protected function mapResponse(HttpResponse $reactResponse, SymfonyResponse $syR | |
$headers['Set-Cookie'] = $cookies; | ||
} | ||
|
||
$reactResponse->writeHead($syResponse->getStatusCode(), $headers); | ||
if ($syResponse instanceof SymfonyStreamedResponse) { | ||
$reactResponse->writeHead($syResponse->getStatusCode(), $headers); | ||
|
||
// asynchronously get content | ||
ob_start(function($buffer) use ($reactResponse) { | ||
$reactResponse->write($buffer); | ||
return ''; | ||
}, 4096); | ||
|
||
$syResponse->sendContent(); | ||
|
||
$stdOut = ''; | ||
while ($buffer = @ob_get_clean()) { | ||
$stdOut .= $buffer; | ||
// flush remaining content | ||
@ob_end_flush(); | ||
$reactResponse->end(); | ||
} | ||
else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no line break please |
||
ob_start(); | ||
$content = $syResponse->getContent(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do you expect something to happen by calling this getter? Or why are you wrapping it with ob_start? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @marcj I don't but this is actually already the case in the current implementation with buffering started here https://github.com/php-pm/php-pm-httpkernel/blob/master/Bridges/HttpKernel.php#L84 Is there a specific function you had intended to buffer in the current implementation? Then I could limit it to that one. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, then let it that way 👍 |
||
@ob_end_flush(); | ||
|
||
$reactResponse->end($stdOut . $content); | ||
if (!isset($headers['Content-Length'])) { | ||
$headers['Content-Length'] = strlen($content); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if we have utf8 content? Regarding http spec, content-length is not mandatory and the client reads until connection drops. I'd go the safe path and don't set content-length for now. Did you experience issues with not setting it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Imho content-length is the number of octets. strlen will do that correctly for utf8, too
It excludes "simple" clients since react will deliver a chunked response if content-length is not present. So the argument goes more in direction of react than this workaround? I'm inventing the content-length to prevent react from inventing chunked... Ok? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah ok! :) |
||
} | ||
|
||
$reactResponse->writeHead($syResponse->getStatusCode(), $headers); | ||
$reactResponse->end($content); | ||
} | ||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need a
ob_end_clean
call also in catch block I think. I guess its better to just let ob_start out oftry
and addob_end_clean
after the whole try-catch.Btw, tabs detected :P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need a ob_end_clean call also in catch block I think. I guess its better to just let ob_start out of try and add
ob_end_clean after the whole try-catch.
@marcj Will fix this. Could you also comment on the content-length?