From 3fdfb59616de87617bb09360aa14d2b03a4c98b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20VAI=CC=88SSE?= Date: Fri, 7 Dec 2018 12:20:04 +0100 Subject: [PATCH 1/2] Update header parsing to handle http code with a better constrain suite --- .coveralls.yml | 2 +- .travis.yml | 1 + Curl/Collector/HeaderCollector.php | 79 ------- Curl/CurlHeaderCollector.php | 149 -------------- composer.json | 5 +- phpunit.xml.dist | 12 +- .../Controller}/ReplayController.php | 0 .../Curl}/Collector/CollectorInterface.php | 0 .../Curl}/Collector/ContentCollector.php | 0 src/Curl/Collector/HeaderCollector.php | 192 ++++++++++++++++++ {Curl => src/Curl}/CurlErrorException.php | 0 {Curl => src/Curl}/CurlEvents.php | 0 src/Curl/CurlHeaderCollector.php | 17 ++ {Curl => src/Curl}/CurlOptions.php | 0 {Curl => src/Curl}/CurlRequest.php | 0 {Curl => src/Curl}/MultiInfo.php | 0 {Curl => src/Curl}/MultiInfoEvent.php | 0 {Curl => src/Curl}/MultiManager.php | 0 {Curl => src/Curl}/Request.php | 0 {Curl => src/Curl}/RequestGenerator.php | 0 .../DataCollector}/LazyJsonEncoder.php | 0 .../DataCollector}/ProfilerDataCollector.php | 0 .../DependencyInjection}/Configuration.php | 0 .../SimpleHttpExtension.php | 0 {Http => src/Http}/Event.php | 0 {Http => src/Http}/Exception.php | 0 .../Exception/BadGatewayHttpException.php | 0 .../Exception/ClientErrorHttpException.php | 0 .../Exception/CurlTransportException.php | 0 .../Http}/Exception/ErrorHttpException.php | 0 .../ExpectationFailedHttpException.php | 0 .../Exception/ForbiddenHttpException.php | 0 .../Exception/GatewayTimeoutHttpException.php | 0 .../Http}/Exception/HostNotFoundException.php | 0 .../HttpVersionNotSupportedHttpException.php | 0 .../InternalServerErrorHttpException.php | 0 .../InvalidResponseBodyException.php | 0 .../Exception/NotImplementedHttpException.php | 0 ...oxyAuthenticationRequiredHttpException.php | 0 .../RequestEntityTooLargeHttpException.php | 0 .../Http}/Exception/RequestException.php | 0 .../Exception/RequestNotSentException.php | 0 .../Exception/RequestTimeoutHttpException.php | 0 .../RequestUriTooLongHttpException.php | 0 ...uestedRangeNotSatisfiableHttpException.php | 0 .../Http}/Exception/ResponseException.php | 0 .../Exception/ServerErrorHttpException.php | 0 {Http => src/Http}/Exception/SslException.php | 0 .../Http}/Exception/TimeoutException.php | 0 .../Http}/Exception/TransportException.php | 0 .../Exception/UnknownTransportException.php | 0 {Http => src/Http}/Kernel.php | 0 .../Http}/Kernel/RemoteHttpKernel.php | 0 {Http => src/Http}/ReadOnlyCookieJar.php | 0 {Http => src/Http}/Request.php | 0 {Http => src/Http}/Response.php | 0 {Http => src/Http}/SessionCookieJar.php | 0 {Http => src/Http}/Statement.php | 0 {Http => src/Http}/StatementEvents.php | 0 .../Resources}/config/routing.yml | 0 .../Resources}/config/services.yml | 0 {Resources => src/Resources}/doc/index.rst | 0 .../Resources}/doc/profiler-panels.png | Bin .../Resources}/doc/profiler-timeline.png | Bin .../Resources}/doc/profiler-toolbar.png | Bin .../libs/highlightjs/highlightjs-8.5.css | 0 .../libs/highlightjs/highlightjs-8.5.js | 0 .../public/libs/jquery/jquery-1.12.1.js | 0 .../Resources}/public/simple-http-bundle.css | 0 .../Resources}/public/simple-http-bundle.js | 0 .../Resources}/translations/messages.fr.xlf | 0 .../Assets/dollardom/dollardom.min.0.9.2b.js | 0 .../Assets/highlightjs/highlightjs-8.5.css | 0 .../Assets/highlightjs/highlightjs-8.5.js | 0 .../views/Collector/partials/auth.html.twig | 0 .../views/Collector/partials/call.html.twig | 0 .../Collector/partials/cookies.html.twig | 0 .../views/Collector/partials/data.html.twig | 0 .../views/Collector/partials/error.html.twig | 0 .../Collector/partials/javascript.html.twig | 0 .../Collector/partials/request.html.twig | 0 .../Collector/partials/response.html.twig | 0 .../Collector/partials/summary.html.twig | 0 .../views/Collector/profiler.html.twig | 0 .../Serializer}/CustomGetSetNormalizer.php | 0 {Service => src/Service}/Helper.php | 0 {Twig => src/Twig}/Extension.php | 0 {Tests => tests}/Fixtures/greenimg.jpg | Bin {Tests => tests}/Unit/AbstractTests.php | 0 {Tests => tests}/Unit/ContainerTest.php | 0 {Tests => tests}/Unit/ContentTypesTest.php | 0 {Tests => tests}/Unit/CookieSessionTest.php | 0 {Tests => tests}/Unit/CurlExceptionTest.php | 0 .../Unit/CurlRequestGeneratorTest.php | 0 {Tests => tests}/Unit/ExceptionsTest.php | 0 {Tests => tests}/Unit/FacadeTest.php | 0 {Tests => tests}/Unit/FileUploadsTest.php | 0 tests/Unit/HeaderCollectorTest.php | 135 ++++++++++++ .../Unit/HeaderManipulationTest.php | 0 {Tests => tests}/Unit/KernelTests.php | 0 {Tests => tests}/Unit/OAuthClientTest.php | 0 .../Unit/ParrallelExecutionTest.php | 0 {Tests => tests}/Unit/PromisesTest.php | 0 {Tests => tests}/Unit/RequestBodyTest.php | 0 {Tests => tests}/Unit/ReturnCodeTest.php | 0 {Tests => tests}/Unit/SimpleRoutingTest.php | 0 {Tests => tests}/Unit/SslTest.php | 0 {Tests => tests}/Unit/TimeoutTest.php | 0 {Tests => tests}/autoload.php.dist | 0 109 files changed, 356 insertions(+), 236 deletions(-) delete mode 100644 Curl/Collector/HeaderCollector.php delete mode 100644 Curl/CurlHeaderCollector.php rename {Controller => src/Controller}/ReplayController.php (100%) rename {Curl => src/Curl}/Collector/CollectorInterface.php (100%) rename {Curl => src/Curl}/Collector/ContentCollector.php (100%) create mode 100644 src/Curl/Collector/HeaderCollector.php rename {Curl => src/Curl}/CurlErrorException.php (100%) rename {Curl => src/Curl}/CurlEvents.php (100%) create mode 100644 src/Curl/CurlHeaderCollector.php rename {Curl => src/Curl}/CurlOptions.php (100%) rename {Curl => src/Curl}/CurlRequest.php (100%) rename {Curl => src/Curl}/MultiInfo.php (100%) rename {Curl => src/Curl}/MultiInfoEvent.php (100%) rename {Curl => src/Curl}/MultiManager.php (100%) rename {Curl => src/Curl}/Request.php (100%) rename {Curl => src/Curl}/RequestGenerator.php (100%) rename {DataCollector => src/DataCollector}/LazyJsonEncoder.php (100%) rename {DataCollector => src/DataCollector}/ProfilerDataCollector.php (100%) rename {DependencyInjection => src/DependencyInjection}/Configuration.php (100%) rename {DependencyInjection => src/DependencyInjection}/SimpleHttpExtension.php (100%) rename {Http => src/Http}/Event.php (100%) rename {Http => src/Http}/Exception.php (100%) rename {Http => src/Http}/Exception/BadGatewayHttpException.php (100%) rename {Http => src/Http}/Exception/ClientErrorHttpException.php (100%) rename {Http => src/Http}/Exception/CurlTransportException.php (100%) rename {Http => src/Http}/Exception/ErrorHttpException.php (100%) rename {Http => src/Http}/Exception/ExpectationFailedHttpException.php (100%) rename {Http => src/Http}/Exception/ForbiddenHttpException.php (100%) rename {Http => src/Http}/Exception/GatewayTimeoutHttpException.php (100%) rename {Http => src/Http}/Exception/HostNotFoundException.php (100%) rename {Http => src/Http}/Exception/HttpVersionNotSupportedHttpException.php (100%) rename {Http => src/Http}/Exception/InternalServerErrorHttpException.php (100%) rename {Http => src/Http}/Exception/InvalidResponseBodyException.php (100%) rename {Http => src/Http}/Exception/NotImplementedHttpException.php (100%) rename {Http => src/Http}/Exception/ProxyAuthenticationRequiredHttpException.php (100%) rename {Http => src/Http}/Exception/RequestEntityTooLargeHttpException.php (100%) rename {Http => src/Http}/Exception/RequestException.php (100%) rename {Http => src/Http}/Exception/RequestNotSentException.php (100%) rename {Http => src/Http}/Exception/RequestTimeoutHttpException.php (100%) rename {Http => src/Http}/Exception/RequestUriTooLongHttpException.php (100%) rename {Http => src/Http}/Exception/RequestedRangeNotSatisfiableHttpException.php (100%) rename {Http => src/Http}/Exception/ResponseException.php (100%) rename {Http => src/Http}/Exception/ServerErrorHttpException.php (100%) rename {Http => src/Http}/Exception/SslException.php (100%) rename {Http => src/Http}/Exception/TimeoutException.php (100%) rename {Http => src/Http}/Exception/TransportException.php (100%) rename {Http => src/Http}/Exception/UnknownTransportException.php (100%) rename {Http => src/Http}/Kernel.php (100%) rename {Http => src/Http}/Kernel/RemoteHttpKernel.php (100%) rename {Http => src/Http}/ReadOnlyCookieJar.php (100%) rename {Http => src/Http}/Request.php (100%) rename {Http => src/Http}/Response.php (100%) rename {Http => src/Http}/SessionCookieJar.php (100%) rename {Http => src/Http}/Statement.php (100%) rename {Http => src/Http}/StatementEvents.php (100%) rename {Resources => src/Resources}/config/routing.yml (100%) rename {Resources => src/Resources}/config/services.yml (100%) rename {Resources => src/Resources}/doc/index.rst (100%) rename {Resources => src/Resources}/doc/profiler-panels.png (100%) rename {Resources => src/Resources}/doc/profiler-timeline.png (100%) rename {Resources => src/Resources}/doc/profiler-toolbar.png (100%) rename {Resources => src/Resources}/public/libs/highlightjs/highlightjs-8.5.css (100%) rename {Resources => src/Resources}/public/libs/highlightjs/highlightjs-8.5.js (100%) rename {Resources => src/Resources}/public/libs/jquery/jquery-1.12.1.js (100%) rename {Resources => src/Resources}/public/simple-http-bundle.css (100%) rename {Resources => src/Resources}/public/simple-http-bundle.js (100%) rename {Resources => src/Resources}/translations/messages.fr.xlf (100%) rename {Resources => src/Resources}/views/Assets/dollardom/dollardom.min.0.9.2b.js (100%) rename {Resources => src/Resources}/views/Assets/highlightjs/highlightjs-8.5.css (100%) rename {Resources => src/Resources}/views/Assets/highlightjs/highlightjs-8.5.js (100%) rename {Resources => src/Resources}/views/Collector/partials/auth.html.twig (100%) rename {Resources => src/Resources}/views/Collector/partials/call.html.twig (100%) rename {Resources => src/Resources}/views/Collector/partials/cookies.html.twig (100%) rename {Resources => src/Resources}/views/Collector/partials/data.html.twig (100%) rename {Resources => src/Resources}/views/Collector/partials/error.html.twig (100%) rename {Resources => src/Resources}/views/Collector/partials/javascript.html.twig (100%) rename {Resources => src/Resources}/views/Collector/partials/request.html.twig (100%) rename {Resources => src/Resources}/views/Collector/partials/response.html.twig (100%) rename {Resources => src/Resources}/views/Collector/partials/summary.html.twig (100%) rename {Resources => src/Resources}/views/Collector/profiler.html.twig (100%) rename {Serializer => src/Serializer}/CustomGetSetNormalizer.php (100%) rename {Service => src/Service}/Helper.php (100%) rename {Twig => src/Twig}/Extension.php (100%) rename {Tests => tests}/Fixtures/greenimg.jpg (100%) rename {Tests => tests}/Unit/AbstractTests.php (100%) rename {Tests => tests}/Unit/ContainerTest.php (100%) rename {Tests => tests}/Unit/ContentTypesTest.php (100%) rename {Tests => tests}/Unit/CookieSessionTest.php (100%) rename {Tests => tests}/Unit/CurlExceptionTest.php (100%) rename {Tests => tests}/Unit/CurlRequestGeneratorTest.php (100%) rename {Tests => tests}/Unit/ExceptionsTest.php (100%) rename {Tests => tests}/Unit/FacadeTest.php (100%) rename {Tests => tests}/Unit/FileUploadsTest.php (100%) create mode 100644 tests/Unit/HeaderCollectorTest.php rename {Tests => tests}/Unit/HeaderManipulationTest.php (100%) rename {Tests => tests}/Unit/KernelTests.php (100%) rename {Tests => tests}/Unit/OAuthClientTest.php (100%) rename {Tests => tests}/Unit/ParrallelExecutionTest.php (100%) rename {Tests => tests}/Unit/PromisesTest.php (100%) rename {Tests => tests}/Unit/RequestBodyTest.php (100%) rename {Tests => tests}/Unit/ReturnCodeTest.php (100%) rename {Tests => tests}/Unit/SimpleRoutingTest.php (100%) rename {Tests => tests}/Unit/SslTest.php (100%) rename {Tests => tests}/Unit/TimeoutTest.php (100%) rename {Tests => tests}/autoload.php.dist (100%) diff --git a/.coveralls.yml b/.coveralls.yml index 5697ff1..cbd906c 100644 --- a/.coveralls.yml +++ b/.coveralls.yml @@ -1,3 +1,3 @@ -src_dir: . +src_dir: src coverage_clover: build/logs/clover.xml json_path: build/logs/coveralls-upload.json \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index ed96065..41e7648 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: php php: - 5.6 - 7.0 + - 7.2 - hhvm diff --git a/Curl/Collector/HeaderCollector.php b/Curl/Collector/HeaderCollector.php deleted file mode 100644 index ca19e26..0000000 --- a/Curl/Collector/HeaderCollector.php +++ /dev/null @@ -1,79 +0,0 @@ -parseHttp($cleanHeader); - } else { - $this->parseHeader($cleanHeader); - } - - return strlen($headerString); - } - - /** - * Parse the `HTTP/1.0 200 OK' header into the proper - * Status Code/Message and Protocol Version fields - * - * @param string $header - */ - private function parseHttp($header) { - list($version,$code,$message) = explode(" ", $header); - - $versionParts = explode("/",$version); - $this->version = end($versionParts); - $this->code = $code; - $this->message = $message; - } - - /** - * Parse the standard `Header-name: value' headers into - * individual header name/value pairs - * - * @param string $header - */ - private function parseHeader($header) { - if(!empty($header)) { - $pos = strpos($header, ": "); - - if(false !== $pos) { - $name = substr($header,0,$pos); - $value = substr($header,$pos+2); - - $this->headers[$name] = $value; - } - } - } - - public function retrieve() { - return $this->headers; - } - - public function getVersion() { - return $this->version; - } - - public function getMessage() { - return $this->message; - } - - public function getCode() { - return $this->code; - } -} diff --git a/Curl/CurlHeaderCollector.php b/Curl/CurlHeaderCollector.php deleted file mode 100644 index 94303f5..0000000 --- a/Curl/CurlHeaderCollector.php +++ /dev/null @@ -1,149 +0,0 @@ -parseHttp($cleanHeader); - } else { - $this->parseHeader($cleanHeader); - } - - return strlen($headerString); - } - - /** - * Parse the `HTTP/1.0 200 OK' header into the proper - * Status Code/Message and Protocol Version fields - * - * @param string $header - */ - private function parseHttp($header) { - list($version,$code,$message) = explode(" ", $header); - - $this->transactionHeaders[] = $header; - - $versionParts = explode("/",$version); - $this->version = end($versionParts); - $this->code = $code; - $this->message = $message; - } - - /** - * Parse the standard `Header-name: value' headers into - * individual header name/value pairs - * - * @param string $header - */ - private function parseHeader($header) { - - - if (empty($header)) { - return; - } - - $pos = strpos($header, ": "); - - if (false !== $pos) { - - $name = trim(substr($header, 0, $pos)); - $value = substr($header, $pos+2); - - if (strtolower($name) == "set-cookie") { - - $cookie = CookieParser::fromString($value); - $this->cookies[] = new Cookie( - $cookie->getName(), - $cookie->getRawValue(), - (int)$cookie->getExpiresTime(), - $cookie->getPath(), - $cookie->getDomain(), - $cookie->isSecure(), - $cookie->isHttpOnly() - ); - - } else { - - $this->headers[$name] = $value; - - } - } - - } - - public function retrieve() { - return $this->headers; - } - - public function getVersion() { - return $this->version; - } - - public function getMessage() { - return $this->message; - } - - public function getCode() { - return $this->code; - } - - /** - * @return array|Cookie get a list of Cookie instances - */ - public function getCookies() - { - return $this->cookies; - } - - /** - * @return array - */ - public function getTransactionHeaders() - { - return $this->transactionHeaders; - } - - - -} \ No newline at end of file diff --git a/composer.json b/composer.json index 2d42625..3daaa27 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,10 @@ }, "license": "MIT", "autoload": { - "psr-4": { "evaisse\\SimpleHttpBundle\\": "" } + "psr-4": { + "evaisse\\SimpleHttpBundle\\Tests\\": "tests/", + "evaisse\\SimpleHttpBundle\\": "src/" + } }, "authors": [ { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index dc0f8e8..dfdb6c3 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,18 +1,18 @@ - + - - ./Tests + + ./tests - ./Http - ./Service + ./src + ./src/Twig ./vendor - ./Tests + ./tests diff --git a/Controller/ReplayController.php b/src/Controller/ReplayController.php similarity index 100% rename from Controller/ReplayController.php rename to src/Controller/ReplayController.php diff --git a/Curl/Collector/CollectorInterface.php b/src/Curl/Collector/CollectorInterface.php similarity index 100% rename from Curl/Collector/CollectorInterface.php rename to src/Curl/Collector/CollectorInterface.php diff --git a/Curl/Collector/ContentCollector.php b/src/Curl/Collector/ContentCollector.php similarity index 100% rename from Curl/Collector/ContentCollector.php rename to src/Curl/Collector/ContentCollector.php diff --git a/src/Curl/Collector/HeaderCollector.php b/src/Curl/Collector/HeaderCollector.php new file mode 100644 index 0000000..63ea424 --- /dev/null +++ b/src/Curl/Collector/HeaderCollector.php @@ -0,0 +1,192 @@ +parseHttpVersionheader($cleanHeader)) { + $this->parseHeader($cleanHeader); + } + + return strlen($headerString); + } + + /** + * handle the 100 continue & 200 Connection establish code + * by stripping any extra headers directive before the latest one. + * + * e.g. "HTTP\/1.1 100 Continue\r\n\r\nHTTP\/1.1 200 Connection established\r\n\r\nHTTP\/1.1 200 OK\r\nContent-Type: application\/json; charset=utf-8\r\nDate: Mon, 17 Oct 2016 14:59:22 GMT\r\nExpires: Mon, 17 Oct 2016 14:59:22 GMT\r\nCache-Control: private, max-age=0\r\nX-Content-Type-Options: nosniff\r\nX-XSS-Protection: 1; mode=block\r\nServer: GSE\r\nAlt-Svc: quic=\":443\"; ma=2592000; v=\"36,35,34,33,32\"\r\nAccept-Ranges: none\r\nVary: Accept-Encoding\r\nTransfer-Encoding: chunked\r\n\r\n{\n??\"success\": true,\n??\"challenge_ts\": \"2016-10-17T14:59:12Z\",\n??\"hostname\": \"clients.boursorama.com\"\n}" + * + * let's celebrate : https://httpstatusdogs.com/100-continue + * @param string $header + */ + protected function parseHttpVersionheader($header) + { + if (!preg_match('/^http\/(\d+\.\d+)\s+(\d+)\s+(.+)/i', $header, $r)) { + return; + } + + $this->transactionHeaders[] = $header; + + // reset headers records since another transaction header has started + $this->cookies = $this->headers = []; + + $this->version = $r[1]; + $this->code = (int)$r[2]; + $this->message = trim($r[3]); + } + + /** + * Parse the standard `Header-name: value' headers into + * individual header name/value pairs + * + * @param string $header + */ + protected function parseHeader($header) + { + + // skip empty headers lines + if (empty($header)) { + return; + } + + $pos = strpos($header, ": "); + + if (false !== $pos) { + + $name = trim(substr($header, 0, $pos)); + $value = substr($header, $pos+2); + + if (strtolower($name) == "set-cookie") { + + try { + $cookie = CookieParser::fromString($value); + } catch (\InvalidArgumentException $e) { + // skip invalid cookie line + return; + } + + $this->cookies[] = new Cookie( + $cookie->getName(), + $cookie->getRawValue(), + (int)$cookie->getExpiresTime(), + $cookie->getPath(), + $cookie->getDomain(), + $cookie->isSecure(), + $cookie->isHttpOnly() + ); + + } else { + + $this->headers[$name] = $value; + + } + } + + } + + /** + * @return string[] + */ + public function retrieve() + { + return $this->headers; + } + + + /** + * @return Cookie[] get a list of Cookie instances + */ + public function getCookies() + { + return $this->cookies; + } + + /** + * @return string[] + */ + public function getTransactionHeaders() + { + return $this->transactionHeaders; + } + + /** + * @return string[] + */ + public function getHeaders() + { + return $this->headers; + } + + /** + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * @return int + */ + public function getCode() + { + return $this->code; + } + + /** + * @return string + */ + public function getMessage() + { + return $this->message; + } + + +} diff --git a/Curl/CurlErrorException.php b/src/Curl/CurlErrorException.php similarity index 100% rename from Curl/CurlErrorException.php rename to src/Curl/CurlErrorException.php diff --git a/Curl/CurlEvents.php b/src/Curl/CurlEvents.php similarity index 100% rename from Curl/CurlEvents.php rename to src/Curl/CurlEvents.php diff --git a/src/Curl/CurlHeaderCollector.php b/src/Curl/CurlHeaderCollector.php new file mode 100644 index 0000000..7e5c811 --- /dev/null +++ b/src/Curl/CurlHeaderCollector.php @@ -0,0 +1,17 @@ +collect(null, $h); + } + $this->assertEquals(null, $collector->getCode()); + $this->assertEquals(null, $collector->getMessage()); + $this->assertEquals(null, $collector->getVersion()); + } + + public function testHttpHeaderParsing() + { + $collector = new HeaderCollector(); + $headers = [ + "HTTP/1.1 200 Ok", + "HTTP/1.1 200 Ok", + "X-Debug: foo/http/", + "User-Agent: blablablab", + ]; + foreach ($headers as $h) { + $collector->collect(null, $h); + } + + $this->assertEquals(200, $collector->getCode()); + $this->assertEquals("Ok", $collector->getMessage()); + $this->assertEquals("1.1", $collector->getVersion()); + } + + /** + * test em + */ + public function testHttpEmptyStatusHeaderParsing() + { + $collector = new HeaderCollector(); + $headers = [ + "HTTP/1.1 100 Continue", + "", + "HTTP/1.1 200 Ok", + "X-Debug: foo/http/", + "User-Agent: blablablab", + ]; + + foreach ($headers as $h) { + $collector->collect(null, $h); + } + + $this->assertEquals(200, $collector->getCode()); + $this->assertEquals("Ok", $collector->getMessage()); + $this->assertEquals("1.1", $collector->getVersion()); + + } + + public function testbadCookieHeaderParsing() + { + $collector = new HeaderCollector(); + $headers = [ + "HTTP/1.1 100 Continue", + "", + "HTTP/1.1 200 Ok", + "", + "X-Debug: foo/http/", + "HTTP/1.1 302 Found", + "X-Debug: foo/http/", + "User-Agent: blablablab", + "Set-Cookie: blabla", + ]; + + foreach ($headers as $h) { + $collector->collect(null, $h); + } + + $this->assertEquals(302, $collector->getCode()); + $this->assertEquals("Found", $collector->getMessage()); + $this->assertEquals("1.1", $collector->getVersion()); + $this->assertEquals([ + "X-Debug" => "foo/http/", + "User-Agent" => "blablablab", + ], $collector->getHeaders()); + $this->assertCount(0, $collector->getCookies()); + } + + + public function testCookieHeaderParsing() + { + + $collector = new HeaderCollector(); + $headers = [ + "HTTP/1.1 100 Continue", + "", + "HTTP/1.1 200 Ok", + "X-Debug: foo/http/", + "X-Debug: foo/http/", + "HTTP/1.1 302 Found", + "X-Debug: foo/http/", + "User-Agent: blablablab", + "Set-Cookie: blabla=1", + ]; + + foreach ($headers as $h) { + $collector->collect(null, $h); + } + + $this->assertEquals(302, $collector->getCode()); + $this->assertEquals("Found", $collector->getMessage()); + $this->assertEquals("1.1", $collector->getVersion()); + $this->assertEquals([ + "X-Debug" => "foo/http/", + "User-Agent" => "blablablab", + ], $collector->getHeaders()); + $this->assertCount(1, $collector->getCookies()); + } + +} \ No newline at end of file diff --git a/Tests/Unit/HeaderManipulationTest.php b/tests/Unit/HeaderManipulationTest.php similarity index 100% rename from Tests/Unit/HeaderManipulationTest.php rename to tests/Unit/HeaderManipulationTest.php diff --git a/Tests/Unit/KernelTests.php b/tests/Unit/KernelTests.php similarity index 100% rename from Tests/Unit/KernelTests.php rename to tests/Unit/KernelTests.php diff --git a/Tests/Unit/OAuthClientTest.php b/tests/Unit/OAuthClientTest.php similarity index 100% rename from Tests/Unit/OAuthClientTest.php rename to tests/Unit/OAuthClientTest.php diff --git a/Tests/Unit/ParrallelExecutionTest.php b/tests/Unit/ParrallelExecutionTest.php similarity index 100% rename from Tests/Unit/ParrallelExecutionTest.php rename to tests/Unit/ParrallelExecutionTest.php diff --git a/Tests/Unit/PromisesTest.php b/tests/Unit/PromisesTest.php similarity index 100% rename from Tests/Unit/PromisesTest.php rename to tests/Unit/PromisesTest.php diff --git a/Tests/Unit/RequestBodyTest.php b/tests/Unit/RequestBodyTest.php similarity index 100% rename from Tests/Unit/RequestBodyTest.php rename to tests/Unit/RequestBodyTest.php diff --git a/Tests/Unit/ReturnCodeTest.php b/tests/Unit/ReturnCodeTest.php similarity index 100% rename from Tests/Unit/ReturnCodeTest.php rename to tests/Unit/ReturnCodeTest.php diff --git a/Tests/Unit/SimpleRoutingTest.php b/tests/Unit/SimpleRoutingTest.php similarity index 100% rename from Tests/Unit/SimpleRoutingTest.php rename to tests/Unit/SimpleRoutingTest.php diff --git a/Tests/Unit/SslTest.php b/tests/Unit/SslTest.php similarity index 100% rename from Tests/Unit/SslTest.php rename to tests/Unit/SslTest.php diff --git a/Tests/Unit/TimeoutTest.php b/tests/Unit/TimeoutTest.php similarity index 100% rename from Tests/Unit/TimeoutTest.php rename to tests/Unit/TimeoutTest.php diff --git a/Tests/autoload.php.dist b/tests/autoload.php.dist similarity index 100% rename from Tests/autoload.php.dist rename to tests/autoload.php.dist From 0203b9d8df0c48faaa57d58a904f8a1237bd0b83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20VAI=CC=88SSE?= Date: Fri, 7 Dec 2018 12:52:38 +0100 Subject: [PATCH 2/2] Fix header parsing --- src/Curl/Collector/HeaderCollector.php | 71 ++++++++++++--------- src/DataCollector/ProfilerDataCollector.php | 9 +-- src/Http/Kernel.php | 8 ++- src/Http/Response.php | 2 - src/Http/Statement.php | 8 ++- tests/Unit/CookieSessionTest.php | 7 +- 6 files changed, 63 insertions(+), 42 deletions(-) diff --git a/src/Curl/Collector/HeaderCollector.php b/src/Curl/Collector/HeaderCollector.php index 63ea424..7b4650e 100644 --- a/src/Curl/Collector/HeaderCollector.php +++ b/src/Curl/Collector/HeaderCollector.php @@ -39,6 +39,11 @@ class HeaderCollector implements CollectorInterface */ protected $cookies = []; + /** + * @var string + */ + protected $rawHeaders = ""; + /** * @return int */ @@ -46,6 +51,8 @@ public function collect() { list($handle, $headerString) = func_get_args(); + $this->rawHeaders .= $headerString; + $cleanHeader = trim($headerString); // The HTTP/X.X XXX XXX header is also passed through this function @@ -74,8 +81,10 @@ protected function parseHttpVersionheader($header) $this->transactionHeaders[] = $header; - // reset headers records since another transaction header has started - $this->cookies = $this->headers = []; + // reset headers records since another transaction header has started, but keep cookies for redirections + $this->headers = []; + + $this->version = $r[1]; $this->code = (int)$r[2]; @@ -96,37 +105,34 @@ protected function parseHeader($header) return; } - $pos = strpos($header, ": "); - - if (false !== $pos) { - - $name = trim(substr($header, 0, $pos)); - $value = substr($header, $pos+2); - - if (strtolower($name) == "set-cookie") { - - try { - $cookie = CookieParser::fromString($value); - } catch (\InvalidArgumentException $e) { - // skip invalid cookie line - return; - } - - $this->cookies[] = new Cookie( - $cookie->getName(), - $cookie->getRawValue(), - (int)$cookie->getExpiresTime(), - $cookie->getPath(), - $cookie->getDomain(), - $cookie->isSecure(), - $cookie->isHttpOnly() - ); + if (!preg_match('/([a-z0-9][a-z0-9\-]*)\:\s*(.*)/i', $header, $h)) { + return; + } - } else { + $name = str_replace(' ', '-', ucwords(str_replace('-', ' ', strtolower($h[1])))); + $value = $h[2]; - $this->headers[$name] = $value; + if (strtolower($name) == "set-cookie") { + try { + $cookie = CookieParser::fromString($value); + } catch (\InvalidArgumentException $e) { + // skip invalid cookie line + return; } + + $this->cookies[] = new Cookie( + $cookie->getName(), + $cookie->getRawValue(), + (int)$cookie->getExpiresTime(), + $cookie->getPath(), + $cookie->getDomain(), + $cookie->isSecure(), + $cookie->isHttpOnly() + ); + + } else { + $this->headers[$name] = $value; } } @@ -188,5 +194,12 @@ public function getMessage() return $this->message; } + /** + * @return string + */ + public function getAllTransactionHeaders() + { + return $this->rawHeaders; + } } diff --git a/src/DataCollector/ProfilerDataCollector.php b/src/DataCollector/ProfilerDataCollector.php index cacc9c1..ba16988 100644 --- a/src/DataCollector/ProfilerDataCollector.php +++ b/src/DataCollector/ProfilerDataCollector.php @@ -269,13 +269,6 @@ public function fetchRequestInfos(Request $request) */ public function fetchResponseInfos(Response $response) { - if ($response->headers->get('charset', '') == "utf-8" - || stripos($response->headers->get('content-type', ''), 'utf-8') !== 0 - ) { - $encoders = array(new LazyJsonEncoder()); - } else { - $encoders = array(new LazyJsonEncoder()); - } $normalizers = array(new CustomGetSetNormalizer()); $encoders = array(new LazyJsonEncoder()); @@ -584,4 +577,6 @@ public function urlsafeB64Encode($input) return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); } + + } diff --git a/src/Http/Kernel.php b/src/Http/Kernel.php index d32b107..5d54224 100644 --- a/src/Http/Kernel.php +++ b/src/Http/Kernel.php @@ -9,6 +9,7 @@ namespace evaisse\SimpleHttpBundle\Http; +use evaisse\SimpleHttpBundle\Curl\Collector\HeaderCollector; use evaisse\SimpleHttpBundle\Http\Exception\CurlTransportException; use evaisse\SimpleHttpBundle\Http\Exception\HostNotFoundException; use evaisse\SimpleHttpBundle\Http\Exception\ClientErrorHttpException; @@ -124,6 +125,11 @@ public function handleMultiInfoEvent(MultiInfoEvent $e) $stmt = $value[0]; $request = $stmt->getRequest(); + /** + * @var HeaderCollector $headersCollector + * @var ContentCollector $contentCollector + * @var CurlRequest $curlRequest + */ list($curlRequest, $contentCollector, $headersCollector) = $value[1]; $this->updateRequestHeadersFromCurlInfos($request, $e->getRequest()->getInfo()); @@ -164,7 +170,7 @@ public function handleMultiInfoEvent(MultiInfoEvent $e) $response->setStatusCode($headersCollector->getCode(), $headersCollector->getMessage()); $response->setTransferInfos(array_merge($e->getRequest()->getInfo(), [ - 'additionnalProxyHeaders' => $headersCollector->getTransactionHeaders() + 'allHeaders' => $headersCollector->getAllTransactionHeaders() ])); $event = new Event\FilterResponseEvent($this, $request, $requestType, $response); diff --git a/src/Http/Response.php b/src/Http/Response.php index 0d77899..7b2efb6 100644 --- a/src/Http/Response.php +++ b/src/Http/Response.php @@ -2,8 +2,6 @@ namespace evaisse\SimpleHttpBundle\Http; - - use evaisse\SimpleHttpBundle\Http\Exception\ErrorHttpException; use evaisse\SimpleHttpBundle\Http\Exception\InvalidResponseBodyException; diff --git a/src/Http/Statement.php b/src/Http/Statement.php index 02a4ec7..2fa0534 100644 --- a/src/Http/Statement.php +++ b/src/Http/Statement.php @@ -9,7 +9,6 @@ */ namespace evaisse\SimpleHttpBundle\Http; - use evaisse\SimpleHttpBundle\Http\Exception\RequestNotSentException; use React\Promise\Deferred; use React\Promise\Promise; @@ -37,7 +36,7 @@ class Statement /** * $error : Error * - * @var Error + * @var \Error * @access protected */ protected $error; @@ -48,6 +47,11 @@ class Statement */ protected $response; + /** + * @var Response[] + */ + protected $responses = []; + /** * @var Promise A Deferred object */ diff --git a/tests/Unit/CookieSessionTest.php b/tests/Unit/CookieSessionTest.php index b086553..8274737 100644 --- a/tests/Unit/CookieSessionTest.php +++ b/tests/Unit/CookieSessionTest.php @@ -13,6 +13,7 @@ use evaisse\SimpleHttpBundle\Http\Request; use evaisse\SimpleHttpBundle\Http\Statement; use evaisse\SimpleHttpBundle\Service\Helper; +use Psr\Container\ContainerInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\Container; @@ -24,6 +25,11 @@ class CookieSessionTest extends AbstractTests public function testCookieStore() { + /** + * @var Kernel $httpKernel + * @var Helper $helper + * @var ContainerInterface $container + */ list($helper, $httpKernel, $container) = $this->createContext(); $i = (int)rand(0,100); @@ -48,7 +54,6 @@ public function testCookieStore() $this->assertEquals($cookies['tmp'], 1); - /* * Set and delete some async */