From b67d39508909e72913c97a050b25b51a9b1bd707 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 29 Aug 2015 14:52:37 +0200 Subject: [PATCH 1/3] allow streamed responses in http client --- lib/private/http/client/client.php | 3 ++- lib/private/http/client/response.php | 15 ++++++++++++--- lib/public/http/client/iresponse.php | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/private/http/client/client.php b/lib/private/http/client/client.php index 323fc0d324fd..b0aff10a413d 100644 --- a/lib/private/http/client/client.php +++ b/lib/private/http/client/client.php @@ -120,7 +120,8 @@ private function getProxyUri() { */ public function get($uri, array $options = []) { $response = $this->client->get($uri, $options); - return new Response($response); + $isStream = isset($options['stream']) && $options['stream']; + return new Response($response, $isStream); } /** diff --git a/lib/private/http/client/response.php b/lib/private/http/client/response.php index 4e9df51d77fc..558482491d11 100644 --- a/lib/private/http/client/response.php +++ b/lib/private/http/client/response.php @@ -33,18 +33,27 @@ class Response implements IResponse { /** @var GuzzleResponse */ private $response; + /** + * @var bool + */ + private $stream; + /** * @param GuzzleResponse $response + * @param bool $stream */ - public function __construct(GuzzleResponse $response) { + public function __construct(GuzzleResponse $response, $stream = false) { $this->response = $response; + $this->stream = $stream; } /** - * @return string + * @return string|resource */ public function getBody() { - return $this->response->getBody()->getContents(); + return $this->stream ? + $this->response->getBody()->detach(): + $this->response->getBody()->getContents(); } /** diff --git a/lib/public/http/client/iresponse.php b/lib/public/http/client/iresponse.php index 0e0ef4c50144..92bb7af82ddf 100644 --- a/lib/public/http/client/iresponse.php +++ b/lib/public/http/client/iresponse.php @@ -30,7 +30,7 @@ */ interface IResponse { /** - * @return string + * @return string|resource * @since 8.1.0 */ public function getBody(); From df8cb2cc638bdd8fe0c82604b4347502b2fdb1c3 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 29 Aug 2015 14:53:59 +0200 Subject: [PATCH 2/3] stream webdav downloads using http client --- lib/private/files/storage/dav.php | 44 +++++++++++-------------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/lib/private/files/storage/dav.php b/lib/private/files/storage/dav.php index 6e89dcccbcd9..2b6262f37200 100644 --- a/lib/private/files/storage/dav.php +++ b/lib/private/files/storage/dav.php @@ -40,6 +40,7 @@ use OC\Files\Stream\Close; use Icewind\Streams\IteratorDirectory; use OC\MemCache\ArrayCache; +use OCP\AppFramework\Http; use OCP\Constants; use OCP\Files; use OCP\Files\FileInfo; @@ -337,38 +338,23 @@ public function fopen($path, $mode) { if (!$this->file_exists($path)) { return false; } - //straight up curl instead of sabredav here, sabredav put's the entire get result in memory - $curl = curl_init(); - $fp = fopen('php://temp', 'r+'); - curl_setopt($curl, CURLOPT_USERPWD, $this->user . ':' . $this->password); - curl_setopt($curl, CURLOPT_URL, $this->createBaseUri() . $this->encodePath($path)); - curl_setopt($curl, CURLOPT_FILE, $fp); - curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); - if(defined('CURLOPT_PROTOCOLS')) { - curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); - } - if(defined('CURLOPT_REDIR_PROTOCOLS')) { - curl_setopt($curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); - } - if ($this->secure === true) { - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); - curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); - if ($this->certPath) { - curl_setopt($curl, CURLOPT_CAINFO, $this->certPath); - } - } - - curl_exec($curl); - $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); - if ($statusCode !== 200) { - Util::writeLog("webdav client", 'curl GET ' . curl_getinfo($curl, CURLINFO_EFFECTIVE_URL) . ' returned status code ' . $statusCode, Util::ERROR); - if ($statusCode === 423) { + $httpClient = \OC::$server->getHTTPClientService(); + $response = $httpClient + ->newClient() + ->get($this->createBaseUri() . $this->encodePath($path), [ + 'auth' => [$this->user, $this->password], + 'stream' => true + ]); + + if ($response->getStatusCode() !== Http::STATUS_OK) { + if ($response->getStatusCode() === Http::STATUS_LOCKED) { throw new \OCP\Lock\LockedException($path); + } else { + Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR); } } - curl_close($curl); - rewind($fp); - return $fp; + + return $response->getBody(); case 'w': case 'wb': case 'a': From 1c10fb5c9f583d562184097fd2fd97e1b121773f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 1 Sep 2015 18:01:27 +0200 Subject: [PATCH 3/3] also use httpclient for uploadFile --- lib/private/files/storage/dav.php | 53 +++++++++++-------------------- 1 file changed, 18 insertions(+), 35 deletions(-) diff --git a/lib/private/files/storage/dav.php b/lib/private/files/storage/dav.php index 2b6262f37200..c1cf17abdee1 100644 --- a/lib/private/files/storage/dav.php +++ b/lib/private/files/storage/dav.php @@ -78,6 +78,8 @@ class DAV extends Common { private $statCache; /** @var array */ private static $tempFiles = []; + /** @var \OCP\Http\Client\IClientService */ + private $httpClientService; /** * @param array $params @@ -85,6 +87,7 @@ class DAV extends Common { */ public function __construct($params) { $this->statCache = new ArrayCache(); + $this->httpClientService = \OC::$server->getHTTPClientService(); if (isset($params['host']) && isset($params['user']) && isset($params['password'])) { $host = $params['host']; //remove leading http[s], will be generated in createBaseUri() @@ -233,7 +236,7 @@ public function opendir($path) { * If not, request it from the server then store to cache. * * @param string $path path to propfind - * + * * @return array propfind response * * @throws NotFound @@ -338,8 +341,7 @@ public function fopen($path, $mode) { if (!$this->file_exists($path)) { return false; } - $httpClient = \OC::$server->getHTTPClientService(); - $response = $httpClient + $response = $this->httpClientService ->newClient() ->get($this->createBaseUri() . $this->encodePath($path), [ 'auth' => [$this->user, $this->password], @@ -464,38 +466,19 @@ public function file_put_contents($path, $data) { */ protected function uploadFile($path, $target) { $this->init(); + // invalidate $target = $this->cleanPath($target); $this->statCache->remove($target); $source = fopen($path, 'r'); - $curl = curl_init(); - curl_setopt($curl, CURLOPT_USERPWD, $this->user . ':' . $this->password); - curl_setopt($curl, CURLOPT_URL, $this->createBaseUri() . $this->encodePath($target)); - curl_setopt($curl, CURLOPT_BINARYTRANSFER, true); - curl_setopt($curl, CURLOPT_INFILE, $source); // file pointer - curl_setopt($curl, CURLOPT_INFILESIZE, filesize($path)); - curl_setopt($curl, CURLOPT_PUT, true); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); - curl_setopt($curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); - if ($this->secure === true) { - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); - curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); - if ($this->certPath) { - curl_setopt($curl, CURLOPT_CAINFO, $this->certPath); - } - } - curl_exec($curl); - $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); - if ($statusCode !== 200) { - Util::writeLog("webdav client", 'curl GET ' . curl_getinfo($curl, CURLINFO_EFFECTIVE_URL) . ' returned status code ' . $statusCode, Util::ERROR); - if ($statusCode === 423) { - throw new \OCP\Lock\LockedException($path); - } - } - curl_close($curl); - fclose($source); + $this->httpClientService + ->newClient() + ->put($this->createBaseUri() . $this->encodePath($target), [ + 'body' => $source, + 'auth' => [$this->user, $this->password] + ]); + $this->removeCachedFile($target); } @@ -754,7 +737,7 @@ public function hasUpdated($path, $time) { if ($e->getHttpStatus() === 404 || $e->getHttpStatus() === 405) { if ($path === '') { // if root is gone it means the storage is not available - throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage()); + throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage()); } return false; } @@ -788,19 +771,19 @@ private function convertException(Exception $e, $path = '') { } if ($e->getHttpStatus() === 401) { // either password was changed or was invalid all along - throw new StorageInvalidException(get_class($e).': '.$e->getMessage()); + throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage()); } else if ($e->getHttpStatus() === 405) { // ignore exception for MethodNotAllowed, false will be returned return; } - throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage()); + throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage()); } else if ($e instanceof ClientException) { // connection timeout or refused, server could be temporarily down - throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage()); + throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage()); } else if ($e instanceof \InvalidArgumentException) { // parse error because the server returned HTML instead of XML, // possibly temporarily down - throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage()); + throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage()); } else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) { // rethrow throw $e;