Skip to content

Commit 5dea067

Browse files
committed
Add ChunkedDecoder to Server
Add ServerTest
1 parent 1a1b0aa commit 5dea067

File tree

2 files changed

+188
-5
lines changed

2 files changed

+188
-5
lines changed

src/Server.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public function handleConnection(ConnectionInterface $conn)
8888
$that->handleRequest($conn, $request);
8989

9090
if ($bodyBuffer !== '') {
91-
$request->emit('data', array($bodyBuffer));
91+
$conn->emit('data', array($bodyBuffer));
9292
}
9393
});
9494

@@ -122,6 +122,15 @@ public function handleRequest(ConnectionInterface $conn, Request $request)
122122
'[]'
123123
);
124124

125+
$stream = $conn;
126+
if ($request->hasHeader('Transfer-Encoding')) {
127+
$transferEncodingHeader = $request->getHeader('Transfer-Encoding');
128+
// 'chunked' must always be the final value of 'Transfer-Encoding' according to: https://tools.ietf.org/html/rfc7230#section-3.3.1
129+
if (strtolower(end($transferEncodingHeader)) === 'chunked') {
130+
$stream = new ChunkedDecoder($conn);
131+
}
132+
}
133+
125134
// forward pause/resume calls to underlying connection
126135
$request->on('pause', array($conn, 'pause'));
127136
$request->on('resume', array($conn, 'resume'));
@@ -133,13 +142,14 @@ public function handleRequest(ConnectionInterface $conn, Request $request)
133142
});
134143

135144
// forward connection events to request
136-
$conn->on('end', function () use ($request) {
137-
$request->emit('end');
138-
});
139-
$conn->on('data', function ($data) use ($request) {
145+
$stream->on('data', function ($data) use ($request) {
140146
$request->emit('data', array($data));
141147
});
142148

149+
$stream->on('end', function () use ($request) {
150+
$request->emit('end', array());
151+
});
152+
143153
$this->emit('request', array($request, $response));
144154
}
145155

tests/ServerTest.php

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,179 @@ function ($data) use (&$buffer) {
265265
$this->assertContains("\r\n\r\nError 400: Bad Request", $buffer);
266266
}
267267

268+
public function testBodyDataWillBeSendViaRequestEvent()
269+
{
270+
$server = new Server($this->socket);
271+
272+
$dataEvent = $this->expectCallableOnceWith('hello');
273+
$endEvent = $this->expectCallableNever();
274+
$closeEvent = $this->expectCallableNever();
275+
$errorEvent = $this->expectCallableNever();
276+
277+
$server->on('request', function (Request $request, Response $response) use ($dataEvent, $endEvent, $closeEvent, $errorEvent) {
278+
$request->on('data', $dataEvent);
279+
$request->on('end', $endEvent);
280+
$request->on('close', $closeEvent);
281+
$request->on('error', $errorEvent);
282+
});
283+
284+
$this->socket->emit('connection', array($this->connection));
285+
286+
$data = "GET / HTTP/1.1\r\n";
287+
$data .= "Host: example.com:80\r\n";
288+
$data .= "Connection: close\r\n";
289+
$data .= "Content-Length: 5\r\n";
290+
$data .= "\r\n";
291+
$data .= "hello";
292+
293+
$this->connection->emit('data', array($data));
294+
}
295+
296+
public function testChunkedEncodedRequestWillBeParsedForRequestEvent()
297+
{
298+
$server = new Server($this->socket);
299+
300+
$dataEvent = $this->expectCallableOnceWith('hello');
301+
$endEvent = $this->expectCallableOnce();
302+
$closeEvent = $this->expectCallableNever();
303+
$errorEvent = $this->expectCallableNever();
304+
305+
$server->on('request', function (Request $request, Response $response) use ($dataEvent, $endEvent, $closeEvent, $errorEvent) {
306+
$request->on('data', $dataEvent);
307+
$request->on('end', $endEvent);
308+
$request->on('close', $closeEvent);
309+
$request->on('error', $errorEvent);
310+
});
311+
312+
$this->socket->emit('connection', array($this->connection));
313+
314+
$data = "GET / HTTP/1.1\r\n";
315+
$data .= "Host: example.com:80\r\n";
316+
$data .= "Connection: close\r\n";
317+
$data .= "Transfer-Encoding: chunked\r\n";
318+
$data .= "\r\n";
319+
$data .= "5\r\nhello\r\n";
320+
$data .= "0\r\n\r\n";
321+
322+
$this->connection->emit('data', array($data));
323+
}
324+
325+
public function testChunkedEncodedRequestAdditionalDataWontBeEmitted()
326+
{
327+
$server = new Server($this->socket);
328+
329+
$dataEvent = $this->expectCallableOnceWith('hello');
330+
$endEvent = $this->expectCallableOnce();
331+
$closeEvent = $this->expectCallableNever();
332+
$errorEvent = $this->expectCallableNever();
333+
334+
$server->on('request', function (Request $request, Response $response) use ($dataEvent, $endEvent, $closeEvent, $errorEvent) {
335+
$request->on('data', $dataEvent);
336+
$request->on('end', $endEvent);
337+
$request->on('close', $closeEvent);
338+
$request->on('error', $errorEvent);
339+
});
340+
341+
$this->socket->emit('connection', array($this->connection));
342+
343+
$data = "GET / HTTP/1.1\r\n";
344+
$data .= "Host: example.com:80\r\n";
345+
$data .= "Connection: close\r\n";
346+
$data .= "Transfer-Encoding: chunked\r\n";
347+
$data .= "\r\n";
348+
$data .= "5\r\nhello\r\n";
349+
$data .= "0\r\n\r\n";
350+
$data .= "2\r\nhi\r\n";
351+
352+
$this->connection->emit('data', array($data));
353+
}
354+
355+
public function testEmptyChunkedEncodedRequest()
356+
{
357+
$server = new Server($this->socket);
358+
359+
$dataEvent = $this->expectCallableNever();
360+
$endEvent = $this->expectCallableOnce();
361+
$closeEvent = $this->expectCallableNever();
362+
$errorEvent = $this->expectCallableNever();
363+
364+
$server->on('request', function (Request $request, Response $response) use ($dataEvent, $endEvent, $closeEvent, $errorEvent) {
365+
$request->on('data', $dataEvent);
366+
$request->on('end', $endEvent);
367+
$request->on('close', $closeEvent);
368+
$request->on('error', $errorEvent);
369+
});
370+
371+
$this->socket->emit('connection', array($this->connection));
372+
373+
$data = "GET / HTTP/1.1\r\n";
374+
$data .= "Host: example.com:80\r\n";
375+
$data .= "Connection: close\r\n";
376+
$data .= "Transfer-Encoding: chunked\r\n";
377+
$data .= "\r\n";
378+
$data .= "0\r\n\r\n";
379+
380+
$this->connection->emit('data', array($data));
381+
}
382+
383+
public function testChunkedIsUpperCase()
384+
{
385+
$server = new Server($this->socket);
386+
387+
$dataEvent = $this->expectCallableOnceWith('hello');
388+
$endEvent = $this->expectCallableOnce();
389+
$closeEvent = $this->expectCallableNever();
390+
$errorEvent = $this->expectCallableNever();
391+
392+
$server->on('request', function (Request $request, Response $response) use ($dataEvent, $endEvent, $closeEvent, $errorEvent) {
393+
$request->on('data', $dataEvent);
394+
$request->on('end', $endEvent);
395+
$request->on('close', $closeEvent);
396+
$request->on('error', $errorEvent);
397+
});
398+
399+
$this->socket->emit('connection', array($this->connection));
400+
401+
$data = "GET / HTTP/1.1\r\n";
402+
$data .= "Host: example.com:80\r\n";
403+
$data .= "Connection: close\r\n";
404+
$data .= "Transfer-Encoding: CHUNKED\r\n";
405+
$data .= "\r\n";
406+
$data .= "5\r\nhello\r\n";
407+
$data .= "0\r\n\r\n";
408+
409+
$this->connection->emit('data', array($data));
410+
}
411+
412+
public function testChunkedIsMixedUpperAndLowerCase()
413+
{
414+
$server = new Server($this->socket);
415+
416+
$dataEvent = $this->expectCallableOnceWith('hello');
417+
$endEvent = $this->expectCallableOnce();
418+
$closeEvent = $this->expectCallableNever();
419+
$errorEvent = $this->expectCallableNever();
420+
421+
$server->on('request', function (Request $request, Response $response) use ($dataEvent, $endEvent, $closeEvent, $errorEvent) {
422+
$request->on('data', $dataEvent);
423+
$request->on('end', $endEvent);
424+
$request->on('close', $closeEvent);
425+
$request->on('error', $errorEvent);
426+
});
427+
428+
$this->socket->emit('connection', array($this->connection));
429+
430+
$data = "GET / HTTP/1.1\r\n";
431+
$data .= "Host: example.com:80\r\n";
432+
$data .= "Connection: close\r\n";
433+
$data .= "Transfer-Encoding: CHunKeD\r\n";
434+
$data .= "\r\n";
435+
$data .= "5\r\nhello\r\n";
436+
$data .= "0\r\n\r\n";
437+
438+
$this->connection->emit('data', array($data));
439+
}
440+
268441
private function createGetRequest()
269442
{
270443
$data = "GET / HTTP/1.1\r\n";

0 commit comments

Comments
 (0)