Skip to content

Commit 4e363d7

Browse files
Improved shutdown behaviour in connection handler and added tests to improve coverage
1 parent 446ce6e commit 4e363d7

File tree

2 files changed

+99
-23
lines changed

2 files changed

+99
-23
lines changed

src/ConnectionHandler/ConnectionHandler.php

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class ConnectionHandler implements LoggerAwareInterface
2727
/**
2828
* @var bool
2929
*/
30-
protected $shuttingDown;
30+
protected $shutdown;
3131

3232
/**
3333
* @var KernelInterface
@@ -65,7 +65,7 @@ public function __construct(KernelInterface $kernel, ConnectionInterface $connec
6565
{
6666
$this->setLogger((null === $logger) ? new NullLogger() : $logger);
6767

68-
$this->shuttingDown = false;
68+
$this->shutdown = false;
6969
$this->kernel = $kernel;
7070
$this->connection = $connection;
7171
$this->requests = [];
@@ -100,9 +100,7 @@ public function ready()
100100
*/
101101
public function shutdown()
102102
{
103-
foreach ($this->requests as &$request) {
104-
$request['keepAlive'] = false;
105-
}
103+
$this->shutdown = true;
106104
}
107105

108106
/**
@@ -220,16 +218,15 @@ protected function endRequest($requestId, $appStatus = 0, $protocolStatus = Daem
220218
$content = pack('NCx3', $appStatus, $protocolStatus);
221219
$this->writeRecord($requestId, DaemonInterface::FCGI_END_REQUEST, $content);
222220

223-
if (isset($this->requests[$requestId])) {
224-
$keepAlive = $this->requests[$requestId]['keepAlive'];
221+
$keepAlive = $this->requests[$requestId]['keepAlive'];
225222

223+
if (isset($this->requests[$requestId]['builder'])) {
226224
$this->requests[$requestId]['builder']->clean();
227-
unset($this->requests[$requestId]);
225+
}
228226

229-
if (!$keepAlive) {
230-
$this->close();
231-
}
232-
} else {
227+
unset($this->requests[$requestId]);
228+
229+
if (!$keepAlive) {
233230
$this->close();
234231
}
235232
}
@@ -283,25 +280,25 @@ protected function processBeginRequestRecord($requestId, $contentData)
283280
throw new ProtocolException('Unexpected FCGI_BEGIN_REQUEST record');
284281
}
285282

286-
if ($this->shuttingDown) {
287-
$this->endRequest($requestId, 0, DaemonInterface::FCGI_OVERLOADED);
288-
return;
289-
}
290-
291283
$contentFormat = 'nrole/Cflags/x5';
292284

293285
$content = unpack($contentFormat, $contentData);
294286

295287
$keepAlive = DaemonInterface::FCGI_KEEP_CONNECTION & $content['flags'];
296288

289+
$this->requests[$requestId] = ['keepAlive' => $keepAlive];
290+
291+
if ($this->shutdown) {
292+
$this->endRequest($requestId, 0, DaemonInterface::FCGI_OVERLOADED);
293+
return;
294+
}
295+
297296
if (DaemonInterface::FCGI_RESPONDER !== $content['role']) {
298297
$this->endRequest($requestId, 0, DaemonInterface::FCGI_UNKNOWN_ROLE);
299-
} else {
300-
$this->requests[$requestId] = [
301-
'keepAlive' => $keepAlive,
302-
'builder' => new RequestBuilder(),
303-
];
298+
return;
304299
}
300+
301+
$this->requests[$requestId]['builder'] = new RequestBuilder();
305302
}
306303

307304
/**

test/ConnectionHandler/ConnectionHandlerTest.php

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,85 @@ public function testClosedConnection()
175175
$this->assertTrue($context['connection']->isClosed());
176176
}
177177

178+
/**
179+
* Test that the daemon doesn't accept new requests once it has been
180+
* shutdown but still handles old ones
181+
*/
182+
public function testShutdown()
183+
{
184+
$response = new Response($this->toStream('Hello World'), 200);
185+
186+
$kernelCalled = false;
187+
188+
// Create test environment
189+
$context = $this->createTestingContext(function (ServerRequestInterface $request) use ($response, &$kernelCalled) {
190+
$this->assertEquals(['FOO' => 'bar'], $request->getServerParams());
191+
192+
$kernelCalled = true;
193+
194+
return $response;
195+
});
196+
197+
// Write half of the first request (id 0)
198+
$context['clientWrapper']->writeBeginRequestRecord(0, DaemonInterface::FCGI_RESPONDER, DaemonInterface::FCGI_KEEP_CONNECTION);
199+
$context['clientWrapper']->writeParamsRecord(0, 'foo', 'bar');
200+
$context['clientWrapper']->writeParamsRecord(0);
201+
202+
// Process first half of the first request
203+
$context['handler']->ready();
204+
205+
// Trigger the shutdown method
206+
$context['handler']->shutdown();
207+
208+
// Try creating a new request (id 1)
209+
$context['clientWrapper']->writeBeginRequestRecord(1, DaemonInterface::FCGI_RESPONDER, DaemonInterface::FCGI_KEEP_CONNECTION);
210+
211+
// Process the attempt at a second request after the shutdown
212+
$context['handler']->ready();
213+
214+
// The application should end the second request immediately
215+
$record = $context['clientWrapper']->readRecord($this);
216+
$this->assertEquals(DaemonInterface::FCGI_END_REQUEST, $record['type']);
217+
$this->assertEquals(1, $record['requestId']);
218+
219+
// The application should declare an overloaded protocol status
220+
$content = unpack('NappStatus/CprotocolStatus/x3', $record['contentData']);
221+
$this->assertEquals(DaemonInterface::FCGI_OVERLOADED, $content['protocolStatus']);
222+
223+
// Check daemon hasn't closed server side connection
224+
$this->assertFalse($context['connection']->isClosed());
225+
226+
// Write the second half of the first request
227+
$context['clientWrapper']->writeStdinRecord(0);
228+
229+
// Process the second half of the first request
230+
$context['handler']->ready();
231+
232+
// Assert that kernel was called
233+
$this->assertTrue($kernelCalled);
234+
235+
// Receive response
236+
$rawResponse = '';
237+
238+
do {
239+
$record = $context['clientWrapper']->readRecord($this);
240+
241+
$this->assertEquals(0, $record['requestId']);
242+
243+
if (DaemonInterface::FCGI_STDOUT === $record['type']) {
244+
$rawResponse .= $record['contentData'];
245+
}
246+
} while (DaemonInterface::FCGI_END_REQUEST !== $record['type']);
247+
248+
$expectedResponse = "Status: 200 OK\r\n\r\nHello World";
249+
250+
// Check response
251+
$this->assertEquals($expectedResponse, $rawResponse);
252+
253+
// Close the client side socket
254+
fclose($context['sockets'][0]);
255+
}
256+
178257
/**
179258
* Test that the daemon correctly handles a kernel exception.
180259
*/
@@ -403,7 +482,7 @@ public function testUnknownRole()
403482

404483
// The application should respond with an end request record
405484
$record = $context['clientWrapper']->readRecord($this);
406-
$this->assertEquals(DaemonInterface::FCGI_END_REQUEST, $record['type']);
485+
$this->assertEquals(DaemonInterface::FCGI_END_REQUEST, $record['type']);
407486

408487
// The application should declare an unknown role protocol status
409488
$content = unpack('NappStatus/CprotocolStatus/x3', $record['contentData']);

0 commit comments

Comments
 (0)