Skip to content

Streaming request body parsing #41

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

Closed
wants to merge 66 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
b3e9253
All request bodies now come in streaming
WyriHaximus Oct 1, 2015
e1b0d03
Moved parsers to their own namespace
WyriHaximus Oct 1, 2015
dca940d
File object representing uploaded files
WyriHaximus Oct 1, 2015
816d1c6
Cleaned up old test code I've forgotten to remove
WyriHaximus Oct 1, 2015
9eb1a93
https://github.com/reactphp/http/issues/44#issuecomment-151941120
WyriHaximus Oct 28, 2015
9bb6116
End the stream when not streaming
WyriHaximus Mar 19, 2016
41eaa92
Properly trim off the file contents
WyriHaximus Mar 19, 2016
2860f10
Make sure extra headers are ignored
WyriHaximus Mar 19, 2016
64446d1
Emit the post vars instead of setting them from the FormUrlencoded pa…
WyriHaximus Mar 20, 2016
25f4718
FormParserFactory
WyriHaximus Mar 21, 2016
db68230
Changed both parsers to work with only the request
WyriHaximus Mar 21, 2016
ab99c8f
Revert old behavior and set request (partial) body
WyriHaximus Mar 22, 2016
20682f6
Updated tests and removed multipart and urlencoded tests
WyriHaximus Mar 22, 2016
1501ab8
Replaced array_key_exists with isset
WyriHaximus Mar 23, 2016
8c0b93e
Don't exactly match content type for FormUrlencoded in case extra dat…
WyriHaximus Mar 23, 2016
a09ec5f
Fetch content type and force it's contents to lowercase so we only ha…
WyriHaximus Mar 23, 2016
e4925f9
Moved initial content length header detection to the constructor
WyriHaximus Mar 23, 2016
085e01d
Removed post property from Request
WyriHaximus Mar 23, 2016
92b6a73
Remove body property from Request, restoring it to the state it was i…
WyriHaximus Mar 23, 2016
6500579
Case insensitive multipart chunk headers
WyriHaximus Mar 24, 2016
d525755
Headers are made case insensitive after getHeaders
WyriHaximus Mar 24, 2016
1755230
Removed un-used imports
WyriHaximus Mar 24, 2016
578f288
NoBody object return from the form parser factory in case we don't ha…
WyriHaximus Apr 14, 2016
78e2ed0
Renamed to GPL as we are going to replace it
WyriHaximus Apr 15, 2016
3b0de3a
Bare skeleton of the new Multipart parser
WyriHaximus Apr 17, 2016
e9bac7c
Parser interface
WyriHaximus Apr 17, 2016
261e850
Detect boundary if none is present in headers; https://github.com/rea…
WyriHaximus Apr 17, 2016
4de83e1
Pre set boundary ending and size
WyriHaximus Apr 18, 2016
3c76b9f
Parse header and post chunks
WyriHaximus Apr 19, 2016
64ff430
Return NoBody parser in case the factory can't find out which parser …
WyriHaximus Apr 23, 2016
4b86553
Added file support to the multipart parser
WyriHaximus Apr 23, 2016
9b959d7
Removed GPL multipart parser
WyriHaximus Apr 23, 2016
2928193
Added close event forwarding
WyriHaximus Apr 23, 2016
8c34c92
Emit events on parser instead of request
WyriHaximus Apr 23, 2016
598d50f
Typo, $this->request doesn't exists
WyriHaximus Apr 24, 2016
44f2517
Deferred stream that waits until the request emits end
WyriHaximus May 2, 2016
eb72ded
Emit end (not close) when the full body has been parsed
WyriHaximus May 2, 2016
621ee71
Missed an emit close
WyriHaximus May 2, 2016
87cf49e
Detect body end instead of waiting for end event
WyriHaximus May 5, 2016
1384f81
Added isDone method to parsers for those cases where an end would be …
WyriHaximus May 5, 2016
c68bb16
Dropped the done trait
WyriHaximus May 10, 2016
b80c745
Introducing the RawBody for when we can't determin the content type b…
WyriHaximus May 10, 2016
78b38ca
Section in the readme on FormParserFactory and DeferredStream
WyriHaximus May 10, 2016
b888423
Typo: quest => request
WyriHaximus May 17, 2016
dbc2356
Correctly handle file names with colons in them
WyriHaximus Jul 3, 2016
74c2150
Ensure we always have content type
WyriHaximus Jul 3, 2016
29f50ed
Ensure correct multipart boundary handling
WyriHaximus Jul 3, 2016
29a9e5d
Slice off two characters at the beginning of detected boundaries
WyriHaximus Jul 3, 2016
cabf9d9
Merge branch 'master' into streaming-multipart
WyriHaximus Jul 3, 2016
3794e36
Resolved issues that come up when merging master into this branch
WyriHaximus Jul 3, 2016
92f347b
Renamed File::getType to File::getContentType as a result of: https:/…
WyriHaximus Jul 7, 2016
d35937f
Merge branch 'master' into streaming-multipart
WyriHaximus Jul 7, 2016
7c83595
Removed branch alias after merge
WyriHaximus Jul 7, 2016
b2fddd2
Corrected promise targetting: https://github.com/reactphp/http/pull/4…
WyriHaximus Jul 7, 2016
77fd1b2
Simplefied content type and length detection: https://github.com/reac…
WyriHaximus Jul 7, 2016
7d75b00
Forgot to replace || with &&
WyriHaximus Jul 7, 2016
7873ca0
Renamed Parser namespace to BodyParser and also moved the FormParserF…
WyriHaximus Jul 8, 2016
1676b33
Renamed BodyParser to StreamingBodyParser to make it more explicit wh…
WyriHaximus Jul 8, 2016
339416d
Don't attach fieldname to File object, emit it with file instead: htt…
WyriHaximus Jul 8, 2016
32edca3
Addressed @clue's concerns for the mixing concerns, by creating Conte…
WyriHaximus Jul 8, 2016
57bdc26
Appended Parser to all streaming body parsers as suggested by @jsor a…
WyriHaximus Aug 3, 2016
ae172a5
Renamed DeferredStream to BufferedSink and modled it more like react/…
WyriHaximus Aug 3, 2016
359781c
Renamed StreamingParserInterface to ParserInterface as suggested by @…
WyriHaximus Aug 3, 2016
f045fc4
Readme clarifications (suggested by @jsor)
WyriHaximus Aug 3, 2016
abb4ca0
Catch the case where no content-type header is send
WyriHaximus Sep 6, 2016
d4cef86
Deal with an edge case where a zero length will never fulfill the pro…
WyriHaximus Sep 7, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,25 @@ This is an HTTP server which responds with `Hello World` to every request.
$socket->listen(1337);
$loop->run();
```

## StreamingBodyParser\Factory and StreamingBodyParser\BufferedSink Usage

The `StreamingBodyParser\FormParserFactory` parses a request and determines which body parser to use (multipart, formurlencoded, raw body, or no body). Those body parsers emit events on `post` fields, `file` on files, and raw body emits `body` when it received the whole body. `StreamingBodyParser\BufferedSink` listens for those events and returns them through a promise when done.

```php
$loop = React\EventLoop\Factory::create();
$socket = new React\Socket\Server($loop);

$http = new React\Http\Server($socket);
$http->on('request', function ($request, $response) {
$parser = React\Http\StreamingBodyParser\Factory::create($request);
React\Http\StreamingBodyParser\BufferedSink::createPromise($parser)->then(function ($result) use ($response) {
var_export($result);
$response->writeHead(200, array('Content-Type' => 'text/plain'));
$response->end("Hello World!\n");
});
});

$socket->listen(1337);
$loop->run();
```
6 changes: 5 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@
"guzzlehttp/psr7": "^1.0",
"react/socket": "^0.4",
"react/stream": "^0.4",
"evenement/evenement": "^2.0"
"evenement/evenement": "^2.0",
"react/promise": "^2.0 || ^1.1"
},
"autoload": {
"psr-4": {
"React\\Http\\": "src"
}
},
"require-dev": {
"clue/block-react": "^1.1"
}
}
59 changes: 59 additions & 0 deletions src/File.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace React\Http;

use React\Stream\ReadableStreamInterface;

class File implements FileInterface
{
/**
* @var string
*/
protected $filename;

/**
* @var string
*/
protected $contentType;

/**
* @var ReadableStreamInterface
*/
protected $stream;

/**
* @param string $filename
* @param string $contentType
* @param ReadableStreamInterface $stream
*/
public function __construct($filename, $contentType, ReadableStreamInterface $stream)
{
$this->filename = $filename;
$this->contentType = $contentType;
$this->stream = $stream;
}

/**
* @return string
*/
public function getFilename()
{
return $this->filename;
}

/**
* @return string
*/
public function getContentType()
{
return $this->contentType;
}

/**
* @return ReadableStreamInterface
*/
public function getStream()
{
return $this->stream;
}
}
23 changes: 23 additions & 0 deletions src/FileInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace React\Http;

use React\Stream\ReadableStreamInterface;

interface FileInterface
{
/**
* @return string
*/
public function getFilename();

/**
* @return string
*/
public function getContentType();

/**
* @return ReadableStreamInterface
*/
public function getStream();
}
203 changes: 0 additions & 203 deletions src/MultipartParser.php

This file was deleted.

36 changes: 1 addition & 35 deletions src/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,17 @@ class Request extends EventEmitter implements ReadableStreamInterface
private $query;
private $httpVersion;
private $headers;
private $body;
private $post = [];
private $files = [];

// metadata, implicitly added externally
public $remoteAddress;

public function __construct($method, $url, $query = array(), $httpVersion = '1.1', $headers = array(), $body = '')
public function __construct($method, $url, $query = array(), $httpVersion = '1.1', $headers = array())
{
$this->method = $method;
$this->url = $url;
$this->query = $query;
$this->httpVersion = $httpVersion;
$this->headers = $headers;
$this->body = $body;
}

public function getMethod()
Expand Down Expand Up @@ -62,36 +58,6 @@ public function getHeaders()
return $this->headers;
}

public function getBody()
{
return $this->body;
}

public function setBody($body)
{
$this->body = $body;
}

public function getFiles()
{
return $this->files;
}

public function setFiles($files)
{
$this->files = $files;
}

public function getPost()
{
return $this->post;
}

public function setPost($post)
{
$this->post = $post;
}

public function getRemoteAddress()
{
return $this->remoteAddress;
Expand Down
Loading