diff --git a/composer.json b/composer.json index cd1194da..c186927d 100644 --- a/composer.json +++ b/composer.json @@ -14,10 +14,5 @@ "psr-4": { "React\\Http\\": "src" } - }, - "extra": { - "branch-alias": { - "dev-master": "0.5-dev" - } } } diff --git a/src/MultipartParser.php b/src/MultipartParser.php deleted file mode 100644 index d4c0f4b9..00000000 --- a/src/MultipartParser.php +++ /dev/null @@ -1,203 +0,0 @@ -input = $input; - $this->boundary = $boundary; - } - - /** - * @return array - */ - public function getPost() - { - return $this->post; - } - - /** - * @return array - */ - public function getFiles() - { - return $this->files; - } - - /** - * Do the actual parsing - */ - public function parse() - { - $blocks = $this->split($this->boundary); - - foreach ($blocks as $value) { - if (empty($value)) { - continue; - } - - $this->parseBlock($value); - } - } - - /** - * @param $boundary string - * @returns Array - */ - protected function split($boundary) - { - $boundary = preg_quote($boundary); - $result = preg_split("/\\-+$boundary/", $this->input); - array_pop($result); - return $result; - } - - /** - * Decide if we handle a file, post value or octet stream - * - * @param $string string - * @returns void - */ - protected function parseBlock($string) - { - if (strpos($string, 'filename') !== false) { - $this->file($string); - return; - } - - // This may never be called, if an octet stream - // has a filename it is catched by the previous - // condition already. - if (strpos($string, 'application/octet-stream') !== false) { - $this->octetStream($string); - return; - } - - $this->post($string); - } - - /** - * Parse a raw octet stream - * - * @param $string - * @return array - */ - protected function octetStream($string) - { - preg_match('/name=\"([^\"]*)\".*stream[\n|\r]+([^\n\r].*)?$/s', $string, $match); - - $this->addResolved('post', $match[1], $match[2]); - } - - /** - * Parse a file - * - * @param $string - * @return array - */ - protected function file($string) - { - preg_match('/name=\"([^\"]*)\"; filename=\"([^\"]*)\"[\n|\r]+([^\n\r].*)?\r$/s', $string, $match); - preg_match('/Content-Type: (.*)?/', $match[3], $mime); - - $content = preg_replace('/Content-Type: (.*)[^\n\r]/', '', $match[3]); - $content = ltrim($content, "\r\n"); - - // Put content in a stream - $stream = fopen('php://memory', 'r+'); - if ($content !== '') { - fwrite($stream, $content); - fseek($stream, 0); - } - - $data = [ - 'name' => $match[2], - 'type' => trim($mime[1]), - 'stream' => $stream, // Instead of writing to a file, we write to a stream. - 'error' => UPLOAD_ERR_OK, - 'size' => function_exists('mb_strlen')? mb_strlen($content, '8bit') : strlen($content), - ]; - - //TODO :: have an option to write to files to emulate the same functionality as a real php server - //$path = tempnam(sys_get_temp_dir(), "php"); - //$err = file_put_contents($path, $content); - //$data['tmp_name'] = $path; - //$data['error'] = ($err === false) ? UPLOAD_ERR_NO_FILE : UPLOAD_ERR_OK; - - $this->addResolved('files', $match[1], $data); - } - - /** - * Parse POST values - * - * @param $string - * @return array - */ - protected function post($string) - { - preg_match('/name=\"([^\"]*)\"[\n|\r]+([^\n\r].*)?\r$/s', $string, $match); - - $this->addResolved('post', $match[1], $match[2]); - } - - /** - * Put the file or post where it belongs, - * The key names can be simple, or containing [] - * it can also be a named key - * - * @param $type - * @param $key - * @param $content - */ - protected function addResolved($type, $key, $content) - { - if (preg_match('/^(.*)\[(.*)\]$/i', $key, $tmp)) { - if (!empty($tmp[2])) { - $this->{$type}[$tmp[1]][$tmp[2]] = $content; - } else { - $this->{$type}[$tmp[1]][] = $content; - } - } else { - $this->{$type}[$key] = $content; - } - } -} diff --git a/src/Request.php b/src/Request.php index 0607b8e7..605b909e 100644 --- a/src/Request.php +++ b/src/Request.php @@ -11,25 +11,21 @@ class Request extends EventEmitter implements ReadableStreamInterface { private $readable = true; private $method; - private $url; + private $path; 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, $path, $query = array(), $httpVersion = '1.1', $headers = array()) { $this->method = $method; - $this->url = $url; + $this->path = $path; $this->query = $query; $this->httpVersion = $httpVersion; $this->headers = $headers; - $this->body = $body; } public function getMethod() @@ -39,12 +35,7 @@ public function getMethod() public function getPath() { - return $this->url->getPath(); - } - - public function getUrl() - { - return $this->url; + return $this->path; } public function getQuery() @@ -62,41 +53,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; - } - public function expectsContinue() { return isset($this->headers['Expect']) && '100-continue' === $this->headers['Expect']; diff --git a/src/RequestHeaderParser.php b/src/RequestHeaderParser.php new file mode 100644 index 00000000..3d0a4a7c --- /dev/null +++ b/src/RequestHeaderParser.php @@ -0,0 +1,65 @@ +buffer) + strlen($data) > $this->maxSize) { + $this->emit('error', array(new \OverflowException("Maximum header size of {$this->maxSize} exceeded."), $this)); + + return; + } + + $this->buffer .= $data; + + if (false !== strpos($this->buffer, "\r\n\r\n")) { + list($request, $bodyBuffer) = $this->parseRequest($this->buffer); + + $this->emit('headers', array($request, $bodyBuffer)); + $this->removeAllListeners(); + } + } + + public function parseRequest($data) + { + list($headers, $bodyBuffer) = explode("\r\n\r\n", $data, 2); + + $psrRequest = g7\parse_request($headers); + + $parsedQuery = []; + $queryString = $psrRequest->getUri()->getQuery(); + if ($queryString) { + parse_str($queryString, $parsedQuery); + } + + $headers = array_map(function($val) { + if (1 === count($val)) { + $val = $val[0]; + } + + return $val; + }, $psrRequest->getHeaders()); + + $request = new Request( + $psrRequest->getMethod(), + $psrRequest->getUri()->getPath(), + $parsedQuery, + $psrRequest->getProtocolVersion(), + $headers + ); + + return array($request, $bodyBuffer); + } +} diff --git a/src/RequestParser.php b/src/RequestParser.php deleted file mode 100644 index c4b913b8..00000000 --- a/src/RequestParser.php +++ /dev/null @@ -1,153 +0,0 @@ -buffer .= $data; - - if (!$this->request && false !== strpos($this->buffer, "\r\n\r\n")) { - - // Extract the header from the buffer - // in case the content isn't complete - list($headers, $this->buffer) = explode("\r\n\r\n", $this->buffer, 2); - - // Fail before parsing if the - if (strlen($headers) > $this->maxSize) { - $this->headerSizeExceeded(); - return; - } - - $this->request = $this->parseHeaders($headers . "\r\n\r\n"); - - if($this->request->expectsContinue()) { - $this->emit('expects_continue'); - } - } - - // if there is a request (meaning the headers are parsed) and - // we have the right content size, we can finish the parsing - if ($this->request && $this->isRequestComplete()) { - $this->parseBody(substr($this->buffer, 0, $this->length)); - $this->finishParsing(); - return; - } - - // fail if the header hasn't finished but it is already too large - if (!$this->request && strlen($this->buffer) > $this->maxSize) { - $this->headerSizeExceeded(); - return; - } - } - - protected function isRequestComplete() - { - $headers = $this->request->getHeaders(); - - // if there is no content length, there should - // be no content so we can say it's done - if (!array_key_exists('Content-Length', $headers)) { - return true; - } - - // if the content is present and has the - // right length, we're good to go - if (array_key_exists('Content-Length', $headers) && strlen($this->buffer) >= $headers['Content-Length']) { - - // store the expected content length - $this->length = $this->request->getHeaders()['Content-Length']; - - return true; - } - - return false; - } - - protected function finishParsing() - { - $this->emit('headers', array($this->request, $this->request->getBody())); - $this->removeAllListeners(); - $this->request = null; - } - - protected function headerSizeExceeded() - { - $this->emit('error', array(new \OverflowException("Maximum header size of {$this->maxSize} exceeded."), $this)); - } - - public function parseHeaders($data) - { - $psrRequest = gPsr\parse_request($data); - - $parsedQuery = []; - $queryString = $psrRequest->getUri()->getQuery(); - if ($queryString) { - parse_str($queryString, $parsedQuery); - } - - $headers = array_map(function($val) { - if (1 === count($val)) { - $val = $val[0]; - } - - return $val; - }, $psrRequest->getHeaders()); - - return new Request( - $psrRequest->getMethod(), - $psrRequest->getUri(), - $parsedQuery, - $psrRequest->getProtocolVersion(), - $headers - ); - } - - public function parseBody($content) - { - $headers = $this->request->getHeaders(); - - if (array_key_exists('Content-Type', $headers)) { - if (strpos($headers['Content-Type'], 'multipart/') === 0) { - //TODO :: parse the content while it is streaming - preg_match("/boundary=\"?(.*)\"?$/", $headers['Content-Type'], $matches); - $boundary = $matches[1]; - - $parser = new MultipartParser($content, $boundary); - $parser->parse(); - - $this->request->setPost($parser->getPost()); - $this->request->setFiles($parser->getFiles()); - return; - } - - if (strtolower($headers['Content-Type']) == 'application/x-www-form-urlencoded') { - parse_str($content, $result); - $this->request->setPost($result); - - return; - } - } - - - - $this->request->setBody($content); - } -} diff --git a/src/Server.php b/src/Server.php index 2948128c..e1abfd8e 100644 --- a/src/Server.php +++ b/src/Server.php @@ -18,8 +18,9 @@ public function __construct(SocketServerInterface $io) $this->io->on('connection', function (ConnectionInterface $conn) { // TODO: http 1.1 keep-alive // TODO: chunked transfer encoding (also for outgoing data) + // TODO: multipart parsing - $parser = new RequestParser(); + $parser = new RequestHeaderParser(); $parser->on('headers', function (Request $request, $bodyBuffer) use ($conn, $parser) { // attach remote ip to the request as metadata $request->remoteAddress = $conn->getRemoteAddress(); @@ -42,10 +43,6 @@ public function __construct(SocketServerInterface $io) }); $conn->on('data', array($parser, 'feed')); - - $parser->on('expects_continue', function() use($conn) { - $conn->write("HTTP/1.1 100 Continue\r\n\r\n"); - }); }); } diff --git a/tests/MultipartParserTest.php b/tests/MultipartParserTest.php deleted file mode 100644 index 39a02c71..00000000 --- a/tests/MultipartParserTest.php +++ /dev/null @@ -1,102 +0,0 @@ -parse(); - - $this->assertEmpty($parser->getFiles()); - $this->assertEquals( - ['users' => ['one' => 'single', 'two' => 'second']], - $parser->getPost() - ); - } - - public function testFileUpload() { - - $file = base64_decode("R0lGODlhAQABAIAAAP///wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw=="); - - $boundary = "---------------------------12758086162038677464950549563"; - - $data = "--$boundary\r\n"; - $data .= "Content-Disposition: form-data; name=\"user\"\r\n"; - $data .= "\r\n"; - $data .= "single\r\n"; - $data .= "--$boundary\r\n"; - $data .= "Content-Disposition: form-data; name=\"user2\"\r\n"; - $data .= "\r\n"; - $data .= "second\r\n"; - $data .= "--$boundary\r\n"; - $data .= "Content-Disposition: form-data; name=\"users[]\"\r\n"; - $data .= "\r\n"; - $data .= "first in array\r\n"; - $data .= "--$boundary\r\n"; - $data .= "Content-Disposition: form-data; name=\"users[]\"\r\n"; - $data .= "\r\n"; - $data .= "second in array\r\n"; - $data .= "--$boundary\r\n"; - $data .= "Content-Disposition: form-data; name=\"file\"; filename=\"User.php\"\r\n"; - $data .= "Content-Type: text/php\r\n"; - $data .= "\r\n"; - $data .= "parse(); - - $this->assertEquals(2, count($parser->getFiles())); - $this->assertEquals(2, count($parser->getFiles()['files'])); - $this->assertEquals( - ['user' => 'single', 'user2' => 'second', 'users' => ['first in array', 'second in array']], - $parser->getPost() - ); - - $uploaded_blank = $parser->getFiles()['files'][0]; - - // The original test was `file_get_contents($uploaded_blank['tmp_name'])` - // but as we moved to resources, we can't use that anymore, this is the only - // difference with a stock php implementation - $this->assertEquals($file, stream_get_contents($uploaded_blank['stream'])); - - $uploaded_blank['stream'] = 'file'; //override the resource as it is random - $expected_file = [ - 'name' => 'blank.gif', - 'type' => 'image/gif', - 'stream' => 'file', - 'error' => 0, - 'size' => 43, - ]; - - $this->assertEquals($expected_file, $uploaded_blank); - } -} diff --git a/tests/RequestHeaderParserTest.php b/tests/RequestHeaderParserTest.php new file mode 100644 index 00000000..dd3a0cbe --- /dev/null +++ b/tests/RequestHeaderParserTest.php @@ -0,0 +1,136 @@ +on('headers', $this->expectCallableNever()); + + $parser->feed("GET / HTTP/1.1\r\n"); + $parser->feed("Host: example.com:80\r\n"); + $parser->feed("Connection: close\r\n"); + + $parser->removeAllListeners(); + $parser->on('headers', $this->expectCallableOnce()); + + $parser->feed("\r\n"); + } + + public function testFeedInOneGo() + { + $parser = new RequestHeaderParser(); + $parser->on('headers', $this->expectCallableOnce()); + + $data = $this->createGetRequest(); + $parser->feed($data); + } + + public function testHeadersEventShouldReturnRequestAndBodyBuffer() + { + $request = null; + $bodyBuffer = null; + + $parser = new RequestHeaderParser(); + $parser->on('headers', function ($parsedRequest, $parsedBodyBuffer) use (&$request, &$bodyBuffer) { + $request = $parsedRequest; + $bodyBuffer = $parsedBodyBuffer; + }); + + $data = $this->createGetRequest(); + $data .= 'RANDOM DATA'; + $parser->feed($data); + + $this->assertInstanceOf('React\Http\Request', $request); + $this->assertSame('GET', $request->getMethod()); + $this->assertSame('/', $request->getPath()); + $this->assertSame(array(), $request->getQuery()); + $this->assertSame('1.1', $request->getHttpVersion()); + $this->assertSame(array('Host' => 'example.com:80', 'Connection' => 'close'), $request->getHeaders()); + + $this->assertSame('RANDOM DATA', $bodyBuffer); + } + + public function testHeadersEventShouldReturnBinaryBodyBuffer() + { + $bodyBuffer = null; + + $parser = new RequestHeaderParser(); + $parser->on('headers', function ($parsedRequest, $parsedBodyBuffer) use (&$bodyBuffer) { + $bodyBuffer = $parsedBodyBuffer; + }); + + $data = $this->createGetRequest(); + $data .= "\0x01\0x02\0x03\0x04\0x05"; + $parser->feed($data); + + $this->assertSame("\0x01\0x02\0x03\0x04\0x05", $bodyBuffer); + } + + public function testHeadersEventShouldParsePathAndQueryString() + { + $request = null; + + $parser = new RequestHeaderParser(); + $parser->on('headers', function ($parsedRequest, $parsedBodyBuffer) use (&$request) { + $request = $parsedRequest; + }); + + $data = $this->createAdvancedPostRequest(); + $parser->feed($data); + + $this->assertInstanceOf('React\Http\Request', $request); + $this->assertSame('POST', $request->getMethod()); + $this->assertSame('/foo', $request->getPath()); + $this->assertSame(array('bar' => 'baz'), $request->getQuery()); + $this->assertSame('1.1', $request->getHttpVersion()); + $headers = array( + 'Host' => 'example.com:80', + 'User-Agent' => 'react/alpha', + 'Connection' => 'close', + ); + $this->assertSame($headers, $request->getHeaders()); + } + + public function testHeaderOverflowShouldEmitError() + { + $error = null; + + $parser = new RequestHeaderParser(); + $parser->on('headers', $this->expectCallableNever()); + $parser->on('error', function ($message) use (&$error) { + $error = $message; + }); + + $data = str_repeat('A', 4097); + $parser->feed($data); + + $this->assertInstanceOf('OverflowException', $error); + $this->assertSame('Maximum header size of 4096 exceeded.', $error->getMessage()); + } + + private function createGetRequest() + { + $data = "GET / HTTP/1.1\r\n"; + $data .= "Host: example.com:80\r\n"; + $data .= "Connection: close\r\n"; + $data .= "\r\n"; + + return $data; + } + + private function createAdvancedPostRequest() + { + $data = "POST /foo?bar=baz HTTP/1.1\r\n"; + $data .= "Host: example.com:80\r\n"; + $data .= "User-Agent: react/alpha\r\n"; + $data .= "Connection: close\r\n"; + $data .= "\r\n"; + + return $data; + } +} diff --git a/tests/RequestParserTest.php b/tests/RequestParserTest.php deleted file mode 100644 index 728166b3..00000000 --- a/tests/RequestParserTest.php +++ /dev/null @@ -1,309 +0,0 @@ -on('headers', $this->expectCallableNever()); - - $parser->feed("GET / HTTP/1.1\r\n"); - $parser->feed("Host: example.com:80\r\n"); - $parser->feed("Connection: close\r\n"); - - $parser->removeAllListeners(); - $parser->on('headers', $this->expectCallableOnce()); - - $parser->feed("\r\n"); - } - - public function testFeedInOneGo() - { - $parser = new RequestParser(); - $parser->on('headers', $this->expectCallableOnce()); - - $data = $this->createGetRequest(); - $parser->feed($data); - } - - public function testHeadersEventShouldReturnRequestAndBodyBuffer() - { - $request = null; - $bodyBuffer = null; - - $parser = new RequestParser(); - $parser->on('headers', function ($parsedRequest, $parsedBodyBuffer) use (&$request, &$bodyBuffer) { - $request = $parsedRequest; - $bodyBuffer = $parsedBodyBuffer; - }); - - $data = $this->createGetRequest('RANDOM DATA', 11); - $parser->feed($data); - - $this->assertInstanceOf('React\Http\Request', $request); - $this->assertSame('GET', $request->getMethod()); - $this->assertSame('/', $request->getPath()); - $this->assertSame(array(), $request->getQuery()); - $this->assertSame('1.1', $request->getHttpVersion()); - $this->assertSame( - array('Host' => 'example.com:80', 'Connection' => 'close', 'Content-Length' => '11'), - $request->getHeaders() - ); - - $this->assertSame('RANDOM DATA', $bodyBuffer); - } - - public function testHeadersEventShouldReturnBinaryBodyBuffer() - { - $bodyBuffer = null; - - $parser = new RequestParser(); - $parser->on('headers', function ($parsedRequest, $parsedBodyBuffer) use (&$bodyBuffer) { - $bodyBuffer = $parsedBodyBuffer; - }); - - $data = $this->createGetRequest("\0x01\0x02\0x03\0x04\0x05", strlen("\0x01\0x02\0x03\0x04\0x05")); - $parser->feed($data); - - $this->assertSame("\0x01\0x02\0x03\0x04\0x05", $bodyBuffer); - } - - public function testHeadersEventShouldParsePathAndQueryString() - { - $request = null; - - $parser = new RequestParser(); - $parser->on('headers', function ($parsedRequest, $parsedBodyBuffer) use (&$request) { - $request = $parsedRequest; - }); - - $data = $this->createAdvancedPostRequest(); - $parser->feed($data); - - $this->assertInstanceOf('React\Http\Request', $request); - $this->assertSame('POST', $request->getMethod()); - $this->assertSame('/foo', $request->getPath()); - $this->assertSame(array('bar' => 'baz'), $request->getQuery()); - $this->assertSame('1.1', $request->getHttpVersion()); - $headers = array( - 'Host' => 'example.com:80', - 'User-Agent' => 'react/alpha', - 'Connection' => 'close', - ); - $this->assertSame($headers, $request->getHeaders()); - } - - public function testShouldReceiveBodyContent() - { - $content1 = "{\"test\":"; $content2 = " \"value\"}"; - - $request = null; - $body = null; - - $parser = new RequestParser(); - $parser->on('headers', function ($parsedRequest, $parsedBodyBuffer) use (&$request, &$body) { - $request = $parsedRequest; - $body = $parsedBodyBuffer; - }); - - $data = $this->createAdvancedPostRequest('', 17); - $parser->feed($data); - $parser->feed($content1); - $parser->feed($content2 . "\r\n"); - - $this->assertInstanceOf('React\Http\Request', $request); - $this->assertEquals($content1 . $content2, $request->getBody()); - $this->assertSame($body, $request->getBody()); - } - - public function testShouldReceiveMultiPartBody() - { - - $request = null; - $body = null; - - $parser = new RequestParser(); - $parser->on('headers', function ($parsedRequest, $parsedBodyBuffer) use (&$request, &$body) { - $request = $parsedRequest; - $body = $parsedBodyBuffer; - }); - - $parser->feed($this->createMultipartRequest()); - - $this->assertInstanceOf('React\Http\Request', $request); - $this->assertEquals( - $request->getPost(), - ['user' => 'single', 'user2' => 'second', 'users' => ['first in array', 'second in array']] - ); - $this->assertEquals(2, count($request->getFiles())); - $this->assertEquals(2, count($request->getFiles()['files'])); - } - - public function testShouldReceivePostInBody() - { - $request = null; - $body = null; - - $parser = new RequestParser(); - $parser->on('headers', function ($parsedRequest, $parsedBodyBuffer) use (&$request, &$body) { - $request = $parsedRequest; - $body = $parsedBodyBuffer; - }); - - $parser->feed($this->createPostWithContent()); - - $this->assertInstanceOf('React\Http\Request', $request); - $this->assertSame('', $body); - $this->assertEquals( - $request->getPost(), - ['user' => 'single', 'user2' => 'second', 'users' => ['first in array', 'second in array']] - ); - } - - public function testHeaderOverflowShouldEmitError() - { - $error = null; - - $parser = new RequestParser(); - $parser->on('headers', $this->expectCallableNever()); - $parser->on('error', function ($message) use (&$error) { - $error = $message; - }); - - $data = str_repeat('A', 4097); - $parser->feed($data); - - $this->assertInstanceOf('OverflowException', $error); - $this->assertSame('Maximum header size of 4096 exceeded.', $error->getMessage()); - } - - public function testOnePassHeaderTooLarge() - { - $error = null; - - $parser = new RequestParser(); - $parser->on('headers', $this->expectCallableNever()); - $parser->on('error', function ($message) use (&$error) { - $error = $message; - }); - - $data = "POST /foo?bar=baz HTTP/1.1\r\n"; - $data .= "Host: example.com:80\r\n"; - $data .= "Cookie: " . str_repeat('A', 4097) . "\r\n"; - $data .= "\r\n"; - $parser->feed($data); - - $this->assertInstanceOf('OverflowException', $error); - $this->assertSame('Maximum header size of 4096 exceeded.', $error->getMessage()); - } - - public function testBodyShouldNotOverflowHeader() - { - $error = null; - - $parser = new RequestParser(); - $parser->on('headers', $this->expectCallableOnce()); - $parser->on('error', function ($message) use (&$error) { - $error = $message; - }); - - $data = str_repeat('A', 4097); - $parser->feed($this->createAdvancedPostRequest() . $data); - - $this->assertNull($error); - } - - private function createGetRequest($content = '', $len = 0) - { - $data = "GET / HTTP/1.1\r\n"; - $data .= "Host: example.com:80\r\n"; - $data .= "Connection: close\r\n"; - if($len) { - $data .= "Content-Length: $len\r\n"; - } - $data .= "\r\n"; - $data .= $content; - - return $data; - } - - private function createAdvancedPostRequest($content = '', $len = 0) - { - $data = "POST /foo?bar=baz HTTP/1.1\r\n"; - $data .= "Host: example.com:80\r\n"; - $data .= "User-Agent: react/alpha\r\n"; - $data .= "Connection: close\r\n"; - if($len) { - $data .= "Content-Length: $len\r\n"; - } - $data .= "\r\n"; - $data .= $content; - - return $data; - } - - private function createPostWithContent() - { - $data = "POST /foo?bar=baz HTTP/1.1\r\n"; - $data .= "Host: localhost:8080\r\n"; - $data .= "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:32.0) Gecko/20100101 Firefox/32.0\r\n"; - $data .= "Connection: close\r\n"; - $data .= "Content-Type: application/x-www-form-urlencoded\r\n"; - $data .= "Content-Length: 79\r\n"; - $data .= "\r\n"; - $data .= "user=single&user2=second&users%5B%5D=first+in+array&users%5B%5D=second+in+array\r\n"; - - return $data; - } - - private function createMultipartRequest() - { - $data = "POST / HTTP/1.1\r\n"; - $data .= "Host: localhost:8080\r\n"; - $data .= "Connection: close\r\n"; - $data .= "Content-Type: multipart/form-data; boundary=---------------------------12758086162038677464950549563\r\n"; - $data .= "Content-Length: 1097\r\n"; - $data .= "\r\n"; - - $data .= "-----------------------------12758086162038677464950549563\r\n"; - $data .= "Content-Disposition: form-data; name=\"user\"\r\n"; - $data .= "\r\n"; - $data .= "single\r\n"; - $data .= "-----------------------------12758086162038677464950549563\r\n"; - $data .= "Content-Disposition: form-data; name=\"user2\"\r\n"; - $data .= "\r\n"; - $data .= "second\r\n"; - $data .= "-----------------------------12758086162038677464950549563\r\n"; - $data .= "Content-Disposition: form-data; name=\"users[]\"\r\n"; - $data .= "\r\n"; - $data .= "first in array\r\n"; - $data .= "-----------------------------12758086162038677464950549563\r\n"; - $data .= "Content-Disposition: form-data; name=\"users[]\"\r\n"; - $data .= "\r\n"; - $data .= "second in array\r\n"; - $data .= "-----------------------------12758086162038677464950549563\r\n"; - $data .= "Content-Disposition: form-data; name=\"file\"; filename=\"User.php\"\r\n"; - $data .= "Content-Type: text/php\r\n"; - $data .= "\r\n"; - $data .= "assertInstanceOf('React\Http\Request', $request); $this->assertSame('/', $request->getPath()); $this->assertSame('GET', $request->getMethod()); - $this->assertSame('127.0.0.1', $request->getRemoteAddress()); + $this->assertSame('127.0.0.1', $request->remoteAddress); $this->assertInstanceOf('React\Http\Response', $response); }); @@ -75,45 +75,4 @@ private function createGetRequest() return $data; } - - public function testServerRespondsToExpectContinue() - { - $io = new ServerStub(); - $server = new Server($io); - $conn = new ConnectionStub(); - $io->emit('connection', array($conn)); - - $requestReceived = false; - $postBody = '{"React":true}'; - $httpRequestText = $this->createPostRequestWithExpect($postBody); - - $conn->emit('data', array($httpRequestText)); - - $server->on('request', function ($request, $_) use (&$requestReceived, $postBody) { - $requestReceived = true; - $this->assertEquals($postBody, $request->getBody()); - }); - - // If server received Expect: 100-continue - the client won't send the body right away - $this->assertEquals(false, $requestReceived); - - $this->assertEquals("HTTP/1.1 100 Continue\r\n\r\n", $conn->getData()); - - $conn->emit('data', array($postBody)); - - $this->assertEquals(true, $requestReceived); - - } - - private function createPostRequestWithExpect($postBody) - { - $data = "POST / HTTP/1.1\r\n"; - $data .= "Host: example.com:80\r\n"; - $data .= "Content-Type: application/json\r\n"; - $data .= "Content-Length: " . strlen($postBody) . "\r\n"; - $data .= "Expect: 100-continue\r\n"; - $data .= "\r\n"; - - return $data; - } }