diff --git a/main/Net/Http/CurlHttpClient.class.php b/main/Net/Http/CurlHttpClient.class.php index 0c528dc848..b03f371598 100644 --- a/main/Net/Http/CurlHttpClient.class.php +++ b/main/Net/Http/CurlHttpClient.class.php @@ -15,7 +15,7 @@ final class CurlHttpClient implements HttpClient { private $options = array(); - + private $followLocation = null; private $maxFileSize = null; private $noBody = null; @@ -26,7 +26,12 @@ final class CurlHttpClient implements HttpClient * @deprecated in the furure will work like this value is false; */ private $oldUrlConstructor = ONPHP_CURL_CLIENT_OLD_TO_STRING; - + private $headerParser = null; + + public function __construct() { + $this->headerParser = HeaderParser::create(); + } + /** * @return CurlHttpClient **/ @@ -34,35 +39,35 @@ public static function create() { return new self; } - + /** * @return CurlHttpClient **/ public function setOption($key, $value) { $this->options[$key] = $value; - + return $this; } - + /** * @return CurlHttpClient **/ public function dropOption($key) { unset($this->options[$key]); - + return $this; } - + public function getOption($key) { if (isset($this->options[$key])) return $this->options[$key]; - + throw new MissingElementException(); } - + /** * @param $timeout in seconds * @return CurlHttpClient @@ -70,10 +75,10 @@ public function getOption($key) public function setTimeout($timeout) { $this->options[CURLOPT_TIMEOUT] = $timeout; - + return $this; } - + /** * @deprecated by getOption() **/ @@ -81,10 +86,10 @@ public function getTimeout() { if (isset($this->options[CURLOPT_TIMEOUT])) return $this->options[CURLOPT_TIMEOUT]; - + return null; } - + /** * whether to follow header Location or not * @@ -97,12 +102,12 @@ public function setFollowLocation($really) $this->followLocation = $really; return $this; } - + public function isFollowLocation() { return $this->followLocation; } - + /** * @param $really boolean * @return CurlHttpClient @@ -113,30 +118,30 @@ public function setNoBody($really) $this->noBody = $really; return $this; } - + public function hasNoBody() { return $this->noBody; } - + /** * @return CurlHttpClient **/ public function setMaxRedirects($maxRedirects) { $this->options[CURLOPT_MAXREDIRS] = $maxRedirects; - + return $this; } - + public function getMaxRedirects() { if (isset($this->options[CURLOPT_MAXREDIRS])) return $this->options[CURLOPT_MAXREDIRS]; - + return null; } - + /** * @return CurlHttpClient **/ @@ -145,23 +150,23 @@ public function setMaxFileSize($maxFileSize) $this->maxFileSize = $maxFileSize; return $this; } - + public function getMaxFileSize() { return $this->maxFileSize; } - + /** * @deprecated in the future value always false and method will be removed * @param bool $oldUrlConstructor - * @return CurlHttpClient + * @return CurlHttpClient */ public function setOldUrlConstructor($oldUrlConstructor = false) { $this->oldUrlConstructor = ($oldUrlConstructor == true); return $this; } - + /** * @deprecated in the future value always false and method will be removed * @return bool @@ -170,40 +175,40 @@ public function isOldUrlConstructor() { return $this->oldUrlConstructor; } - + /** * @return CurlHttpClient **/ public function addRequest(HttpRequest $request, $options = array()) { Assert::isArray($options); - + $key = $this->getRequestKey($request); - + if (isset($this->multiRequests[$key])) throw new WrongArgumentException('There is allready such alias'); - + $this->multiRequests[$key] = $request; - + foreach ($options as $k => $val) $this->multiThreadOptions[$key][$k] = $val; - + return $this; } - + /** * @return CurlHttpResponse **/ public function getResponse(HttpRequest $request) { $key = $this->getRequestKey($request); - + if (!isset($this->multiResponses[$key])) throw new WrongArgumentException('There is no response fo this alias'); - + return $this->multiResponses[$key]; } - + /** * @return HttpResponse **/ @@ -211,9 +216,9 @@ public function send(HttpRequest $request) { $response = CurlHttpResponse::create()-> setMaxFileSize($this->maxFileSize); - + $handle = $this->makeHandle($request, $response); - + if (curl_exec($handle) === false) { $code = curl_errno($handle); throw new NetworkException( @@ -222,135 +227,136 @@ public function send(HttpRequest $request) $code ); } - + + $this->assembleRequestHeaders($handle, $request); $this->makeResponse($handle, $response); - + curl_close($handle); - + return $response; } - + public function multiSend() { Assert::isNotEmptyArray($this->multiRequests); - + $handles = array(); $mh = curl_multi_init(); - + foreach ($this->multiRequests as $alias => $request) { $this->multiResponses[$alias] = new CurlHttpResponse(); - + $handles[$alias] = $this->makeHandle( $request, $this->multiResponses[$alias] ); - + if (isset($this->multiThreadOptions[$alias])) foreach ($this->multiThreadOptions[$alias] as $key => $value) curl_setopt($handles[$alias], $key, $value); - + curl_multi_add_handle($mh, $handles[$alias]); } - + $running = null; do { curl_multi_exec($mh, $running); } while ($running > 0); - + foreach ($this->multiResponses as $alias => $response) { $this->makeResponse($handles[$alias], $response); curl_multi_remove_handle($mh, $handles[$alias]); curl_close($handles[$alias]); } - + curl_multi_close($mh); - + return true; } - + protected function getRequestKey(HttpRequest $request) { return md5(serialize($request)); } - + protected function makeHandle(HttpRequest $request, CurlHttpResponse $response) { $handle = curl_init(); Assert::isNotNull($request->getMethod()); - + $options = array( CURLOPT_WRITEFUNCTION => array($response, 'writeBody'), CURLOPT_HEADERFUNCTION => array($response, 'writeHeader'), CURLOPT_URL => $request->getUrl()->toString(), CURLOPT_USERAGENT => 'onPHP::'.__CLASS__ ); - + if ($this->noBody !== null) $options[CURLOPT_NOBODY] = $this->noBody; - + if ($this->followLocation !== null) $options[CURLOPT_FOLLOWLOCATION] = $this->followLocation; - + switch ($request->getMethod()->getId()) { case HttpMethod::GET: $options[CURLOPT_HTTPGET] = true; - + if ($request->getGet()) $options[CURLOPT_URL] .= ($request->getUrl()->getQuery() ? '&' : '?') .$this->argumentsToString($request->getGet()); break; - + case HttpMethod::POST: if ($request->getGet()) $options[CURLOPT_URL] .= ($request->getUrl()->getQuery() ? '&' : '?') .$this->argumentsToString($request->getGet()); - + $options[CURLOPT_POST] = true; $options[CURLOPT_POSTFIELDS] = $this->getPostFields($request); - + break; - + default: $options[CURLOPT_CUSTOMREQUEST] = $request->getMethod()->getName(); break; } - + $headers = array(); foreach ($request->getHeaderList() as $headerName => $headerValue) { $headers[] = "{$headerName}: $headerValue"; } - + if ($headers) { $options[CURLOPT_HTTPHEADER] = $headers; } - + if ($request->getCookie()) { $cookies = array(); foreach ($request->getCookie() as $name => $value) $cookies[] = $name.'='.urlencode($value); - + $options[CURLOPT_COOKIE] = implode('; ', $cookies); } - + foreach ($this->options as $key => $value) { $options[$key] = $value; } - + curl_setopt_array($handle, $options); - + return $handle; } - + /** * @return CurlHttpClient **/ protected function makeResponse($handle, CurlHttpResponse $response) { Assert::isNotNull($handle); - + $httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE); try { $response->setStatus( @@ -361,10 +367,27 @@ protected function makeResponse($handle, CurlHttpResponse $response) 'curl error, strange http code: '.$httpCode ); } - + + return $this; + } + /** + * @return CurlHttpClient + **/ + protected function assembleRequestHeaders($handle, HttpRequest $request) + { + Assert::isNotNull($handle); + + $request->setHeaders( + $this->headerParser + ->parse( + curl_getinfo($handle, CURLINFO_HEADER_OUT) + ) + ->getHeaders() + ); + return $this; } - + private function argumentsToString($array, $isFile = false) { if ($this->oldUrlConstructor) @@ -372,7 +395,7 @@ private function argumentsToString($array, $isFile = false) else return UrlParamsUtils::toString($array); } - + private function getPostFields(HttpRequest $request) { if ($request->hasBody()) { @@ -394,13 +417,13 @@ private function getPostFields(HttpRequest $request) 'Security excepion: not allowed send post param '.$atParam . ' which begins from @ in request which contains files' ); - + return array_merge($postList, $fileList); } } } } - + /** * Return param name which start with symbol @ or null * @param array $postList @@ -411,10 +434,10 @@ private function findAtParamInPost($postList) foreach ($postList as $param) if (mb_stripos($param, '@') === 0) return $param; - + return null; } - + /** * using in getPostFields - array_map func * @param string $value