Pārlūkot izejas kodu

添加又拍云图片上传插件

wqg 3 nedēļas atpakaļ
vecāks
revīzija
38894f118e
100 mainītis faili ar 16980 papildinājumiem un 0 dzēšanām
  1. 817 0
      usr/plugins/UpyunFile/Plugin.php
  2. 44 0
      usr/plugins/UpyunFile/Upyun/src/Upyun/Api/Form.php
  3. 84 0
      usr/plugins/UpyunFile/Upyun/src/Upyun/Api/Pretreat.php
  4. 108 0
      usr/plugins/UpyunFile/Upyun/src/Upyun/Api/Rest.php
  5. 42 0
      usr/plugins/UpyunFile/Upyun/src/Upyun/Api/SyncVideo.php
  6. 147 0
      usr/plugins/UpyunFile/Upyun/src/Upyun/Config.php
  7. 98 0
      usr/plugins/UpyunFile/Upyun/src/Upyun/Signature.php
  8. 120 0
      usr/plugins/UpyunFile/Upyun/src/Upyun/Uploader.php
  9. 538 0
      usr/plugins/UpyunFile/Upyun/src/Upyun/Upyun.php
  10. 90 0
      usr/plugins/UpyunFile/Upyun/src/Upyun/Util.php
  11. 7 0
      usr/plugins/UpyunFile/Upyun/vendor/autoload.php
  12. 445 0
      usr/plugins/UpyunFile/Upyun/vendor/composer/ClassLoader.php
  13. 21 0
      usr/plugins/UpyunFile/Upyun/vendor/composer/LICENSE
  14. 95 0
      usr/plugins/UpyunFile/Upyun/vendor/composer/autoload_classmap.php
  15. 12 0
      usr/plugins/UpyunFile/Upyun/vendor/composer/autoload_files.php
  16. 9 0
      usr/plugins/UpyunFile/Upyun/vendor/composer/autoload_namespaces.php
  17. 15 0
      usr/plugins/UpyunFile/Upyun/vendor/composer/autoload_psr4.php
  18. 70 0
      usr/plugins/UpyunFile/Upyun/vendor/composer/autoload_real.php
  19. 158 0
      usr/plugins/UpyunFile/Upyun/vendor/composer/autoload_static.php
  20. 241 0
      usr/plugins/UpyunFile/Upyun/vendor/composer/installed.json
  21. 1264 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/CHANGELOG.md
  22. 19 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/LICENSE
  23. 89 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/README.md
  24. 1203 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/UPGRADING.md
  25. 44 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/composer.json
  26. 414 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Client.php
  27. 84 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/ClientInterface.php
  28. 314 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php
  29. 84 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php
  30. 90 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php
  31. 71 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php
  32. 404 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php
  33. 27 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php
  34. 7 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/ClientException.php
  35. 37 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php
  36. 4 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php
  37. 217 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php
  38. 27 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php
  39. 7 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php
  40. 4 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php
  41. 4 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/TransferException.php
  42. 559 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php
  43. 27 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php
  44. 45 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php
  45. 197 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php
  46. 92 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php
  47. 189 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php
  48. 55 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php
  49. 533 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php
  50. 273 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/HandlerStack.php
  51. 182 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/MessageFormatter.php
  52. 254 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Middleware.php
  53. 123 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Pool.php
  54. 106 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php
  55. 237 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php
  56. 255 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/RequestOptions.php
  57. 112 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/RetryMiddleware.php
  58. 126 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/TransferStats.php
  59. 241 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/UriTemplate.php
  60. 331 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/functions.php
  61. 6 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/functions_include.php
  62. 65 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/CHANGELOG.md
  63. 19 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/LICENSE
  64. 13 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/Makefile
  65. 504 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/README.md
  66. 34 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/composer.json
  67. 16 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/AggregateException.php
  68. 9 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/CancellationException.php
  69. 151 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/Coroutine.php
  70. 229 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/EachPromise.php
  71. 82 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/FulfilledPromise.php
  72. 280 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/Promise.php
  73. 93 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/PromiseInterface.php
  74. 15 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/PromisorInterface.php
  75. 87 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/RejectedPromise.php
  76. 47 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/RejectionException.php
  77. 66 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/TaskQueue.php
  78. 25 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/TaskQueueInterface.php
  79. 457 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/functions.php
  80. 6 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/functions_include.php
  81. 110 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/CHANGELOG.md
  82. 19 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/LICENSE
  83. 739 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/README.md
  84. 39 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/composer.json
  85. 233 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/AppendStream.php
  86. 137 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/BufferStream.php
  87. 138 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/CachingStream.php
  88. 42 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/DroppingStream.php
  89. 149 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/FnStream.php
  90. 52 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/InflateStream.php
  91. 39 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/LazyOpenStream.php
  92. 155 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/LimitStream.php
  93. 183 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/MessageTrait.php
  94. 153 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/MultipartStream.php
  95. 22 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/NoSeekStream.php
  96. 165 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/PumpStream.php
  97. 142 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/Request.php
  98. 132 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/Response.php
  99. 358 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/ServerRequest.php
  100. 257 0
      usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/Stream.php

+ 817 - 0
usr/plugins/UpyunFile/Plugin.php

@@ -0,0 +1,817 @@
+<?php
+/**
+ * 又拍云(UpYun)文件管理 Modified by <a href="https://blog.sspirits.top">SSpirits</a>
+ *
+ * @package UpyunFile
+ * @author codesee
+ * @version 1.0.4
+ * @link https://blog.sspirits.top
+ * @dependence 1.0-*
+ * @date 2019-5-10
+ */
+
+use Upyun\Config;
+use Upyun\Upyun;
+
+class UpyunFile_Plugin implements Typecho_Plugin_Interface {
+
+    //上传文件目录
+    const UPLOAD_DIR = '/typecho/uploads';
+    const IMG_EXT = ['JPG', 'JPEG', 'PNG', 'BMP'];
+
+    /**
+     * 激活插件方法,如果激活失败,直接抛出异常
+     *
+     * @access public
+     * @return void
+     * @throws Typecho_Plugin_Exception
+     */
+    public static function activate() {
+        Typecho_Plugin::factory('Widget_Upload')->uploadHandle = array('UpyunFile_Plugin', 'uploadHandle');
+        Typecho_Plugin::factory('Widget_Upload')->modifyHandle = array('UpyunFile_Plugin', 'modifyHandle');
+        Typecho_Plugin::factory('Widget_Upload')->deleteHandle = array('UpyunFile_Plugin', 'deleteHandle');
+        Typecho_Plugin::factory('Widget_Upload')->attachmentHandle = array('UpyunFile_Plugin', 'attachmentHandle');
+        Typecho_Plugin::factory('Widget_Upload')->attachmentDataHandle = array('UpyunFile_Plugin', 'attachmentDataHandle');
+        Typecho_Plugin::factory('Widget_Abstract_Contents')->contentEx = array('UpyunFile_Plugin', 'replace');
+        Typecho_Plugin::factory('Widget_Archive')->beforeRender = array('UpyunFile_Plugin', 'Widget_Archive_beforeRender');
+        return _t('插件已经激活,请正确设置插件');
+    }
+
+    /**
+     * 禁用插件方法,如果禁用失败,直接抛出异常
+     *
+     * @static
+     * @access public
+     * @return void
+     * @throws Typecho_Plugin_Exception
+     */
+    public static function deactivate() {
+        return _t('插件已被禁用');
+    }
+
+    /**
+     * 获取插件配置面板
+     *
+     * @access public
+     * @param Typecho_Widget_Helper_Form $form 配置面板
+     * @return void
+     */
+    public static function config(Typecho_Widget_Helper_Form $form) {
+        $convert = new Typecho_Widget_Helper_Form_Element_Radio('convert', array('1' => _t('开启'), '0' => _t('关闭')), '0', _t('图片链接修改'), _t('把文章中图片的链接修改为又拍云 CDN 链接,启用前请把放在 typecho 上传目录中的图片同步到又拍云中'));
+        $form->addInput($convert);
+        $upyundomain = new Typecho_Widget_Helper_Form_Element_Text('upyundomain', NULL, 'https://', _t('绑定域名:'), _t('该绑定域名为绑定Upyun服务的域名,由Upyun提供,注意以 http(s):// 开头,最后不要加 /'));
+        $form->addInput($upyundomain->addRule('required', _t('您必须填写绑定域名,它是由 Upyun 提供')));
+
+        $upyunpathmode = new Typecho_Widget_Helper_Form_Element_Radio(
+            'mode',
+            array('typecho' => _t('Typecho结构(' . self::getUploadDir() . '/年/月/文件名)'), 'simple' => _t('精简结构(/年/月/文件名)')),
+            'typecho',
+            _t('目录结构模式'),
+            _t('默认为 Typecho 结构模式')
+        );
+
+        $form->addInput($upyunpathmode);
+
+        $upyunhost = new Typecho_Widget_Helper_Form_Element_Text('upyunhost', NULL, NULL, _t('服务名称:'));
+        $upyunhost->input->setAttribute('class', 'mini');
+        $form->addInput($upyunhost->addRule('required', _t('您必须填写服务名称,它是由Upyun提供')));
+
+        $upyunuser = new Typecho_Widget_Helper_Form_Element_Text('upyunuser', NULL, NULL, _t('操作员:'));
+        $upyunuser->input->setAttribute('class', 'mini');
+        $form->addInput($upyunuser->addRule('required', _t('您必须填写操作员,它是由 Upyun 提供')));
+
+        $upyunpwd = new Typecho_Widget_Helper_Form_Element_Password('upyunpwd', NULL, NULL, _t('密码:'));
+        $form->addInput($upyunpwd->addRule('required', _t('您必须填写密码,它是由 Upyun 提供'))
+            ->addRule(array('UpyunFile_Plugin', 'validate'), _t('验证不通过,请核对 Upyun 操作员和密码是否输入正确')));
+
+        $convertPic = new Typecho_Widget_Helper_Form_Element_Radio('convertPic', array('1' => _t('开启'), '0' => _t('关闭')), '0', _t('又拍云图片处理'), _t('启用本功能需在又拍云控制台中创建缩略图版本,又拍云文档:<a href="https://help.upyun.com/knowledge-base/image/#thumb">https://help.upyun.com/knowledge-base/image/#thumb</a><br>本功能不会处理带有后缀 <b>_nothumb</b> 的图片(比如:example_nothumb.png)'));
+        $form->addInput($convertPic);
+
+        $thumbId = new Typecho_Widget_Helper_Form_Element_Text('thumbId', NULL, NULL, _t('图片处理 - 缩略图版本名称:'), _t('启用又拍云图片处理必须正确填写缩略图版本名称'));
+        $thumbId->input->setAttribute('class', 'mini');
+        $form->addInput($thumbId);
+
+        $outputFormat = new Typecho_Widget_Helper_Form_Element_Text('outputFormat', NULL, NULL, _t('图片处理 - 转码输出格式:'), _t('如:jpg、png、webp 等,不填即为源文件拓展名'));
+        $outputFormat->input->setAttribute('class', 'mini');
+        $form->addInput($outputFormat);
+
+        $addToken = new Typecho_Widget_Helper_Form_Element_Radio('addToken', array('1' => _t('开启'), '0' => _t('关闭')), '0', _t('又拍云 Token 防盗链'), _t('启用本功能需在又拍云控制台中启用 Token 防盗链'));
+        $form->addInput($addToken);
+
+        $secret = new Typecho_Widget_Helper_Form_Element_Password('secret', NULL, NULL, _t('密钥'), _t('启用又拍云 Token 防盗链必须正确填写又拍云控制台中设置的密钥'));
+        $form->addInput($secret);
+
+        $etime = new Typecho_Widget_Helper_Form_Element_Text('etime', NULL, NULL, _t('签名过期时间'), _t('单位为秒'));
+        $etime->input->setAttribute('class', 'mini');
+        $form->addInput($etime);
+    }
+
+    /**
+     * 个人用户的配置面板
+     *
+     * @access public
+     * @param Typecho_Widget_Helper_Form $form
+     * @return void
+     */
+    public static function personalConfig(Typecho_Widget_Helper_Form $form) {
+    }
+
+    /**
+     * 上传文件处理函数
+     *
+     * @access public
+     * @param array $file 上传的文件
+     * @return mixed
+     */
+    public static function uploadHandle($file) {
+        
+        if (empty($file['name'])) {
+            return false;
+        }
+
+        //获取扩展名
+        $ext = self::getSafeName($file['name']);
+
+        if (!Widget_Upload::checkFileType($ext) || Typecho_Common::isAppEngine()) {
+            return false;
+        }
+
+        $options = Typecho_Widget::widget('Widget_Options');
+
+        $date = new Typecho_Date($options->gmtTime);
+
+        //构建路径 /year/month/
+        $path = '/' . $date->year . '/' . $date->month;
+        $settings = $options->plugin('UpyunFile');
+        $thumbId = '';
+        if ($settings->mode === 'typecho') {
+            $path = self::getUploadDir() . $path;
+        }
+
+        $nothumb = strpos($file['name'], '_nothumb') !== false;
+        //获取文件名及文件路径
+        if (!$nothumb && !empty($settings->convertPic) && !empty($settings->outputFormat)) {
+            foreach (self::IMG_EXT as $item) {
+                if (strcasecmp($ext, $item) == 0) {
+                    $ext = $settings->outputFormat;
+                    $thumbId = $settings->thumbId;
+                    break;
+                }
+            }
+        }
+        $fileName = sprintf('%u', crc32(uniqid())) . ".$ext";
+        $path = $path . '/' . $fileName;
+
+        $uploadfile = self::getUploadFile($file);
+        if (!isset($uploadfile)) {
+            return false;
+        }
+        
+        //上传文件
+        $upyun = self::upyunInit();
+        $fh = fopen($uploadfile, 'rb');
+        if ($nothumb || empty($thumbId)) {
+            $upyun->write($path, $fh);
+        } else {
+            $upyun->write($path, $fh, array('x-gmkerl-thumb' => $settings->thumbId));
+        }
+
+       /* @fclose($fh);*/
+
+        if (!isset($file['size'])) {
+            $fileInfo = $upyun->info($path);
+            $file['size'] = $fileInfo['x-upyun-file-size'];
+        }
+
+        //返回相对存储路径
+        return array(
+            'name' => $nothumb || empty($settings->outputFormat) ? $file['name'] : substr($file['name'], 0, strrpos($file['name'], '.') + 1) . $ext,
+            'path' => $path,
+            'size' => $file['size'],
+            'type' => $ext,
+            'mime' => self::mimeContentType($path)
+        );
+    }
+
+    /**
+     * 修改文件处理函数
+     *
+     * @access public
+     * @param array $content 老文件
+     * @param array $file 新上传的文件
+     * @return mixed
+     */
+    public static function modifyHandle($content, $file) {
+        if (empty($file['name'])) {
+            return false;
+        }
+
+        //获取扩展名
+        $ext = self::getSafeName($file['name']);
+
+        if ($content['attachment']->type !== $ext || Typecho_Common::isAppEngine()) {
+            return false;
+        }
+
+        //获取文件路径
+        $path = $content['attachment']->path;
+
+        $uploadfile = self::getUploadFile($file);
+
+        if (!isset($uploadfile)) {
+            return false;
+        }
+        //修改文件
+        $settings = Typecho_Widget::widget('Widget_Options')->plugin('UpyunFile');
+        $upyun = self::upyunInit();
+        $thumbId = '';
+        $nothumb = strpos($file['name'], '_nothumb') !== false;
+        if (!$nothumb && !empty($settings->convertPic) && !empty($settings->outputFormat)) {
+            foreach (self::IMG_EXT as $item) {
+                if (strcasecmp($ext, $item) == 0) {
+                    $thumbId = $settings->thumbId;
+                    break;
+                }
+            }
+        }
+        $fh = fopen($uploadfile, 'rb');
+        if ($nothumb && empty($thumbId)) {
+            $upyun->write($path, $fh);
+        } else {
+            $upyun->write($path, $fh, array('x-gmkerl-thumb' => $settings->thumbId));
+        }
+        /*@fclose($fh);*/
+
+        if (!isset($file['size'])) {
+            $fileInfo = $upyun->info($path);
+            $file['size'] = $fileInfo['x-upyun-file-size'];
+        }
+
+        //返回相对存储路径
+        return array(
+            'name' => $content['attachment']->name,
+            'path' => $content['attachment']->path,
+            'size' => $file['size'],
+            'type' => $content['attachment']->type,
+            'mime' => $content['attachment']->mime
+        );
+    }
+
+    /**
+     * 删除文件
+     *
+     * @access public
+     * @param array $content 文件相关信息
+     * @return string
+     */
+    public static function deleteHandle(array $content) {
+        $upyun = self::upyunInit();
+        $path = $content['attachment']->path;
+
+        return $upyun->delete($path);
+    }
+
+    /**
+     * 获取实际文件绝对访问路径
+     *
+     * @access public
+     * @param array $content 文件相关信息
+     * @return string
+     */
+    public static function attachmentHandle(array $content) {
+        $settings = Typecho_Widget::widget('Widget_Options')->plugin('UpyunFile');
+        $domain = $settings->upyundomain;
+        $url = Typecho_Common::url($content['attachment']->path, $domain);
+        if ($settings->addToken != 1) {
+            return $url;
+        }
+        $etime = self::getEtime($settings->etime);
+        $sign = substr(md5($settings->secret . '&' . $etime . '&' . parse_url($url, PHP_URL_PATH)), 12, 8) . $etime;
+        return self::addParameter($url, '_upt', $sign);
+    }
+
+    /**
+     * 获取实际文件数据
+     *
+     * @access public
+     * @param array $content
+     * @return string
+     */
+    public static function attachmentDataHandle(array $content) {
+        $upyun = self::upyunInit();
+        return $upyun->info($content['attachment']->path);
+    }
+
+    /**
+     * 验证Upyun签名
+     *
+     * @access public
+     *
+     * @return boolean
+     */
+    public static function validate() {
+        $host = Typecho_Request::getInstance()->upyunhost;
+        $user = Typecho_Request::getInstance()->upyunuser;
+        $pwd = Typecho_Request::getInstance()->upyunpwd;
+
+        try {
+            require_once 'Upyun/vendor/autoload.php';
+            $serviceConfig = new Config($host, $user, $pwd);
+            $upyun = new Upyun($serviceConfig);
+            $hostUsage = (int)$upyun->usage();
+        } catch (Exception $e) {
+            $hostUsage = -1;
+        }
+
+        return $hostUsage >= 0;
+    }
+
+    /**
+     * Upyun初始化
+     *
+     * @access public
+     * @return object
+     */
+    public static function upyunInit() {
+        $options = Typecho_Widget::widget('Widget_Options')->plugin('UpyunFile');
+        require_once 'Upyun/vendor/autoload.php';
+        $serviceConfig = new Config($options->upyunhost, $options->upyunuser, $options->upyunpwd);
+        return new Upyun($serviceConfig);
+    }
+
+    /**
+     * 获取上传文件
+     *
+     * @param array $file 上传的文件
+     * @access private
+     * @return string
+     */
+    private static function getUploadFile($file) {
+        
+        return isset($file['tmp_name']) ? $file['tmp_name'] : (isset($file['bytes']) ? $file['bytes'] : (isset($file['bits']) ? $file['bits'] : ''));
+    }
+
+    /**
+     * 获取安全的文件名
+     *
+     * @param string $name
+     * @static
+     * @access private
+     * @return string
+     */
+    private static function getSafeName(&$name) {
+        $name = str_replace(array('\\', '"', '<', '>'), '/', $name);
+        $name = false === strpos($name, '/') ? ('a' . $name) : str_replace('/', '/a', $name);
+        $info = pathinfo($name);
+        $name = substr($info['basename'], 1);
+
+        return isset($info['extension']) ? strtolower($info['extension']) : '';
+    }
+
+    /**
+     *获取文件上传目录
+     * @access private
+     * @return string
+     */
+    private static function getUploadDir() {
+        if (defined('__TYPECHO_UPLOAD_DIR__')) {
+            return __TYPECHO_UPLOAD_DIR__;
+        }
+        return self::UPLOAD_DIR;
+    }
+
+    /**
+     *获取文件Mime类型,处理掉异常
+     * @access private
+     * @return string
+     */
+    private static function mimeContentType($fileName) {
+        //TODO:避免该方法
+        //避免Typecho mime-content-type引发的异常
+        /*异常详情:
+        *Warning</b>:  mime_content_type(/path/filename.jpg) [<a href='function.mime-content-type'>function.mime-content-type</a>]:
+        *failed to open stream: No such file or directory in <b>/webroot/var/Typecho/Common.php</b> on line <b>1058</b>
+        */
+        @$mime = Typecho_Common::mimeContentType($fileName);
+
+        if (!$mime) {
+            return self::getMime($fileName);
+        }
+        return $mime;
+    }
+
+    /**
+     *获取文件Mime类型
+     * @access private
+     * @return string
+     */
+    private static function getMime($fileName) {
+        $mimeTypes = array(
+            'ez' => 'application/andrew-inset',
+            'csm' => 'application/cu-seeme',
+            'cu' => 'application/cu-seeme',
+            'tsp' => 'application/dsptype',
+            'spl' => 'application/x-futuresplash',
+            'hta' => 'application/hta',
+            'cpt' => 'image/x-corelphotopaint',
+            'hqx' => 'application/mac-binhex40',
+            'nb' => 'application/mathematica',
+            'mdb' => 'application/msaccess',
+            'doc' => 'application/msword',
+            'dot' => 'application/msword',
+            'bin' => 'application/octet-stream',
+            'oda' => 'application/oda',
+            'ogg' => 'application/ogg',
+            'prf' => 'application/pics-rules',
+            'key' => 'application/pgp-keys',
+            'pdf' => 'application/pdf',
+            'pgp' => 'application/pgp-signature',
+            'ps' => 'application/postscript',
+            'ai' => 'application/postscript',
+            'eps' => 'application/postscript',
+            'rss' => 'application/rss+xml',
+            'rtf' => 'text/rtf',
+            'smi' => 'application/smil',
+            'smil' => 'application/smil',
+            'wp5' => 'application/wordperfect5.1',
+            'xht' => 'application/xhtml+xml',
+            'xhtml' => 'application/xhtml+xml',
+            'zip' => 'application/zip',
+            'cdy' => 'application/vnd.cinderella',
+            'mif' => 'application/x-mif',
+            'xls' => 'application/vnd.ms-excel',
+            'xlb' => 'application/vnd.ms-excel',
+            'cat' => 'application/vnd.ms-pki.seccat',
+            'stl' => 'application/vnd.ms-pki.stl',
+            'ppt' => 'application/vnd.ms-powerpoint',
+            'pps' => 'application/vnd.ms-powerpoint',
+            'pot' => 'application/vnd.ms-powerpoint',
+            'sdc' => 'application/vnd.stardivision.calc',
+            'sda' => 'application/vnd.stardivision.draw',
+            'sdd' => 'application/vnd.stardivision.impress',
+            'sdp' => 'application/vnd.stardivision.impress',
+            'smf' => 'application/vnd.stardivision.math',
+            'sdw' => 'application/vnd.stardivision.writer',
+            'vor' => 'application/vnd.stardivision.writer',
+            'sgl' => 'application/vnd.stardivision.writer-global',
+            'sxc' => 'application/vnd.sun.xml.calc',
+            'stc' => 'application/vnd.sun.xml.calc.template',
+            'sxd' => 'application/vnd.sun.xml.draw',
+            'std' => 'application/vnd.sun.xml.draw.template',
+            'sxi' => 'application/vnd.sun.xml.impress',
+            'sti' => 'application/vnd.sun.xml.impress.template',
+            'sxm' => 'application/vnd.sun.xml.math',
+            'sxw' => 'application/vnd.sun.xml.writer',
+            'sxg' => 'application/vnd.sun.xml.writer.global',
+            'stw' => 'application/vnd.sun.xml.writer.template',
+            'sis' => 'application/vnd.symbian.install',
+            'wbxml' => 'application/vnd.wap.wbxml',
+            'wmlc' => 'application/vnd.wap.wmlc',
+            'wmlsc' => 'application/vnd.wap.wmlscriptc',
+            'wk' => 'application/x-123',
+            'dmg' => 'application/x-apple-diskimage',
+            'bcpio' => 'application/x-bcpio',
+            'torrent' => 'application/x-bittorrent',
+            'cdf' => 'application/x-cdf',
+            'vcd' => 'application/x-cdlink',
+            'pgn' => 'application/x-chess-pgn',
+            'cpio' => 'application/x-cpio',
+            'csh' => 'text/x-csh',
+            'deb' => 'application/x-debian-package',
+            'dcr' => 'application/x-director',
+            'dir' => 'application/x-director',
+            'dxr' => 'application/x-director',
+            'wad' => 'application/x-doom',
+            'dms' => 'application/x-dms',
+            'dvi' => 'application/x-dvi',
+            'pfa' => 'application/x-font',
+            'pfb' => 'application/x-font',
+            'gsf' => 'application/x-font',
+            'pcf' => 'application/x-font',
+            'pcf.Z' => 'application/x-font',
+            'gnumeric' => 'application/x-gnumeric',
+            'sgf' => 'application/x-go-sgf',
+            'gcf' => 'application/x-graphing-calculator',
+            'gtar' => 'application/x-gtar',
+            'tgz' => 'application/x-gtar',
+            'taz' => 'application/x-gtar',
+            'gz' => 'application/x-gtar',
+            'hdf' => 'application/x-hdf',
+            'phtml' => 'application/x-httpd-php',
+            'pht' => 'application/x-httpd-php',
+            'php' => 'application/x-httpd-php',
+            'phps' => 'application/x-httpd-php-source',
+            'php3' => 'application/x-httpd-php3',
+            'php3p' => 'application/x-httpd-php3-preprocessed',
+            'php4' => 'application/x-httpd-php4',
+            'ica' => 'application/x-ica',
+            'ins' => 'application/x-internet-signup',
+            'isp' => 'application/x-internet-signup',
+            'iii' => 'application/x-iphone',
+            'jar' => 'application/x-java-archive',
+            'jnlp' => 'application/x-java-jnlp-file',
+            'ser' => 'application/x-java-serialized-object',
+            'class' => 'application/x-java-vm',
+            'js' => 'application/x-javascript',
+            'chrt' => 'application/x-kchart',
+            'kil' => 'application/x-killustrator',
+            'kpr' => 'application/x-kpresenter',
+            'kpt' => 'application/x-kpresenter',
+            'skp' => 'application/x-koan',
+            'skd' => 'application/x-koan',
+            'skt' => 'application/x-koan',
+            'skm' => 'application/x-koan',
+            'ksp' => 'application/x-kspread',
+            'kwd' => 'application/x-kword',
+            'kwt' => 'application/x-kword',
+            'latex' => 'application/x-latex',
+            'lha' => 'application/x-lha',
+            'lzh' => 'application/x-lzh',
+            'lzx' => 'application/x-lzx',
+            'frm' => 'application/x-maker',
+            'maker' => 'application/x-maker',
+            'frame' => 'application/x-maker',
+            'fm' => 'application/x-maker',
+            'fb' => 'application/x-maker',
+            'book' => 'application/x-maker',
+            'fbdoc' => 'application/x-maker',
+            'wmz' => 'application/x-ms-wmz',
+            'wmd' => 'application/x-ms-wmd',
+            'com' => 'application/x-msdos-program',
+            'exe' => 'application/x-msdos-program',
+            'bat' => 'application/x-msdos-program',
+            'dll' => 'application/x-msdos-program',
+            'msi' => 'application/x-msi',
+            'nc' => 'application/x-netcdf',
+            'pac' => 'application/x-ns-proxy-autoconfig',
+            'nwc' => 'application/x-nwc',
+            'o' => 'application/x-object',
+            'oza' => 'application/x-oz-application',
+            'pl' => 'application/x-perl',
+            'pm' => 'application/x-perl',
+            'p7r' => 'application/x-pkcs7-certreqresp',
+            'crl' => 'application/x-pkcs7-crl',
+            'qtl' => 'application/x-quicktimeplayer',
+            'rpm' => 'audio/x-pn-realaudio-plugin',
+            'shar' => 'application/x-shar',
+            'swf' => 'application/x-shockwave-flash',
+            'swfl' => 'application/x-shockwave-flash',
+            'sh' => 'text/x-sh',
+            'sit' => 'application/x-stuffit',
+            'sv4cpio' => 'application/x-sv4cpio',
+            'sv4crc' => 'application/x-sv4crc',
+            'tar' => 'application/x-tar',
+            'tcl' => 'text/x-tcl',
+            'tex' => 'text/x-tex',
+            'gf' => 'application/x-tex-gf',
+            'pk' => 'application/x-tex-pk',
+            'texinfo' => 'application/x-texinfo',
+            'texi' => 'application/x-texinfo',
+            '~' => 'application/x-trash',
+            '%' => 'application/x-trash',
+            'bak' => 'application/x-trash',
+            'old' => 'application/x-trash',
+            'sik' => 'application/x-trash',
+            't' => 'application/x-troff',
+            'tr' => 'application/x-troff',
+            'roff' => 'application/x-troff',
+            'man' => 'application/x-troff-man',
+            'me' => 'application/x-troff-me',
+            'ms' => 'application/x-troff-ms',
+            'ustar' => 'application/x-ustar',
+            'src' => 'application/x-wais-source',
+            'wz' => 'application/x-wingz',
+            'crt' => 'application/x-x509-ca-cert',
+            'fig' => 'application/x-xfig',
+            'au' => 'audio/basic',
+            'snd' => 'audio/basic',
+            'mid' => 'audio/midi',
+            'midi' => 'audio/midi',
+            'kar' => 'audio/midi',
+            'mpga' => 'audio/mpeg',
+            'mpega' => 'audio/mpeg',
+            'mp2' => 'audio/mpeg',
+            'mp3' => 'audio/mpeg',
+            'm3u' => 'audio/x-mpegurl',
+            'sid' => 'audio/prs.sid',
+            'aif' => 'audio/x-aiff',
+            'aiff' => 'audio/x-aiff',
+            'aifc' => 'audio/x-aiff',
+            'gsm' => 'audio/x-gsm',
+            'wma' => 'audio/x-ms-wma',
+            'wax' => 'audio/x-ms-wax',
+            'ra' => 'audio/x-realaudio',
+            'rm' => 'audio/x-pn-realaudio',
+            'ram' => 'audio/x-pn-realaudio',
+            'pls' => 'audio/x-scpls',
+            'sd2' => 'audio/x-sd2',
+            'wav' => 'audio/x-wav',
+            'pdb' => 'chemical/x-pdb',
+            'xyz' => 'chemical/x-xyz',
+            'bmp' => 'image/x-ms-bmp',
+            'gif' => 'image/gif',
+            'ief' => 'image/ief',
+            'jpeg' => 'image/jpeg',
+            'jpg' => 'image/jpeg',
+            'jpe' => 'image/jpeg',
+            'pcx' => 'image/pcx',
+            'png' => 'image/png',
+            'svg' => 'image/svg+xml',
+            'svgz' => 'image/svg+xml',
+            'tiff' => 'image/tiff',
+            'tif' => 'image/tiff',
+            'wbmp' => 'image/vnd.wap.wbmp',
+            'ras' => 'image/x-cmu-raster',
+            'cdr' => 'image/x-coreldraw',
+            'pat' => 'image/x-coreldrawpattern',
+            'cdt' => 'image/x-coreldrawtemplate',
+            'djvu' => 'image/x-djvu',
+            'djv' => 'image/x-djvu',
+            'ico' => 'image/x-icon',
+            'art' => 'image/x-jg',
+            'jng' => 'image/x-jng',
+            'psd' => 'image/x-photoshop',
+            'pnm' => 'image/x-portable-anymap',
+            'pbm' => 'image/x-portable-bitmap',
+            'pgm' => 'image/x-portable-graymap',
+            'ppm' => 'image/x-portable-pixmap',
+            'rgb' => 'image/x-rgb',
+            'xbm' => 'image/x-xbitmap',
+            'xpm' => 'image/x-xpixmap',
+            'xwd' => 'image/x-xwindowdump',
+            'igs' => 'model/iges',
+            'iges' => 'model/iges',
+            'msh' => 'model/mesh',
+            'mesh' => 'model/mesh',
+            'silo' => 'model/mesh',
+            'wrl' => 'x-world/x-vrml',
+            'vrml' => 'x-world/x-vrml',
+            'csv' => 'text/comma-separated-values',
+            'css' => 'text/css',
+            '323' => 'text/h323',
+            'htm' => 'text/html',
+            'html' => 'text/html',
+            'uls' => 'text/iuls',
+            'mml' => 'text/mathml',
+            'asc' => 'text/plain',
+            'txt' => 'text/plain',
+            'text' => 'text/plain',
+            'diff' => 'text/plain',
+            'rtx' => 'text/richtext',
+            'sct' => 'text/scriptlet',
+            'wsc' => 'text/scriptlet',
+            'tm' => 'text/texmacs',
+            'ts' => 'text/texmacs',
+            'tsv' => 'text/tab-separated-values',
+            'jad' => 'text/vnd.sun.j2me.app-descriptor',
+            'wml' => 'text/vnd.wap.wml',
+            'wmls' => 'text/vnd.wap.wmlscript',
+            'xml' => 'text/xml',
+            'xsl' => 'text/xml',
+            'h++' => 'text/x-c++hdr',
+            'hpp' => 'text/x-c++hdr',
+            'hxx' => 'text/x-c++hdr',
+            'hh' => 'text/x-c++hdr',
+            'c++' => 'text/x-c++src',
+            'cpp' => 'text/x-c++src',
+            'cxx' => 'text/x-c++src',
+            'cc' => 'text/x-c++src',
+            'h' => 'text/x-chdr',
+            'c' => 'text/x-csrc',
+            'java' => 'text/x-java',
+            'moc' => 'text/x-moc',
+            'p' => 'text/x-pascal',
+            'pas' => 'text/x-pascal',
+            '***' => 'text/x-pcs-***',
+            'shtml' => 'text/x-server-parsed-html',
+            'etx' => 'text/x-setext',
+            'tk' => 'text/x-tcl',
+            'ltx' => 'text/x-tex',
+            'sty' => 'text/x-tex',
+            'cls' => 'text/x-tex',
+            'vcs' => 'text/x-vcalendar',
+            'vcf' => 'text/x-vcard',
+            'dl' => 'video/dl',
+            'fli' => 'video/fli',
+            'gl' => 'video/gl',
+            'mpeg' => 'video/mpeg',
+            'mpg' => 'video/mpeg',
+            'mpe' => 'video/mpeg',
+            'qt' => 'video/quicktime',
+            'mov' => 'video/quicktime',
+            'mxu' => 'video/vnd.mpegurl',
+            'dif' => 'video/x-dv',
+            'dv' => 'video/x-dv',
+            'lsf' => 'video/x-la-asf',
+            'lsx' => 'video/x-la-asf',
+            'mng' => 'video/x-mng',
+            'asf' => 'video/x-ms-asf',
+            'asx' => 'video/x-ms-asf',
+            'wm' => 'video/x-ms-wm',
+            'wmv' => 'video/x-ms-wmv',
+            'wmx' => 'video/x-ms-wmx',
+            'wvx' => 'video/x-ms-wvx',
+            'avi' => 'video/x-msvideo',
+            'movie' => 'video/x-sgi-movie',
+            'ice' => 'x-conference/x-cooltalk',
+            'vrm' => 'x-world/x-vrml',
+            'rar' => 'application/x-rar-compressed',
+            'cab' => 'application/vnd.ms-cab-compressed'
+        );
+
+        $part = explode('.', $fileName);
+        $size = count($part);
+
+        if ($size > 1) {
+            $ext = $part[$size - 1];
+            if (isset($mimeTypes[$ext])) {
+                return $mimeTypes[$ext];
+            }
+        }
+
+        return 'application/octet-stream';
+    }
+
+    /**
+     * 图片链接软修改
+     *
+     * @access public
+     * @param $content
+     * @param $class
+     * @return $content
+     */
+    public static function replace($text, $widget, $lastResult) {
+        $text = empty($lastResult) ? $text : $lastResult;
+        $options = Typecho_Widget::widget('Widget_Options');
+        $settings = $options->plugin('UpyunFile');
+        if ($settings->convert == 1) {
+            if (($widget instanceof Widget_Archive) || ($widget instanceof Widget_Abstract_Comments)) {
+                preg_match_all('/<img[^>]*src=[\'"]?([^>\'"\s]*)[\'"]?[^>]*>/i', $text, $matches);
+                if ($matches) {
+                    foreach ($matches[1] as $val) {
+                        if (strpos($val, rtrim($options->siteUrl, '/')) !== false) {
+                            if ($settings->mode === 'typecho') {
+                                $text = str_replace(rtrim($options->siteUrl, '/') . '/usr/uploads', $settings->upyundomain . self::getUploadDir(), $text);
+                            } else {
+                                $text = str_replace(rtrim($options->siteUrl, '/') . '/usr/uploads', $settings->upyundomain, $text);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return $text;
+    }
+
+    public static function Widget_Archive_beforeRender() {
+        ob_start('UpyunFile_Plugin::beforeRender');
+    }
+
+    public static function addParameter($url, $key, $val) {
+        $query = parse_url($url, PHP_URL_QUERY);
+        if (!empty($query)) {
+            if (strpos($query, $key) === false) {
+                $url .= "&$key=$val";
+            } else {
+                $url = substr($url, 0, -1 * strlen($query));
+                $url .= preg_replace('/(.+?)=([^&?]*)/', "$key=$val", $query);
+            }
+        } else {
+            $url .= "?$key=$val";
+        }
+        return $url;
+    }
+
+    public static function getEtime($timeout) {
+        static $isFirst = true;
+        if (isset($_COOKIE["upyun_token_etime"]) && $_COOKIE["upyun_token_etime"] - time() > 120) {
+            return $etime = $_COOKIE["upyun_token_etime"];
+        }
+        $etime = time() + $timeout;
+        if ($isFirst) {
+            setcookie("upyun_token_etime", $etime);
+            $isFirst = false;
+        }
+        return $etime;
+    }
+
+    public static function beforeRender($text) {
+        $settings = Typecho_Widget::widget('Widget_Options')->plugin('UpyunFile');
+        if ($settings->addToken == 1) {
+            return preg_replace_callback(
+                '/https?:\/\/[-A-Za-z0-9+&@#\/\%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/\%=~_|]/i',
+                function ($matches) use ($settings) {
+                    $etime = self::getEtime($settings->etime);
+                    $url = $matches[0];
+                    if (strpos($url, $settings->upyundomain) !== false) {
+                        $sign = substr(md5($settings->secret . '&' . $etime . '&' . parse_url($url, PHP_URL_PATH)), 12, 8) . $etime;
+                        $url = self::addParameter($url, '_upt', $sign);
+                    }
+                    return $url;
+                },
+                $text
+            );
+        }
+        return $text;
+    }
+}

+ 44 - 0
usr/plugins/UpyunFile/Upyun/src/Upyun/Api/Form.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace Upyun\Api;
+
+use Upyun\Signature;
+use Upyun\Util;
+use GuzzleHttp\Client;
+
+class Form extends Rest
+{
+    public function upload($path, $stream, $params)
+    {
+        $params['save-key'] = $path;
+        $params['service'] = $this->config->serviceName;
+        if (!isset($params['expiration'])) {
+            $params['expiration'] = time() + 30 * 60 * 60; // 30 分钟
+        }
+
+        $policy = Util::base64Json($params);
+        $method = 'POST';
+        $signature = Signature::getBodySignature($this->config, $method, '/' . $params['service'], null, $policy);
+        $client = new Client([
+            'timeout' => $this->config->timeout,
+        ]);
+
+        $response = $client->request($method, $this->endpoint, array(
+            'multipart' => array(
+                array(
+                    'name' => 'policy',
+                    'contents' => $policy,
+                ),
+                array(
+                    'name' => 'authorization',
+                    'contents' => $signature,
+                ),
+                array(
+                    'name' => 'file',
+                    'contents' => $stream,
+                )
+            )
+        ));
+        return $response->getStatusCode() === 200;
+    }
+}

+ 84 - 0
usr/plugins/UpyunFile/Upyun/src/Upyun/Api/Pretreat.php

@@ -0,0 +1,84 @@
+<?php
+namespace Upyun\Api;
+
+use GuzzleHttp\Client;
+use Upyun\Config;
+use Upyun\Signature;
+use Upyun\Util;
+
+class Pretreat
+{
+
+    /**
+     * @var Config
+     */
+    protected $config;
+
+    public function __construct(Config $config)
+    {
+        if (!$config->processNotifyUrl) {
+            throw new \Exception("should config prosessNotifyUrl first.");
+        }
+        $this->config = $config;
+    }
+
+    public function process($tasks, $optionalParams = array())
+    {
+        $encodedTasks = Util::base64Json($tasks);
+
+        $client = new Client([
+            'timeout' => $this->config->timeout,
+        ]);
+
+        $params = array(
+            'service' => $this->config->serviceName,
+            'notify_url' => $this->config->processNotifyUrl,
+            'tasks' => $encodedTasks,
+        );
+
+        $params = array_merge($params, $optionalParams);
+
+        $path = '/pretreatment/';
+        $method = 'POST';
+        $signedHeaders = Signature::getHeaderSign($this->config, $method, $path);
+
+        $url = $this->config->getPretreatEndPoint() . $path;
+        $response = $client->request($method, $url, [
+            'headers' => $signedHeaders,
+            'form_params' => $params
+        ]);
+
+        $body = $response->getBody()->getContents();
+        return json_decode($body, true);
+    }
+
+
+    public function query($taskIds, $path)
+    {
+        $client = new Client([
+            'timeout' => $this->config->timeout,
+        ]);
+
+        $params = array(
+            'service' => $this->config->serviceName,
+            'task_ids' => implode(',', $taskIds)
+        );
+        $path = $path . '?' . http_build_query($params);
+
+        $method = 'GET';
+        $url = $this->config->getPretreatEndPoint() . $path;
+        $signedHeaders = Signature::getHeaderSign($this->config, $method, $path);
+        $response = $client->request($method, $url, [
+            'headers' => $signedHeaders
+        ]);
+
+        if ($response->getStatusCode() === 200) {
+            $body = $response->getBody()->getContents();
+            $result = json_decode($body, true);
+            if (is_array($result)) {
+                return $result['tasks'];
+            }
+        }
+        return false;
+    }
+}

+ 108 - 0
usr/plugins/UpyunFile/Upyun/src/Upyun/Api/Rest.php

@@ -0,0 +1,108 @@
+<?php
+
+namespace Upyun\Api;
+
+use GuzzleHttp\Client;
+use GuzzleHttp\Psr7;
+use Upyun\Config;
+use Upyun\Signature;
+use Upyun\Util;
+
+class Rest
+{
+    /**
+     * @var Config
+     */
+    protected $config;
+
+    protected $endpoint;
+    protected $method;
+    protected $storagePath;
+    public $headers = [];
+
+    /**
+     * @var Psr7\Stream
+     */
+    protected $file;
+
+
+    public function __construct(Config $config)
+    {
+        $this->config   = $config;
+        $this->endpoint = $config->getProtocol() . Config::$restApiEndPoint . '/' . $config->serviceName;
+    }
+
+    public function request($method, $storagePath)
+    {
+        $this->method = strtoupper($method);
+        $this->storagePath = '/' . ltrim($storagePath, '/');
+        return $this;
+    }
+
+
+    /**
+     * @param string|resource $file
+     *
+     * @return $this
+     */
+    public function withFile($file)
+    {
+        $stream = Psr7\stream_for($file);
+        $this->file = $stream;
+
+        return $this;
+    }
+
+    /**
+     * @return mixed|\Psr\Http\Message\ResponseInterface
+     */
+    public function send()
+    {
+        $client = new Client([
+            'timeout' => $this->config->timeout,
+        ]);
+
+        $url = $this->endpoint . $this->storagePath;
+        $body = null;
+        if ($this->file && $this->method === 'PUT') {
+            $body = $this->file;
+        }
+
+        $request = new Psr7\Request(
+            $this->method,
+            Util::encodeURI($url),
+            $this->headers,
+            $body
+        );
+        $authHeader = Signature::getHeaderSign($this->config,
+            $this->method,
+            $request->getUri()->getPath()
+        );
+        foreach ($authHeader as $head => $value) {
+            $request = $request->withHeader($head, $value);
+        }
+        $response = $client->send($request, [
+            'debug' => $this->config->debug
+        ]);
+
+        return $response;
+    }
+
+    public function withHeader($header, $value)
+    {
+        $header = strtolower(trim($header));
+
+        $this->headers[$header] = $value;
+        return $this;
+    }
+
+    public function withHeaders($headers)
+    {
+        if (is_array($headers)) {
+            foreach ($headers as $header => $value) {
+                $this->withHeader($header, $value);
+            }
+        }
+        return $this;
+    }
+}

+ 42 - 0
usr/plugins/UpyunFile/Upyun/src/Upyun/Api/SyncVideo.php

@@ -0,0 +1,42 @@
+<?php
+/**
+ * 同步视频处理
+ */
+
+namespace Upyun\Api;
+
+use GuzzleHttp\Client;
+use Upyun\Config;
+use Upyun\Signature;
+
+
+class SyncVideo {
+    /**
+     * @var Config
+     */
+    protected $config;
+
+    public function __construct(Config $config)
+    {
+        $this->config = $config;
+    }
+
+    public function process($params, $path) {
+        $client = new Client([
+            'timeout' => $this->config->timeout,
+        ]);
+
+        $path = '/' . $this->config->serviceName . $path;
+        $method = 'POST';
+        $signedHeaders = Signature::getHeaderSign($this->config, $method, $path);
+
+        $url = $this->config->getSyncVideoEndPoint() . $path;
+        $response = $client->request($method, $url, [
+            'headers' => $signedHeaders,
+            'json' => $params
+        ]);
+
+        $body = $response->getBody()->getContents();
+        return json_decode($body, true);
+    }
+}

+ 147 - 0
usr/plugins/UpyunFile/Upyun/src/Upyun/Config.php

@@ -0,0 +1,147 @@
+<?php
+namespace Upyun;
+
+/**
+ * Class Config
+ *
+ * @package Upyun
+ */
+class Config
+{
+    /**
+     * @var string 服务名称,将会被弃用
+     */
+    public $bucketName;
+
+    /**
+     * @var string 服务名称
+     */
+    public $serviceName;
+    /**
+     * @var string 操作员名
+     */
+    public $operatorName;
+    /**
+     * @var string 操作员密码 md5 hash 值
+     */
+    public $operatorPassword;
+
+    /**
+     * @var bool 是否使用 https
+     */
+    public $useSsl;
+
+    /**
+     * @var string 上传使用的接口类型,可以设置为 `REST`:使用 rest api 上传,`AUTO` 根据文件大小自动判断,`BLOCK` 使用断点续传
+     * 当上传小文件时,不推荐使用断点续传;上传时如果设置了异步预处理`withAsyncProcess=true`,将会使用表单 api 上传
+     */
+    public $uploadType = 'AUTO';
+
+    /**
+     * @var int 上传的接口类型设置为 `AUTO` 时,文件大小的边界值:小于该值时,使用 rest api,否则使用断点续传。 默认 30M
+     */
+    public $sizeBoundary = 31457280;
+
+    /**
+     * @var int request timeout seconds
+     */
+    public $timeout = 60;
+
+
+    /**
+     * @var string 异步云处理的回调通知地址
+     */
+    public $processNotifyUrl;
+
+    /**
+     * @var boolean curl debug
+     */
+    public $debug = false;
+
+    private $version = '3.0.0';
+
+
+
+    /**
+     * @var string 表单 api 的秘钥
+     */
+    private $formApiKey;
+
+    /**
+     * @var string rest api 和 form api 的接口地址
+     */
+    public static $restApiEndPoint;
+
+
+    /**
+     * rest api 和 form api 接口请求地址,详见:http://docs.upyun.com/api/rest_api/
+     */
+    const ED_AUTO            = 'v0.api.upyun.com';
+    const ED_TELECOM         = 'v1.api.upyun.com';
+    const ED_CNC             = 'v2.api.upyun.com';
+    const ED_CTT             = 'v3.api.upyun.com';
+
+    /**
+     * 异步云处理接口地址
+     */
+    const ED_VIDEO           = 'p0.api.upyun.com';
+
+    /**
+     * 刷新接口地址
+     */
+    const ED_PURGE           = 'http://purge.upyun.com/purge/';
+
+    /**
+     * 同步视频处理接口地址
+     */
+    const ED_SYNC_VIDEO           = 'p1.api.upyun.com';
+
+    public function __construct($serviceName, $operatorName, $operatorPassword)
+    {
+        $this->serviceName = $serviceName;
+        $this->bucketName = $serviceName;
+        $this->operatorName = $operatorName;
+        $this->setOperatorPassword($operatorPassword);
+        $this->useSsl          = false;
+        self::$restApiEndPoint = self::ED_AUTO;
+    }
+
+    public function setOperatorPassword($operatorPassword)
+    {
+        $this->operatorPassword = md5($operatorPassword);
+    }
+
+    public function getFormApiKey()
+    {
+        if (! $this->formApiKey) {
+            throw new \Exception('form api key is empty.');
+        }
+
+        return $this->formApiKey;
+    }
+
+    public function setFormApiKey($key)
+    {
+        $this->formApiKey = $key;
+    }
+
+    public function getVersion()
+    {
+        return $this->version;
+    }
+
+    public function getPretreatEndPoint()
+    {
+        return $this->getProtocol() . self::ED_VIDEO;
+    }
+
+    public function getSyncVideoEndPoint()
+    {
+        return $this->getProtocol() . self::ED_SYNC_VIDEO;
+    }
+
+    public function getProtocol()
+    {
+        return $this->useSsl ? 'https://' : 'http://';
+    }
+}

+ 98 - 0
usr/plugins/UpyunFile/Upyun/src/Upyun/Signature.php

@@ -0,0 +1,98 @@
+<?php
+namespace Upyun;
+
+/**
+ * Class Signature
+ * @package Upyun
+ */
+class Signature
+{
+    /**
+     * 获取分块上传接口的签名
+     */
+    const SIGN_MULTIPART     = 1;
+    /**
+     * 生成视频处理接口的签名
+     */
+    const SIGN_VIDEO         = 2;
+    /**
+     * 生成视频处理接口的签名(不需要操作员时使用)
+     */
+    const SIGN_VIDEO_NO_OPERATOR   = 3;
+
+    /**
+     * 获取 Header 签名需要的请求头
+     *
+     * @param Config $serviceConfig
+     * @param $method 请求方法
+     * @param $path  请求路径
+     * @param $contentMd5 文件内容 md5
+     *
+     * @return array
+     */
+    public static function getHeaderSign($serviceConfig, $method, $path, $contentMd5 = null)
+    {
+        $gmtDate = gmdate('D, d M Y H:i:s \G\M\T');
+
+        $policy = null;
+        $sign = self::getBodySignature($serviceConfig, $method, $path, $gmtDate, $policy, $contentMd5);
+
+        $headers = array(
+            'Authorization' => $sign,
+            'Date' => $gmtDate,
+            'User-agent' => 'Php-Sdk/' . $serviceConfig->getVersion()
+        );
+        return $headers;
+    }
+
+    /**
+     * 获取请求缓存刷新接口需要的签名头
+     *
+     * @param Config $serviceConfig
+     * @param $urlString
+     *
+     * @return array
+     */
+    public static function getPurgeSignHeader(Config $serviceConfig, $urlString)
+    {
+        $gmtDate = gmdate('D, d M Y H:i:s \G\M\T');
+        $sign = md5("$urlString&{$serviceConfig->serviceName}&$gmtDate&{$serviceConfig->operatorPassword}");
+        return array(
+            'Authorization' => "UpYun {$serviceConfig->serviceName}:{$serviceConfig->operatorName}:$sign",
+            'Date' => $gmtDate,
+            'User-agent' => 'Php-Sdk/' . $serviceConfig->getVersion() . ' (purge api)'
+        );
+    }
+
+    /**
+     * 获取表单 API 需要的签名,依据 body 签名规则计算
+     * @param Config $serviceConfig
+     * @param $method 请求方法
+     * @param $uri 请求路径
+     * @param $date 请求时间
+     * @param $policy
+     * @param $contentMd5 请求 body 的 md5
+     *
+     * @return array
+     */
+    public static function getBodySignature(Config $serviceConfig, $method, $uri, $date = null, $policy = null, $contentMd5 = null)
+    {
+        $data = array(
+            $method,
+            $uri
+        );
+        if ($date) {
+            $data[] = $date;
+        }
+
+        if ($policy) {
+            $data[] = $policy;
+        }
+
+        if ($contentMd5) {
+            $data[] = $contentMd5;
+        }
+        $signature = base64_encode(hash_hmac('sha1', implode('&', $data), $serviceConfig->operatorPassword, true));
+        return 'UPYUN ' . $serviceConfig->operatorName . ':' . $signature;
+    }
+}

+ 120 - 0
usr/plugins/UpyunFile/Upyun/src/Upyun/Uploader.php

@@ -0,0 +1,120 @@
+<?php
+namespace Upyun;
+
+use Upyun\Api\Rest;
+use Upyun\Api\Form;
+use GuzzleHttp\Psr7;
+
+class Uploader
+{
+    /**
+     * @var Config
+     */
+    protected $config;
+
+    protected $useBlock = false;
+
+
+    public function __construct(Config $config)
+    {
+        $this->config = $config;
+    }
+
+    public function upload($path, $file, $params, $withAsyncProcess)
+    {
+        $stream = Psr7\stream_for($file);
+        $size = $stream->getSize();
+        $useBlock = $this->needUseBlock($size);
+
+        if ($withAsyncProcess) {
+            $req = new Form($this->config);
+            return $req->upload($path, $stream, $params);
+        }
+
+        if (! $useBlock) {
+            $req = new Rest($this->config);
+            return $req->request('PUT', $path)
+                       ->withHeaders($params)
+                       ->withFile($stream)
+                       ->send();
+        } else {
+            return $this->pointUpload($path, $stream, $params);
+        }
+    }
+
+    /**
+     *  断点续传
+     * @param $path
+     * @param $stream
+     * @param $params
+     *
+     * @return mixed|\Psr\Http\Message\ResponseInterface
+     * @throws \Exception
+     */
+    private function pointUpload($path, $stream, $params)
+    {
+        $req = new Rest($this->config);
+        $headers = array();
+        if (is_array($params)) {
+            foreach ($params as $key => $val) {
+                $headers['X-Upyun-Meta-' . $key] = $val;
+            }
+        }
+        $res = $req->request('PUT', $path)
+            ->withHeaders(array_merge(array(
+                'X-Upyun-Multi-Stage' => 'initiate',
+                'X-Upyun-Multi-Type' => Psr7\mimetype_from_filename($path),
+                'X-Upyun-Multi-Length' => $stream->getSize(),
+            ), $headers))
+            ->send();
+        if ($res->getStatusCode() !== 204) {
+            throw new \Exception('init request failed when poinit upload!');
+        }
+
+        $init      = Util::getHeaderParams($res->getHeaders());
+        $uuid      = $init['x-upyun-multi-uuid'];
+        $blockSize = 1024 * 1024;
+        $partId    = 0;
+        do {
+            $fileBlock = $stream->read($blockSize);
+            $res = $req->request('PUT', $path)
+                ->withHeaders(array(
+                    'X-Upyun-Multi-Stage' => 'upload',
+                    'X-Upyun-Multi-Uuid' => $uuid,
+                    'X-Upyun-Part-Id' => $partId
+                ))
+                ->withFile(Psr7\stream_for($fileBlock))
+                ->send();
+
+            if ($res->getStatusCode() !== 204) {
+                throw new \Exception('upload request failed when poinit upload!');
+            }
+            $data   = Util::getHeaderParams($res->getHeaders());
+            $partId = $data['x-upyun-next-part-id'];
+        } while ($partId != -1);
+
+        $res = $req->request('PUT', $path)
+            ->withHeaders(array(
+                'X-Upyun-Multi-Uuid' => $uuid,
+                'X-Upyun-Multi-Stage' => 'complete'
+            ))
+            ->send();
+
+        if ($res->getStatusCode() != 204 && $res->getStatusCode() != 201) {
+            throw new \Exception('end request failed when poinit upload!');
+        }
+        return $res;
+    }
+
+    private function needUseBlock($fileSize)
+    {
+        if ($this->config->uploadType === 'BLOCK') {
+            return true;
+        } elseif ($this->config->uploadType === 'AUTO' &&
+                  $fileSize >= $this->config->sizeBoundary) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}

+ 538 - 0
usr/plugins/UpyunFile/Upyun/src/Upyun/Upyun.php

@@ -0,0 +1,538 @@
+<?php
+/**
+ * 又拍云 PHP-SDK
+ */
+namespace Upyun;
+
+use Upyun\Api\Rest;
+
+use GuzzleHttp\Client;
+use GuzzleHttp\Psr7;
+use GuzzleHttp;
+
+/**
+ * Class Upyun
+ *
+ * 又拍云云存储、云处理接口
+ *
+ * Upyun 类实现了又拍云云存储和云处理的所有接口,通过该类可以实现文件上传、下载;图片视频等多媒体资源云处理。
+ * 本文档中,提到的"服务"是指又拍云文件加速回又拍云源类型的服务(即原先的存储类空间)。
+ *
+ * @package Upyun
+ */
+class Upyun
+{
+
+    /**
+     * @var Config: 服务配置
+     */
+    protected $config;
+
+    // 异步云处理任务类型
+    /**
+     * @var string 异步音视频处理
+     */
+    public static $PROCESS_TYPE_MEDIA = 'media';
+    /**
+     * @var string 文件压缩
+     */
+    public static $PROCESS_TYPE_ZIP = 'zip-file';
+    /**
+     * @var string 解压缩
+     */
+    public static $PROCESS_TYPE_UNZIP = 'unzip-file';
+    /**
+     * @var string 文件拉取
+     */
+    public static $PROCESS_TYPE_SYNC_FILE = 'sync-remote-file-to-upyun';
+    /**
+     * @var string 文档转换
+     */
+    public static $PROCESS_TYPE_CONVERT = 'document-type-convert';
+    /**
+     * @var string 异步图片拼接
+     */
+    public static $PROCESS_TYPE_STITCH = 'picture-stitch';
+
+    /**
+     * Upyun constructor.
+     *
+     * @param Config $config 服务配置
+     */
+    public function __construct(Config $config)
+    {
+        $this->setConfig($config);
+    }
+
+    /**
+     * 配置服务信息
+     *
+     * 当需要操作的新的服务时,使用该方法传入新的服务配置即可
+     *
+     * @param Config $config 服务配置
+     *
+     * @return $this
+     */
+    public function setConfig(Config $config)
+    {
+        $this->config = $config;
+        return $this;
+    }
+
+    /**
+     * 上传一个文件到又拍云存储
+     *
+     * 上传的文件格式支持文件流或者字符串方式上传。除简单的文件上传外,针对多媒体资源(图片、音视频),还可以设置同步/异步预处理多媒体资源,例如:图片的裁剪缩放,音视频的转码截图等等众多又拍云强大的云处理功能
+     *
+     * @param string $path 被上传的文件在又拍云存储服务中保存的路径
+     * @param string|resource $content 被上传的文件内容(字符串),或者打开该文件获得的文件句柄(文件流)。当上传本地大文件时,推荐使用文件流的方式上传
+     * @param array $params 上传文件时,附加的自定义参数。支持 Content-MD5 Content-Type Content-Secret 等,详见 [上传参数](http://docs.upyun.com/api/rest_api/#_2),例如:
+     * - 设置文件[保护秘钥](http://docs.upyun.com/api/rest_api/#Content-Secret) `write($path, $content, array('Content-Secret' => 'my-secret'))`;
+     * - 添加[文件元信息](http://docs.upyun.com/api/rest_api/#metadata) `write($path, $content, array('X-Upyun-Meta-Foo' =>
+     * 'bar'))`
+     * - [图片同步预处理](http://docs.upyun.com/cloud/image/#_5) `write($path, $content, array('x-gmkerl-thumb' => '/format/png'))`
+     * @param bool $withAsyncProcess  默认为 `false`,当上传图片或者音视频资源时,可以设置该参数为 `true`,开启图片音视频的[异步处理功能](http://docs.upyun.com/api/form_api/#_6) ,例如:
+     *```
+     * // 以下参数会将新上传的图片,再异步生成另一份 png 格式的图片,原图不受影响
+     * write($path, $content, array(
+     *    'apps' => array(
+     *        array(
+     *            'name' => 'thumb',         //异步图片处理任务
+     *            'x-gmkerl-thumb' => '/format/png', // 格式化图片为 png 格式
+     *            'save_as': '/iamge/png/new.png',   // 处理成功后的图片保存路径
+     *            'notify_url': 'http://your.notify.url'  // 异步任务完成后的回调地址
+     *        )
+     *    )
+     * ), true);
+     *```
+     *
+     *
+     *
+     * @return array|bool 若文件是图片则返回图片基本信息,如:`array('x-upyun-width' => 123, 'x-upyun-height' => 50, 'x-upyun-frames'
+     * => 1, 'x-upyun-file-type' => 'JPEG')`,否则返回空数组。当使用异步预处理功能时,返回结果为布尔值,成功为 `true`。
+     *
+     * @throws \Exception 上传失败时,抛出异常
+     */
+    public function write($path, $content, $params = array(), $withAsyncProcess = false)
+    {
+        if (!$content) {
+            throw new \Exception('write content can not be empty.');
+        }
+
+        $upload = new Uploader($this->config);
+        $response = $upload->upload($path, $content, $params, $withAsyncProcess);
+        if ($withAsyncProcess) {
+            return $response;
+        }
+        return Util::getHeaderParams($response->getHeaders());
+    }
+
+    /**
+     * 读取云存储文件/目录内容
+     *
+     * @param string $path 又拍云存储中的文件或者目录路径
+     * @param resource $saveHandler 文件内容写入本地文件流。例如 `$saveHandler = fopen('/local/file', 'w')
+     * `。当设置该参数时,将以文件流的方式,直接将又拍云中的文件写入本地的文件流,或其他可以写入的流
+     * @param array $params  可选参数,读取目录内容时,需要设置三个参数: `X-List-Iter` 分页开始位置(第一页不需要设置),`X-List-Limit` 获取的文件数量(默认 100,最大
+     * 10000),`X-List-Order` 结果以时间正序或者倒序
+     *
+     * @return mixed $return 当读取文件且没有设置 `$saveHandler` 参数时,返回一个字符串类型,表示文件内容;设置了 `$saveHandler` 参数时,返回布尔值
+     * `true`。当读取目录时,返回一个数组,表示目录下的文件列表。目录下文件内容过多时,需要通过判断返回数组中的 `is_end` 属性,进行分页读取内容
+     *
+     * @throws \Exception
+     */
+    public function read($path, $saveHandler = null, $params = array())
+    {
+        $req = new Rest($this->config);
+        $response = $req->request('GET', $path)
+            ->withHeaders($params)
+            ->send();
+
+
+        $params = Util::getHeaderParams($response->getHeaders());
+
+
+        if (! isset($params['x-upyun-list-iter'])) {
+            if (is_resource($saveHandler)) {
+                Psr7\copy_to_stream($response->getBody(), Psr7\stream_for($saveHandler));
+                return true;
+            } else {
+                return $response->getBody()->getContents();
+            }
+        } else {
+            $files = Util::parseDir($response->getBody()->getContents());
+            return array('files' => $files, 'is_end' => $params['x-upyun-list-iter'] === 'g2gCZAAEbmV4dGQAA2VvZg', 'iter' => $params['x-upyun-list-iter']);
+        }
+    }
+
+    /**
+     * 判断文件是否存在于又拍云存储
+     *
+     * 注意: 对刚删除的文件, 立即调用该方法可能会返回 true, 因为服务端执行删除操作后可能会有很短暂的延迟.
+     *
+     * @param string $path 云存储的文件路径
+     *
+     * @return bool 存在时返回 `true`,否则返回 `false`
+     * @throws \Exception
+     */
+    public function has($path)
+    {
+        $req = new Rest($this->config);
+        try {
+            $req->request('HEAD', $path)
+                            ->send();
+        } catch (GuzzleHttp\Exception\BadResponseException $e) {
+            $statusCode = $e->getResponse()->getStatusCode();
+            if ($statusCode === 404) {
+                return false;
+            } else {
+                throw $e;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * 获取云存储文件/目录的基本信息
+     *
+     * @param string $path 云存储的文件路径
+     * @param array $otherHeaders 设置了后,方法将返回其他 http header 中的信息,默认为空
+     *
+     * @return array 返回一个数组,默认包含以下 key
+     * - `x-upyun-file-type` 当 $path 是目录时,值为 *folder*,当 $path 是文件时,值为 *file*,
+     * - `x-upyun-file-size` 文件大小
+     * - `x-upyun-file-date` 文件的创建时间
+     */
+    public function info($path, $otherHeaders = array())
+    {
+        $req = new Rest($this->config);
+        $response = $req->request('HEAD', $path)
+                        ->send();
+        return Util::getHeaderParams($response->getHeaders(), $otherHeaders);
+    }
+
+    /**
+     * 获取文件的文档类型
+     * @param string $path 云存储文件路径
+     * @return string 文档类型,e.g: `appcation/json`,获取失败返回空字符串
+     */
+    public function getMimetype($path)
+    {
+        $params = $this->info($path, array('content-type'));
+        if (isset($params['content-type'])) {
+            return explode(';', $params['content-type'])[0];
+        }
+        return '';
+    }
+
+    /**
+     * 删除文件或者目录
+     *
+     * @param string $path 文件或目录在又拍云存储的路径
+     * @param bool $async 是否异步删除,默认为 false,表示同步删除。当需要批量删除大量文件时,必须选择异步删除
+     *
+     * @return bool 删除成功返回 true,否则 false
+     * @throws \Exception 删除不存在的文件将会抛出异常
+     */
+    public function delete($path, $async = false)
+    {
+        $req = new Rest($this->config);
+        $req->request('DELETE', $path);
+        if ($async) {
+            $req->withHeader('x-upyun-async', 'true');
+        }
+        $res = $req->send();
+        return $res->getStatusCode() === 200;
+    }
+
+    /**
+     * 创建目录
+     *
+     * @param string $path 需要在又拍云存储创建的目录路径
+     *
+     * @return bool 创建成功返回 true,否则返回 false
+     * @throws \Exception
+     */
+    public function createDir($path)
+    {
+        $path = rtrim($path, '/') . '/';
+        $req = new Rest($this->config);
+        $res = $req->request('POST', $path)
+            ->withHeader('folder', 'true')
+            ->send();
+        return $res->getStatusCode() === 200;
+    }
+
+    /**
+     * 删除文件或者目录
+     *
+     * @param string $path 需要被删除的云存储文件或目录路径
+     *
+     * @return bool 成功返回 true,否则 false
+     * @throws \Exception
+     */
+    public function deleteDir($path)
+    {
+        return $this->delete($path);
+    }
+
+    /**
+     * 获取目录下存储使用量
+     *
+     * @param string $path 云存储目录路径,默认为根目录,表示整个云存储服务使用的空间大小
+     * @return string 存储使用量,单位字节
+     * @throws \Exception
+     */
+    public function usage($path = '/')
+    {
+        $path = rtrim($path, '/') . '/';
+        $req = new Rest($this->config);
+        $response = $req->request('GET', $path . '?usage')
+            ->send();
+
+        return $response->getBody()->getContents();
+    }
+
+    /**
+     * 刷新缓存
+     *
+     * @param array|string $urls 需要刷新的文件 url 列表
+     *
+     * @return array 刷新失败的 url 列表,若全部刷新成功则为空数组
+     */
+    public function purge($urls)
+    {
+        $urlString = $urls;
+        if (is_array($urls)) {
+            $urlString = implode("\n", $urls);
+        }
+
+        $client = new Client([
+            'timeout' => $this->config->timeout
+        ]);
+        $response = $client->request('POST', Config::ED_PURGE, [
+            'headers' =>  Signature::getPurgeSignHeader($this->config, $urlString),
+            'form_params' => ['purge' => $urlString]
+        ]);
+        $result = json_decode($response->getBody()->getContents(), true);
+        return $result['invalid_domain_of_url'];
+    }
+
+    /**
+     * 异步云处理
+     *
+     * 该方法是基于[又拍云云处理](http://docs.upyun.com/cloud/) 服务实现,可以实现音视频的转码、切片、剪辑;文件的压缩解压缩;文件拉取功能
+     *
+     * 注意:
+     * - 所有需要调用该方法处理的资源,必须已经上传到云存储服务
+     * - 使用 `process` 之前,必须配置 `config->processNotifyUrl`,否则会提交任务失败
+     *
+     * 例如视频转码:
+     * ```
+     *  process(array(
+     *    array(
+     *        'type' => 'video',  // video 表示视频任务, audio 表示音频任务
+     *        'avopts' => '/s/240p(4:3)/as/1/r/30', // 处理参数,`s` 表示输出的分辨率,`r` 表示视频帧率,`as` 表示是否自动调整分辨率
+     *        'save_as' => '/video/240/new.mp4', // 新视频在又拍云存储的保存路径
+     *    ),
+     *    ... // 同时还可以添加其他任务
+     * ), Upyun::$PROCESS_TYPE_MEDIA, $source)
+     * ```
+     *
+     * @param array $tasks 需要处理的任务
+     * @param string $type 异步云处理任务类型,可选值:
+     * - `Upyun::$PROCESS_TYPE_MEDIA` 异步音视频处理
+     * - `Upyun::$PROCESS_TYPE_ZIP` 文件压缩
+     * - `Upyun::$PROCESS_TYPE_UNZIP` 文件解压
+     * - `Upyun::$PROCESS_TYPE_SYNC_FILE` 文件拉取
+     * - `Upyun::$PROCESS_TYPE_STITCH` 图片拼接
+     * @param string $source 可选参数,处理异步音视频任务时,需要传递该参数,表示需要处理的文件路径
+     *
+     * @return array 任务 ID,提交了多少任务,便会返回多少任务 ID,与提交任务的顺序保持一致。可以通过任务 ID 查询处理进度。格式如下:
+     * ```
+     * array(
+     * '35f0148d414a688a275bf915ba7cebb2',
+     * '98adbaa52b2f63d6d7f327a0ff223348',
+     * )
+     * ```
+     * @throws \Exception
+     */
+    public function process($tasks, $type, $source = '')
+    {
+        $video = new Api\Pretreat($this->config);
+
+        $options = array();
+        switch($type) {
+            case self::$PROCESS_TYPE_MEDIA:
+                $options['accept'] = 'json';
+                $options['source'] = $source;
+                break;
+            case self::$PROCESS_TYPE_ZIP:
+                $options['app_name'] = 'compress';
+                break;
+            case self::$PROCESS_TYPE_UNZIP:
+                $options['app_name'] = 'depress';
+                break;
+            case self::$PROCESS_TYPE_SYNC_FILE:
+                $options['app_name'] = 'spiderman';
+                break;
+            case self::$PROCESS_TYPE_SYNC_FILE:
+                $options['app_name'] = 'uconvert';
+                break;
+            case self::$PROCESS_TYPE_STITCH:
+                $options['app_name'] = 'jigsaw';
+                break;
+            default:
+                throw new \Exception('upyun - not support process type.');
+
+        }
+        return $video->process($tasks, $options);
+    }
+
+    /**
+     * 查询异步云处理任务进度
+     *
+     * 根据 `process` 方法返回的任务 ID,通过该访问查询处理进度
+     *
+     * @param array $taskIds 任务 ID
+     *
+     * @return bool|array 查询失败返回布尔值 `false`,否则返回每个任务的百分比进度信息,格式如下:
+     * ```
+     * array(
+     *     '35f0148d414a688a275bf915ba7cebb2' => 100,  // 100 表示任务完成
+     *     'c3103189fa906a5354d29bd807e8dc51' => 35,
+     *     '98adbaa52b2f63d6d7f327a0ff223348' => null, // null 表示任务未开始,或异常
+     * )
+     * ```
+     */
+    public function queryProcessStatus($taskIds)
+    {
+        $video = new Api\Pretreat($this->config);
+        return $video->query($taskIds, '/status/');
+    }
+
+    /**
+     *  查询异步云处理任务结果
+     *
+     * 根据 `process` 方法返回的任务 ID,通过该访问查询处理结果,会包含每个任务详细信息
+     * @param array $taskIds 任务 ID
+     *
+     * @return bool|mixed 查询失败返回 `false`,否则返回每个任务的处理结果,格式如下:
+     * ```
+     * array(
+     *    '9d9c32b63a1034834e77672c6f51f661' => array(
+     *         'path' => array('/v2.mp4'),
+     *         'signature' => '4042c1f07f546d28',
+     *         'status_code' => 200,
+     *         'service_name' => 'your_storage_service',
+     *         'description' => 'OK',
+     *         'task_id' => '9d9c32b63a1034834e77672c6f51f661',
+     *         'timestamp' => 1472010684
+     *    )
+     * )
+     * ```
+     */
+    public function queryProcessResult($taskIds)
+    {
+        $video = new Api\Pretreat($this->config);
+        return $video->query($taskIds, '/result/');
+    }
+
+    /**
+     * 多个 m3u8 文件拼接
+     * @param array $files  保存在又拍云云存储中的多个 m3u8 文件路径
+     * @param string $saveAs 拼接生成的新 m3u8 文件保存路径
+     *
+     * @return array 见 [m3u8 拼接 - 响应](http://docs.upyun.com/cloud/sync_video/#_3)
+     */
+    public function m3u8Concat($files, $saveAs)
+    {
+        $p = new Api\SyncVideo($this->config);
+        return $p->process([
+            'm3u8s' => $files,
+            'save_as' => $saveAs,
+        ], '/m3u8er/concat');
+    }
+
+    /**
+     * 单个 m3u8 文件剪辑
+     * @param string $file 需要剪辑的又拍云云存储中的 m3u8 文件路径
+     * @param string $saveAs 剪辑完成后新的 m3u8 文件保存路径
+     * @param array $slice 需要被保留或删除的片段。
+     * @param bool $$isInclude 默认为 `true` 表示 `$slice` 参数描述的片段被保留,否则表示 `$slice` 参数描述的片段被删除
+     * @param bool $index 指定 `$slice` 参数的格式,默认为 `false` 表示使用时间范围描述片段,单位秒:`[<开始时间>, <结束时间>]`;`true` 表示使用 `m3u8` 文件的分片序号,从 0 开始,这种方式可以一次对多个片段操作
+     *
+     * @return array 见 [m3u8 剪辑 - 响应](http://docs.upyun.com/cloud/sync_video/#_6)
+     */
+    public function m3u8Clip($file, $saveAs, $slice = array(), $isInclude = true, $index = false)
+    {
+        $p = new Api\SyncVideo($this->config);
+        $params = [
+            'm3u8' => $file,
+            'save_as' => $saveAs,
+            'index' => $index,
+        ];
+        if ($$isInclude) {
+            $params['include'] = $slice;
+        } else {
+            $params['exclude'] = $slice;
+        }
+        return $p->process($params, '/m3u8er/clip');
+    }
+
+    /**
+     * 获取单个 m3u8 文件描述信息
+     * @param string $file 又拍云云存储的中的 m3u8 文件路径
+     *
+     * @return array 见 [获取 m3u8 信息 - 响应](http://docs.upyun.com/cloud/sync_video/#_6)
+     */
+    public function m3u8Meta($file)
+    {
+        $p = new Api\SyncVideo($this->config);
+        return $p->process([
+            'm3u8' => $file,
+        ], '/m3u8er/get_meta');
+    }
+
+    /**
+     * 视频截图,可以对 mp4、m3u8 等视频文件进行截图
+     * @param string $file 需要截图的又拍云云存储中的视频文件路径
+     * @param string $saveAs 截图保存路径
+     * @param string $point 截图时间点,`HH:MM:SS` 格式
+     * @param string $size 截图尺寸 `宽x高` 格式的字符串。默认和视频尺寸一致
+     * @param string $format 截图保存的格式,默认根据 `$saveAs` 参数的后缀生成,可以指定 `jpg | png | webp` 三种格式
+     *
+     * @return array 见 [视频截图 - 响应](http://docs.upyun.com/cloud/sync_video/#m3u8_2)
+     */
+    public function snapshot($file, $saveAs, $point, $size = '', $format = '')
+    {
+        $p = new Api\SyncVideo($this->config);
+        $params = [
+            'source' => $file,
+            'save_as' => $saveAs,
+            'point' => $point,
+        ];
+        if ($size) {
+            $params['size'] = $size;
+        }
+        if ($format) {
+            $params['format'] = $format;
+        }
+        return $p->process($params, '/snapshot');
+    }
+
+    /**
+     * 获取音视频文件元信息
+     * @param string $file 又拍云云存储的中的音视频文件路径
+     *
+     * @return array 见 [获取音视频文件信息 - 响应](http://docs.upyun.com/cloud/sync_video/#_16)
+     */
+    public function avMeta($file)
+    {
+        $p = new Api\SyncVideo($this->config);
+        return $p->process([
+            'source' => $file,
+        ], '/avmeta/get_meta');
+    }
+}

+ 90 - 0
usr/plugins/UpyunFile/Upyun/src/Upyun/Util.php

@@ -0,0 +1,90 @@
+<?php
+namespace Upyun;
+
+class Util
+{
+    public static function trim($str)
+    {
+        if (is_array($str)) {
+            return array_map(array('Util', 'trim'), $str);
+        } else {
+            return trim($str);
+        }
+    }
+
+    public static function getHeaderParams($headers, $otherParams = array())
+    {
+        $params = [];
+        $otherParams = array_map('strtolower', $otherParams);
+        foreach ($headers as $header => $value) {
+            $header = strtolower($header);
+            if (strpos($header, 'x-upyun-') !== false) {
+                $params[$header] = $value[0];
+            } else if (in_array($header, $otherParams)) {
+                $params[$header] = $value[0];
+            }
+        }
+        return $params;
+    }
+
+    public static function parseDir($body)
+    {
+        $files = array();
+        if (!$body) {
+            return array();
+        }
+
+        $lines = explode("\n", $body);
+        foreach ($lines as $line) {
+            $file = [];
+            list($file['name'], $file['type'], $file['size'], $file['time']) = explode("\t", $line, 4);
+            $files[] = $file;
+        }
+
+        return $files;
+    }
+
+    public static function base64Json($params)
+    {
+        return base64_encode(json_encode($params, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
+    }
+
+    public static function stringifyHeaders($headers)
+    {
+        $return = array();
+        foreach ($headers as $key => $value) {
+            $return[] = "$key: $value";
+        }
+        return $return;
+    }
+
+    public static function md5Hash($resource)
+    {
+        rewind($resource);
+        $ctx = hash_init('md5');
+        hash_update_stream($ctx, $resource);
+        $md5 = hash_final($ctx);
+        return $md5;
+    }
+
+    /**
+     * GuzzleHttp\Psr\Uri use `parse_url`,not good for utf-8,
+     * So should `encodeURI` first, before `new Psr7\Request`
+     * @see http://stackoverflow.com/questions/4929584/encodeuri-in-php
+     */
+    public static function encodeURI($url)
+    {
+        $unescaped = array(
+            '%2D'=>'-','%5F'=>'_','%2E'=>'.','%21'=>'!', '%7E'=>'~',
+            '%2A'=>'*', '%27'=>"'", '%28'=>'(', '%29'=>')'
+        );
+        $reserved = array(
+            '%3B'=>';','%2C'=>',','%2F'=>'/','%3F'=>'?','%3A'=>':',
+            '%40'=>'@','%26'=>'&','%3D'=>'=','%2B'=>'+','%24'=>'$'
+        );
+        $score = array(
+            '%23'=>'#'
+        );
+        return strtr(rawurlencode($url), array_merge($reserved, $unescaped, $score));
+    }
+}

+ 7 - 0
usr/plugins/UpyunFile/Upyun/vendor/autoload.php

@@ -0,0 +1,7 @@
+<?php
+
+// autoload.php @generated by Composer
+
+require_once __DIR__ . '/composer/autoload_real.php';
+
+return ComposerAutoloaderInita84e991fabf2a525d44852e19efdb0b7::getLoader();

+ 445 - 0
usr/plugins/UpyunFile/Upyun/vendor/composer/ClassLoader.php

@@ -0,0 +1,445 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ *     $loader = new \Composer\Autoload\ClassLoader();
+ *
+ *     // register classes with namespaces
+ *     $loader->add('Symfony\Component', __DIR__.'/component');
+ *     $loader->add('Symfony',           __DIR__.'/framework');
+ *
+ *     // activate the autoloader
+ *     $loader->register();
+ *
+ *     // to enable searching the include path (eg. for PEAR packages)
+ *     $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @see    http://www.php-fig.org/psr/psr-0/
+ * @see    http://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+    // PSR-4
+    private $prefixLengthsPsr4 = array();
+    private $prefixDirsPsr4 = array();
+    private $fallbackDirsPsr4 = array();
+
+    // PSR-0
+    private $prefixesPsr0 = array();
+    private $fallbackDirsPsr0 = array();
+
+    private $useIncludePath = false;
+    private $classMap = array();
+    private $classMapAuthoritative = false;
+    private $missingClasses = array();
+    private $apcuPrefix;
+
+    public function getPrefixes()
+    {
+        if (!empty($this->prefixesPsr0)) {
+            return call_user_func_array('array_merge', $this->prefixesPsr0);
+        }
+
+        return array();
+    }
+
+    public function getPrefixesPsr4()
+    {
+        return $this->prefixDirsPsr4;
+    }
+
+    public function getFallbackDirs()
+    {
+        return $this->fallbackDirsPsr0;
+    }
+
+    public function getFallbackDirsPsr4()
+    {
+        return $this->fallbackDirsPsr4;
+    }
+
+    public function getClassMap()
+    {
+        return $this->classMap;
+    }
+
+    /**
+     * @param array $classMap Class to filename map
+     */
+    public function addClassMap(array $classMap)
+    {
+        if ($this->classMap) {
+            $this->classMap = array_merge($this->classMap, $classMap);
+        } else {
+            $this->classMap = $classMap;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix, either
+     * appending or prepending to the ones previously set for this prefix.
+     *
+     * @param string       $prefix  The prefix
+     * @param array|string $paths   The PSR-0 root directories
+     * @param bool         $prepend Whether to prepend the directories
+     */
+    public function add($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            if ($prepend) {
+                $this->fallbackDirsPsr0 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr0
+                );
+            } else {
+                $this->fallbackDirsPsr0 = array_merge(
+                    $this->fallbackDirsPsr0,
+                    (array) $paths
+                );
+            }
+
+            return;
+        }
+
+        $first = $prefix[0];
+        if (!isset($this->prefixesPsr0[$first][$prefix])) {
+            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+            return;
+        }
+        if ($prepend) {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixesPsr0[$first][$prefix]
+            );
+        } else {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                $this->prefixesPsr0[$first][$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace, either
+     * appending or prepending to the ones previously set for this namespace.
+     *
+     * @param string       $prefix  The prefix/namespace, with trailing '\\'
+     * @param array|string $paths   The PSR-4 base directories
+     * @param bool         $prepend Whether to prepend the directories
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function addPsr4($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            // Register directories for the root namespace.
+            if ($prepend) {
+                $this->fallbackDirsPsr4 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr4
+                );
+            } else {
+                $this->fallbackDirsPsr4 = array_merge(
+                    $this->fallbackDirsPsr4,
+                    (array) $paths
+                );
+            }
+        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+            // Register directories for a new namespace.
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        } elseif ($prepend) {
+            // Prepend directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixDirsPsr4[$prefix]
+            );
+        } else {
+            // Append directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                $this->prefixDirsPsr4[$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix,
+     * replacing any others previously set for this prefix.
+     *
+     * @param string       $prefix The prefix
+     * @param array|string $paths  The PSR-0 base directories
+     */
+    public function set($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr0 = (array) $paths;
+        } else {
+            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace,
+     * replacing any others previously set for this namespace.
+     *
+     * @param string       $prefix The prefix/namespace, with trailing '\\'
+     * @param array|string $paths  The PSR-4 base directories
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function setPsr4($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr4 = (array) $paths;
+        } else {
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Turns on searching the include path for class files.
+     *
+     * @param bool $useIncludePath
+     */
+    public function setUseIncludePath($useIncludePath)
+    {
+        $this->useIncludePath = $useIncludePath;
+    }
+
+    /**
+     * Can be used to check if the autoloader uses the include path to check
+     * for classes.
+     *
+     * @return bool
+     */
+    public function getUseIncludePath()
+    {
+        return $this->useIncludePath;
+    }
+
+    /**
+     * Turns off searching the prefix and fallback directories for classes
+     * that have not been registered with the class map.
+     *
+     * @param bool $classMapAuthoritative
+     */
+    public function setClassMapAuthoritative($classMapAuthoritative)
+    {
+        $this->classMapAuthoritative = $classMapAuthoritative;
+    }
+
+    /**
+     * Should class lookup fail if not found in the current class map?
+     *
+     * @return bool
+     */
+    public function isClassMapAuthoritative()
+    {
+        return $this->classMapAuthoritative;
+    }
+
+    /**
+     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+     *
+     * @param string|null $apcuPrefix
+     */
+    public function setApcuPrefix($apcuPrefix)
+    {
+        $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
+    }
+
+    /**
+     * The APCu prefix in use, or null if APCu caching is not enabled.
+     *
+     * @return string|null
+     */
+    public function getApcuPrefix()
+    {
+        return $this->apcuPrefix;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param bool $prepend Whether to prepend the autoloader or not
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+    }
+
+    /**
+     * Unregisters this instance as an autoloader.
+     */
+    public function unregister()
+    {
+        spl_autoload_unregister(array($this, 'loadClass'));
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param  string    $class The name of the class
+     * @return bool|null True if loaded, null otherwise
+     */
+    public function loadClass($class)
+    {
+        if ($file = $this->findFile($class)) {
+            includeFile($file);
+
+            return true;
+        }
+    }
+
+    /**
+     * Finds the path to the file where the class is defined.
+     *
+     * @param string $class The name of the class
+     *
+     * @return string|false The path if found, false otherwise
+     */
+    public function findFile($class)
+    {
+        // class map lookup
+        if (isset($this->classMap[$class])) {
+            return $this->classMap[$class];
+        }
+        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+            return false;
+        }
+        if (null !== $this->apcuPrefix) {
+            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+            if ($hit) {
+                return $file;
+            }
+        }
+
+        $file = $this->findFileWithExtension($class, '.php');
+
+        // Search for Hack files if we are running on HHVM
+        if (false === $file && defined('HHVM_VERSION')) {
+            $file = $this->findFileWithExtension($class, '.hh');
+        }
+
+        if (null !== $this->apcuPrefix) {
+            apcu_add($this->apcuPrefix.$class, $file);
+        }
+
+        if (false === $file) {
+            // Remember that this class does not exist.
+            $this->missingClasses[$class] = true;
+        }
+
+        return $file;
+    }
+
+    private function findFileWithExtension($class, $ext)
+    {
+        // PSR-4 lookup
+        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+        $first = $class[0];
+        if (isset($this->prefixLengthsPsr4[$first])) {
+            $subPath = $class;
+            while (false !== $lastPos = strrpos($subPath, '\\')) {
+                $subPath = substr($subPath, 0, $lastPos);
+                $search = $subPath.'\\';
+                if (isset($this->prefixDirsPsr4[$search])) {
+                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
+                        $length = $this->prefixLengthsPsr4[$first][$search];
+                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-4 fallback dirs
+        foreach ($this->fallbackDirsPsr4 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 lookup
+        if (false !== $pos = strrpos($class, '\\')) {
+            // namespaced class name
+            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+        } else {
+            // PEAR-like class name
+            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+        }
+
+        if (isset($this->prefixesPsr0[$first])) {
+            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+                if (0 === strpos($class, $prefix)) {
+                    foreach ($dirs as $dir) {
+                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-0 fallback dirs
+        foreach ($this->fallbackDirsPsr0 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 include paths.
+        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+            return $file;
+        }
+
+        return false;
+    }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+    include $file;
+}

+ 21 - 0
usr/plugins/UpyunFile/Upyun/vendor/composer/LICENSE

@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+

+ 95 - 0
usr/plugins/UpyunFile/Upyun/vendor/composer/autoload_classmap.php

@@ -0,0 +1,95 @@
+<?php
+
+// autoload_classmap.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+    'GuzzleHttp\\Client' => $vendorDir . '/guzzlehttp/guzzle/src/Client.php',
+    'GuzzleHttp\\ClientInterface' => $vendorDir . '/guzzlehttp/guzzle/src/ClientInterface.php',
+    'GuzzleHttp\\Cookie\\CookieJar' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/CookieJar.php',
+    'GuzzleHttp\\Cookie\\CookieJarInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php',
+    'GuzzleHttp\\Cookie\\FileCookieJar' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php',
+    'GuzzleHttp\\Cookie\\SessionCookieJar' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php',
+    'GuzzleHttp\\Cookie\\SetCookie' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/SetCookie.php',
+    'GuzzleHttp\\Exception\\BadResponseException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/BadResponseException.php',
+    'GuzzleHttp\\Exception\\ClientException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/ClientException.php',
+    'GuzzleHttp\\Exception\\ConnectException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/ConnectException.php',
+    'GuzzleHttp\\Exception\\GuzzleException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/GuzzleException.php',
+    'GuzzleHttp\\Exception\\RequestException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/RequestException.php',
+    'GuzzleHttp\\Exception\\SeekException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/SeekException.php',
+    'GuzzleHttp\\Exception\\ServerException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/ServerException.php',
+    'GuzzleHttp\\Exception\\TooManyRedirectsException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php',
+    'GuzzleHttp\\Exception\\TransferException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/TransferException.php',
+    'GuzzleHttp\\HandlerStack' => $vendorDir . '/guzzlehttp/guzzle/src/HandlerStack.php',
+    'GuzzleHttp\\Handler\\CurlFactory' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlFactory.php',
+    'GuzzleHttp\\Handler\\CurlFactoryInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php',
+    'GuzzleHttp\\Handler\\CurlHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlHandler.php',
+    'GuzzleHttp\\Handler\\CurlMultiHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php',
+    'GuzzleHttp\\Handler\\EasyHandle' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/EasyHandle.php',
+    'GuzzleHttp\\Handler\\MockHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/MockHandler.php',
+    'GuzzleHttp\\Handler\\Proxy' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/Proxy.php',
+    'GuzzleHttp\\Handler\\StreamHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/StreamHandler.php',
+    'GuzzleHttp\\MessageFormatter' => $vendorDir . '/guzzlehttp/guzzle/src/MessageFormatter.php',
+    'GuzzleHttp\\Middleware' => $vendorDir . '/guzzlehttp/guzzle/src/Middleware.php',
+    'GuzzleHttp\\Pool' => $vendorDir . '/guzzlehttp/guzzle/src/Pool.php',
+    'GuzzleHttp\\PrepareBodyMiddleware' => $vendorDir . '/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php',
+    'GuzzleHttp\\Promise\\AggregateException' => $vendorDir . '/guzzlehttp/promises/src/AggregateException.php',
+    'GuzzleHttp\\Promise\\CancellationException' => $vendorDir . '/guzzlehttp/promises/src/CancellationException.php',
+    'GuzzleHttp\\Promise\\Coroutine' => $vendorDir . '/guzzlehttp/promises/src/Coroutine.php',
+    'GuzzleHttp\\Promise\\EachPromise' => $vendorDir . '/guzzlehttp/promises/src/EachPromise.php',
+    'GuzzleHttp\\Promise\\FulfilledPromise' => $vendorDir . '/guzzlehttp/promises/src/FulfilledPromise.php',
+    'GuzzleHttp\\Promise\\Promise' => $vendorDir . '/guzzlehttp/promises/src/Promise.php',
+    'GuzzleHttp\\Promise\\PromiseInterface' => $vendorDir . '/guzzlehttp/promises/src/PromiseInterface.php',
+    'GuzzleHttp\\Promise\\PromisorInterface' => $vendorDir . '/guzzlehttp/promises/src/PromisorInterface.php',
+    'GuzzleHttp\\Promise\\RejectedPromise' => $vendorDir . '/guzzlehttp/promises/src/RejectedPromise.php',
+    'GuzzleHttp\\Promise\\RejectionException' => $vendorDir . '/guzzlehttp/promises/src/RejectionException.php',
+    'GuzzleHttp\\Promise\\TaskQueue' => $vendorDir . '/guzzlehttp/promises/src/TaskQueue.php',
+    'GuzzleHttp\\Promise\\TaskQueueInterface' => $vendorDir . '/guzzlehttp/promises/src/TaskQueueInterface.php',
+    'GuzzleHttp\\Psr7\\AppendStream' => $vendorDir . '/guzzlehttp/psr7/src/AppendStream.php',
+    'GuzzleHttp\\Psr7\\BufferStream' => $vendorDir . '/guzzlehttp/psr7/src/BufferStream.php',
+    'GuzzleHttp\\Psr7\\CachingStream' => $vendorDir . '/guzzlehttp/psr7/src/CachingStream.php',
+    'GuzzleHttp\\Psr7\\DroppingStream' => $vendorDir . '/guzzlehttp/psr7/src/DroppingStream.php',
+    'GuzzleHttp\\Psr7\\FnStream' => $vendorDir . '/guzzlehttp/psr7/src/FnStream.php',
+    'GuzzleHttp\\Psr7\\InflateStream' => $vendorDir . '/guzzlehttp/psr7/src/InflateStream.php',
+    'GuzzleHttp\\Psr7\\LazyOpenStream' => $vendorDir . '/guzzlehttp/psr7/src/LazyOpenStream.php',
+    'GuzzleHttp\\Psr7\\LimitStream' => $vendorDir . '/guzzlehttp/psr7/src/LimitStream.php',
+    'GuzzleHttp\\Psr7\\MessageTrait' => $vendorDir . '/guzzlehttp/psr7/src/MessageTrait.php',
+    'GuzzleHttp\\Psr7\\MultipartStream' => $vendorDir . '/guzzlehttp/psr7/src/MultipartStream.php',
+    'GuzzleHttp\\Psr7\\NoSeekStream' => $vendorDir . '/guzzlehttp/psr7/src/NoSeekStream.php',
+    'GuzzleHttp\\Psr7\\PumpStream' => $vendorDir . '/guzzlehttp/psr7/src/PumpStream.php',
+    'GuzzleHttp\\Psr7\\Request' => $vendorDir . '/guzzlehttp/psr7/src/Request.php',
+    'GuzzleHttp\\Psr7\\Response' => $vendorDir . '/guzzlehttp/psr7/src/Response.php',
+    'GuzzleHttp\\Psr7\\ServerRequest' => $vendorDir . '/guzzlehttp/psr7/src/ServerRequest.php',
+    'GuzzleHttp\\Psr7\\Stream' => $vendorDir . '/guzzlehttp/psr7/src/Stream.php',
+    'GuzzleHttp\\Psr7\\StreamDecoratorTrait' => $vendorDir . '/guzzlehttp/psr7/src/StreamDecoratorTrait.php',
+    'GuzzleHttp\\Psr7\\StreamWrapper' => $vendorDir . '/guzzlehttp/psr7/src/StreamWrapper.php',
+    'GuzzleHttp\\Psr7\\UploadedFile' => $vendorDir . '/guzzlehttp/psr7/src/UploadedFile.php',
+    'GuzzleHttp\\Psr7\\Uri' => $vendorDir . '/guzzlehttp/psr7/src/Uri.php',
+    'GuzzleHttp\\Psr7\\UriNormalizer' => $vendorDir . '/guzzlehttp/psr7/src/UriNormalizer.php',
+    'GuzzleHttp\\Psr7\\UriResolver' => $vendorDir . '/guzzlehttp/psr7/src/UriResolver.php',
+    'GuzzleHttp\\RedirectMiddleware' => $vendorDir . '/guzzlehttp/guzzle/src/RedirectMiddleware.php',
+    'GuzzleHttp\\RequestOptions' => $vendorDir . '/guzzlehttp/guzzle/src/RequestOptions.php',
+    'GuzzleHttp\\RetryMiddleware' => $vendorDir . '/guzzlehttp/guzzle/src/RetryMiddleware.php',
+    'GuzzleHttp\\TransferStats' => $vendorDir . '/guzzlehttp/guzzle/src/TransferStats.php',
+    'GuzzleHttp\\UriTemplate' => $vendorDir . '/guzzlehttp/guzzle/src/UriTemplate.php',
+    'Psr\\Http\\Message\\MessageInterface' => $vendorDir . '/psr/http-message/src/MessageInterface.php',
+    'Psr\\Http\\Message\\RequestInterface' => $vendorDir . '/psr/http-message/src/RequestInterface.php',
+    'Psr\\Http\\Message\\ResponseInterface' => $vendorDir . '/psr/http-message/src/ResponseInterface.php',
+    'Psr\\Http\\Message\\ServerRequestInterface' => $vendorDir . '/psr/http-message/src/ServerRequestInterface.php',
+    'Psr\\Http\\Message\\StreamInterface' => $vendorDir . '/psr/http-message/src/StreamInterface.php',
+    'Psr\\Http\\Message\\UploadedFileInterface' => $vendorDir . '/psr/http-message/src/UploadedFileInterface.php',
+    'Psr\\Http\\Message\\UriInterface' => $vendorDir . '/psr/http-message/src/UriInterface.php',
+    'Upyun\\Api\\Form' => $baseDir . '/src/Upyun/Api/Form.php',
+    'Upyun\\Api\\Pretreat' => $baseDir . '/src/Upyun/Api/Pretreat.php',
+    'Upyun\\Api\\Rest' => $baseDir . '/src/Upyun/Api/Rest.php',
+    'Upyun\\Api\\SyncVideo' => $baseDir . '/src/Upyun/Api/SyncVideo.php',
+    'Upyun\\Config' => $baseDir . '/src/Upyun/Config.php',
+    'Upyun\\Signature' => $baseDir . '/src/Upyun/Signature.php',
+    'Upyun\\Tests\\SignatureTest' => $baseDir . '/tests/SignatureTest.php',
+    'Upyun\\Tests\\UpyunTest' => $baseDir . '/tests/UpyunTest.php',
+    'Upyun\\Uploader' => $baseDir . '/src/Upyun/Uploader.php',
+    'Upyun\\Upyun' => $baseDir . '/src/Upyun/Upyun.php',
+    'Upyun\\Util' => $baseDir . '/src/Upyun/Util.php',
+);

+ 12 - 0
usr/plugins/UpyunFile/Upyun/vendor/composer/autoload_files.php

@@ -0,0 +1,12 @@
+<?php
+
+// autoload_files.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+    'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
+    'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
+    '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
+);

+ 9 - 0
usr/plugins/UpyunFile/Upyun/vendor/composer/autoload_namespaces.php

@@ -0,0 +1,9 @@
+<?php
+
+// autoload_namespaces.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+);

+ 15 - 0
usr/plugins/UpyunFile/Upyun/vendor/composer/autoload_psr4.php

@@ -0,0 +1,15 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+    'Upyun\\Tests\\' => array($baseDir . '/tests'),
+    'Upyun\\' => array($baseDir . '/src/Upyun'),
+    'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
+    'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
+    'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
+    'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
+);

+ 70 - 0
usr/plugins/UpyunFile/Upyun/vendor/composer/autoload_real.php

@@ -0,0 +1,70 @@
+<?php
+
+// autoload_real.php @generated by Composer
+
+class ComposerAutoloaderInita84e991fabf2a525d44852e19efdb0b7
+{
+    private static $loader;
+
+    public static function loadClassLoader($class)
+    {
+        if ('Composer\Autoload\ClassLoader' === $class) {
+            require __DIR__ . '/ClassLoader.php';
+        }
+    }
+
+    public static function getLoader()
+    {
+        if (null !== self::$loader) {
+            return self::$loader;
+        }
+
+        spl_autoload_register(array('ComposerAutoloaderInita84e991fabf2a525d44852e19efdb0b7', 'loadClassLoader'), true, true);
+        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
+        spl_autoload_unregister(array('ComposerAutoloaderInita84e991fabf2a525d44852e19efdb0b7', 'loadClassLoader'));
+
+        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
+        if ($useStaticLoader) {
+            require_once __DIR__ . '/autoload_static.php';
+
+            call_user_func(\Composer\Autoload\ComposerStaticInita84e991fabf2a525d44852e19efdb0b7::getInitializer($loader));
+        } else {
+            $map = require __DIR__ . '/autoload_namespaces.php';
+            foreach ($map as $namespace => $path) {
+                $loader->set($namespace, $path);
+            }
+
+            $map = require __DIR__ . '/autoload_psr4.php';
+            foreach ($map as $namespace => $path) {
+                $loader->setPsr4($namespace, $path);
+            }
+
+            $classMap = require __DIR__ . '/autoload_classmap.php';
+            if ($classMap) {
+                $loader->addClassMap($classMap);
+            }
+        }
+
+        $loader->register(true);
+
+        if ($useStaticLoader) {
+            $includeFiles = Composer\Autoload\ComposerStaticInita84e991fabf2a525d44852e19efdb0b7::$files;
+        } else {
+            $includeFiles = require __DIR__ . '/autoload_files.php';
+        }
+        foreach ($includeFiles as $fileIdentifier => $file) {
+            composerRequirea84e991fabf2a525d44852e19efdb0b7($fileIdentifier, $file);
+        }
+
+        return $loader;
+    }
+}
+
+function composerRequirea84e991fabf2a525d44852e19efdb0b7($fileIdentifier, $file)
+{
+    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+        require $file;
+
+        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+    }
+}

+ 158 - 0
usr/plugins/UpyunFile/Upyun/vendor/composer/autoload_static.php

@@ -0,0 +1,158 @@
+<?php
+
+// autoload_static.php @generated by Composer
+
+namespace Composer\Autoload;
+
+class ComposerStaticInita84e991fabf2a525d44852e19efdb0b7
+{
+    public static $files = array (
+        'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
+        'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
+        '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
+    );
+
+    public static $prefixLengthsPsr4 = array (
+        'U' => 
+        array (
+            'Upyun\\Tests\\' => 12,
+            'Upyun\\' => 6,
+        ),
+        'P' => 
+        array (
+            'Psr\\Http\\Message\\' => 17,
+        ),
+        'G' => 
+        array (
+            'GuzzleHttp\\Psr7\\' => 16,
+            'GuzzleHttp\\Promise\\' => 19,
+            'GuzzleHttp\\' => 11,
+        ),
+    );
+
+    public static $prefixDirsPsr4 = array (
+        'Upyun\\Tests\\' => 
+        array (
+            0 => __DIR__ . '/../..' . '/tests',
+        ),
+        'Upyun\\' => 
+        array (
+            0 => __DIR__ . '/../..' . '/src/Upyun',
+        ),
+        'Psr\\Http\\Message\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/psr/http-message/src',
+        ),
+        'GuzzleHttp\\Psr7\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
+        ),
+        'GuzzleHttp\\Promise\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/guzzlehttp/promises/src',
+        ),
+        'GuzzleHttp\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src',
+        ),
+    );
+
+    public static $classMap = array (
+        'GuzzleHttp\\Client' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Client.php',
+        'GuzzleHttp\\ClientInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/ClientInterface.php',
+        'GuzzleHttp\\Cookie\\CookieJar' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/CookieJar.php',
+        'GuzzleHttp\\Cookie\\CookieJarInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php',
+        'GuzzleHttp\\Cookie\\FileCookieJar' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php',
+        'GuzzleHttp\\Cookie\\SessionCookieJar' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php',
+        'GuzzleHttp\\Cookie\\SetCookie' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/SetCookie.php',
+        'GuzzleHttp\\Exception\\BadResponseException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/BadResponseException.php',
+        'GuzzleHttp\\Exception\\ClientException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/ClientException.php',
+        'GuzzleHttp\\Exception\\ConnectException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/ConnectException.php',
+        'GuzzleHttp\\Exception\\GuzzleException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/GuzzleException.php',
+        'GuzzleHttp\\Exception\\RequestException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/RequestException.php',
+        'GuzzleHttp\\Exception\\SeekException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/SeekException.php',
+        'GuzzleHttp\\Exception\\ServerException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/ServerException.php',
+        'GuzzleHttp\\Exception\\TooManyRedirectsException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php',
+        'GuzzleHttp\\Exception\\TransferException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/TransferException.php',
+        'GuzzleHttp\\HandlerStack' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/HandlerStack.php',
+        'GuzzleHttp\\Handler\\CurlFactory' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlFactory.php',
+        'GuzzleHttp\\Handler\\CurlFactoryInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php',
+        'GuzzleHttp\\Handler\\CurlHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlHandler.php',
+        'GuzzleHttp\\Handler\\CurlMultiHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php',
+        'GuzzleHttp\\Handler\\EasyHandle' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/EasyHandle.php',
+        'GuzzleHttp\\Handler\\MockHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/MockHandler.php',
+        'GuzzleHttp\\Handler\\Proxy' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/Proxy.php',
+        'GuzzleHttp\\Handler\\StreamHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/StreamHandler.php',
+        'GuzzleHttp\\MessageFormatter' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/MessageFormatter.php',
+        'GuzzleHttp\\Middleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Middleware.php',
+        'GuzzleHttp\\Pool' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Pool.php',
+        'GuzzleHttp\\PrepareBodyMiddleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php',
+        'GuzzleHttp\\Promise\\AggregateException' => __DIR__ . '/..' . '/guzzlehttp/promises/src/AggregateException.php',
+        'GuzzleHttp\\Promise\\CancellationException' => __DIR__ . '/..' . '/guzzlehttp/promises/src/CancellationException.php',
+        'GuzzleHttp\\Promise\\Coroutine' => __DIR__ . '/..' . '/guzzlehttp/promises/src/Coroutine.php',
+        'GuzzleHttp\\Promise\\EachPromise' => __DIR__ . '/..' . '/guzzlehttp/promises/src/EachPromise.php',
+        'GuzzleHttp\\Promise\\FulfilledPromise' => __DIR__ . '/..' . '/guzzlehttp/promises/src/FulfilledPromise.php',
+        'GuzzleHttp\\Promise\\Promise' => __DIR__ . '/..' . '/guzzlehttp/promises/src/Promise.php',
+        'GuzzleHttp\\Promise\\PromiseInterface' => __DIR__ . '/..' . '/guzzlehttp/promises/src/PromiseInterface.php',
+        'GuzzleHttp\\Promise\\PromisorInterface' => __DIR__ . '/..' . '/guzzlehttp/promises/src/PromisorInterface.php',
+        'GuzzleHttp\\Promise\\RejectedPromise' => __DIR__ . '/..' . '/guzzlehttp/promises/src/RejectedPromise.php',
+        'GuzzleHttp\\Promise\\RejectionException' => __DIR__ . '/..' . '/guzzlehttp/promises/src/RejectionException.php',
+        'GuzzleHttp\\Promise\\TaskQueue' => __DIR__ . '/..' . '/guzzlehttp/promises/src/TaskQueue.php',
+        'GuzzleHttp\\Promise\\TaskQueueInterface' => __DIR__ . '/..' . '/guzzlehttp/promises/src/TaskQueueInterface.php',
+        'GuzzleHttp\\Psr7\\AppendStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/AppendStream.php',
+        'GuzzleHttp\\Psr7\\BufferStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/BufferStream.php',
+        'GuzzleHttp\\Psr7\\CachingStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/CachingStream.php',
+        'GuzzleHttp\\Psr7\\DroppingStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/DroppingStream.php',
+        'GuzzleHttp\\Psr7\\FnStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/FnStream.php',
+        'GuzzleHttp\\Psr7\\InflateStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/InflateStream.php',
+        'GuzzleHttp\\Psr7\\LazyOpenStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/LazyOpenStream.php',
+        'GuzzleHttp\\Psr7\\LimitStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/LimitStream.php',
+        'GuzzleHttp\\Psr7\\MessageTrait' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/MessageTrait.php',
+        'GuzzleHttp\\Psr7\\MultipartStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/MultipartStream.php',
+        'GuzzleHttp\\Psr7\\NoSeekStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/NoSeekStream.php',
+        'GuzzleHttp\\Psr7\\PumpStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/PumpStream.php',
+        'GuzzleHttp\\Psr7\\Request' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Request.php',
+        'GuzzleHttp\\Psr7\\Response' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Response.php',
+        'GuzzleHttp\\Psr7\\ServerRequest' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/ServerRequest.php',
+        'GuzzleHttp\\Psr7\\Stream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Stream.php',
+        'GuzzleHttp\\Psr7\\StreamDecoratorTrait' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/StreamDecoratorTrait.php',
+        'GuzzleHttp\\Psr7\\StreamWrapper' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/StreamWrapper.php',
+        'GuzzleHttp\\Psr7\\UploadedFile' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UploadedFile.php',
+        'GuzzleHttp\\Psr7\\Uri' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Uri.php',
+        'GuzzleHttp\\Psr7\\UriNormalizer' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UriNormalizer.php',
+        'GuzzleHttp\\Psr7\\UriResolver' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UriResolver.php',
+        'GuzzleHttp\\RedirectMiddleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/RedirectMiddleware.php',
+        'GuzzleHttp\\RequestOptions' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/RequestOptions.php',
+        'GuzzleHttp\\RetryMiddleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/RetryMiddleware.php',
+        'GuzzleHttp\\TransferStats' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/TransferStats.php',
+        'GuzzleHttp\\UriTemplate' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/UriTemplate.php',
+        'Psr\\Http\\Message\\MessageInterface' => __DIR__ . '/..' . '/psr/http-message/src/MessageInterface.php',
+        'Psr\\Http\\Message\\RequestInterface' => __DIR__ . '/..' . '/psr/http-message/src/RequestInterface.php',
+        'Psr\\Http\\Message\\ResponseInterface' => __DIR__ . '/..' . '/psr/http-message/src/ResponseInterface.php',
+        'Psr\\Http\\Message\\ServerRequestInterface' => __DIR__ . '/..' . '/psr/http-message/src/ServerRequestInterface.php',
+        'Psr\\Http\\Message\\StreamInterface' => __DIR__ . '/..' . '/psr/http-message/src/StreamInterface.php',
+        'Psr\\Http\\Message\\UploadedFileInterface' => __DIR__ . '/..' . '/psr/http-message/src/UploadedFileInterface.php',
+        'Psr\\Http\\Message\\UriInterface' => __DIR__ . '/..' . '/psr/http-message/src/UriInterface.php',
+        'Upyun\\Api\\Form' => __DIR__ . '/../..' . '/src/Upyun/Api/Form.php',
+        'Upyun\\Api\\Pretreat' => __DIR__ . '/../..' . '/src/Upyun/Api/Pretreat.php',
+        'Upyun\\Api\\Rest' => __DIR__ . '/../..' . '/src/Upyun/Api/Rest.php',
+        'Upyun\\Api\\SyncVideo' => __DIR__ . '/../..' . '/src/Upyun/Api/SyncVideo.php',
+        'Upyun\\Config' => __DIR__ . '/../..' . '/src/Upyun/Config.php',
+        'Upyun\\Signature' => __DIR__ . '/../..' . '/src/Upyun/Signature.php',
+        'Upyun\\Tests\\SignatureTest' => __DIR__ . '/../..' . '/tests/SignatureTest.php',
+        'Upyun\\Tests\\UpyunTest' => __DIR__ . '/../..' . '/tests/UpyunTest.php',
+        'Upyun\\Uploader' => __DIR__ . '/../..' . '/src/Upyun/Uploader.php',
+        'Upyun\\Upyun' => __DIR__ . '/../..' . '/src/Upyun/Upyun.php',
+        'Upyun\\Util' => __DIR__ . '/../..' . '/src/Upyun/Util.php',
+    );
+
+    public static function getInitializer(ClassLoader $loader)
+    {
+        return \Closure::bind(function () use ($loader) {
+            $loader->prefixLengthsPsr4 = ComposerStaticInita84e991fabf2a525d44852e19efdb0b7::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInita84e991fabf2a525d44852e19efdb0b7::$prefixDirsPsr4;
+            $loader->classMap = ComposerStaticInita84e991fabf2a525d44852e19efdb0b7::$classMap;
+
+        }, null, ClassLoader::class);
+    }
+}

+ 241 - 0
usr/plugins/UpyunFile/Upyun/vendor/composer/installed.json

@@ -0,0 +1,241 @@
+[
+    {
+        "name": "guzzlehttp/promises",
+        "version": "v1.3.1",
+        "version_normalized": "1.3.1.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/guzzle/promises.git",
+            "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://files.phpcomposer.com/files/guzzle/promises/a59da6cf61d80060647ff4d3eb2c03a2bc694646.zip",
+            "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.5.0"
+        },
+        "require-dev": {
+            "phpunit/phpunit": "^4.0"
+        },
+        "time": "2016-12-20T10:07:11+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.4-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "GuzzleHttp\\Promise\\": "src/"
+            },
+            "files": [
+                "src/functions_include.php"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Michael Dowling",
+                "email": "mtdowling@gmail.com",
+                "homepage": "https://github.com/mtdowling"
+            }
+        ],
+        "description": "Guzzle promises library",
+        "keywords": [
+            "promise"
+        ]
+    },
+    {
+        "name": "psr/http-message",
+        "version": "1.0.1",
+        "version_normalized": "1.0.1.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/php-fig/http-message.git",
+            "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://files.phpcomposer.com/files/php-fig/http-message/f6561bf28d520154e4b0ec72be95418abe6d9363.zip",
+            "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.0"
+        },
+        "time": "2016-08-06T14:39:51+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.0.x-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Psr\\Http\\Message\\": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "PHP-FIG",
+                "homepage": "http://www.php-fig.org/"
+            }
+        ],
+        "description": "Common interface for HTTP messages",
+        "homepage": "https://github.com/php-fig/http-message",
+        "keywords": [
+            "http",
+            "http-message",
+            "psr",
+            "psr-7",
+            "request",
+            "response"
+        ]
+    },
+    {
+        "name": "guzzlehttp/psr7",
+        "version": "1.4.2",
+        "version_normalized": "1.4.2.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/guzzle/psr7.git",
+            "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://files.phpcomposer.com/files/guzzle/psr7/f5b8a8512e2b58b0071a7280e39f14f72e05d87c.zip",
+            "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.4.0",
+            "psr/http-message": "~1.0"
+        },
+        "provide": {
+            "psr/http-message-implementation": "1.0"
+        },
+        "require-dev": {
+            "phpunit/phpunit": "~4.0"
+        },
+        "time": "2017-03-20T17:10:46+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.4-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "GuzzleHttp\\Psr7\\": "src/"
+            },
+            "files": [
+                "src/functions_include.php"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Michael Dowling",
+                "email": "mtdowling@gmail.com",
+                "homepage": "https://github.com/mtdowling"
+            },
+            {
+                "name": "Tobias Schultze",
+                "homepage": "https://github.com/Tobion"
+            }
+        ],
+        "description": "PSR-7 message implementation that also provides common utility methods",
+        "keywords": [
+            "http",
+            "message",
+            "request",
+            "response",
+            "stream",
+            "uri",
+            "url"
+        ]
+    },
+    {
+        "name": "guzzlehttp/guzzle",
+        "version": "6.3.0",
+        "version_normalized": "6.3.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/guzzle/guzzle.git",
+            "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://files.phpcomposer.com/files/guzzle/guzzle/f4db5a78a5ea468d4831de7f0bf9d9415e348699.zip",
+            "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699",
+            "shasum": ""
+        },
+        "require": {
+            "guzzlehttp/promises": "^1.0",
+            "guzzlehttp/psr7": "^1.4",
+            "php": ">=5.5"
+        },
+        "require-dev": {
+            "ext-curl": "*",
+            "phpunit/phpunit": "^4.0 || ^5.0",
+            "psr/log": "^1.0"
+        },
+        "suggest": {
+            "psr/log": "Required for using the Log middleware"
+        },
+        "time": "2017-06-22T18:50:49+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "6.2-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "files": [
+                "src/functions_include.php"
+            ],
+            "psr-4": {
+                "GuzzleHttp\\": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Michael Dowling",
+                "email": "mtdowling@gmail.com",
+                "homepage": "https://github.com/mtdowling"
+            }
+        ],
+        "description": "Guzzle is a PHP HTTP client library",
+        "homepage": "http://guzzlephp.org/",
+        "keywords": [
+            "client",
+            "curl",
+            "framework",
+            "http",
+            "http client",
+            "rest",
+            "web service"
+        ]
+    }
+]

+ 1264 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/CHANGELOG.md

@@ -0,0 +1,1264 @@
+# CHANGELOG
+
+## 6.3.0 - 2017-06-22
+
+* Feature: force IP resolution (ipv4 or ipv6) [#1608](https://github.com/guzzle/guzzle/pull/1608), [#1659](https://github.com/guzzle/guzzle/pull/1659)
+* Improvement: Don't include summary in exception message when body is empty [#1621](https://github.com/guzzle/guzzle/pull/1621)
+* Improvement: Handle `on_headers` option in MockHandler [#1580](https://github.com/guzzle/guzzle/pull/1580)
+* Improvement: Added SUSE Linux CA path [#1609](https://github.com/guzzle/guzzle/issues/1609)
+* Improvement: Use class reference for getting the name of the class instead of using hardcoded strings [#1641](https://github.com/guzzle/guzzle/pull/1641)
+* Feature: Added `read_timeout` option [#1611](https://github.com/guzzle/guzzle/pull/1611)
+* Bug fix: PHP 7.x fixes [#1685](https://github.com/guzzle/guzzle/pull/1685), [#1686](https://github.com/guzzle/guzzle/pull/1686), [#1811](https://github.com/guzzle/guzzle/pull/1811)
+* Deprecation: BadResponseException instantiation without a response [#1642](https://github.com/guzzle/guzzle/pull/1642)
+* Feature: Added NTLM auth [#1569](https://github.com/guzzle/guzzle/pull/1569)
+* Feature: Track redirect HTTP status codes [#1711](https://github.com/guzzle/guzzle/pull/1711)
+* Improvement: Check handler type during construction [#1745](https://github.com/guzzle/guzzle/pull/1745)
+* Improvement: Always include the Content-Length if there's a body [#1721](https://github.com/guzzle/guzzle/pull/1721)
+* Feature: Added convenience method to access a cookie by name [#1318](https://github.com/guzzle/guzzle/pull/1318)
+* Bug fix: Fill `CURLOPT_CAPATH` and `CURLOPT_CAINFO` properly [#1684](https://github.com/guzzle/guzzle/pull/1684)
+* Improvement:  	Use `\GuzzleHttp\Promise\rejection_for` function instead of object init [#1827](https://github.com/guzzle/guzzle/pull/1827)
+
+
++ Minor code cleanups, documentation fixes and clarifications.
+
+## 6.2.3 - 2017-02-28
+
+* Fix deprecations with guzzle/psr7 version 1.4
+
+## 6.2.2 - 2016-10-08
+
+* Allow to pass nullable Response to delay callable
+* Only add scheme when host is present
+* Fix drain case where content-length is the literal string zero
+* Obfuscate in-URL credentials in exceptions
+
+## 6.2.1 - 2016-07-18
+
+* Address HTTP_PROXY security vulnerability, CVE-2016-5385:
+  https://httpoxy.org/
+* Fixing timeout bug with StreamHandler:
+  https://github.com/guzzle/guzzle/pull/1488
+* Only read up to `Content-Length` in PHP StreamHandler to avoid timeouts when
+  a server does not honor `Connection: close`.
+* Ignore URI fragment when sending requests.
+
+## 6.2.0 - 2016-03-21
+
+* Feature: added `GuzzleHttp\json_encode` and `GuzzleHttp\json_decode`.
+  https://github.com/guzzle/guzzle/pull/1389
+* Bug fix: Fix sleep calculation when waiting for delayed requests.
+  https://github.com/guzzle/guzzle/pull/1324
+* Feature: More flexible history containers.
+  https://github.com/guzzle/guzzle/pull/1373
+* Bug fix: defer sink stream opening in StreamHandler.
+  https://github.com/guzzle/guzzle/pull/1377
+* Bug fix: do not attempt to escape cookie values.
+  https://github.com/guzzle/guzzle/pull/1406
+* Feature: report original content encoding and length on decoded responses.
+  https://github.com/guzzle/guzzle/pull/1409
+* Bug fix: rewind seekable request bodies before dispatching to cURL.
+  https://github.com/guzzle/guzzle/pull/1422
+* Bug fix: provide an empty string to `http_build_query` for HHVM workaround.
+  https://github.com/guzzle/guzzle/pull/1367
+
+## 6.1.1 - 2015-11-22
+
+* Bug fix: Proxy::wrapSync() now correctly proxies to the appropriate handler
+  https://github.com/guzzle/guzzle/commit/911bcbc8b434adce64e223a6d1d14e9a8f63e4e4
+* Feature: HandlerStack is now more generic.
+  https://github.com/guzzle/guzzle/commit/f2102941331cda544745eedd97fc8fd46e1ee33e
+* Bug fix: setting verify to false in the StreamHandler now disables peer
+  verification. https://github.com/guzzle/guzzle/issues/1256
+* Feature: Middleware now uses an exception factory, including more error
+  context. https://github.com/guzzle/guzzle/pull/1282
+* Feature: better support for disabled functions.
+  https://github.com/guzzle/guzzle/pull/1287
+* Bug fix: fixed regression where MockHandler was not using `sink`.
+  https://github.com/guzzle/guzzle/pull/1292
+
+## 6.1.0 - 2015-09-08
+
+* Feature: Added the `on_stats` request option to provide access to transfer
+  statistics for requests. https://github.com/guzzle/guzzle/pull/1202
+* Feature: Added the ability to persist session cookies in CookieJars.
+  https://github.com/guzzle/guzzle/pull/1195
+* Feature: Some compatibility updates for Google APP Engine
+  https://github.com/guzzle/guzzle/pull/1216
+* Feature: Added support for NO_PROXY to prevent the use of a proxy based on
+  a simple set of rules. https://github.com/guzzle/guzzle/pull/1197
+* Feature: Cookies can now contain square brackets.
+  https://github.com/guzzle/guzzle/pull/1237
+* Bug fix: Now correctly parsing `=` inside of quotes in Cookies.
+  https://github.com/guzzle/guzzle/pull/1232
+* Bug fix: Cusotm cURL options now correctly override curl options of the
+  same name. https://github.com/guzzle/guzzle/pull/1221
+* Bug fix: Content-Type header is now added when using an explicitly provided
+  multipart body. https://github.com/guzzle/guzzle/pull/1218
+* Bug fix: Now ignoring Set-Cookie headers that have no name.
+* Bug fix: Reason phrase is no longer cast to an int in some cases in the
+  cURL handler. https://github.com/guzzle/guzzle/pull/1187
+* Bug fix: Remove the Authorization header when redirecting if the Host
+  header changes. https://github.com/guzzle/guzzle/pull/1207
+* Bug fix: Cookie path matching fixes
+  https://github.com/guzzle/guzzle/issues/1129
+* Bug fix: Fixing the cURL `body_as_string` setting
+  https://github.com/guzzle/guzzle/pull/1201
+* Bug fix: quotes are no longer stripped when parsing cookies.
+  https://github.com/guzzle/guzzle/issues/1172
+* Bug fix: `form_params` and `query` now always uses the `&` separator.
+  https://github.com/guzzle/guzzle/pull/1163
+* Bug fix: Adding a Content-Length to PHP stream wrapper requests if not set.
+  https://github.com/guzzle/guzzle/pull/1189
+
+## 6.0.2 - 2015-07-04
+
+* Fixed a memory leak in the curl handlers in which references to callbacks
+  were not being removed by `curl_reset`.
+* Cookies are now extracted properly before redirects.
+* Cookies now allow more character ranges.
+* Decoded Content-Encoding responses are now modified to correctly reflect
+  their state if the encoding was automatically removed by a handler. This
+  means that the `Content-Encoding` header may be removed an the
+  `Content-Length` modified to reflect the message size after removing the
+  encoding.
+* Added a more explicit error message when trying to use `form_params` and
+  `multipart` in the same request.
+* Several fixes for HHVM support.
+* Functions are now conditionally required using an additional level of
+  indirection to help with global Composer installations.
+
+## 6.0.1 - 2015-05-27
+
+* Fixed a bug with serializing the `query` request option where the `&`
+  separator was missing.
+* Added a better error message for when `body` is provided as an array. Please
+  use `form_params` or `multipart` instead.
+* Various doc fixes.
+
+## 6.0.0 - 2015-05-26
+
+* See the UPGRADING.md document for more information.
+* Added `multipart` and `form_params` request options.
+* Added `synchronous` request option.
+* Added the `on_headers` request option.
+* Fixed `expect` handling.
+* No longer adding default middlewares in the client ctor. These need to be
+  present on the provided handler in order to work.
+* Requests are no longer initiated when sending async requests with the
+  CurlMultiHandler. This prevents unexpected recursion from requests completing
+  while ticking the cURL loop.
+* Removed the semantics of setting `default` to `true`. This is no longer
+  required now that the cURL loop is not ticked for async requests.
+* Added request and response logging middleware.
+* No longer allowing self signed certificates when using the StreamHandler.
+* Ensuring that `sink` is valid if saving to a file.
+* Request exceptions now include a "handler context" which provides handler
+  specific contextual information.
+* Added `GuzzleHttp\RequestOptions` to allow request options to be applied
+  using constants.
+* `$maxHandles` has been removed from CurlMultiHandler.
+* `MultipartPostBody` is now part of the `guzzlehttp/psr7` package.
+
+## 5.3.0 - 2015-05-19
+
+* Mock now supports `save_to`
+* Marked `AbstractRequestEvent::getTransaction()` as public.
+* Fixed a bug in which multiple headers using different casing would overwrite
+  previous headers in the associative array.
+* Added `Utils::getDefaultHandler()`
+* Marked `GuzzleHttp\Client::getDefaultUserAgent` as deprecated.
+* URL scheme is now always lowercased.
+
+## 6.0.0-beta.1
+
+* Requires PHP >= 5.5
+* Updated to use PSR-7
+  * Requires immutable messages, which basically means an event based system
+    owned by a request instance is no longer possible.
+  * Utilizing the [Guzzle PSR-7 package](https://github.com/guzzle/psr7).
+  * Removed the dependency on `guzzlehttp/streams`. These stream abstractions
+    are available in the `guzzlehttp/psr7` package under the `GuzzleHttp\Psr7`
+    namespace.
+* Added middleware and handler system
+  * Replaced the Guzzle event and subscriber system with a middleware system.
+  * No longer depends on RingPHP, but rather places the HTTP handlers directly
+    in Guzzle, operating on PSR-7 messages.
+  * Retry logic is now encapsulated in `GuzzleHttp\Middleware::retry`, which
+    means the `guzzlehttp/retry-subscriber` is now obsolete.
+  * Mocking responses is now handled using `GuzzleHttp\Handler\MockHandler`.
+* Asynchronous responses
+  * No longer supports the `future` request option to send an async request.
+    Instead, use one of the `*Async` methods of a client (e.g., `requestAsync`,
+    `getAsync`, etc.).
+  * Utilizing `GuzzleHttp\Promise` instead of React's promise library to avoid
+    recursion required by chaining and forwarding react promises. See
+    https://github.com/guzzle/promises
+  * Added `requestAsync` and `sendAsync` to send request asynchronously.
+  * Added magic methods for `getAsync()`, `postAsync()`, etc. to send requests
+    asynchronously.
+* Request options
+  * POST and form updates
+    * Added the `form_fields` and `form_files` request options.
+    * Removed the `GuzzleHttp\Post` namespace.
+    * The `body` request option no longer accepts an array for POST requests.
+  * The `exceptions` request option has been deprecated in favor of the
+    `http_errors` request options.
+  * The `save_to` request option has been deprecated in favor of `sink` request
+    option.
+* Clients no longer accept an array of URI template string and variables for
+  URI variables. You will need to expand URI templates before passing them
+  into a client constructor or request method.
+* Client methods `get()`, `post()`, `put()`, `patch()`, `options()`, etc. are
+  now magic methods that will send synchronous requests.
+* Replaced `Utils.php` with plain functions in `functions.php`.
+* Removed `GuzzleHttp\Collection`.
+* Removed `GuzzleHttp\BatchResults`. Batched pool results are now returned as
+  an array.
+* Removed `GuzzleHttp\Query`. Query string handling is now handled using an
+  associative array passed into the `query` request option. The query string
+  is serialized using PHP's `http_build_query`. If you need more control, you
+  can pass the query string in as a string.
+* `GuzzleHttp\QueryParser` has been replaced with the
+  `GuzzleHttp\Psr7\parse_query`.
+
+## 5.2.0 - 2015-01-27
+
+* Added `AppliesHeadersInterface` to make applying headers to a request based
+  on the body more generic and not specific to `PostBodyInterface`.
+* Reduced the number of stack frames needed to send requests.
+* Nested futures are now resolved in the client rather than the RequestFsm
+* Finishing state transitions is now handled in the RequestFsm rather than the
+  RingBridge.
+* Added a guard in the Pool class to not use recursion for request retries.
+
+## 5.1.0 - 2014-12-19
+
+* Pool class no longer uses recursion when a request is intercepted.
+* The size of a Pool can now be dynamically adjusted using a callback.
+  See https://github.com/guzzle/guzzle/pull/943.
+* Setting a request option to `null` when creating a request with a client will
+  ensure that the option is not set. This allows you to overwrite default
+  request options on a per-request basis.
+  See https://github.com/guzzle/guzzle/pull/937.
+* Added the ability to limit which protocols are allowed for redirects by
+  specifying a `protocols` array in the `allow_redirects` request option.
+* Nested futures due to retries are now resolved when waiting for synchronous
+  responses. See https://github.com/guzzle/guzzle/pull/947.
+* `"0"` is now an allowed URI path. See
+  https://github.com/guzzle/guzzle/pull/935.
+* `Query` no longer typehints on the `$query` argument in the constructor,
+  allowing for strings and arrays.
+* Exceptions thrown in the `end` event are now correctly wrapped with Guzzle
+  specific exceptions if necessary.
+
+## 5.0.3 - 2014-11-03
+
+This change updates query strings so that they are treated as un-encoded values
+by default where the value represents an un-encoded value to send over the
+wire. A Query object then encodes the value before sending over the wire. This
+means that even value query string values (e.g., ":") are url encoded. This
+makes the Query class match PHP's http_build_query function. However, if you
+want to send requests over the wire using valid query string characters that do
+not need to be encoded, then you can provide a string to Url::setQuery() and
+pass true as the second argument to specify that the query string is a raw
+string that should not be parsed or encoded (unless a call to getQuery() is
+subsequently made, forcing the query-string to be converted into a Query
+object).
+
+## 5.0.2 - 2014-10-30
+
+* Added a trailing `\r\n` to multipart/form-data payloads. See
+  https://github.com/guzzle/guzzle/pull/871
+* Added a `GuzzleHttp\Pool::send()` convenience method to match the docs.
+* Status codes are now returned as integers. See
+  https://github.com/guzzle/guzzle/issues/881
+* No longer overwriting an existing `application/x-www-form-urlencoded` header
+  when sending POST requests, allowing for customized headers. See
+  https://github.com/guzzle/guzzle/issues/877
+* Improved path URL serialization.
+
+  * No longer double percent-encoding characters in the path or query string if
+    they are already encoded.
+  * Now properly encoding the supplied path to a URL object, instead of only
+    encoding ' ' and '?'.
+  * Note: This has been changed in 5.0.3 to now encode query string values by
+    default unless the `rawString` argument is provided when setting the query
+    string on a URL: Now allowing many more characters to be present in the
+    query string without being percent encoded. See http://tools.ietf.org/html/rfc3986#appendix-A
+
+## 5.0.1 - 2014-10-16
+
+Bugfix release.
+
+* Fixed an issue where connection errors still returned response object in
+  error and end events event though the response is unusable. This has been
+  corrected so that a response is not returned in the `getResponse` method of
+  these events if the response did not complete. https://github.com/guzzle/guzzle/issues/867
+* Fixed an issue where transfer statistics were not being populated in the
+  RingBridge. https://github.com/guzzle/guzzle/issues/866
+
+## 5.0.0 - 2014-10-12
+
+Adding support for non-blocking responses and some minor API cleanup.
+
+### New Features
+
+* Added support for non-blocking responses based on `guzzlehttp/guzzle-ring`.
+* Added a public API for creating a default HTTP adapter.
+* Updated the redirect plugin to be non-blocking so that redirects are sent
+  concurrently. Other plugins like this can now be updated to be non-blocking.
+* Added a "progress" event so that you can get upload and download progress
+  events.
+* Added `GuzzleHttp\Pool` which implements FutureInterface and transfers
+  requests concurrently using a capped pool size as efficiently as possible.
+* Added `hasListeners()` to EmitterInterface.
+* Removed `GuzzleHttp\ClientInterface::sendAll` and marked
+  `GuzzleHttp\Client::sendAll` as deprecated (it's still there, just not the
+  recommended way).
+
+### Breaking changes
+
+The breaking changes in this release are relatively minor. The biggest thing to
+look out for is that request and response objects no longer implement fluent
+interfaces.
+
+* Removed the fluent interfaces (i.e., `return $this`) from requests,
+  responses, `GuzzleHttp\Collection`, `GuzzleHttp\Url`,
+  `GuzzleHttp\Query`, `GuzzleHttp\Post\PostBody`, and
+  `GuzzleHttp\Cookie\SetCookie`. This blog post provides a good outline of
+  why I did this: http://ocramius.github.io/blog/fluent-interfaces-are-evil/.
+  This also makes the Guzzle message interfaces compatible with the current
+  PSR-7 message proposal.
+* Removed "functions.php", so that Guzzle is truly PSR-4 compliant. Except
+  for the HTTP request functions from function.php, these functions are now
+  implemented in `GuzzleHttp\Utils` using camelCase. `GuzzleHttp\json_decode`
+  moved to `GuzzleHttp\Utils::jsonDecode`. `GuzzleHttp\get_path` moved to
+  `GuzzleHttp\Utils::getPath`. `GuzzleHttp\set_path` moved to
+  `GuzzleHttp\Utils::setPath`. `GuzzleHttp\batch` should now be
+  `GuzzleHttp\Pool::batch`, which returns an `objectStorage`. Using functions.php
+  caused problems for many users: they aren't PSR-4 compliant, require an
+  explicit include, and needed an if-guard to ensure that the functions are not
+  declared multiple times.
+* Rewrote adapter layer.
+    * Removing all classes from `GuzzleHttp\Adapter`, these are now
+      implemented as callables that are stored in `GuzzleHttp\Ring\Client`.
+    * Removed the concept of "parallel adapters". Sending requests serially or
+      concurrently is now handled using a single adapter.
+    * Moved `GuzzleHttp\Adapter\Transaction` to `GuzzleHttp\Transaction`. The
+      Transaction object now exposes the request, response, and client as public
+      properties. The getters and setters have been removed.
+* Removed the "headers" event. This event was only useful for changing the
+  body a response once the headers of the response were known. You can implement
+  a similar behavior in a number of ways. One example might be to use a
+  FnStream that has access to the transaction being sent. For example, when the
+  first byte is written, you could check if the response headers match your
+  expectations, and if so, change the actual stream body that is being
+  written to.
+* Removed the `asArray` parameter from
+  `GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header
+  value as an array, then use the newly added `getHeaderAsArray()` method of
+  `MessageInterface`. This change makes the Guzzle interfaces compatible with
+  the PSR-7 interfaces.
+* `GuzzleHttp\Message\MessageFactory` no longer allows subclasses to add
+  custom request options using double-dispatch (this was an implementation
+  detail). Instead, you should now provide an associative array to the
+  constructor which is a mapping of the request option name mapping to a
+  function that applies the option value to a request.
+* Removed the concept of "throwImmediately" from exceptions and error events.
+  This control mechanism was used to stop a transfer of concurrent requests
+  from completing. This can now be handled by throwing the exception or by
+  cancelling a pool of requests or each outstanding future request individually.
+* Updated to "GuzzleHttp\Streams" 3.0.
+    * `GuzzleHttp\Stream\StreamInterface::getContents()` no longer accepts a
+      `maxLen` parameter. This update makes the Guzzle streams project
+      compatible with the current PSR-7 proposal.
+    * `GuzzleHttp\Stream\Stream::__construct`,
+      `GuzzleHttp\Stream\Stream::factory`, and
+      `GuzzleHttp\Stream\Utils::create` no longer accept a size in the second
+      argument. They now accept an associative array of options, including the
+      "size" key and "metadata" key which can be used to provide custom metadata.
+
+## 4.2.2 - 2014-09-08
+
+* Fixed a memory leak in the CurlAdapter when reusing cURL handles.
+* No longer using `request_fulluri` in stream adapter proxies.
+* Relative redirects are now based on the last response, not the first response.
+
+## 4.2.1 - 2014-08-19
+
+* Ensuring that the StreamAdapter does not always add a Content-Type header
+* Adding automated github releases with a phar and zip
+
+## 4.2.0 - 2014-08-17
+
+* Now merging in default options using a case-insensitive comparison.
+  Closes https://github.com/guzzle/guzzle/issues/767
+* Added the ability to automatically decode `Content-Encoding` response bodies
+  using the `decode_content` request option. This is set to `true` by default
+  to decode the response body if it comes over the wire with a
+  `Content-Encoding`. Set this value to `false` to disable decoding the
+  response content, and pass a string to provide a request `Accept-Encoding`
+  header and turn on automatic response decoding. This feature now allows you
+  to pass an `Accept-Encoding` header in the headers of a request but still
+  disable automatic response decoding.
+  Closes https://github.com/guzzle/guzzle/issues/764
+* Added the ability to throw an exception immediately when transferring
+  requests in parallel. Closes https://github.com/guzzle/guzzle/issues/760
+* Updating guzzlehttp/streams dependency to ~2.1
+* No longer utilizing the now deprecated namespaced methods from the stream
+  package.
+
+## 4.1.8 - 2014-08-14
+
+* Fixed an issue in the CurlFactory that caused setting the `stream=false`
+  request option to throw an exception.
+  See: https://github.com/guzzle/guzzle/issues/769
+* TransactionIterator now calls rewind on the inner iterator.
+  See: https://github.com/guzzle/guzzle/pull/765
+* You can now set the `Content-Type` header to `multipart/form-data`
+  when creating POST requests to force multipart bodies.
+  See https://github.com/guzzle/guzzle/issues/768
+
+## 4.1.7 - 2014-08-07
+
+* Fixed an error in the HistoryPlugin that caused the same request and response
+  to be logged multiple times when an HTTP protocol error occurs.
+* Ensuring that cURL does not add a default Content-Type when no Content-Type
+  has been supplied by the user. This prevents the adapter layer from modifying
+  the request that is sent over the wire after any listeners may have already
+  put the request in a desired state (e.g., signed the request).
+* Throwing an exception when you attempt to send requests that have the
+  "stream" set to true in parallel using the MultiAdapter.
+* Only calling curl_multi_select when there are active cURL handles. This was
+  previously changed and caused performance problems on some systems due to PHP
+  always selecting until the maximum select timeout.
+* Fixed a bug where multipart/form-data POST fields were not correctly
+  aggregated (e.g., values with "&").
+
+## 4.1.6 - 2014-08-03
+
+* Added helper methods to make it easier to represent messages as strings,
+  including getting the start line and getting headers as a string.
+
+## 4.1.5 - 2014-08-02
+
+* Automatically retrying cURL "Connection died, retrying a fresh connect"
+  errors when possible.
+* cURL implementation cleanup
+* Allowing multiple event subscriber listeners to be registered per event by
+  passing an array of arrays of listener configuration.
+
+## 4.1.4 - 2014-07-22
+
+* Fixed a bug that caused multi-part POST requests with more than one field to
+  serialize incorrectly.
+* Paths can now be set to "0"
+* `ResponseInterface::xml` now accepts a `libxml_options` option and added a
+  missing default argument that was required when parsing XML response bodies.
+* A `save_to` stream is now created lazily, which means that files are not
+  created on disk unless a request succeeds.
+
+## 4.1.3 - 2014-07-15
+
+* Various fixes to multipart/form-data POST uploads
+* Wrapping function.php in an if-statement to ensure Guzzle can be used
+  globally and in a Composer install
+* Fixed an issue with generating and merging in events to an event array
+* POST headers are only applied before sending a request to allow you to change
+  the query aggregator used before uploading
+* Added much more robust query string parsing
+* Fixed various parsing and normalization issues with URLs
+* Fixing an issue where multi-valued headers were not being utilized correctly
+  in the StreamAdapter
+
+## 4.1.2 - 2014-06-18
+
+* Added support for sending payloads with GET requests
+
+## 4.1.1 - 2014-06-08
+
+* Fixed an issue related to using custom message factory options in subclasses
+* Fixed an issue with nested form fields in a multi-part POST
+* Fixed an issue with using the `json` request option for POST requests
+* Added `ToArrayInterface` to `GuzzleHttp\Cookie\CookieJar`
+
+## 4.1.0 - 2014-05-27
+
+* Added a `json` request option to easily serialize JSON payloads.
+* Added a `GuzzleHttp\json_decode()` wrapper to safely parse JSON.
+* Added `setPort()` and `getPort()` to `GuzzleHttp\Message\RequestInterface`.
+* Added the ability to provide an emitter to a client in the client constructor.
+* Added the ability to persist a cookie session using $_SESSION.
+* Added a trait that can be used to add event listeners to an iterator.
+* Removed request method constants from RequestInterface.
+* Fixed warning when invalid request start-lines are received.
+* Updated MessageFactory to work with custom request option methods.
+* Updated cacert bundle to latest build.
+
+4.0.2 (2014-04-16)
+------------------
+
+* Proxy requests using the StreamAdapter now properly use request_fulluri (#632)
+* Added the ability to set scalars as POST fields (#628)
+
+## 4.0.1 - 2014-04-04
+
+* The HTTP status code of a response is now set as the exception code of
+  RequestException objects.
+* 303 redirects will now correctly switch from POST to GET requests.
+* The default parallel adapter of a client now correctly uses the MultiAdapter.
+* HasDataTrait now initializes the internal data array as an empty array so
+  that the toArray() method always returns an array.
+
+## 4.0.0 - 2014-03-29
+
+* For more information on the 4.0 transition, see:
+  http://mtdowling.com/blog/2014/03/15/guzzle-4-rc/
+* For information on changes and upgrading, see:
+  https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40
+* Added `GuzzleHttp\batch()` as a convenience function for sending requests in
+  parallel without needing to write asynchronous code.
+* Restructured how events are added to `GuzzleHttp\ClientInterface::sendAll()`.
+  You can now pass a callable or an array of associative arrays where each
+  associative array contains the "fn", "priority", and "once" keys.
+
+## 4.0.0.rc-2 - 2014-03-25
+
+* Removed `getConfig()` and `setConfig()` from clients to avoid confusion
+  around whether things like base_url, message_factory, etc. should be able to
+  be retrieved or modified.
+* Added `getDefaultOption()` and `setDefaultOption()` to ClientInterface
+* functions.php functions were renamed using snake_case to match PHP idioms
+* Added support for `HTTP_PROXY`, `HTTPS_PROXY`, and
+  `GUZZLE_CURL_SELECT_TIMEOUT` environment variables
+* Added the ability to specify custom `sendAll()` event priorities
+* Added the ability to specify custom stream context options to the stream
+  adapter.
+* Added a functions.php function for `get_path()` and `set_path()`
+* CurlAdapter and MultiAdapter now use a callable to generate curl resources
+* MockAdapter now properly reads a body and emits a `headers` event
+* Updated Url class to check if a scheme and host are set before adding ":"
+  and "//". This allows empty Url (e.g., "") to be serialized as "".
+* Parsing invalid XML no longer emits warnings
+* Curl classes now properly throw AdapterExceptions
+* Various performance optimizations
+* Streams are created with the faster `Stream\create()` function
+* Marked deprecation_proxy() as internal
+* Test server is now a collection of static methods on a class
+
+## 4.0.0-rc.1 - 2014-03-15
+
+* See https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40
+
+## 3.8.1 - 2014-01-28
+
+* Bug: Always using GET requests when redirecting from a 303 response
+* Bug: CURLOPT_SSL_VERIFYHOST is now correctly set to false when setting `$certificateAuthority` to false in
+  `Guzzle\Http\ClientInterface::setSslVerification()`
+* Bug: RedirectPlugin now uses strict RFC 3986 compliance when combining a base URL with a relative URL
+* Bug: The body of a request can now be set to `"0"`
+* Sending PHP stream requests no longer forces `HTTP/1.0`
+* Adding more information to ExceptionCollection exceptions so that users have more context, including a stack trace of
+  each sub-exception
+* Updated the `$ref` attribute in service descriptions to merge over any existing parameters of a schema (rather than
+  clobbering everything).
+* Merging URLs will now use the query string object from the relative URL (thus allowing custom query aggregators)
+* Query strings are now parsed in a way that they do no convert empty keys with no value to have a dangling `=`.
+  For example `foo&bar=baz` is now correctly parsed and recognized as `foo&bar=baz` rather than `foo=&bar=baz`.
+* Now properly escaping the regular expression delimiter when matching Cookie domains.
+* Network access is now disabled when loading XML documents
+
+## 3.8.0 - 2013-12-05
+
+* Added the ability to define a POST name for a file
+* JSON response parsing now properly walks additionalProperties
+* cURL error code 18 is now retried automatically in the BackoffPlugin
+* Fixed a cURL error when URLs contain fragments
+* Fixed an issue in the BackoffPlugin retry event where it was trying to access all exceptions as if they were
+  CurlExceptions
+* CURLOPT_PROGRESS function fix for PHP 5.5 (69fcc1e)
+* Added the ability for Guzzle to work with older versions of cURL that do not support `CURLOPT_TIMEOUT_MS`
+* Fixed a bug that was encountered when parsing empty header parameters
+* UriTemplate now has a `setRegex()` method to match the docs
+* The `debug` request parameter now checks if it is truthy rather than if it exists
+* Setting the `debug` request parameter to true shows verbose cURL output instead of using the LogPlugin
+* Added the ability to combine URLs using strict RFC 3986 compliance
+* Command objects can now return the validation errors encountered by the command
+* Various fixes to cache revalidation (#437 and 29797e5)
+* Various fixes to the AsyncPlugin
+* Cleaned up build scripts
+
+## 3.7.4 - 2013-10-02
+
+* Bug fix: 0 is now an allowed value in a description parameter that has a default value (#430)
+* Bug fix: SchemaFormatter now returns an integer when formatting to a Unix timestamp
+  (see https://github.com/aws/aws-sdk-php/issues/147)
+* Bug fix: Cleaned up and fixed URL dot segment removal to properly resolve internal dots
+* Minimum PHP version is now properly specified as 5.3.3 (up from 5.3.2) (#420)
+* Updated the bundled cacert.pem (#419)
+* OauthPlugin now supports adding authentication to headers or query string (#425)
+
+## 3.7.3 - 2013-09-08
+
+* Added the ability to get the exception associated with a request/command when using `MultiTransferException` and
+  `CommandTransferException`.
+* Setting `additionalParameters` of a response to false is now honored when parsing responses with a service description
+* Schemas are only injected into response models when explicitly configured.
+* No longer guessing Content-Type based on the path of a request. Content-Type is now only guessed based on the path of
+  an EntityBody.
+* Bug fix: ChunkedIterator can now properly chunk a \Traversable as well as an \Iterator.
+* Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`.
+* Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody()
+* Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin
+* Bug fix: Visiting XML attributes first before visiting XML children when serializing requests
+* Bug fix: Properly parsing headers that contain commas contained in quotes
+* Bug fix: mimetype guessing based on a filename is now case-insensitive
+
+## 3.7.2 - 2013-08-02
+
+* Bug fix: Properly URL encoding paths when using the PHP-only version of the UriTemplate expander
+  See https://github.com/guzzle/guzzle/issues/371
+* Bug fix: Cookie domains are now matched correctly according to RFC 6265
+  See https://github.com/guzzle/guzzle/issues/377
+* Bug fix: GET parameters are now used when calculating an OAuth signature
+* Bug fix: Fixed an issue with cache revalidation where the If-None-Match header was being double quoted
+* `Guzzle\Common\AbstractHasDispatcher::dispatch()` now returns the event that was dispatched
+* `Guzzle\Http\QueryString::factory()` now guesses the most appropriate query aggregator to used based on the input.
+  See https://github.com/guzzle/guzzle/issues/379
+* Added a way to add custom domain objects to service description parsing using the `operation.parse_class` event. See
+  https://github.com/guzzle/guzzle/pull/380
+* cURL multi cleanup and optimizations
+
+## 3.7.1 - 2013-07-05
+
+* Bug fix: Setting default options on a client now works
+* Bug fix: Setting options on HEAD requests now works. See #352
+* Bug fix: Moving stream factory before send event to before building the stream. See #353
+* Bug fix: Cookies no longer match on IP addresses per RFC 6265
+* Bug fix: Correctly parsing header parameters that are in `<>` and quotes
+* Added `cert` and `ssl_key` as request options
+* `Host` header can now diverge from the host part of a URL if the header is set manually
+* `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter
+* OAuth parameters are only added via the plugin if they aren't already set
+* Exceptions are now thrown when a URL cannot be parsed
+* Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails
+* Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin
+
+## 3.7.0 - 2013-06-10
+
+* See UPGRADING.md for more information on how to upgrade.
+* Requests now support the ability to specify an array of $options when creating a request to more easily modify a
+  request. You can pass a 'request.options' configuration setting to a client to apply default request options to
+  every request created by a client (e.g. default query string variables, headers, curl options, etc.).
+* Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`.
+  See `Guzzle\Http\StaticClient::mount`.
+* Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests
+      created by a command (e.g. custom headers, query string variables, timeout settings, etc.).
+* Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the
+  headers of a response
+* Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key
+  (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`)
+* ServiceBuilders now support storing and retrieving arbitrary data
+* CachePlugin can now purge all resources for a given URI
+* CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource
+* CachePlugin now uses the Vary header to determine if a resource is a cache hit
+* `Guzzle\Http\Message\Response` now implements `\Serializable`
+* Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters
+* `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable
+* Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()`
+* Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size
+* `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message
+* Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older
+  Symfony users can still use the old version of Monolog.
+* Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`.
+  Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`.
+* Several performance improvements to `Guzzle\Common\Collection`
+* Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`:
+  createRequest, head, delete, put, patch, post, options, prepareRequest
+* Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()`
+* Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface`
+* Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to
+  `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a
+  resource, string, or EntityBody into the $options parameter to specify the download location of the response.
+* Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a
+  default `array()`
+* Added `Guzzle\Stream\StreamInterface::isRepeatable`
+* Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use
+  $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or
+  $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`.
+* Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`.
+* Removed `Guzzle\Http\ClientInterface::expandTemplate()`
+* Removed `Guzzle\Http\ClientInterface::setRequestFactory()`
+* Removed `Guzzle\Http\ClientInterface::getCurlMulti()`
+* Removed `Guzzle\Http\Message\RequestInterface::canCache`
+* Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`
+* Removed `Guzzle\Http\Message\RequestInterface::isRedirect`
+* Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods.
+* You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting
+  `Guzzle\Common\Version::$emitWarnings` to true.
+* Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use
+      `$request->getResponseBody()->isRepeatable()` instead.
+* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use
+  `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
+* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use
+  `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
+* Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead.
+* Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead.
+* Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated
+* Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand.
+  These will work through Guzzle 4.0
+* Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params].
+* Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client.
+* Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`.
+* Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`.
+* Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8.
+* Marked `Guzzle\Common\Collection::inject()` as deprecated.
+* Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');`
+* CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a
+  CacheStorageInterface. These two objects and interface will be removed in a future version.
+* Always setting X-cache headers on cached responses
+* Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin
+* `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface
+  $request, Response $response);`
+* `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);`
+* `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);`
+* Added `CacheStorageInterface::purge($url)`
+* `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin
+  $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache,
+  CanCacheStrategyInterface $canCache = null)`
+* Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)`
+
+## 3.6.0 - 2013-05-29
+
+* ServiceDescription now implements ToArrayInterface
+* Added command.hidden_params to blacklist certain headers from being treated as additionalParameters
+* Guzzle can now correctly parse incomplete URLs
+* Mixed casing of headers are now forced to be a single consistent casing across all values for that header.
+* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution
+* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader().
+* Specific header implementations can be created for complex headers. When a message creates a header, it uses a
+  HeaderFactory which can map specific headers to specific header classes. There is now a Link header and
+  CacheControl header implementation.
+* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate
+* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti()
+* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in
+  Guzzle\Http\Curl\RequestMediator
+* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string.
+* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface
+* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders()
+* Removed Guzzle\Parser\ParserRegister::get(). Use getParser()
+* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser().
+* All response header helper functions return a string rather than mixing Header objects and strings inconsistently
+* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle
+  directly via interfaces
+* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
+  but are a no-op until removed.
+* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a
+  `Guzzle\Service\Command\ArrayCommandInterface`.
+* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response
+  on a request while the request is still being transferred
+* The ability to case-insensitively search for header values
+* Guzzle\Http\Message\Header::hasExactHeader
+* Guzzle\Http\Message\Header::raw. Use getAll()
+* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object
+  instead.
+* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess
+* Added the ability to cast Model objects to a string to view debug information.
+
+## 3.5.0 - 2013-05-13
+
+* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times
+* Bug: Better cleanup of one-time events across the board (when an event is meant to fire once, it will now remove
+  itself from the EventDispatcher)
+* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values
+* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too
+* Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a
+  non-existent key
+* Bug: All __call() method arguments are now required (helps with mocking frameworks)
+* Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference
+  to help with refcount based garbage collection of resources created by sending a request
+* Deprecating ZF1 cache and log adapters. These will be removed in the next major version.
+* Deprecating `Response::getPreviousResponse()` (method signature still exists, but it's deprecated). Use the
+  HistoryPlugin for a history.
+* Added a `responseBody` alias for the `response_body` location
+* Refactored internals to no longer rely on Response::getRequest()
+* HistoryPlugin can now be cast to a string
+* HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests
+  and responses that are sent over the wire
+* Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects
+
+## 3.4.3 - 2013-04-30
+
+* Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response
+* Added a check to re-extract the temp cacert bundle from the phar before sending each request
+
+## 3.4.2 - 2013-04-29
+
+* Bug fix: Stream objects now work correctly with "a" and "a+" modes
+* Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present
+* Bug fix: AsyncPlugin no longer forces HEAD requests
+* Bug fix: DateTime timezones are now properly handled when using the service description schema formatter
+* Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails
+* Setting a response on a request will write to the custom request body from the response body if one is specified
+* LogPlugin now writes to php://output when STDERR is undefined
+* Added the ability to set multiple POST files for the same key in a single call
+* application/x-www-form-urlencoded POSTs now use the utf-8 charset by default
+* Added the ability to queue CurlExceptions to the MockPlugin
+* Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send)
+* Configuration loading now allows remote files
+
+## 3.4.1 - 2013-04-16
+
+* Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti
+  handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost.
+* Exceptions are now properly grouped when sending requests in parallel
+* Redirects are now properly aggregated when a multi transaction fails
+* Redirects now set the response on the original object even in the event of a failure
+* Bug fix: Model names are now properly set even when using $refs
+* Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax
+* Added support for oauth_callback in OAuth signatures
+* Added support for oauth_verifier in OAuth signatures
+* Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection
+
+## 3.4.0 - 2013-04-11
+
+* Bug fix: URLs are now resolved correctly based on http://tools.ietf.org/html/rfc3986#section-5.2. #289
+* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289
+* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263
+* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264.
+* Bug fix: Added `number` type to service descriptions.
+* Bug fix: empty parameters are removed from an OAuth signature
+* Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header
+* Bug fix: Fixed "array to string" error when validating a union of types in a service description
+* Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream
+* Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin.
+* Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs.
+* The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections.
+* Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if
+  the Content-Type can be determined based on the entity body or the path of the request.
+* Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder.
+* Added support for a PSR-3 LogAdapter.
+* Added a `command.after_prepare` event
+* Added `oauth_callback` parameter to the OauthPlugin
+* Added the ability to create a custom stream class when using a stream factory
+* Added a CachingEntityBody decorator
+* Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized.
+* The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar.
+* You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies
+* POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This
+  means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use
+  POST fields or files (the latter is only used when emulating a form POST in the browser).
+* Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest
+
+## 3.3.1 - 2013-03-10
+
+* Added the ability to create PHP streaming responses from HTTP requests
+* Bug fix: Running any filters when parsing response headers with service descriptions
+* Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing
+* Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across
+  response location visitors.
+* Bug fix: Removed the possibility of creating configuration files with circular dependencies
+* RequestFactory::create() now uses the key of a POST file when setting the POST file name
+* Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set
+
+## 3.3.0 - 2013-03-03
+
+* A large number of performance optimizations have been made
+* Bug fix: Added 'wb' as a valid write mode for streams
+* Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned
+* Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()`
+* BC: Removed `Guzzle\Http\Utils` class
+* BC: Setting a service description on a client will no longer modify the client's command factories.
+* BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using
+  the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io'
+* BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to
+  lowercase
+* Operation parameter objects are now lazy loaded internally
+* Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses
+* Added support for instantiating responseType=class responseClass classes. Classes must implement
+  `Guzzle\Service\Command\ResponseClassInterface`
+* Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These
+  additional properties also support locations and can be used to parse JSON responses where the outermost part of the
+  JSON is an array
+* Added support for nested renaming of JSON models (rename sentAs to name)
+* CachePlugin
+    * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error
+    * Debug headers can now added to cached response in the CachePlugin
+
+## 3.2.0 - 2013-02-14
+
+* CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients.
+* URLs with no path no longer contain a "/" by default
+* Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url.
+* BadResponseException no longer includes the full request and response message
+* Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface
+* Adding getResponseBody() to Guzzle\Http\Message\RequestInterface
+* Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription
+* Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list
+* xmlEncoding can now be customized for the XML declaration of a XML service description operation
+* Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value
+  aggregation and no longer uses callbacks
+* The URL encoding implementation of Guzzle\Http\QueryString can now be customized
+* Bug fix: Filters were not always invoked for array service description parameters
+* Bug fix: Redirects now use a target response body rather than a temporary response body
+* Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded
+* Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives
+
+## 3.1.2 - 2013-01-27
+
+* Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the
+  response body. For example, the XmlVisitor now parses the XML response into an array in the before() method.
+* Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent
+* CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444)
+* Fixed a bug where redirect responses were not chained correctly using getPreviousResponse()
+* Setting default headers on a client after setting the user-agent will not erase the user-agent setting
+
+## 3.1.1 - 2013-01-20
+
+* Adding wildcard support to Guzzle\Common\Collection::getPath()
+* Adding alias support to ServiceBuilder configs
+* Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface
+
+## 3.1.0 - 2013-01-12
+
+* BC: CurlException now extends from RequestException rather than BadResponseException
+* BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse()
+* Added getData to ServiceDescriptionInterface
+* Added context array to RequestInterface::setState()
+* Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http
+* Bug: Adding required content-type when JSON request visitor adds JSON to a command
+* Bug: Fixing the serialization of a service description with custom data
+* Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing
+  an array of successful and failed responses
+* Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection
+* Added Guzzle\Http\IoEmittingEntityBody
+* Moved command filtration from validators to location visitors
+* Added `extends` attributes to service description parameters
+* Added getModels to ServiceDescriptionInterface
+
+## 3.0.7 - 2012-12-19
+
+* Fixing phar detection when forcing a cacert to system if null or true
+* Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()`
+* Cleaning up `Guzzle\Common\Collection::inject` method
+* Adding a response_body location to service descriptions
+
+## 3.0.6 - 2012-12-09
+
+* CurlMulti performance improvements
+* Adding setErrorResponses() to Operation
+* composer.json tweaks
+
+## 3.0.5 - 2012-11-18
+
+* Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin
+* Bug: Response body can now be a string containing "0"
+* Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert
+* Bug: QueryString::fromString now properly parses query string parameters that contain equal signs
+* Added support for XML attributes in service description responses
+* DefaultRequestSerializer now supports array URI parameter values for URI template expansion
+* Added better mimetype guessing to requests and post files
+
+## 3.0.4 - 2012-11-11
+
+* Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value
+* Bug: Cookies can now be added that have a name, domain, or value set to "0"
+* Bug: Using the system cacert bundle when using the Phar
+* Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures
+* Enhanced cookie jar de-duplication
+* Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added
+* Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies
+* Added the ability to create any sort of hash for a stream rather than just an MD5 hash
+
+## 3.0.3 - 2012-11-04
+
+* Implementing redirects in PHP rather than cURL
+* Added PECL URI template extension and using as default parser if available
+* Bug: Fixed Content-Length parsing of Response factory
+* Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams.
+* Adding ToArrayInterface throughout library
+* Fixing OauthPlugin to create unique nonce values per request
+
+## 3.0.2 - 2012-10-25
+
+* Magic methods are enabled by default on clients
+* Magic methods return the result of a command
+* Service clients no longer require a base_url option in the factory
+* Bug: Fixed an issue with URI templates where null template variables were being expanded
+
+## 3.0.1 - 2012-10-22
+
+* Models can now be used like regular collection objects by calling filter, map, etc.
+* Models no longer require a Parameter structure or initial data in the constructor
+* Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator`
+
+## 3.0.0 - 2012-10-15
+
+* Rewrote service description format to be based on Swagger
+    * Now based on JSON schema
+    * Added nested input structures and nested response models
+    * Support for JSON and XML input and output models
+    * Renamed `commands` to `operations`
+    * Removed dot class notation
+    * Removed custom types
+* Broke the project into smaller top-level namespaces to be more component friendly
+* Removed support for XML configs and descriptions. Use arrays or JSON files.
+* Removed the Validation component and Inspector
+* Moved all cookie code to Guzzle\Plugin\Cookie
+* Magic methods on a Guzzle\Service\Client now return the command un-executed.
+* Calling getResult() or getResponse() on a command will lazily execute the command if needed.
+* Now shipping with cURL's CA certs and using it by default
+* Added previousResponse() method to response objects
+* No longer sending Accept and Accept-Encoding headers on every request
+* Only sending an Expect header by default when a payload is greater than 1MB
+* Added/moved client options:
+    * curl.blacklist to curl.option.blacklist
+    * Added ssl.certificate_authority
+* Added a Guzzle\Iterator component
+* Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin
+* Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin)
+* Added a more robust caching plugin
+* Added setBody to response objects
+* Updating LogPlugin to use a more flexible MessageFormatter
+* Added a completely revamped build process
+* Cleaning up Collection class and removing default values from the get method
+* Fixed ZF2 cache adapters
+
+## 2.8.8 - 2012-10-15
+
+* Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did
+
+## 2.8.7 - 2012-09-30
+
+* Bug: Fixed config file aliases for JSON includes
+* Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests
+* Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload
+* Bug: Hardening request and response parsing to account for missing parts
+* Bug: Fixed PEAR packaging
+* Bug: Fixed Request::getInfo
+* Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail
+* Adding the ability for the namespace Iterator factory to look in multiple directories
+* Added more getters/setters/removers from service descriptions
+* Added the ability to remove POST fields from OAuth signatures
+* OAuth plugin now supports 2-legged OAuth
+
+## 2.8.6 - 2012-09-05
+
+* Added the ability to modify and build service descriptions
+* Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command
+* Added a `json` parameter location
+* Now allowing dot notation for classes in the CacheAdapterFactory
+* Using the union of two arrays rather than an array_merge when extending service builder services and service params
+* Ensuring that a service is a string before doing strpos() checks on it when substituting services for references
+  in service builder config files.
+* Services defined in two different config files that include one another will by default replace the previously
+  defined service, but you can now create services that extend themselves and merge their settings over the previous
+* The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like
+  '_default' with a default JSON configuration file.
+
+## 2.8.5 - 2012-08-29
+
+* Bug: Suppressed empty arrays from URI templates
+* Bug: Added the missing $options argument from ServiceDescription::factory to enable caching
+* Added support for HTTP responses that do not contain a reason phrase in the start-line
+* AbstractCommand commands are now invokable
+* Added a way to get the data used when signing an Oauth request before a request is sent
+
+## 2.8.4 - 2012-08-15
+
+* Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin
+* Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable.
+* Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream
+* Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream
+* Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5())
+* Added additional response status codes
+* Removed SSL information from the default User-Agent header
+* DELETE requests can now send an entity body
+* Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries
+* Added the ability of the MockPlugin to consume mocked request bodies
+* LogPlugin now exposes request and response objects in the extras array
+
+## 2.8.3 - 2012-07-30
+
+* Bug: Fixed a case where empty POST requests were sent as GET requests
+* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body
+* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new
+* Added multiple inheritance to service description commands
+* Added an ApiCommandInterface and added `getParamNames()` and `hasParam()`
+* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything
+* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles
+
+## 2.8.2 - 2012-07-24
+
+* Bug: Query string values set to 0 are no longer dropped from the query string
+* Bug: A Collection object is no longer created each time a call is made to `Guzzle\Service\Command\AbstractCommand::getRequestHeaders()`
+* Bug: `+` is now treated as an encoded space when parsing query strings
+* QueryString and Collection performance improvements
+* Allowing dot notation for class paths in filters attribute of a service descriptions
+
+## 2.8.1 - 2012-07-16
+
+* Loosening Event Dispatcher dependency
+* POST redirects can now be customized using CURLOPT_POSTREDIR
+
+## 2.8.0 - 2012-07-15
+
+* BC: Guzzle\Http\Query
+    * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl)
+    * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding()
+    * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool)
+    * Changed the aggregation functions of QueryString to be static methods
+    * Can now use fromString() with querystrings that have a leading ?
+* cURL configuration values can be specified in service descriptions using `curl.` prefixed parameters
+* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body
+* Cookies are no longer URL decoded by default
+* Bug: URI template variables set to null are no longer expanded
+
+## 2.7.2 - 2012-07-02
+
+* BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser.
+* BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty()
+* CachePlugin now allows for a custom request parameter function to check if a request can be cached
+* Bug fix: CachePlugin now only caches GET and HEAD requests by default
+* Bug fix: Using header glue when transferring headers over the wire
+* Allowing deeply nested arrays for composite variables in URI templates
+* Batch divisors can now return iterators or arrays
+
+## 2.7.1 - 2012-06-26
+
+* Minor patch to update version number in UA string
+* Updating build process
+
+## 2.7.0 - 2012-06-25
+
+* BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes.
+* BC: Removed magic setX methods from commands
+* BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method
+* Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable.
+* Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity)
+* Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace
+* Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin
+* Added the ability to set POST fields and files in a service description
+* Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method
+* Adding a command.before_prepare event to clients
+* Added BatchClosureTransfer and BatchClosureDivisor
+* BatchTransferException now includes references to the batch divisor and transfer strategies
+* Fixed some tests so that they pass more reliably
+* Added Guzzle\Common\Log\ArrayLogAdapter
+
+## 2.6.6 - 2012-06-10
+
+* BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin
+* BC: Removing Guzzle\Service\Command\CommandSet
+* Adding generic batching system (replaces the batch queue plugin and command set)
+* Updating ZF cache and log adapters and now using ZF's composer repository
+* Bug: Setting the name of each ApiParam when creating through an ApiCommand
+* Adding result_type, result_doc, deprecated, and doc_url to service descriptions
+* Bug: Changed the default cookie header casing back to 'Cookie'
+
+## 2.6.5 - 2012-06-03
+
+* BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource()
+* BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from
+* BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data
+* BC: Renaming methods in the CookieJarInterface
+* Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations
+* Making the default glue for HTTP headers ';' instead of ','
+* Adding a removeValue to Guzzle\Http\Message\Header
+* Adding getCookies() to request interface.
+* Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber()
+
+## 2.6.4 - 2012-05-30
+
+* BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class.
+* BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand
+* Bug: Fixing magic method command calls on clients
+* Bug: Email constraint only validates strings
+* Bug: Aggregate POST fields when POST files are present in curl handle
+* Bug: Fixing default User-Agent header
+* Bug: Only appending or prepending parameters in commands if they are specified
+* Bug: Not requiring response reason phrases or status codes to match a predefined list of codes
+* Allowing the use of dot notation for class namespaces when using instance_of constraint
+* Added any_match validation constraint
+* Added an AsyncPlugin
+* Passing request object to the calculateWait method of the ExponentialBackoffPlugin
+* Allowing the result of a command object to be changed
+* Parsing location and type sub values when instantiating a service description rather than over and over at runtime
+
+## 2.6.3 - 2012-05-23
+
+* [BC] Guzzle\Common\FromConfigInterface no longer requires any config options.
+* [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields.
+* You can now use an array of data when creating PUT request bodies in the request factory.
+* Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable.
+* [Http] Adding support for Content-Type in multipart POST uploads per upload
+* [Http] Added support for uploading multiple files using the same name (foo[0], foo[1])
+* Adding more POST data operations for easier manipulation of POST data.
+* You can now set empty POST fields.
+* The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files.
+* Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate.
+* CS updates
+
+## 2.6.2 - 2012-05-19
+
+* [Http] Better handling of nested scope requests in CurlMulti.  Requests are now always prepares in the send() method rather than the addRequest() method.
+
+## 2.6.1 - 2012-05-19
+
+* [BC] Removing 'path' support in service descriptions.  Use 'uri'.
+* [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache.
+* [BC] Removing Guzzle\Common\NullObject.  Use https://github.com/mtdowling/NullObject if you need it.
+* [BC] Removing Guzzle\Common\XmlElement.
+* All commands, both dynamic and concrete, have ApiCommand objects.
+* Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits.
+* Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored.
+* Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible.
+
+## 2.6.0 - 2012-05-15
+
+* [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder
+* [BC] Executing a Command returns the result of the command rather than the command
+* [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed.
+* [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args.
+* [BC] Moving ResourceIterator* to Guzzle\Service\Resource
+* [BC] Completely refactored ResourceIterators to iterate over a cloned command object
+* [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate
+* [BC] Guzzle\Guzzle is now deprecated
+* Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject
+* Adding Guzzle\Version class to give version information about Guzzle
+* Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate()
+* Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data
+* ServiceDescription and ServiceBuilder are now cacheable using similar configs
+* Changing the format of XML and JSON service builder configs.  Backwards compatible.
+* Cleaned up Cookie parsing
+* Trimming the default Guzzle User-Agent header
+* Adding a setOnComplete() method to Commands that is called when a command completes
+* Keeping track of requests that were mocked in the MockPlugin
+* Fixed a caching bug in the CacheAdapterFactory
+* Inspector objects can be injected into a Command object
+* Refactoring a lot of code and tests to be case insensitive when dealing with headers
+* Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL
+* Adding the ability to set global option overrides to service builder configs
+* Adding the ability to include other service builder config files from within XML and JSON files
+* Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method.
+
+## 2.5.0 - 2012-05-08
+
+* Major performance improvements
+* [BC] Simplifying Guzzle\Common\Collection.  Please check to see if you are using features that are now deprecated.
+* [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component.
+* [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates.  Use "{}"
+* Added the ability to passed parameters to all requests created by a client
+* Added callback functionality to the ExponentialBackoffPlugin
+* Using microtime in ExponentialBackoffPlugin to allow more granular backoff strategies.
+* Rewinding request stream bodies when retrying requests
+* Exception is thrown when JSON response body cannot be decoded
+* Added configurable magic method calls to clients and commands.  This is off by default.
+* Fixed a defect that added a hash to every parsed URL part
+* Fixed duplicate none generation for OauthPlugin.
+* Emitting an event each time a client is generated by a ServiceBuilder
+* Using an ApiParams object instead of a Collection for parameters of an ApiCommand
+* cache.* request parameters should be renamed to params.cache.*
+* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc.). See CurlHandle.
+* Added the ability to disable type validation of service descriptions
+* ServiceDescriptions and ServiceBuilders are now Serializable

+ 19 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2011-2016 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 89 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/README.md

@@ -0,0 +1,89 @@
+Guzzle, PHP HTTP client
+=======================
+
+[![Build Status](https://travis-ci.org/guzzle/guzzle.svg?branch=master)](https://travis-ci.org/guzzle/guzzle)
+
+Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and
+trivial to integrate with web services.
+
+- Simple interface for building query strings, POST requests, streaming large
+  uploads, streaming large downloads, using HTTP cookies, uploading JSON data,
+  etc...
+- Can send both synchronous and asynchronous requests using the same interface.
+- Uses PSR-7 interfaces for requests, responses, and streams. This allows you
+  to utilize other PSR-7 compatible libraries with Guzzle.
+- Abstracts away the underlying HTTP transport, allowing you to write
+  environment and transport agnostic code; i.e., no hard dependency on cURL,
+  PHP streams, sockets, or non-blocking event loops.
+- Middleware system allows you to augment and compose client behavior.
+
+```php
+$client = new \GuzzleHttp\Client();
+$res = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle');
+echo $res->getStatusCode();
+// 200
+echo $res->getHeaderLine('content-type');
+// 'application/json; charset=utf8'
+echo $res->getBody();
+// '{"id": 1420053, "name": "guzzle", ...}'
+
+// Send an asynchronous request.
+$request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org');
+$promise = $client->sendAsync($request)->then(function ($response) {
+    echo 'I completed! ' . $response->getBody();
+});
+$promise->wait();
+```
+
+## Help and docs
+
+- [Documentation](http://guzzlephp.org/)
+- [Stack Overflow](http://stackoverflow.com/questions/tagged/guzzle)
+- [Gitter](https://gitter.im/guzzle/guzzle)
+
+
+## Installing Guzzle
+
+The recommended way to install Guzzle is through
+[Composer](http://getcomposer.org).
+
+```bash
+# Install Composer
+curl -sS https://getcomposer.org/installer | php
+```
+
+Next, run the Composer command to install the latest stable version of Guzzle:
+
+```bash
+php composer.phar require guzzlehttp/guzzle
+```
+
+After installing, you need to require Composer's autoloader:
+
+```php
+require 'vendor/autoload.php';
+```
+
+You can then later update Guzzle using composer:
+
+ ```bash
+composer.phar update
+ ```
+
+
+## Version Guidance
+
+| Version | Status     | Packagist           | Namespace    | Repo                | Docs                | PSR-7 | PHP Version |
+|---------|------------|---------------------|--------------|---------------------|---------------------|-------|-------------|
+| 3.x     | EOL        | `guzzle/guzzle`     | `Guzzle`     | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No    | >= 5.3.3    |
+| 4.x     | EOL        | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A                 | No    | >= 5.4      |
+| 5.x     | Maintained | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No    | >= 5.4      |
+| 6.x     | Latest     | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes   | >= 5.5      |
+
+[guzzle-3-repo]: https://github.com/guzzle/guzzle3
+[guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x
+[guzzle-5-repo]: https://github.com/guzzle/guzzle/tree/5.3
+[guzzle-6-repo]: https://github.com/guzzle/guzzle
+[guzzle-3-docs]: http://guzzle3.readthedocs.org/en/latest/
+[guzzle-5-docs]: http://guzzle.readthedocs.org/en/5.3/
+[guzzle-6-docs]: http://guzzle.readthedocs.org/en/latest/

+ 1203 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/UPGRADING.md

@@ -0,0 +1,1203 @@
+Guzzle Upgrade Guide
+====================
+
+5.0 to 6.0
+----------
+
+Guzzle now uses [PSR-7](http://www.php-fig.org/psr/psr-7/) for HTTP messages.
+Due to the fact that these messages are immutable, this prompted a refactoring
+of Guzzle to use a middleware based system rather than an event system. Any
+HTTP message interaction (e.g., `GuzzleHttp\Message\Request`) need to be
+updated to work with the new immutable PSR-7 request and response objects. Any
+event listeners or subscribers need to be updated to become middleware
+functions that wrap handlers (or are injected into a
+`GuzzleHttp\HandlerStack`).
+
+- Removed `GuzzleHttp\BatchResults`
+- Removed `GuzzleHttp\Collection`
+- Removed `GuzzleHttp\HasDataTrait`
+- Removed `GuzzleHttp\ToArrayInterface`
+- The `guzzlehttp/streams` dependency has been removed. Stream functionality
+  is now present in the `GuzzleHttp\Psr7` namespace provided by the
+  `guzzlehttp/psr7` package.
+- Guzzle no longer uses ReactPHP promises and now uses the
+  `guzzlehttp/promises` library. We use a custom promise library for three
+  significant reasons:
+  1. React promises (at the time of writing this) are recursive. Promise
+     chaining and promise resolution will eventually blow the stack. Guzzle
+     promises are not recursive as they use a sort of trampolining technique.
+     Note: there has been movement in the React project to modify promises to
+     no longer utilize recursion.
+  2. Guzzle needs to have the ability to synchronously block on a promise to
+     wait for a result. Guzzle promises allows this functionality (and does
+     not require the use of recursion).
+  3. Because we need to be able to wait on a result, doing so using React
+     promises requires wrapping react promises with RingPHP futures. This
+     overhead is no longer needed, reducing stack sizes, reducing complexity,
+     and improving performance.
+- `GuzzleHttp\Mimetypes` has been moved to a function in
+  `GuzzleHttp\Psr7\mimetype_from_extension` and
+  `GuzzleHttp\Psr7\mimetype_from_filename`.
+- `GuzzleHttp\Query` and `GuzzleHttp\QueryParser` have been removed. Query
+  strings must now be passed into request objects as strings, or provided to
+  the `query` request option when creating requests with clients. The `query`
+  option uses PHP's `http_build_query` to convert an array to a string. If you
+  need a different serialization technique, you will need to pass the query
+  string in as a string. There are a couple helper functions that will make
+  working with query strings easier: `GuzzleHttp\Psr7\parse_query` and
+  `GuzzleHttp\Psr7\build_query`.
+- Guzzle no longer has a dependency on RingPHP. Due to the use of a middleware
+  system based on PSR-7, using RingPHP and it's middleware system as well adds
+  more complexity than the benefits it provides. All HTTP handlers that were
+  present in RingPHP have been modified to work directly with PSR-7 messages
+  and placed in the `GuzzleHttp\Handler` namespace. This significantly reduces
+  complexity in Guzzle, removes a dependency, and improves performance. RingPHP
+  will be maintained for Guzzle 5 support, but will no longer be a part of
+  Guzzle 6.
+- As Guzzle now uses a middleware based systems the event system and RingPHP
+  integration has been removed. Note: while the event system has been removed,
+  it is possible to add your own type of event system that is powered by the
+  middleware system.
+  - Removed the `Event` namespace.
+  - Removed the `Subscriber` namespace.
+  - Removed `Transaction` class
+  - Removed `RequestFsm`
+  - Removed `RingBridge`
+  - `GuzzleHttp\Subscriber\Cookie` is now provided by
+    `GuzzleHttp\Middleware::cookies`
+  - `GuzzleHttp\Subscriber\HttpError` is now provided by
+    `GuzzleHttp\Middleware::httpError`
+  - `GuzzleHttp\Subscriber\History` is now provided by
+    `GuzzleHttp\Middleware::history`
+  - `GuzzleHttp\Subscriber\Mock` is now provided by
+    `GuzzleHttp\Handler\MockHandler`
+  - `GuzzleHttp\Subscriber\Prepare` is now provided by
+    `GuzzleHttp\PrepareBodyMiddleware`
+  - `GuzzleHttp\Subscriber\Redirect` is now provided by
+    `GuzzleHttp\RedirectMiddleware`
+- Guzzle now uses `Psr\Http\Message\UriInterface` (implements in
+  `GuzzleHttp\Psr7\Uri`) for URI support. `GuzzleHttp\Url` is now gone.
+- Static functions in `GuzzleHttp\Utils` have been moved to namespaced
+  functions under the `GuzzleHttp` namespace. This requires either a Composer
+  based autoloader or you to include functions.php.
+- `GuzzleHttp\ClientInterface::getDefaultOption` has been renamed to
+  `GuzzleHttp\ClientInterface::getConfig`.
+- `GuzzleHttp\ClientInterface::setDefaultOption` has been removed.
+- The `json` and `xml` methods of response objects has been removed. With the
+  migration to strictly adhering to PSR-7 as the interface for Guzzle messages,
+  adding methods to message interfaces would actually require Guzzle messages
+  to extend from PSR-7 messages rather then work with them directly.
+
+## Migrating to middleware
+
+The change to PSR-7 unfortunately required significant refactoring to Guzzle
+due to the fact that PSR-7 messages are immutable. Guzzle 5 relied on an event
+system from plugins. The event system relied on mutability of HTTP messages and
+side effects in order to work. With immutable messages, you have to change your
+workflow to become more about either returning a value (e.g., functional
+middlewares) or setting a value on an object. Guzzle v6 has chosen the
+functional middleware approach.
+
+Instead of using the event system to listen for things like the `before` event,
+you now create a stack based middleware function that intercepts a request on
+the way in and the promise of the response on the way out. This is a much
+simpler and more predictable approach than the event system and works nicely
+with PSR-7 middleware. Due to the use of promises, the middleware system is
+also asynchronous.
+
+v5:
+
+```php
+use GuzzleHttp\Event\BeforeEvent;
+$client = new GuzzleHttp\Client();
+// Get the emitter and listen to the before event.
+$client->getEmitter()->on('before', function (BeforeEvent $e) {
+    // Guzzle v5 events relied on mutation
+    $e->getRequest()->setHeader('X-Foo', 'Bar');
+});
+```
+
+v6:
+
+In v6, you can modify the request before it is sent using the `mapRequest`
+middleware. The idiomatic way in v6 to modify the request/response lifecycle is
+to setup a handler middleware stack up front and inject the handler into a
+client.
+
+```php
+use GuzzleHttp\Middleware;
+// Create a handler stack that has all of the default middlewares attached
+$handler = GuzzleHttp\HandlerStack::create();
+// Push the handler onto the handler stack
+$handler->push(Middleware::mapRequest(function (RequestInterface $request) {
+    // Notice that we have to return a request object
+    return $request->withHeader('X-Foo', 'Bar');
+}));
+// Inject the handler into the client
+$client = new GuzzleHttp\Client(['handler' => $handler]);
+```
+
+## POST Requests
+
+This version added the [`form_params`](http://guzzle.readthedocs.org/en/latest/request-options.html#form_params)
+and `multipart` request options. `form_params` is an associative array of
+strings or array of strings and is used to serialize an
+`application/x-www-form-urlencoded` POST request. The
+[`multipart`](http://guzzle.readthedocs.org/en/latest/request-options.html#multipart)
+option is now used to send a multipart/form-data POST request.
+
+`GuzzleHttp\Post\PostFile` has been removed. Use the `multipart` option to add
+POST files to a multipart/form-data request.
+
+The `body` option no longer accepts an array to send POST requests. Please use
+`multipart` or `form_params` instead.
+
+The `base_url` option has been renamed to `base_uri`.
+
+4.x to 5.0
+----------
+
+## Rewritten Adapter Layer
+
+Guzzle now uses [RingPHP](http://ringphp.readthedocs.org/en/latest) to send
+HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor
+is still supported, but it has now been renamed to `handler`. Instead of
+passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP
+`callable` that follows the RingPHP specification.
+
+## Removed Fluent Interfaces
+
+[Fluent interfaces were removed](http://ocramius.github.io/blog/fluent-interfaces-are-evil)
+from the following classes:
+
+- `GuzzleHttp\Collection`
+- `GuzzleHttp\Url`
+- `GuzzleHttp\Query`
+- `GuzzleHttp\Post\PostBody`
+- `GuzzleHttp\Cookie\SetCookie`
+
+## Removed functions.php
+
+Removed "functions.php", so that Guzzle is truly PSR-4 compliant. The following
+functions can be used as replacements.
+
+- `GuzzleHttp\json_decode` -> `GuzzleHttp\Utils::jsonDecode`
+- `GuzzleHttp\get_path` -> `GuzzleHttp\Utils::getPath`
+- `GuzzleHttp\Utils::setPath` -> `GuzzleHttp\set_path`
+- `GuzzleHttp\Pool::batch` -> `GuzzleHttp\batch`. This function is, however,
+  deprecated in favor of using `GuzzleHttp\Pool::batch()`.
+
+The "procedural" global client has been removed with no replacement (e.g.,
+`GuzzleHttp\get()`, `GuzzleHttp\post()`, etc.). Use a `GuzzleHttp\Client`
+object as a replacement.
+
+## `throwImmediately` has been removed
+
+The concept of "throwImmediately" has been removed from exceptions and error
+events. This control mechanism was used to stop a transfer of concurrent
+requests from completing. This can now be handled by throwing the exception or
+by cancelling a pool of requests or each outstanding future request
+individually.
+
+## headers event has been removed
+
+Removed the "headers" event. This event was only useful for changing the
+body a response once the headers of the response were known. You can implement
+a similar behavior in a number of ways. One example might be to use a
+FnStream that has access to the transaction being sent. For example, when the
+first byte is written, you could check if the response headers match your
+expectations, and if so, change the actual stream body that is being
+written to.
+
+## Updates to HTTP Messages
+
+Removed the `asArray` parameter from
+`GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header
+value as an array, then use the newly added `getHeaderAsArray()` method of
+`MessageInterface`. This change makes the Guzzle interfaces compatible with
+the PSR-7 interfaces.
+
+3.x to 4.0
+----------
+
+## Overarching changes:
+
+- Now requires PHP 5.4 or greater.
+- No longer requires cURL to send requests.
+- Guzzle no longer wraps every exception it throws. Only exceptions that are
+  recoverable are now wrapped by Guzzle.
+- Various namespaces have been removed or renamed.
+- No longer requiring the Symfony EventDispatcher. A custom event dispatcher
+  based on the Symfony EventDispatcher is
+  now utilized in `GuzzleHttp\Event\EmitterInterface` (resulting in significant
+  speed and functionality improvements).
+
+Changes per Guzzle 3.x namespace are described below.
+
+## Batch
+
+The `Guzzle\Batch` namespace has been removed. This is best left to
+third-parties to implement on top of Guzzle's core HTTP library.
+
+## Cache
+
+The `Guzzle\Cache` namespace has been removed. (Todo: No suitable replacement
+has been implemented yet, but hoping to utilize a PSR cache interface).
+
+## Common
+
+- Removed all of the wrapped exceptions. It's better to use the standard PHP
+  library for unrecoverable exceptions.
+- `FromConfigInterface` has been removed.
+- `Guzzle\Common\Version` has been removed. The VERSION constant can be found
+  at `GuzzleHttp\ClientInterface::VERSION`.
+
+### Collection
+
+- `getAll` has been removed. Use `toArray` to convert a collection to an array.
+- `inject` has been removed.
+- `keySearch` has been removed.
+- `getPath` no longer supports wildcard expressions. Use something better like
+  JMESPath for this.
+- `setPath` now supports appending to an existing array via the `[]` notation.
+
+### Events
+
+Guzzle no longer requires Symfony's EventDispatcher component. Guzzle now uses
+`GuzzleHttp\Event\Emitter`.
+
+- `Symfony\Component\EventDispatcher\EventDispatcherInterface` is replaced by
+  `GuzzleHttp\Event\EmitterInterface`.
+- `Symfony\Component\EventDispatcher\EventDispatcher` is replaced by
+  `GuzzleHttp\Event\Emitter`.
+- `Symfony\Component\EventDispatcher\Event` is replaced by
+  `GuzzleHttp\Event\Event`, and Guzzle now has an EventInterface in
+  `GuzzleHttp\Event\EventInterface`.
+- `AbstractHasDispatcher` has moved to a trait, `HasEmitterTrait`, and
+  `HasDispatcherInterface` has moved to `HasEmitterInterface`. Retrieving the
+  event emitter of a request, client, etc. now uses the `getEmitter` method
+  rather than the `getDispatcher` method.
+
+#### Emitter
+
+- Use the `once()` method to add a listener that automatically removes itself
+  the first time it is invoked.
+- Use the `listeners()` method to retrieve a list of event listeners rather than
+  the `getListeners()` method.
+- Use `emit()` instead of `dispatch()` to emit an event from an emitter.
+- Use `attach()` instead of `addSubscriber()` and `detach()` instead of
+  `removeSubscriber()`.
+
+```php
+$mock = new Mock();
+// 3.x
+$request->getEventDispatcher()->addSubscriber($mock);
+$request->getEventDispatcher()->removeSubscriber($mock);
+// 4.x
+$request->getEmitter()->attach($mock);
+$request->getEmitter()->detach($mock);
+```
+
+Use the `on()` method to add a listener rather than the `addListener()` method.
+
+```php
+// 3.x
+$request->getEventDispatcher()->addListener('foo', function (Event $event) { /* ... */ } );
+// 4.x
+$request->getEmitter()->on('foo', function (Event $event, $name) { /* ... */ } );
+```
+
+## Http
+
+### General changes
+
+- The cacert.pem certificate has been moved to `src/cacert.pem`.
+- Added the concept of adapters that are used to transfer requests over the
+  wire.
+- Simplified the event system.
+- Sending requests in parallel is still possible, but batching is no longer a
+  concept of the HTTP layer. Instead, you must use the `complete` and `error`
+  events to asynchronously manage parallel request transfers.
+- `Guzzle\Http\Url` has moved to `GuzzleHttp\Url`.
+- `Guzzle\Http\QueryString` has moved to `GuzzleHttp\Query`.
+- QueryAggregators have been rewritten so that they are simply callable
+  functions.
+- `GuzzleHttp\StaticClient` has been removed. Use the functions provided in
+  `functions.php` for an easy to use static client instance.
+- Exceptions in `GuzzleHttp\Exception` have been updated to all extend from
+  `GuzzleHttp\Exception\TransferException`.
+
+### Client
+
+Calling methods like `get()`, `post()`, `head()`, etc. no longer create and
+return a request, but rather creates a request, sends the request, and returns
+the response.
+
+```php
+// 3.0
+$request = $client->get('/');
+$response = $request->send();
+
+// 4.0
+$response = $client->get('/');
+
+// or, to mirror the previous behavior
+$request = $client->createRequest('GET', '/');
+$response = $client->send($request);
+```
+
+`GuzzleHttp\ClientInterface` has changed.
+
+- The `send` method no longer accepts more than one request. Use `sendAll` to
+  send multiple requests in parallel.
+- `setUserAgent()` has been removed. Use a default request option instead. You
+  could, for example, do something like:
+  `$client->setConfig('defaults/headers/User-Agent', 'Foo/Bar ' . $client::getDefaultUserAgent())`.
+- `setSslVerification()` has been removed. Use default request options instead,
+  like `$client->setConfig('defaults/verify', true)`.
+
+`GuzzleHttp\Client` has changed.
+
+- The constructor now accepts only an associative array. You can include a
+  `base_url` string or array to use a URI template as the base URL of a client.
+  You can also specify a `defaults` key that is an associative array of default
+  request options. You can pass an `adapter` to use a custom adapter,
+  `batch_adapter` to use a custom adapter for sending requests in parallel, or
+  a `message_factory` to change the factory used to create HTTP requests and
+  responses.
+- The client no longer emits a `client.create_request` event.
+- Creating requests with a client no longer automatically utilize a URI
+  template. You must pass an array into a creational method (e.g.,
+  `createRequest`, `get`, `put`, etc.) in order to expand a URI template.
+
+### Messages
+
+Messages no longer have references to their counterparts (i.e., a request no
+longer has a reference to it's response, and a response no loger has a
+reference to its request). This association is now managed through a
+`GuzzleHttp\Adapter\TransactionInterface` object. You can get references to
+these transaction objects using request events that are emitted over the
+lifecycle of a request.
+
+#### Requests with a body
+
+- `GuzzleHttp\Message\EntityEnclosingRequest` and
+  `GuzzleHttp\Message\EntityEnclosingRequestInterface` have been removed. The
+  separation between requests that contain a body and requests that do not
+  contain a body has been removed, and now `GuzzleHttp\Message\RequestInterface`
+  handles both use cases.
+- Any method that previously accepts a `GuzzleHttp\Response` object now accept a
+  `GuzzleHttp\Message\ResponseInterface`.
+- `GuzzleHttp\Message\RequestFactoryInterface` has been renamed to
+  `GuzzleHttp\Message\MessageFactoryInterface`. This interface is used to create
+  both requests and responses and is implemented in
+  `GuzzleHttp\Message\MessageFactory`.
+- POST field and file methods have been removed from the request object. You
+  must now use the methods made available to `GuzzleHttp\Post\PostBodyInterface`
+  to control the format of a POST body. Requests that are created using a
+  standard `GuzzleHttp\Message\MessageFactoryInterface` will automatically use
+  a `GuzzleHttp\Post\PostBody` body if the body was passed as an array or if
+  the method is POST and no body is provided.
+
+```php
+$request = $client->createRequest('POST', '/');
+$request->getBody()->setField('foo', 'bar');
+$request->getBody()->addFile(new PostFile('file_key', fopen('/path/to/content', 'r')));
+```
+
+#### Headers
+
+- `GuzzleHttp\Message\Header` has been removed. Header values are now simply
+  represented by an array of values or as a string. Header values are returned
+  as a string by default when retrieving a header value from a message. You can
+  pass an optional argument of `true` to retrieve a header value as an array
+  of strings instead of a single concatenated string.
+- `GuzzleHttp\PostFile` and `GuzzleHttp\PostFileInterface` have been moved to
+  `GuzzleHttp\Post`. This interface has been simplified and now allows the
+  addition of arbitrary headers.
+- Custom headers like `GuzzleHttp\Message\Header\Link` have been removed. Most
+  of the custom headers are now handled separately in specific
+  subscribers/plugins, and `GuzzleHttp\Message\HeaderValues::parseParams()` has
+  been updated to properly handle headers that contain parameters (like the
+  `Link` header).
+
+#### Responses
+
+- `GuzzleHttp\Message\Response::getInfo()` and
+  `GuzzleHttp\Message\Response::setInfo()` have been removed. Use the event
+  system to retrieve this type of information.
+- `GuzzleHttp\Message\Response::getRawHeaders()` has been removed.
+- `GuzzleHttp\Message\Response::getMessage()` has been removed.
+- `GuzzleHttp\Message\Response::calculateAge()` and other cache specific
+  methods have moved to the CacheSubscriber.
+- Header specific helper functions like `getContentMd5()` have been removed.
+  Just use `getHeader('Content-MD5')` instead.
+- `GuzzleHttp\Message\Response::setRequest()` and
+  `GuzzleHttp\Message\Response::getRequest()` have been removed. Use the event
+  system to work with request and response objects as a transaction.
+- `GuzzleHttp\Message\Response::getRedirectCount()` has been removed. Use the
+  Redirect subscriber instead.
+- `GuzzleHttp\Message\Response::isSuccessful()` and other related methods have
+  been removed. Use `getStatusCode()` instead.
+
+#### Streaming responses
+
+Streaming requests can now be created by a client directly, returning a
+`GuzzleHttp\Message\ResponseInterface` object that contains a body stream
+referencing an open PHP HTTP stream.
+
+```php
+// 3.0
+use Guzzle\Stream\PhpStreamRequestFactory;
+$request = $client->get('/');
+$factory = new PhpStreamRequestFactory();
+$stream = $factory->fromRequest($request);
+$data = $stream->read(1024);
+
+// 4.0
+$response = $client->get('/', ['stream' => true]);
+// Read some data off of the stream in the response body
+$data = $response->getBody()->read(1024);
+```
+
+#### Redirects
+
+The `configureRedirects()` method has been removed in favor of a
+`allow_redirects` request option.
+
+```php
+// Standard redirects with a default of a max of 5 redirects
+$request = $client->createRequest('GET', '/', ['allow_redirects' => true]);
+
+// Strict redirects with a custom number of redirects
+$request = $client->createRequest('GET', '/', [
+    'allow_redirects' => ['max' => 5, 'strict' => true]
+]);
+```
+
+#### EntityBody
+
+EntityBody interfaces and classes have been removed or moved to
+`GuzzleHttp\Stream`. All classes and interfaces that once required
+`GuzzleHttp\EntityBodyInterface` now require
+`GuzzleHttp\Stream\StreamInterface`. Creating a new body for a request no
+longer uses `GuzzleHttp\EntityBody::factory` but now uses
+`GuzzleHttp\Stream\Stream::factory` or even better:
+`GuzzleHttp\Stream\create()`.
+
+- `Guzzle\Http\EntityBodyInterface` is now `GuzzleHttp\Stream\StreamInterface`
+- `Guzzle\Http\EntityBody` is now `GuzzleHttp\Stream\Stream`
+- `Guzzle\Http\CachingEntityBody` is now `GuzzleHttp\Stream\CachingStream`
+- `Guzzle\Http\ReadLimitEntityBody` is now `GuzzleHttp\Stream\LimitStream`
+- `Guzzle\Http\IoEmittyinEntityBody` has been removed.
+
+#### Request lifecycle events
+
+Requests previously submitted a large number of requests. The number of events
+emitted over the lifecycle of a request has been significantly reduced to make
+it easier to understand how to extend the behavior of a request. All events
+emitted during the lifecycle of a request now emit a custom
+`GuzzleHttp\Event\EventInterface` object that contains context providing
+methods and a way in which to modify the transaction at that specific point in
+time (e.g., intercept the request and set a response on the transaction).
+
+- `request.before_send` has been renamed to `before` and now emits a
+  `GuzzleHttp\Event\BeforeEvent`
+- `request.complete` has been renamed to `complete` and now emits a
+  `GuzzleHttp\Event\CompleteEvent`.
+- `request.sent` has been removed. Use `complete`.
+- `request.success` has been removed. Use `complete`.
+- `error` is now an event that emits a `GuzzleHttp\Event\ErrorEvent`.
+- `request.exception` has been removed. Use `error`.
+- `request.receive.status_line` has been removed.
+- `curl.callback.progress` has been removed. Use a custom `StreamInterface` to
+  maintain a status update.
+- `curl.callback.write` has been removed. Use a custom `StreamInterface` to
+  intercept writes.
+- `curl.callback.read` has been removed. Use a custom `StreamInterface` to
+  intercept reads.
+
+`headers` is a new event that is emitted after the response headers of a
+request have been received before the body of the response is downloaded. This
+event emits a `GuzzleHttp\Event\HeadersEvent`.
+
+You can intercept a request and inject a response using the `intercept()` event
+of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and
+`GuzzleHttp\Event\ErrorEvent` event.
+
+See: http://docs.guzzlephp.org/en/latest/events.html
+
+## Inflection
+
+The `Guzzle\Inflection` namespace has been removed. This is not a core concern
+of Guzzle.
+
+## Iterator
+
+The `Guzzle\Iterator` namespace has been removed.
+
+- `Guzzle\Iterator\AppendIterator`, `Guzzle\Iterator\ChunkedIterator`, and
+  `Guzzle\Iterator\MethodProxyIterator` are nice, but not a core requirement of
+  Guzzle itself.
+- `Guzzle\Iterator\FilterIterator` is no longer needed because an equivalent
+  class is shipped with PHP 5.4.
+- `Guzzle\Iterator\MapIterator` is not really needed when using PHP 5.5 because
+  it's easier to just wrap an iterator in a generator that maps values.
+
+For a replacement of these iterators, see https://github.com/nikic/iter
+
+## Log
+
+The LogPlugin has moved to https://github.com/guzzle/log-subscriber. The
+`Guzzle\Log` namespace has been removed. Guzzle now relies on
+`Psr\Log\LoggerInterface` for all logging. The MessageFormatter class has been
+moved to `GuzzleHttp\Subscriber\Log\Formatter`.
+
+## Parser
+
+The `Guzzle\Parser` namespace has been removed. This was previously used to
+make it possible to plug in custom parsers for cookies, messages, URI
+templates, and URLs; however, this level of complexity is not needed in Guzzle
+so it has been removed.
+
+- Cookie: Cookie parsing logic has been moved to
+  `GuzzleHttp\Cookie\SetCookie::fromString`.
+- Message: Message parsing logic for both requests and responses has been moved
+  to `GuzzleHttp\Message\MessageFactory::fromMessage`. Message parsing is only
+  used in debugging or deserializing messages, so it doesn't make sense for
+  Guzzle as a library to add this level of complexity to parsing messages.
+- UriTemplate: URI template parsing has been moved to
+  `GuzzleHttp\UriTemplate`. The Guzzle library will automatically use the PECL
+  URI template library if it is installed.
+- Url: URL parsing is now performed in `GuzzleHttp\Url::fromString` (previously
+  it was `Guzzle\Http\Url::factory()`). If custom URL parsing is necessary,
+  then developers are free to subclass `GuzzleHttp\Url`.
+
+## Plugin
+
+The `Guzzle\Plugin` namespace has been renamed to `GuzzleHttp\Subscriber`.
+Several plugins are shipping with the core Guzzle library under this namespace.
+
+- `GuzzleHttp\Subscriber\Cookie`: Replaces the old CookiePlugin. Cookie jar
+  code has moved to `GuzzleHttp\Cookie`.
+- `GuzzleHttp\Subscriber\History`: Replaces the old HistoryPlugin.
+- `GuzzleHttp\Subscriber\HttpError`: Throws errors when a bad HTTP response is
+  received.
+- `GuzzleHttp\Subscriber\Mock`: Replaces the old MockPlugin.
+- `GuzzleHttp\Subscriber\Prepare`: Prepares the body of a request just before
+  sending. This subscriber is attached to all requests by default.
+- `GuzzleHttp\Subscriber\Redirect`: Replaces the RedirectPlugin.
+
+The following plugins have been removed (third-parties are free to re-implement
+these if needed):
+
+- `GuzzleHttp\Plugin\Async` has been removed.
+- `GuzzleHttp\Plugin\CurlAuth` has been removed.
+- `GuzzleHttp\Plugin\ErrorResponse\ErrorResponsePlugin` has been removed. This
+  functionality should instead be implemented with event listeners that occur
+  after normal response parsing occurs in the guzzle/command package.
+
+The following plugins are not part of the core Guzzle package, but are provided
+in separate repositories:
+
+- `Guzzle\Http\Plugin\BackoffPlugin` has been rewritten to be much simpler
+  to build custom retry policies using simple functions rather than various
+  chained classes. See: https://github.com/guzzle/retry-subscriber
+- `Guzzle\Http\Plugin\Cache\CachePlugin` has moved to
+  https://github.com/guzzle/cache-subscriber
+- `Guzzle\Http\Plugin\Log\LogPlugin` has moved to
+  https://github.com/guzzle/log-subscriber
+- `Guzzle\Http\Plugin\Md5\Md5Plugin` has moved to
+  https://github.com/guzzle/message-integrity-subscriber
+- `Guzzle\Http\Plugin\Mock\MockPlugin` has moved to
+  `GuzzleHttp\Subscriber\MockSubscriber`.
+- `Guzzle\Http\Plugin\Oauth\OauthPlugin` has moved to
+  https://github.com/guzzle/oauth-subscriber
+
+## Service
+
+The service description layer of Guzzle has moved into two separate packages:
+
+- http://github.com/guzzle/command Provides a high level abstraction over web
+  services by representing web service operations using commands.
+- http://github.com/guzzle/guzzle-services Provides an implementation of
+  guzzle/command that provides request serialization and response parsing using
+  Guzzle service descriptions.
+
+## Stream
+
+Stream have moved to a separate package available at
+https://github.com/guzzle/streams.
+
+`Guzzle\Stream\StreamInterface` has been given a large update to cleanly take
+on the responsibilities of `Guzzle\Http\EntityBody` and
+`Guzzle\Http\EntityBodyInterface` now that they have been removed. The number
+of methods implemented by the `StreamInterface` has been drastically reduced to
+allow developers to more easily extend and decorate stream behavior.
+
+## Removed methods from StreamInterface
+
+- `getStream` and `setStream` have been removed to better encapsulate streams.
+- `getMetadata` and `setMetadata` have been removed in favor of
+  `GuzzleHttp\Stream\MetadataStreamInterface`.
+- `getWrapper`, `getWrapperData`, `getStreamType`, and `getUri` have all been
+  removed. This data is accessible when
+  using streams that implement `GuzzleHttp\Stream\MetadataStreamInterface`.
+- `rewind` has been removed. Use `seek(0)` for a similar behavior.
+
+## Renamed methods
+
+- `detachStream` has been renamed to `detach`.
+- `feof` has been renamed to `eof`.
+- `ftell` has been renamed to `tell`.
+- `readLine` has moved from an instance method to a static class method of
+  `GuzzleHttp\Stream\Stream`.
+
+## Metadata streams
+
+`GuzzleHttp\Stream\MetadataStreamInterface` has been added to denote streams
+that contain additional metadata accessible via `getMetadata()`.
+`GuzzleHttp\Stream\StreamInterface::getMetadata` and
+`GuzzleHttp\Stream\StreamInterface::setMetadata` have been removed.
+
+## StreamRequestFactory
+
+The entire concept of the StreamRequestFactory has been removed. The way this
+was used in Guzzle 3 broke the actual interface of sending streaming requests
+(instead of getting back a Response, you got a StreamInterface). Streaming
+PHP requests are now implemented through the `GuzzleHttp\Adapter\StreamAdapter`.
+
+3.6 to 3.7
+----------
+
+### Deprecations
+
+- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.:
+
+```php
+\Guzzle\Common\Version::$emitWarnings = true;
+```
+
+The following APIs and options have been marked as deprecated:
+
+- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead.
+- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
+- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
+- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead.
+- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead.
+- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated
+- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client.
+- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8.
+- Marked `Guzzle\Common\Collection::inject()` as deprecated.
+- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use
+  `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or
+  `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));`
+
+3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational
+request methods. When paired with a client's configuration settings, these options allow you to specify default settings
+for various aspects of a request. Because these options make other previous configuration options redundant, several
+configuration options and methods of a client and AbstractCommand have been deprecated.
+
+- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`.
+- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`.
+- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')`
+- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0
+
+        $command = $client->getCommand('foo', array(
+            'command.headers' => array('Test' => '123'),
+            'command.response_body' => '/path/to/file'
+        ));
+
+        // Should be changed to:
+
+        $command = $client->getCommand('foo', array(
+            'command.request_options' => array(
+                'headers' => array('Test' => '123'),
+                'save_as' => '/path/to/file'
+            )
+        ));
+
+### Interface changes
+
+Additions and changes (you will need to update any implementations or subclasses you may have created):
+
+- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`:
+  createRequest, head, delete, put, patch, post, options, prepareRequest
+- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()`
+- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface`
+- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to
+  `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a
+  resource, string, or EntityBody into the $options parameter to specify the download location of the response.
+- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a
+  default `array()`
+- Added `Guzzle\Stream\StreamInterface::isRepeatable`
+- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods.
+
+The following methods were removed from interfaces. All of these methods are still available in the concrete classes
+that implement them, but you should update your code to use alternative methods:
+
+- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use
+  `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or
+  `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or
+  `$client->setDefaultOption('headers/{header_name}', 'value')`. or
+  `$client->setDefaultOption('headers', array('header_name' => 'value'))`.
+- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`.
+- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail.
+- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail.
+- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail.
+- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin.
+- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin.
+- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin.
+
+### Cache plugin breaking changes
+
+- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a
+  CacheStorageInterface. These two objects and interface will be removed in a future version.
+- Always setting X-cache headers on cached responses
+- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin
+- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface
+  $request, Response $response);`
+- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);`
+- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);`
+- Added `CacheStorageInterface::purge($url)`
+- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin
+  $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache,
+  CanCacheStrategyInterface $canCache = null)`
+- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)`
+
+3.5 to 3.6
+----------
+
+* Mixed casing of headers are now forced to be a single consistent casing across all values for that header.
+* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution
+* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader().
+  For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader().
+  Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request.
+* Specific header implementations can be created for complex headers. When a message creates a header, it uses a
+  HeaderFactory which can map specific headers to specific header classes. There is now a Link header and
+  CacheControl header implementation.
+* Moved getLinks() from Response to just be used on a Link header object.
+
+If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the
+HeaderInterface (e.g. toArray(), getAll(), etc.).
+
+### Interface changes
+
+* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate
+* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti()
+* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in
+  Guzzle\Http\Curl\RequestMediator
+* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string.
+* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface
+* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders()
+
+### Removed deprecated functions
+
+* Removed Guzzle\Parser\ParserRegister::get(). Use getParser()
+* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser().
+
+### Deprecations
+
+* The ability to case-insensitively search for header values
+* Guzzle\Http\Message\Header::hasExactHeader
+* Guzzle\Http\Message\Header::raw. Use getAll()
+* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object
+  instead.
+
+### Other changes
+
+* All response header helper functions return a string rather than mixing Header objects and strings inconsistently
+* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle
+  directly via interfaces
+* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
+  but are a no-op until removed.
+* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a
+  `Guzzle\Service\Command\ArrayCommandInterface`.
+* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response
+  on a request while the request is still being transferred
+* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess
+
+3.3 to 3.4
+----------
+
+Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs.
+
+3.2 to 3.3
+----------
+
+### Response::getEtag() quote stripping removed
+
+`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header
+
+### Removed `Guzzle\Http\Utils`
+
+The `Guzzle\Http\Utils` class was removed. This class was only used for testing.
+
+### Stream wrapper and type
+
+`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getStreamType()` are no longer converted to lowercase.
+
+### curl.emit_io became emit_io
+
+Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the
+'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io'
+
+3.1 to 3.2
+----------
+
+### CurlMulti is no longer reused globally
+
+Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added
+to a single client can pollute requests dispatched from other clients.
+
+If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the
+ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is
+created.
+
+```php
+$multi = new Guzzle\Http\Curl\CurlMulti();
+$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json');
+$builder->addListener('service_builder.create_client', function ($event) use ($multi) {
+    $event['client']->setCurlMulti($multi);
+}
+});
+```
+
+### No default path
+
+URLs no longer have a default path value of '/' if no path was specified.
+
+Before:
+
+```php
+$request = $client->get('http://www.foo.com');
+echo $request->getUrl();
+// >> http://www.foo.com/
+```
+
+After:
+
+```php
+$request = $client->get('http://www.foo.com');
+echo $request->getUrl();
+// >> http://www.foo.com
+```
+
+### Less verbose BadResponseException
+
+The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and
+response information. You can, however, get access to the request and response object by calling `getRequest()` or
+`getResponse()` on the exception object.
+
+### Query parameter aggregation
+
+Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a
+setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is
+responsible for handling the aggregation of multi-valued query string variables into a flattened hash.
+
+2.8 to 3.x
+----------
+
+### Guzzle\Service\Inspector
+
+Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig`
+
+**Before**
+
+```php
+use Guzzle\Service\Inspector;
+
+class YourClient extends \Guzzle\Service\Client
+{
+    public static function factory($config = array())
+    {
+        $default = array();
+        $required = array('base_url', 'username', 'api_key');
+        $config = Inspector::fromConfig($config, $default, $required);
+
+        $client = new self(
+            $config->get('base_url'),
+            $config->get('username'),
+            $config->get('api_key')
+        );
+        $client->setConfig($config);
+
+        $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));
+
+        return $client;
+    }
+```
+
+**After**
+
+```php
+use Guzzle\Common\Collection;
+
+class YourClient extends \Guzzle\Service\Client
+{
+    public static function factory($config = array())
+    {
+        $default = array();
+        $required = array('base_url', 'username', 'api_key');
+        $config = Collection::fromConfig($config, $default, $required);
+
+        $client = new self(
+            $config->get('base_url'),
+            $config->get('username'),
+            $config->get('api_key')
+        );
+        $client->setConfig($config);
+
+        $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));
+
+        return $client;
+    }
+```
+
+### Convert XML Service Descriptions to JSON
+
+**Before**
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<client>
+    <commands>
+        <!-- Groups -->
+        <command name="list_groups" method="GET" uri="groups.json">
+            <doc>Get a list of groups</doc>
+        </command>
+        <command name="search_groups" method="GET" uri='search.json?query="{{query}} type:group"'>
+            <doc>Uses a search query to get a list of groups</doc>
+            <param name="query" type="string" required="true" />
+        </command>
+        <command name="create_group" method="POST" uri="groups.json">
+            <doc>Create a group</doc>
+            <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/>
+            <param name="Content-Type" location="header" static="application/json"/>
+        </command>
+        <command name="delete_group" method="DELETE" uri="groups/{{id}}.json">
+            <doc>Delete a group by ID</doc>
+            <param name="id" type="integer" required="true"/>
+        </command>
+        <command name="get_group" method="GET" uri="groups/{{id}}.json">
+            <param name="id" type="integer" required="true"/>
+        </command>
+        <command name="update_group" method="PUT" uri="groups/{{id}}.json">
+            <doc>Update a group</doc>
+            <param name="id" type="integer" required="true"/>
+            <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/>
+            <param name="Content-Type" location="header" static="application/json"/>
+        </command>
+    </commands>
+</client>
+```
+
+**After**
+
+```json
+{
+    "name":       "Zendesk REST API v2",
+    "apiVersion": "2012-12-31",
+    "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users",
+    "operations": {
+        "list_groups":  {
+            "httpMethod":"GET",
+            "uri":       "groups.json",
+            "summary":   "Get a list of groups"
+        },
+        "search_groups":{
+            "httpMethod":"GET",
+            "uri":       "search.json?query=\"{query} type:group\"",
+            "summary":   "Uses a search query to get a list of groups",
+            "parameters":{
+                "query":{
+                    "location":   "uri",
+                    "description":"Zendesk Search Query",
+                    "type":       "string",
+                    "required":   true
+                }
+            }
+        },
+        "create_group": {
+            "httpMethod":"POST",
+            "uri":       "groups.json",
+            "summary":   "Create a group",
+            "parameters":{
+                "data":        {
+                    "type":       "array",
+                    "location":   "body",
+                    "description":"Group JSON",
+                    "filters":    "json_encode",
+                    "required":   true
+                },
+                "Content-Type":{
+                    "type":    "string",
+                    "location":"header",
+                    "static":  "application/json"
+                }
+            }
+        },
+        "delete_group": {
+            "httpMethod":"DELETE",
+            "uri":       "groups/{id}.json",
+            "summary":   "Delete a group",
+            "parameters":{
+                "id":{
+                    "location":   "uri",
+                    "description":"Group to delete by ID",
+                    "type":       "integer",
+                    "required":   true
+                }
+            }
+        },
+        "get_group":    {
+            "httpMethod":"GET",
+            "uri":       "groups/{id}.json",
+            "summary":   "Get a ticket",
+            "parameters":{
+                "id":{
+                    "location":   "uri",
+                    "description":"Group to get by ID",
+                    "type":       "integer",
+                    "required":   true
+                }
+            }
+        },
+        "update_group": {
+            "httpMethod":"PUT",
+            "uri":       "groups/{id}.json",
+            "summary":   "Update a group",
+            "parameters":{
+                "id":          {
+                    "location":   "uri",
+                    "description":"Group to update by ID",
+                    "type":       "integer",
+                    "required":   true
+                },
+                "data":        {
+                    "type":       "array",
+                    "location":   "body",
+                    "description":"Group JSON",
+                    "filters":    "json_encode",
+                    "required":   true
+                },
+                "Content-Type":{
+                    "type":    "string",
+                    "location":"header",
+                    "static":  "application/json"
+                }
+            }
+        }
+}
+```
+
+### Guzzle\Service\Description\ServiceDescription
+
+Commands are now called Operations
+
+**Before**
+
+```php
+use Guzzle\Service\Description\ServiceDescription;
+
+$sd = new ServiceDescription();
+$sd->getCommands();     // @returns ApiCommandInterface[]
+$sd->hasCommand($name);
+$sd->getCommand($name); // @returns ApiCommandInterface|null
+$sd->addCommand($command); // @param ApiCommandInterface $command
+```
+
+**After**
+
+```php
+use Guzzle\Service\Description\ServiceDescription;
+
+$sd = new ServiceDescription();
+$sd->getOperations();           // @returns OperationInterface[]
+$sd->hasOperation($name);
+$sd->getOperation($name);       // @returns OperationInterface|null
+$sd->addOperation($operation);  // @param OperationInterface $operation
+```
+
+### Guzzle\Common\Inflection\Inflector
+
+Namespace is now `Guzzle\Inflection\Inflector`
+
+### Guzzle\Http\Plugin
+
+Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below.
+
+### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log
+
+Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively.
+
+**Before**
+
+```php
+use Guzzle\Common\Log\ClosureLogAdapter;
+use Guzzle\Http\Plugin\LogPlugin;
+
+/** @var \Guzzle\Http\Client */
+$client;
+
+// $verbosity is an integer indicating desired message verbosity level
+$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE);
+```
+
+**After**
+
+```php
+use Guzzle\Log\ClosureLogAdapter;
+use Guzzle\Log\MessageFormatter;
+use Guzzle\Plugin\Log\LogPlugin;
+
+/** @var \Guzzle\Http\Client */
+$client;
+
+// $format is a string indicating desired message format -- @see MessageFormatter
+$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT);
+```
+
+### Guzzle\Http\Plugin\CurlAuthPlugin
+
+Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`.
+
+### Guzzle\Http\Plugin\ExponentialBackoffPlugin
+
+Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes.
+
+**Before**
+
+```php
+use Guzzle\Http\Plugin\ExponentialBackoffPlugin;
+
+$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge(
+        ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429)
+    ));
+
+$client->addSubscriber($backoffPlugin);
+```
+
+**After**
+
+```php
+use Guzzle\Plugin\Backoff\BackoffPlugin;
+use Guzzle\Plugin\Backoff\HttpBackoffStrategy;
+
+// Use convenient factory method instead -- see implementation for ideas of what
+// you can do with chaining backoff strategies
+$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge(
+        HttpBackoffStrategy::getDefaultFailureCodes(), array(429)
+    ));
+$client->addSubscriber($backoffPlugin);
+```
+
+### Known Issues
+
+#### [BUG] Accept-Encoding header behavior changed unintentionally.
+
+(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e)
+
+In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to
+properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen.
+See issue #217 for a workaround, or use a version containing the fix.

+ 44 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/composer.json

@@ -0,0 +1,44 @@
+{
+    "name": "guzzlehttp/guzzle",
+    "type": "library",
+    "description": "Guzzle is a PHP HTTP client library",
+    "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"],
+    "homepage": "http://guzzlephp.org/",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Michael Dowling",
+            "email": "mtdowling@gmail.com",
+            "homepage": "https://github.com/mtdowling"
+        }
+    ],
+    "require": {
+        "php": ">=5.5",
+        "guzzlehttp/psr7": "^1.4",
+        "guzzlehttp/promises": "^1.0"
+    },
+    "require-dev": {
+        "ext-curl": "*",
+        "phpunit/phpunit": "^4.0 || ^5.0",
+        "psr/log": "^1.0"
+    },
+    "autoload": {
+        "files": ["src/functions_include.php"],
+        "psr-4": {
+            "GuzzleHttp\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "GuzzleHttp\\Tests\\": "tests/"
+        }
+    },
+    "suggest": {
+        "psr/log": "Required for using the Log middleware"
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "6.2-dev"
+        }
+    }
+}

+ 414 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Client.php

@@ -0,0 +1,414 @@
+<?php
+namespace GuzzleHttp;
+
+use GuzzleHttp\Cookie\CookieJar;
+use GuzzleHttp\Promise;
+use GuzzleHttp\Psr7;
+use Psr\Http\Message\UriInterface;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * @method ResponseInterface get(string|UriInterface $uri, array $options = [])
+ * @method ResponseInterface head(string|UriInterface $uri, array $options = [])
+ * @method ResponseInterface put(string|UriInterface $uri, array $options = [])
+ * @method ResponseInterface post(string|UriInterface $uri, array $options = [])
+ * @method ResponseInterface patch(string|UriInterface $uri, array $options = [])
+ * @method ResponseInterface delete(string|UriInterface $uri, array $options = [])
+ * @method Promise\PromiseInterface getAsync(string|UriInterface $uri, array $options = [])
+ * @method Promise\PromiseInterface headAsync(string|UriInterface $uri, array $options = [])
+ * @method Promise\PromiseInterface putAsync(string|UriInterface $uri, array $options = [])
+ * @method Promise\PromiseInterface postAsync(string|UriInterface $uri, array $options = [])
+ * @method Promise\PromiseInterface patchAsync(string|UriInterface $uri, array $options = [])
+ * @method Promise\PromiseInterface deleteAsync(string|UriInterface $uri, array $options = [])
+ */
+class Client implements ClientInterface
+{
+    /** @var array Default request options */
+    private $config;
+
+    /**
+     * Clients accept an array of constructor parameters.
+     *
+     * Here's an example of creating a client using a base_uri and an array of
+     * default request options to apply to each request:
+     *
+     *     $client = new Client([
+     *         'base_uri'        => 'http://www.foo.com/1.0/',
+     *         'timeout'         => 0,
+     *         'allow_redirects' => false,
+     *         'proxy'           => '192.168.16.1:10'
+     *     ]);
+     *
+     * Client configuration settings include the following options:
+     *
+     * - handler: (callable) Function that transfers HTTP requests over the
+     *   wire. The function is called with a Psr7\Http\Message\RequestInterface
+     *   and array of transfer options, and must return a
+     *   GuzzleHttp\Promise\PromiseInterface that is fulfilled with a
+     *   Psr7\Http\Message\ResponseInterface on success. "handler" is a
+     *   constructor only option that cannot be overridden in per/request
+     *   options. If no handler is provided, a default handler will be created
+     *   that enables all of the request options below by attaching all of the
+     *   default middleware to the handler.
+     * - base_uri: (string|UriInterface) Base URI of the client that is merged
+     *   into relative URIs. Can be a string or instance of UriInterface.
+     * - **: any request option
+     *
+     * @param array $config Client configuration settings.
+     *
+     * @see \GuzzleHttp\RequestOptions for a list of available request options.
+     */
+    public function __construct(array $config = [])
+    {
+        if (!isset($config['handler'])) {
+            $config['handler'] = HandlerStack::create();
+        } elseif (!is_callable($config['handler'])) {
+            throw new \InvalidArgumentException('handler must be a callable');
+        }
+
+        // Convert the base_uri to a UriInterface
+        if (isset($config['base_uri'])) {
+            $config['base_uri'] = Psr7\uri_for($config['base_uri']);
+        }
+
+        $this->configureDefaults($config);
+    }
+
+    public function __call($method, $args)
+    {
+        if (count($args) < 1) {
+            throw new \InvalidArgumentException('Magic request methods require a URI and optional options array');
+        }
+
+        $uri = $args[0];
+        $opts = isset($args[1]) ? $args[1] : [];
+
+        return substr($method, -5) === 'Async'
+            ? $this->requestAsync(substr($method, 0, -5), $uri, $opts)
+            : $this->request($method, $uri, $opts);
+    }
+
+    public function sendAsync(RequestInterface $request, array $options = [])
+    {
+        // Merge the base URI into the request URI if needed.
+        $options = $this->prepareDefaults($options);
+
+        return $this->transfer(
+            $request->withUri($this->buildUri($request->getUri(), $options), $request->hasHeader('Host')),
+            $options
+        );
+    }
+
+    public function send(RequestInterface $request, array $options = [])
+    {
+        $options[RequestOptions::SYNCHRONOUS] = true;
+        return $this->sendAsync($request, $options)->wait();
+    }
+
+    public function requestAsync($method, $uri = '', array $options = [])
+    {
+        $options = $this->prepareDefaults($options);
+        // Remove request modifying parameter because it can be done up-front.
+        $headers = isset($options['headers']) ? $options['headers'] : [];
+        $body = isset($options['body']) ? $options['body'] : null;
+        $version = isset($options['version']) ? $options['version'] : '1.1';
+        // Merge the URI into the base URI.
+        $uri = $this->buildUri($uri, $options);
+        if (is_array($body)) {
+            $this->invalidBody();
+        }
+        $request = new Psr7\Request($method, $uri, $headers, $body, $version);
+        // Remove the option so that they are not doubly-applied.
+        unset($options['headers'], $options['body'], $options['version']);
+
+        return $this->transfer($request, $options);
+    }
+
+    public function request($method, $uri = '', array $options = [])
+    {
+        $options[RequestOptions::SYNCHRONOUS] = true;
+        return $this->requestAsync($method, $uri, $options)->wait();
+    }
+
+    public function getConfig($option = null)
+    {
+        return $option === null
+            ? $this->config
+            : (isset($this->config[$option]) ? $this->config[$option] : null);
+    }
+
+    private function buildUri($uri, array $config)
+    {
+        // for BC we accept null which would otherwise fail in uri_for
+        $uri = Psr7\uri_for($uri === null ? '' : $uri);
+
+        if (isset($config['base_uri'])) {
+            $uri = Psr7\UriResolver::resolve(Psr7\uri_for($config['base_uri']), $uri);
+        }
+
+        return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri;
+    }
+
+    /**
+     * Configures the default options for a client.
+     *
+     * @param array $config
+     */
+    private function configureDefaults(array $config)
+    {
+        $defaults = [
+            'allow_redirects' => RedirectMiddleware::$defaultSettings,
+            'http_errors'     => true,
+            'decode_content'  => true,
+            'verify'          => true,
+            'cookies'         => false
+        ];
+
+        // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set.
+
+        // We can only trust the HTTP_PROXY environment variable in a CLI
+        // process due to the fact that PHP has no reliable mechanism to
+        // get environment variables that start with "HTTP_".
+        if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) {
+            $defaults['proxy']['http'] = getenv('HTTP_PROXY');
+        }
+
+        if ($proxy = getenv('HTTPS_PROXY')) {
+            $defaults['proxy']['https'] = $proxy;
+        }
+
+        if ($noProxy = getenv('NO_PROXY')) {
+            $cleanedNoProxy = str_replace(' ', '', $noProxy);
+            $defaults['proxy']['no'] = explode(',', $cleanedNoProxy);
+        }
+
+        $this->config = $config + $defaults;
+
+        if (!empty($config['cookies']) && $config['cookies'] === true) {
+            $this->config['cookies'] = new CookieJar();
+        }
+
+        // Add the default user-agent header.
+        if (!isset($this->config['headers'])) {
+            $this->config['headers'] = ['User-Agent' => default_user_agent()];
+        } else {
+            // Add the User-Agent header if one was not already set.
+            foreach (array_keys($this->config['headers']) as $name) {
+                if (strtolower($name) === 'user-agent') {
+                    return;
+                }
+            }
+            $this->config['headers']['User-Agent'] = default_user_agent();
+        }
+    }
+
+    /**
+     * Merges default options into the array.
+     *
+     * @param array $options Options to modify by reference
+     *
+     * @return array
+     */
+    private function prepareDefaults($options)
+    {
+        $defaults = $this->config;
+
+        if (!empty($defaults['headers'])) {
+            // Default headers are only added if they are not present.
+            $defaults['_conditional'] = $defaults['headers'];
+            unset($defaults['headers']);
+        }
+
+        // Special handling for headers is required as they are added as
+        // conditional headers and as headers passed to a request ctor.
+        if (array_key_exists('headers', $options)) {
+            // Allows default headers to be unset.
+            if ($options['headers'] === null) {
+                $defaults['_conditional'] = null;
+                unset($options['headers']);
+            } elseif (!is_array($options['headers'])) {
+                throw new \InvalidArgumentException('headers must be an array');
+            }
+        }
+
+        // Shallow merge defaults underneath options.
+        $result = $options + $defaults;
+
+        // Remove null values.
+        foreach ($result as $k => $v) {
+            if ($v === null) {
+                unset($result[$k]);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Transfers the given request and applies request options.
+     *
+     * The URI of the request is not modified and the request options are used
+     * as-is without merging in default options.
+     *
+     * @param RequestInterface $request
+     * @param array            $options
+     *
+     * @return Promise\PromiseInterface
+     */
+    private function transfer(RequestInterface $request, array $options)
+    {
+        // save_to -> sink
+        if (isset($options['save_to'])) {
+            $options['sink'] = $options['save_to'];
+            unset($options['save_to']);
+        }
+
+        // exceptions -> http_errors
+        if (isset($options['exceptions'])) {
+            $options['http_errors'] = $options['exceptions'];
+            unset($options['exceptions']);
+        }
+
+        $request = $this->applyOptions($request, $options);
+        $handler = $options['handler'];
+
+        try {
+            return Promise\promise_for($handler($request, $options));
+        } catch (\Exception $e) {
+            return Promise\rejection_for($e);
+        }
+    }
+
+    /**
+     * Applies the array of request options to a request.
+     *
+     * @param RequestInterface $request
+     * @param array            $options
+     *
+     * @return RequestInterface
+     */
+    private function applyOptions(RequestInterface $request, array &$options)
+    {
+        $modify = [];
+
+        if (isset($options['form_params'])) {
+            if (isset($options['multipart'])) {
+                throw new \InvalidArgumentException('You cannot use '
+                    . 'form_params and multipart at the same time. Use the '
+                    . 'form_params option if you want to send application/'
+                    . 'x-www-form-urlencoded requests, and the multipart '
+                    . 'option to send multipart/form-data requests.');
+            }
+            $options['body'] = http_build_query($options['form_params'], '', '&');
+            unset($options['form_params']);
+            $options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded';
+        }
+
+        if (isset($options['multipart'])) {
+            $options['body'] = new Psr7\MultipartStream($options['multipart']);
+            unset($options['multipart']);
+        }
+
+        if (isset($options['json'])) {
+            $options['body'] = \GuzzleHttp\json_encode($options['json']);
+            unset($options['json']);
+            $options['_conditional']['Content-Type'] = 'application/json';
+        }
+
+        if (!empty($options['decode_content'])
+            && $options['decode_content'] !== true
+        ) {
+            $modify['set_headers']['Accept-Encoding'] = $options['decode_content'];
+        }
+
+        if (isset($options['headers'])) {
+            if (isset($modify['set_headers'])) {
+                $modify['set_headers'] = $options['headers'] + $modify['set_headers'];
+            } else {
+                $modify['set_headers'] = $options['headers'];
+            }
+            unset($options['headers']);
+        }
+
+        if (isset($options['body'])) {
+            if (is_array($options['body'])) {
+                $this->invalidBody();
+            }
+            $modify['body'] = Psr7\stream_for($options['body']);
+            unset($options['body']);
+        }
+
+        if (!empty($options['auth']) && is_array($options['auth'])) {
+            $value = $options['auth'];
+            $type = isset($value[2]) ? strtolower($value[2]) : 'basic';
+            switch ($type) {
+                case 'basic':
+                    $modify['set_headers']['Authorization'] = 'Basic '
+                        . base64_encode("$value[0]:$value[1]");
+                    break;
+                case 'digest':
+                    // @todo: Do not rely on curl
+                    $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST;
+                    $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
+                    break;
+                case 'ntlm':
+                    $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_NTLM;
+                    $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
+                    break;
+            }
+        }
+
+        if (isset($options['query'])) {
+            $value = $options['query'];
+            if (is_array($value)) {
+                $value = http_build_query($value, null, '&', PHP_QUERY_RFC3986);
+            }
+            if (!is_string($value)) {
+                throw new \InvalidArgumentException('query must be a string or array');
+            }
+            $modify['query'] = $value;
+            unset($options['query']);
+        }
+
+        // Ensure that sink is not an invalid value.
+        if (isset($options['sink'])) {
+            // TODO: Add more sink validation?
+            if (is_bool($options['sink'])) {
+                throw new \InvalidArgumentException('sink must not be a boolean');
+            }
+        }
+
+        $request = Psr7\modify_request($request, $modify);
+        if ($request->getBody() instanceof Psr7\MultipartStream) {
+            // Use a multipart/form-data POST if a Content-Type is not set.
+            $options['_conditional']['Content-Type'] = 'multipart/form-data; boundary='
+                . $request->getBody()->getBoundary();
+        }
+
+        // Merge in conditional headers if they are not present.
+        if (isset($options['_conditional'])) {
+            // Build up the changes so it's in a single clone of the message.
+            $modify = [];
+            foreach ($options['_conditional'] as $k => $v) {
+                if (!$request->hasHeader($k)) {
+                    $modify['set_headers'][$k] = $v;
+                }
+            }
+            $request = Psr7\modify_request($request, $modify);
+            // Don't pass this internal value along to middleware/handlers.
+            unset($options['_conditional']);
+        }
+
+        return $request;
+    }
+
+    private function invalidBody()
+    {
+        throw new \InvalidArgumentException('Passing in the "body" request '
+            . 'option as an array to send a POST request has been deprecated. '
+            . 'Please use the "form_params" request option to send a '
+            . 'application/x-www-form-urlencoded request, or the "multipart" '
+            . 'request option to send a multipart/form-data request.');
+    }
+}

+ 84 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/ClientInterface.php

@@ -0,0 +1,84 @@
+<?php
+namespace GuzzleHttp;
+
+use GuzzleHttp\Promise\PromiseInterface;
+use GuzzleHttp\Exception\GuzzleException;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Client interface for sending HTTP requests.
+ */
+interface ClientInterface
+{
+    const VERSION = '6.2.1';
+
+    /**
+     * Send an HTTP request.
+     *
+     * @param RequestInterface $request Request to send
+     * @param array            $options Request options to apply to the given
+     *                                  request and to the transfer.
+     *
+     * @return ResponseInterface
+     * @throws GuzzleException
+     */
+    public function send(RequestInterface $request, array $options = []);
+
+    /**
+     * Asynchronously send an HTTP request.
+     *
+     * @param RequestInterface $request Request to send
+     * @param array            $options Request options to apply to the given
+     *                                  request and to the transfer.
+     *
+     * @return PromiseInterface
+     */
+    public function sendAsync(RequestInterface $request, array $options = []);
+
+    /**
+     * Create and send an HTTP request.
+     *
+     * Use an absolute path to override the base path of the client, or a
+     * relative path to append to the base path of the client. The URL can
+     * contain the query string as well.
+     *
+     * @param string              $method  HTTP method.
+     * @param string|UriInterface $uri     URI object or string.
+     * @param array               $options Request options to apply.
+     *
+     * @return ResponseInterface
+     * @throws GuzzleException
+     */
+    public function request($method, $uri, array $options = []);
+
+    /**
+     * Create and send an asynchronous HTTP request.
+     *
+     * Use an absolute path to override the base path of the client, or a
+     * relative path to append to the base path of the client. The URL can
+     * contain the query string as well. Use an array to provide a URL
+     * template and additional variables to use in the URL template expansion.
+     *
+     * @param string              $method  HTTP method
+     * @param string|UriInterface $uri     URI object or string.
+     * @param array               $options Request options to apply.
+     *
+     * @return PromiseInterface
+     */
+    public function requestAsync($method, $uri, array $options = []);
+
+    /**
+     * Get a client configuration option.
+     *
+     * These options include default request options of the client, a "handler"
+     * (if utilized by the concrete client), and a "base_uri" if utilized by
+     * the concrete client.
+     *
+     * @param string|null $option The config option to retrieve.
+     *
+     * @return mixed
+     */
+    public function getConfig($option = null);
+}

+ 314 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php

@@ -0,0 +1,314 @@
+<?php
+namespace GuzzleHttp\Cookie;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * Cookie jar that stores cookies as an array
+ */
+class CookieJar implements CookieJarInterface
+{
+    /** @var SetCookie[] Loaded cookie data */
+    private $cookies = [];
+
+    /** @var bool */
+    private $strictMode;
+
+    /**
+     * @param bool $strictMode   Set to true to throw exceptions when invalid
+     *                           cookies are added to the cookie jar.
+     * @param array $cookieArray Array of SetCookie objects or a hash of
+     *                           arrays that can be used with the SetCookie
+     *                           constructor
+     */
+    public function __construct($strictMode = false, $cookieArray = [])
+    {
+        $this->strictMode = $strictMode;
+
+        foreach ($cookieArray as $cookie) {
+            if (!($cookie instanceof SetCookie)) {
+                $cookie = new SetCookie($cookie);
+            }
+            $this->setCookie($cookie);
+        }
+    }
+
+    /**
+     * Create a new Cookie jar from an associative array and domain.
+     *
+     * @param array  $cookies Cookies to create the jar from
+     * @param string $domain  Domain to set the cookies to
+     *
+     * @return self
+     */
+    public static function fromArray(array $cookies, $domain)
+    {
+        $cookieJar = new self();
+        foreach ($cookies as $name => $value) {
+            $cookieJar->setCookie(new SetCookie([
+                'Domain'  => $domain,
+                'Name'    => $name,
+                'Value'   => $value,
+                'Discard' => true
+            ]));
+        }
+
+        return $cookieJar;
+    }
+
+    /**
+     * @deprecated
+     */
+    public static function getCookieValue($value)
+    {
+        return $value;
+    }
+
+    /**
+     * Evaluate if this cookie should be persisted to storage
+     * that survives between requests.
+     *
+     * @param SetCookie $cookie Being evaluated.
+     * @param bool $allowSessionCookies If we should persist session cookies
+     * @return bool
+     */
+    public static function shouldPersist(
+        SetCookie $cookie,
+        $allowSessionCookies = false
+    ) {
+        if ($cookie->getExpires() || $allowSessionCookies) {
+            if (!$cookie->getDiscard()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Finds and returns the cookie based on the name
+     *
+     * @param string $name cookie name to search for
+     * @return SetCookie|null cookie that was found or null if not found
+     */
+    public function getCookieByName($name)
+    {
+        // don't allow a null name
+        if($name === null) {
+            return null;
+        }
+        foreach($this->cookies as $cookie) {
+            if($cookie->getName() !== null && strcasecmp($cookie->getName(), $name) === 0) {
+                return $cookie;
+            }
+        }
+    }
+
+    public function toArray()
+    {
+        return array_map(function (SetCookie $cookie) {
+            return $cookie->toArray();
+        }, $this->getIterator()->getArrayCopy());
+    }
+
+    public function clear($domain = null, $path = null, $name = null)
+    {
+        if (!$domain) {
+            $this->cookies = [];
+            return;
+        } elseif (!$path) {
+            $this->cookies = array_filter(
+                $this->cookies,
+                function (SetCookie $cookie) use ($path, $domain) {
+                    return !$cookie->matchesDomain($domain);
+                }
+            );
+        } elseif (!$name) {
+            $this->cookies = array_filter(
+                $this->cookies,
+                function (SetCookie $cookie) use ($path, $domain) {
+                    return !($cookie->matchesPath($path) &&
+                        $cookie->matchesDomain($domain));
+                }
+            );
+        } else {
+            $this->cookies = array_filter(
+                $this->cookies,
+                function (SetCookie $cookie) use ($path, $domain, $name) {
+                    return !($cookie->getName() == $name &&
+                        $cookie->matchesPath($path) &&
+                        $cookie->matchesDomain($domain));
+                }
+            );
+        }
+    }
+
+    public function clearSessionCookies()
+    {
+        $this->cookies = array_filter(
+            $this->cookies,
+            function (SetCookie $cookie) {
+                return !$cookie->getDiscard() && $cookie->getExpires();
+            }
+        );
+    }
+
+    public function setCookie(SetCookie $cookie)
+    {
+        // If the name string is empty (but not 0), ignore the set-cookie
+        // string entirely.
+        $name = $cookie->getName();
+        if (!$name && $name !== '0') {
+            return false;
+        }
+
+        // Only allow cookies with set and valid domain, name, value
+        $result = $cookie->validate();
+        if ($result !== true) {
+            if ($this->strictMode) {
+                throw new \RuntimeException('Invalid cookie: ' . $result);
+            } else {
+                $this->removeCookieIfEmpty($cookie);
+                return false;
+            }
+        }
+
+        // Resolve conflicts with previously set cookies
+        foreach ($this->cookies as $i => $c) {
+
+            // Two cookies are identical, when their path, and domain are
+            // identical.
+            if ($c->getPath() != $cookie->getPath() ||
+                $c->getDomain() != $cookie->getDomain() ||
+                $c->getName() != $cookie->getName()
+            ) {
+                continue;
+            }
+
+            // The previously set cookie is a discard cookie and this one is
+            // not so allow the new cookie to be set
+            if (!$cookie->getDiscard() && $c->getDiscard()) {
+                unset($this->cookies[$i]);
+                continue;
+            }
+
+            // If the new cookie's expiration is further into the future, then
+            // replace the old cookie
+            if ($cookie->getExpires() > $c->getExpires()) {
+                unset($this->cookies[$i]);
+                continue;
+            }
+
+            // If the value has changed, we better change it
+            if ($cookie->getValue() !== $c->getValue()) {
+                unset($this->cookies[$i]);
+                continue;
+            }
+
+            // The cookie exists, so no need to continue
+            return false;
+        }
+
+        $this->cookies[] = $cookie;
+
+        return true;
+    }
+
+    public function count()
+    {
+        return count($this->cookies);
+    }
+
+    public function getIterator()
+    {
+        return new \ArrayIterator(array_values($this->cookies));
+    }
+
+    public function extractCookies(
+        RequestInterface $request,
+        ResponseInterface $response
+    ) {
+        if ($cookieHeader = $response->getHeader('Set-Cookie')) {
+            foreach ($cookieHeader as $cookie) {
+                $sc = SetCookie::fromString($cookie);
+                if (!$sc->getDomain()) {
+                    $sc->setDomain($request->getUri()->getHost());
+                }
+                if (0 !== strpos($sc->getPath(), '/')) {
+                    $sc->setPath($this->getCookiePathFromRequest($request));
+                }
+                $this->setCookie($sc);
+            }
+        }
+    }
+
+    /**
+     * Computes cookie path following RFC 6265 section 5.1.4
+     *
+     * @link https://tools.ietf.org/html/rfc6265#section-5.1.4
+     *
+     * @param RequestInterface $request
+     * @return string
+     */
+    private function getCookiePathFromRequest(RequestInterface $request)
+    {
+        $uriPath = $request->getUri()->getPath();
+        if (''  === $uriPath) {
+            return '/';
+        }
+        if (0 !== strpos($uriPath, '/')) {
+            return '/';
+        }
+        if ('/' === $uriPath) {
+            return '/';
+        }
+        if (0 === $lastSlashPos = strrpos($uriPath, '/')) {
+            return '/';
+        }
+
+        return substr($uriPath, 0, $lastSlashPos);
+    }
+
+    public function withCookieHeader(RequestInterface $request)
+    {
+        $values = [];
+        $uri = $request->getUri();
+        $scheme = $uri->getScheme();
+        $host = $uri->getHost();
+        $path = $uri->getPath() ?: '/';
+
+        foreach ($this->cookies as $cookie) {
+            if ($cookie->matchesPath($path) &&
+                $cookie->matchesDomain($host) &&
+                !$cookie->isExpired() &&
+                (!$cookie->getSecure() || $scheme === 'https')
+            ) {
+                $values[] = $cookie->getName() . '='
+                    . $cookie->getValue();
+            }
+        }
+
+        return $values
+            ? $request->withHeader('Cookie', implode('; ', $values))
+            : $request;
+    }
+
+    /**
+     * If a cookie already exists and the server asks to set it again with a
+     * null value, the cookie must be deleted.
+     *
+     * @param SetCookie $cookie
+     */
+    private function removeCookieIfEmpty(SetCookie $cookie)
+    {
+        $cookieValue = $cookie->getValue();
+        if ($cookieValue === null || $cookieValue === '') {
+            $this->clear(
+                $cookie->getDomain(),
+                $cookie->getPath(),
+                $cookie->getName()
+            );
+        }
+    }
+}

+ 84 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php

@@ -0,0 +1,84 @@
+<?php
+namespace GuzzleHttp\Cookie;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * Stores HTTP cookies.
+ *
+ * It extracts cookies from HTTP requests, and returns them in HTTP responses.
+ * CookieJarInterface instances automatically expire contained cookies when
+ * necessary. Subclasses are also responsible for storing and retrieving
+ * cookies from a file, database, etc.
+ *
+ * @link http://docs.python.org/2/library/cookielib.html Inspiration
+ */
+interface CookieJarInterface extends \Countable, \IteratorAggregate
+{
+    /**
+     * Create a request with added cookie headers.
+     *
+     * If no matching cookies are found in the cookie jar, then no Cookie
+     * header is added to the request and the same request is returned.
+     *
+     * @param RequestInterface $request Request object to modify.
+     *
+     * @return RequestInterface returns the modified request.
+     */
+    public function withCookieHeader(RequestInterface $request);
+
+    /**
+     * Extract cookies from an HTTP response and store them in the CookieJar.
+     *
+     * @param RequestInterface  $request  Request that was sent
+     * @param ResponseInterface $response Response that was received
+     */
+    public function extractCookies(
+        RequestInterface $request,
+        ResponseInterface $response
+    );
+
+    /**
+     * Sets a cookie in the cookie jar.
+     *
+     * @param SetCookie $cookie Cookie to set.
+     *
+     * @return bool Returns true on success or false on failure
+     */
+    public function setCookie(SetCookie $cookie);
+
+    /**
+     * Remove cookies currently held in the cookie jar.
+     *
+     * Invoking this method without arguments will empty the whole cookie jar.
+     * If given a $domain argument only cookies belonging to that domain will
+     * be removed. If given a $domain and $path argument, cookies belonging to
+     * the specified path within that domain are removed. If given all three
+     * arguments, then the cookie with the specified name, path and domain is
+     * removed.
+     *
+     * @param string $domain Clears cookies matching a domain
+     * @param string $path   Clears cookies matching a domain and path
+     * @param string $name   Clears cookies matching a domain, path, and name
+     *
+     * @return CookieJarInterface
+     */
+    public function clear($domain = null, $path = null, $name = null);
+
+    /**
+     * Discard all sessions cookies.
+     *
+     * Removes cookies that don't have an expire field or a have a discard
+     * field set to true. To be called when the user agent shuts down according
+     * to RFC 2965.
+     */
+    public function clearSessionCookies();
+
+    /**
+     * Converts the cookie jar to an array.
+     *
+     * @return array
+     */
+    public function toArray();
+}

+ 90 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php

@@ -0,0 +1,90 @@
+<?php
+namespace GuzzleHttp\Cookie;
+
+/**
+ * Persists non-session cookies using a JSON formatted file
+ */
+class FileCookieJar extends CookieJar
+{
+    /** @var string filename */
+    private $filename;
+
+    /** @var bool Control whether to persist session cookies or not. */
+    private $storeSessionCookies;
+
+    /**
+     * Create a new FileCookieJar object
+     *
+     * @param string $cookieFile        File to store the cookie data
+     * @param bool $storeSessionCookies Set to true to store session cookies
+     *                                  in the cookie jar.
+     *
+     * @throws \RuntimeException if the file cannot be found or created
+     */
+    public function __construct($cookieFile, $storeSessionCookies = false)
+    {
+        $this->filename = $cookieFile;
+        $this->storeSessionCookies = $storeSessionCookies;
+
+        if (file_exists($cookieFile)) {
+            $this->load($cookieFile);
+        }
+    }
+
+    /**
+     * Saves the file when shutting down
+     */
+    public function __destruct()
+    {
+        $this->save($this->filename);
+    }
+
+    /**
+     * Saves the cookies to a file.
+     *
+     * @param string $filename File to save
+     * @throws \RuntimeException if the file cannot be found or created
+     */
+    public function save($filename)
+    {
+        $json = [];
+        foreach ($this as $cookie) {
+            /** @var SetCookie $cookie */
+            if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
+                $json[] = $cookie->toArray();
+            }
+        }
+
+        $jsonStr = \GuzzleHttp\json_encode($json);
+        if (false === file_put_contents($filename, $jsonStr)) {
+            throw new \RuntimeException("Unable to save file {$filename}");
+        }
+    }
+
+    /**
+     * Load cookies from a JSON formatted file.
+     *
+     * Old cookies are kept unless overwritten by newly loaded ones.
+     *
+     * @param string $filename Cookie file to load.
+     * @throws \RuntimeException if the file cannot be loaded.
+     */
+    public function load($filename)
+    {
+        $json = file_get_contents($filename);
+        if (false === $json) {
+            throw new \RuntimeException("Unable to load file {$filename}");
+        } elseif ($json === '') {
+            return;
+        }
+
+        $data = \GuzzleHttp\json_decode($json, true);
+        if (is_array($data)) {
+            foreach (json_decode($json, true) as $cookie) {
+                $this->setCookie(new SetCookie($cookie));
+            }
+        } elseif (strlen($data)) {
+            throw new \RuntimeException("Invalid cookie file: {$filename}");
+        }
+    }
+}

+ 71 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php

@@ -0,0 +1,71 @@
+<?php
+namespace GuzzleHttp\Cookie;
+
+/**
+ * Persists cookies in the client session
+ */
+class SessionCookieJar extends CookieJar
+{
+    /** @var string session key */
+    private $sessionKey;
+    
+    /** @var bool Control whether to persist session cookies or not. */
+    private $storeSessionCookies;
+
+    /**
+     * Create a new SessionCookieJar object
+     *
+     * @param string $sessionKey        Session key name to store the cookie 
+     *                                  data in session
+     * @param bool $storeSessionCookies Set to true to store session cookies
+     *                                  in the cookie jar.
+     */
+    public function __construct($sessionKey, $storeSessionCookies = false)
+    {
+        $this->sessionKey = $sessionKey;
+        $this->storeSessionCookies = $storeSessionCookies;
+        $this->load();
+    }
+
+    /**
+     * Saves cookies to session when shutting down
+     */
+    public function __destruct()
+    {
+        $this->save();
+    }
+
+    /**
+     * Save cookies to the client session
+     */
+    public function save()
+    {
+        $json = [];
+        foreach ($this as $cookie) {
+            /** @var SetCookie $cookie */
+            if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
+                $json[] = $cookie->toArray();
+            }
+        }
+
+        $_SESSION[$this->sessionKey] = json_encode($json);
+    }
+
+    /**
+     * Load the contents of the client session into the data array
+     */
+    protected function load()
+    {
+        if (!isset($_SESSION[$this->sessionKey])) {
+            return;
+        }
+        $data = json_decode($_SESSION[$this->sessionKey], true);
+        if (is_array($data)) {
+            foreach ($data as $cookie) {
+                $this->setCookie(new SetCookie($cookie));
+            }
+        } elseif (strlen($data)) {
+            throw new \RuntimeException("Invalid cookie data");
+        }
+    }
+}

+ 404 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php

@@ -0,0 +1,404 @@
+<?php
+namespace GuzzleHttp\Cookie;
+
+/**
+ * Set-Cookie object
+ */
+class SetCookie
+{
+    /** @var array */
+    private static $defaults = [
+        'Name'     => null,
+        'Value'    => null,
+        'Domain'   => null,
+        'Path'     => '/',
+        'Max-Age'  => null,
+        'Expires'  => null,
+        'Secure'   => false,
+        'Discard'  => false,
+        'HttpOnly' => false
+    ];
+
+    /** @var array Cookie data */
+    private $data;
+
+    /**
+     * Create a new SetCookie object from a string
+     *
+     * @param string $cookie Set-Cookie header string
+     *
+     * @return self
+     */
+    public static function fromString($cookie)
+    {
+        // Create the default return array
+        $data = self::$defaults;
+        // Explode the cookie string using a series of semicolons
+        $pieces = array_filter(array_map('trim', explode(';', $cookie)));
+        // The name of the cookie (first kvp) must include an equal sign.
+        if (empty($pieces) || !strpos($pieces[0], '=')) {
+            return new self($data);
+        }
+
+        // Add the cookie pieces into the parsed data array
+        foreach ($pieces as $part) {
+
+            $cookieParts = explode('=', $part, 2);
+            $key = trim($cookieParts[0]);
+            $value = isset($cookieParts[1])
+                ? trim($cookieParts[1], " \n\r\t\0\x0B")
+                : true;
+
+            // Only check for non-cookies when cookies have been found
+            if (empty($data['Name'])) {
+                $data['Name'] = $key;
+                $data['Value'] = $value;
+            } else {
+                foreach (array_keys(self::$defaults) as $search) {
+                    if (!strcasecmp($search, $key)) {
+                        $data[$search] = $value;
+                        continue 2;
+                    }
+                }
+                $data[$key] = $value;
+            }
+        }
+
+        return new self($data);
+    }
+
+    /**
+     * @param array $data Array of cookie data provided by a Cookie parser
+     */
+    public function __construct(array $data = [])
+    {
+        $this->data = array_replace(self::$defaults, $data);
+        // Extract the Expires value and turn it into a UNIX timestamp if needed
+        if (!$this->getExpires() && $this->getMaxAge()) {
+            // Calculate the Expires date
+            $this->setExpires(time() + $this->getMaxAge());
+        } elseif ($this->getExpires() && !is_numeric($this->getExpires())) {
+            $this->setExpires($this->getExpires());
+        }
+    }
+
+    public function __toString()
+    {
+        $str = $this->data['Name'] . '=' . $this->data['Value'] . '; ';
+        foreach ($this->data as $k => $v) {
+            if ($k !== 'Name' && $k !== 'Value' && $v !== null && $v !== false) {
+                if ($k === 'Expires') {
+                    $str .= 'Expires=' . gmdate('D, d M Y H:i:s \G\M\T', $v) . '; ';
+                } else {
+                    $str .= ($v === true ? $k : "{$k}={$v}") . '; ';
+                }
+            }
+        }
+
+        return rtrim($str, '; ');
+    }
+
+    public function toArray()
+    {
+        return $this->data;
+    }
+
+    /**
+     * Get the cookie name
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->data['Name'];
+    }
+
+    /**
+     * Set the cookie name
+     *
+     * @param string $name Cookie name
+     */
+    public function setName($name)
+    {
+        $this->data['Name'] = $name;
+    }
+
+    /**
+     * Get the cookie value
+     *
+     * @return string
+     */
+    public function getValue()
+    {
+        return $this->data['Value'];
+    }
+
+    /**
+     * Set the cookie value
+     *
+     * @param string $value Cookie value
+     */
+    public function setValue($value)
+    {
+        $this->data['Value'] = $value;
+    }
+
+    /**
+     * Get the domain
+     *
+     * @return string|null
+     */
+    public function getDomain()
+    {
+        return $this->data['Domain'];
+    }
+
+    /**
+     * Set the domain of the cookie
+     *
+     * @param string $domain
+     */
+    public function setDomain($domain)
+    {
+        $this->data['Domain'] = $domain;
+    }
+
+    /**
+     * Get the path
+     *
+     * @return string
+     */
+    public function getPath()
+    {
+        return $this->data['Path'];
+    }
+
+    /**
+     * Set the path of the cookie
+     *
+     * @param string $path Path of the cookie
+     */
+    public function setPath($path)
+    {
+        $this->data['Path'] = $path;
+    }
+
+    /**
+     * Maximum lifetime of the cookie in seconds
+     *
+     * @return int|null
+     */
+    public function getMaxAge()
+    {
+        return $this->data['Max-Age'];
+    }
+
+    /**
+     * Set the max-age of the cookie
+     *
+     * @param int $maxAge Max age of the cookie in seconds
+     */
+    public function setMaxAge($maxAge)
+    {
+        $this->data['Max-Age'] = $maxAge;
+    }
+
+    /**
+     * The UNIX timestamp when the cookie Expires
+     *
+     * @return mixed
+     */
+    public function getExpires()
+    {
+        return $this->data['Expires'];
+    }
+
+    /**
+     * Set the unix timestamp for which the cookie will expire
+     *
+     * @param int $timestamp Unix timestamp
+     */
+    public function setExpires($timestamp)
+    {
+        $this->data['Expires'] = is_numeric($timestamp)
+            ? (int) $timestamp
+            : strtotime($timestamp);
+    }
+
+    /**
+     * Get whether or not this is a secure cookie
+     *
+     * @return null|bool
+     */
+    public function getSecure()
+    {
+        return $this->data['Secure'];
+    }
+
+    /**
+     * Set whether or not the cookie is secure
+     *
+     * @param bool $secure Set to true or false if secure
+     */
+    public function setSecure($secure)
+    {
+        $this->data['Secure'] = $secure;
+    }
+
+    /**
+     * Get whether or not this is a session cookie
+     *
+     * @return null|bool
+     */
+    public function getDiscard()
+    {
+        return $this->data['Discard'];
+    }
+
+    /**
+     * Set whether or not this is a session cookie
+     *
+     * @param bool $discard Set to true or false if this is a session cookie
+     */
+    public function setDiscard($discard)
+    {
+        $this->data['Discard'] = $discard;
+    }
+
+    /**
+     * Get whether or not this is an HTTP only cookie
+     *
+     * @return bool
+     */
+    public function getHttpOnly()
+    {
+        return $this->data['HttpOnly'];
+    }
+
+    /**
+     * Set whether or not this is an HTTP only cookie
+     *
+     * @param bool $httpOnly Set to true or false if this is HTTP only
+     */
+    public function setHttpOnly($httpOnly)
+    {
+        $this->data['HttpOnly'] = $httpOnly;
+    }
+
+    /**
+     * Check if the cookie matches a path value.
+     *
+     * A request-path path-matches a given cookie-path if at least one of
+     * the following conditions holds:
+     *
+     * - The cookie-path and the request-path are identical.
+     * - The cookie-path is a prefix of the request-path, and the last
+     *   character of the cookie-path is %x2F ("/").
+     * - The cookie-path is a prefix of the request-path, and the first
+     *   character of the request-path that is not included in the cookie-
+     *   path is a %x2F ("/") character.
+     *
+     * @param string $requestPath Path to check against
+     *
+     * @return bool
+     */
+    public function matchesPath($requestPath)
+    {
+        $cookiePath = $this->getPath();
+
+        // Match on exact matches or when path is the default empty "/"
+        if ($cookiePath === '/' || $cookiePath == $requestPath) {
+            return true;
+        }
+
+        // Ensure that the cookie-path is a prefix of the request path.
+        if (0 !== strpos($requestPath, $cookiePath)) {
+            return false;
+        }
+
+        // Match if the last character of the cookie-path is "/"
+        if (substr($cookiePath, -1, 1) === '/') {
+            return true;
+        }
+
+        // Match if the first character not included in cookie path is "/"
+        return substr($requestPath, strlen($cookiePath), 1) === '/';
+    }
+
+    /**
+     * Check if the cookie matches a domain value
+     *
+     * @param string $domain Domain to check against
+     *
+     * @return bool
+     */
+    public function matchesDomain($domain)
+    {
+        // Remove the leading '.' as per spec in RFC 6265.
+        // http://tools.ietf.org/html/rfc6265#section-5.2.3
+        $cookieDomain = ltrim($this->getDomain(), '.');
+
+        // Domain not set or exact match.
+        if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) {
+            return true;
+        }
+
+        // Matching the subdomain according to RFC 6265.
+        // http://tools.ietf.org/html/rfc6265#section-5.1.3
+        if (filter_var($domain, FILTER_VALIDATE_IP)) {
+            return false;
+        }
+
+        return (bool) preg_match('/\.' . preg_quote($cookieDomain) . '$/', $domain);
+    }
+
+    /**
+     * Check if the cookie is expired
+     *
+     * @return bool
+     */
+    public function isExpired()
+    {
+        return $this->getExpires() && time() > $this->getExpires();
+    }
+
+    /**
+     * Check if the cookie is valid according to RFC 6265
+     *
+     * @return bool|string Returns true if valid or an error message if invalid
+     */
+    public function validate()
+    {
+        // Names must not be empty, but can be 0
+        $name = $this->getName();
+        if (empty($name) && !is_numeric($name)) {
+            return 'The cookie name must not be empty';
+        }
+
+        // Check if any of the invalid characters are present in the cookie name
+        if (preg_match(
+            '/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5c\x7b\x7d\x7f]/',
+            $name)
+        ) {
+            return 'Cookie name must not contain invalid characters: ASCII '
+                . 'Control characters (0-31;127), space, tab and the '
+                . 'following characters: ()<>@,;:\"/?={}';
+        }
+
+        // Value must not be empty, but can be 0
+        $value = $this->getValue();
+        if (empty($value) && !is_numeric($value)) {
+            return 'The cookie value must not be empty';
+        }
+
+        // Domains must not be empty, but can be 0
+        // A "0" is not a valid internet domain, but may be used as server name
+        // in a private network.
+        $domain = $this->getDomain();
+        if (empty($domain) && !is_numeric($domain)) {
+            return 'The cookie domain must not be empty';
+        }
+
+        return true;
+    }
+}

+ 27 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php

@@ -0,0 +1,27 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * Exception when an HTTP error occurs (4xx or 5xx error)
+ */
+class BadResponseException extends RequestException
+{
+    public function __construct(
+        $message,
+        RequestInterface $request,
+        ResponseInterface $response = null,
+        \Exception $previous = null,
+        array $handlerContext = []
+    ) {
+        if (null === $response) {
+            @trigger_error(
+                'Instantiating the ' . __CLASS__ . ' class without a Response is deprecated since version 6.3 and will be removed in 7.0.',
+                E_USER_DEPRECATED
+            );
+        }
+        parent::__construct($message, $request, $response, $previous, $handlerContext);
+    }
+}

+ 7 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/ClientException.php

@@ -0,0 +1,7 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+/**
+ * Exception when a client error is encountered (4xx codes)
+ */
+class ClientException extends BadResponseException {}

+ 37 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php

@@ -0,0 +1,37 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Exception thrown when a connection cannot be established.
+ *
+ * Note that no response is present for a ConnectException
+ */
+class ConnectException extends RequestException
+{
+    public function __construct(
+        $message,
+        RequestInterface $request,
+        \Exception $previous = null,
+        array $handlerContext = []
+    ) {
+        parent::__construct($message, $request, null, $previous, $handlerContext);
+    }
+
+    /**
+     * @return null
+     */
+    public function getResponse()
+    {
+        return null;
+    }
+
+    /**
+     * @return bool
+     */
+    public function hasResponse()
+    {
+        return false;
+    }
+}

+ 4 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php

@@ -0,0 +1,4 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+interface GuzzleException {}

+ 217 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php

@@ -0,0 +1,217 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use GuzzleHttp\Promise\PromiseInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * HTTP Request exception
+ */
+class RequestException extends TransferException
+{
+    /** @var RequestInterface */
+    private $request;
+
+    /** @var ResponseInterface */
+    private $response;
+
+    /** @var array */
+    private $handlerContext;
+
+    public function __construct(
+        $message,
+        RequestInterface $request,
+        ResponseInterface $response = null,
+        \Exception $previous = null,
+        array $handlerContext = []
+    ) {
+        // Set the code of the exception if the response is set and not future.
+        $code = $response && !($response instanceof PromiseInterface)
+            ? $response->getStatusCode()
+            : 0;
+        parent::__construct($message, $code, $previous);
+        $this->request = $request;
+        $this->response = $response;
+        $this->handlerContext = $handlerContext;
+    }
+
+    /**
+     * Wrap non-RequestExceptions with a RequestException
+     *
+     * @param RequestInterface $request
+     * @param \Exception       $e
+     *
+     * @return RequestException
+     */
+    public static function wrapException(RequestInterface $request, \Exception $e)
+    {
+        return $e instanceof RequestException
+            ? $e
+            : new RequestException($e->getMessage(), $request, null, $e);
+    }
+
+    /**
+     * Factory method to create a new exception with a normalized error message
+     *
+     * @param RequestInterface  $request  Request
+     * @param ResponseInterface $response Response received
+     * @param \Exception        $previous Previous exception
+     * @param array             $ctx      Optional handler context.
+     *
+     * @return self
+     */
+    public static function create(
+        RequestInterface $request,
+        ResponseInterface $response = null,
+        \Exception $previous = null,
+        array $ctx = []
+    ) {
+        if (!$response) {
+            return new self(
+                'Error completing request',
+                $request,
+                null,
+                $previous,
+                $ctx
+            );
+        }
+
+        $level = (int) floor($response->getStatusCode() / 100);
+        if ($level === 4) {
+            $label = 'Client error';
+            $className = ClientException::class;
+        } elseif ($level === 5) {
+            $label = 'Server error';
+            $className = ServerException::class;
+        } else {
+            $label = 'Unsuccessful request';
+            $className = __CLASS__;
+        }
+
+        $uri = $request->getUri();
+        $uri = static::obfuscateUri($uri);
+
+        // Client Error: `GET /` resulted in a `404 Not Found` response:
+        // <html> ... (truncated)
+        $message = sprintf(
+            '%s: `%s %s` resulted in a `%s %s` response',
+            $label,
+            $request->getMethod(),
+            $uri,
+            $response->getStatusCode(),
+            $response->getReasonPhrase()
+        );
+
+        $summary = static::getResponseBodySummary($response);
+
+        if ($summary !== null) {
+            $message .= ":\n{$summary}\n";
+        }
+
+        return new $className($message, $request, $response, $previous, $ctx);
+    }
+
+    /**
+     * Get a short summary of the response
+     *
+     * Will return `null` if the response is not printable.
+     *
+     * @param ResponseInterface $response
+     *
+     * @return string|null
+     */
+    public static function getResponseBodySummary(ResponseInterface $response)
+    {
+        $body = $response->getBody();
+
+        if (!$body->isSeekable()) {
+            return null;
+        }
+
+        $size = $body->getSize();
+
+        if ($size === 0) {
+            return null;
+        }
+
+        $summary = $body->read(120);
+        $body->rewind();
+
+        if ($size > 120) {
+            $summary .= ' (truncated...)';
+        }
+
+        // Matches any printable character, including unicode characters:
+        // letters, marks, numbers, punctuation, spacing, and separators.
+        if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) {
+            return null;
+        }
+
+        return $summary;
+    }
+
+    /**
+     * Obfuscates URI if there is an username and a password present
+     *
+     * @param UriInterface $uri
+     *
+     * @return UriInterface
+     */
+    private static function obfuscateUri($uri)
+    {
+        $userInfo = $uri->getUserInfo();
+
+        if (false !== ($pos = strpos($userInfo, ':'))) {
+            return $uri->withUserInfo(substr($userInfo, 0, $pos), '***');
+        }
+
+        return $uri;
+    }
+
+    /**
+     * Get the request that caused the exception
+     *
+     * @return RequestInterface
+     */
+    public function getRequest()
+    {
+        return $this->request;
+    }
+
+    /**
+     * Get the associated response
+     *
+     * @return ResponseInterface|null
+     */
+    public function getResponse()
+    {
+        return $this->response;
+    }
+
+    /**
+     * Check if a response was received
+     *
+     * @return bool
+     */
+    public function hasResponse()
+    {
+        return $this->response !== null;
+    }
+
+    /**
+     * Get contextual information about the error from the underlying handler.
+     *
+     * The contents of this array will vary depending on which handler you are
+     * using. It may also be just an empty array. Relying on this data will
+     * couple you to a specific handler, but can give more debug information
+     * when needed.
+     *
+     * @return array
+     */
+    public function getHandlerContext()
+    {
+        return $this->handlerContext;
+    }
+}

+ 27 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php

@@ -0,0 +1,27 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Exception thrown when a seek fails on a stream.
+ */
+class SeekException extends \RuntimeException implements GuzzleException
+{
+    private $stream;
+
+    public function __construct(StreamInterface $stream, $pos = 0, $msg = '')
+    {
+        $this->stream = $stream;
+        $msg = $msg ?: 'Could not seek the stream to position ' . $pos;
+        parent::__construct($msg);
+    }
+
+    /**
+     * @return StreamInterface
+     */
+    public function getStream()
+    {
+        return $this->stream;
+    }
+}

+ 7 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php

@@ -0,0 +1,7 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+/**
+ * Exception when a server error is encountered (5xx codes)
+ */
+class ServerException extends BadResponseException {}

+ 4 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php

@@ -0,0 +1,4 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+class TooManyRedirectsException extends RequestException {}

+ 4 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Exception/TransferException.php

@@ -0,0 +1,4 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+class TransferException extends \RuntimeException implements GuzzleException {}

+ 559 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php

@@ -0,0 +1,559 @@
+<?php
+namespace GuzzleHttp\Handler;
+
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Exception\ConnectException;
+use GuzzleHttp\Promise\FulfilledPromise;
+use GuzzleHttp\Promise\RejectedPromise;
+use GuzzleHttp\Psr7;
+use GuzzleHttp\Psr7\LazyOpenStream;
+use GuzzleHttp\TransferStats;
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Creates curl resources from a request
+ */
+class CurlFactory implements CurlFactoryInterface
+{
+    /** @var array */
+    private $handles = [];
+
+    /** @var int Total number of idle handles to keep in cache */
+    private $maxHandles;
+
+    /**
+     * @param int $maxHandles Maximum number of idle handles.
+     */
+    public function __construct($maxHandles)
+    {
+        $this->maxHandles = $maxHandles;
+    }
+
+    public function create(RequestInterface $request, array $options)
+    {
+        if (isset($options['curl']['body_as_string'])) {
+            $options['_body_as_string'] = $options['curl']['body_as_string'];
+            unset($options['curl']['body_as_string']);
+        }
+
+        $easy = new EasyHandle;
+        $easy->request = $request;
+        $easy->options = $options;
+        $conf = $this->getDefaultConf($easy);
+        $this->applyMethod($easy, $conf);
+        $this->applyHandlerOptions($easy, $conf);
+        $this->applyHeaders($easy, $conf);
+        unset($conf['_headers']);
+
+        // Add handler options from the request configuration options
+        if (isset($options['curl'])) {
+            $conf = array_replace($conf, $options['curl']);
+        }
+
+        $conf[CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy);
+        $easy->handle = $this->handles
+            ? array_pop($this->handles)
+            : curl_init();
+        curl_setopt_array($easy->handle, $conf);
+
+        return $easy;
+    }
+
+    public function release(EasyHandle $easy)
+    {
+        $resource = $easy->handle;
+        unset($easy->handle);
+
+        if (count($this->handles) >= $this->maxHandles) {
+            curl_close($resource);
+        } else {
+            // Remove all callback functions as they can hold onto references
+            // and are not cleaned up by curl_reset. Using curl_setopt_array
+            // does not work for some reason, so removing each one
+            // individually.
+            curl_setopt($resource, CURLOPT_HEADERFUNCTION, null);
+            curl_setopt($resource, CURLOPT_READFUNCTION, null);
+            curl_setopt($resource, CURLOPT_WRITEFUNCTION, null);
+            curl_setopt($resource, CURLOPT_PROGRESSFUNCTION, null);
+            curl_reset($resource);
+            $this->handles[] = $resource;
+        }
+    }
+
+    /**
+     * Completes a cURL transaction, either returning a response promise or a
+     * rejected promise.
+     *
+     * @param callable             $handler
+     * @param EasyHandle           $easy
+     * @param CurlFactoryInterface $factory Dictates how the handle is released
+     *
+     * @return \GuzzleHttp\Promise\PromiseInterface
+     */
+    public static function finish(
+        callable $handler,
+        EasyHandle $easy,
+        CurlFactoryInterface $factory
+    ) {
+        if (isset($easy->options['on_stats'])) {
+            self::invokeStats($easy);
+        }
+
+        if (!$easy->response || $easy->errno) {
+            return self::finishError($handler, $easy, $factory);
+        }
+
+        // Return the response if it is present and there is no error.
+        $factory->release($easy);
+
+        // Rewind the body of the response if possible.
+        $body = $easy->response->getBody();
+        if ($body->isSeekable()) {
+            $body->rewind();
+        }
+
+        return new FulfilledPromise($easy->response);
+    }
+
+    private static function invokeStats(EasyHandle $easy)
+    {
+        $curlStats = curl_getinfo($easy->handle);
+        $stats = new TransferStats(
+            $easy->request,
+            $easy->response,
+            $curlStats['total_time'],
+            $easy->errno,
+            $curlStats
+        );
+        call_user_func($easy->options['on_stats'], $stats);
+    }
+
+    private static function finishError(
+        callable $handler,
+        EasyHandle $easy,
+        CurlFactoryInterface $factory
+    ) {
+        // Get error information and release the handle to the factory.
+        $ctx = [
+            'errno' => $easy->errno,
+            'error' => curl_error($easy->handle),
+        ] + curl_getinfo($easy->handle);
+        $factory->release($easy);
+
+        // Retry when nothing is present or when curl failed to rewind.
+        if (empty($easy->options['_err_message'])
+            && (!$easy->errno || $easy->errno == 65)
+        ) {
+            return self::retryFailedRewind($handler, $easy, $ctx);
+        }
+
+        return self::createRejection($easy, $ctx);
+    }
+
+    private static function createRejection(EasyHandle $easy, array $ctx)
+    {
+        static $connectionErrors = [
+            CURLE_OPERATION_TIMEOUTED  => true,
+            CURLE_COULDNT_RESOLVE_HOST => true,
+            CURLE_COULDNT_CONNECT      => true,
+            CURLE_SSL_CONNECT_ERROR    => true,
+            CURLE_GOT_NOTHING          => true,
+        ];
+
+        // If an exception was encountered during the onHeaders event, then
+        // return a rejected promise that wraps that exception.
+        if ($easy->onHeadersException) {
+            return \GuzzleHttp\Promise\rejection_for(
+                new RequestException(
+                    'An error was encountered during the on_headers event',
+                    $easy->request,
+                    $easy->response,
+                    $easy->onHeadersException,
+                    $ctx
+                )
+            );
+        }
+
+        $message = sprintf(
+            'cURL error %s: %s (%s)',
+            $ctx['errno'],
+            $ctx['error'],
+            'see http://curl.haxx.se/libcurl/c/libcurl-errors.html'
+        );
+
+        // Create a connection exception if it was a specific error code.
+        $error = isset($connectionErrors[$easy->errno])
+            ? new ConnectException($message, $easy->request, null, $ctx)
+            : new RequestException($message, $easy->request, $easy->response, null, $ctx);
+
+        return \GuzzleHttp\Promise\rejection_for($error);
+    }
+
+    private function getDefaultConf(EasyHandle $easy)
+    {
+        $conf = [
+            '_headers'             => $easy->request->getHeaders(),
+            CURLOPT_CUSTOMREQUEST  => $easy->request->getMethod(),
+            CURLOPT_URL            => (string) $easy->request->getUri()->withFragment(''),
+            CURLOPT_RETURNTRANSFER => false,
+            CURLOPT_HEADER         => false,
+            CURLOPT_CONNECTTIMEOUT => 150,
+        ];
+
+        if (defined('CURLOPT_PROTOCOLS')) {
+            $conf[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
+        }
+
+        $version = $easy->request->getProtocolVersion();
+        if ($version == 1.1) {
+            $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1;
+        } elseif ($version == 2.0) {
+            $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0;
+        } else {
+            $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
+        }
+
+        return $conf;
+    }
+
+    private function applyMethod(EasyHandle $easy, array &$conf)
+    {
+        $body = $easy->request->getBody();
+        $size = $body->getSize();
+
+        if ($size === null || $size > 0) {
+            $this->applyBody($easy->request, $easy->options, $conf);
+            return;
+        }
+
+        $method = $easy->request->getMethod();
+        if ($method === 'PUT' || $method === 'POST') {
+            // See http://tools.ietf.org/html/rfc7230#section-3.3.2
+            if (!$easy->request->hasHeader('Content-Length')) {
+                $conf[CURLOPT_HTTPHEADER][] = 'Content-Length: 0';
+            }
+        } elseif ($method === 'HEAD') {
+            $conf[CURLOPT_NOBODY] = true;
+            unset(
+                $conf[CURLOPT_WRITEFUNCTION],
+                $conf[CURLOPT_READFUNCTION],
+                $conf[CURLOPT_FILE],
+                $conf[CURLOPT_INFILE]
+            );
+        }
+    }
+
+    private function applyBody(RequestInterface $request, array $options, array &$conf)
+    {
+        $size = $request->hasHeader('Content-Length')
+            ? (int) $request->getHeaderLine('Content-Length')
+            : null;
+
+        // Send the body as a string if the size is less than 1MB OR if the
+        // [curl][body_as_string] request value is set.
+        if (($size !== null && $size < 1000000) ||
+            !empty($options['_body_as_string'])
+        ) {
+            $conf[CURLOPT_POSTFIELDS] = (string) $request->getBody();
+            // Don't duplicate the Content-Length header
+            $this->removeHeader('Content-Length', $conf);
+            $this->removeHeader('Transfer-Encoding', $conf);
+        } else {
+            $conf[CURLOPT_UPLOAD] = true;
+            if ($size !== null) {
+                $conf[CURLOPT_INFILESIZE] = $size;
+                $this->removeHeader('Content-Length', $conf);
+            }
+            $body = $request->getBody();
+            if ($body->isSeekable()) {
+                $body->rewind();
+            }
+            $conf[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) {
+                return $body->read($length);
+            };
+        }
+
+        // If the Expect header is not present, prevent curl from adding it
+        if (!$request->hasHeader('Expect')) {
+            $conf[CURLOPT_HTTPHEADER][] = 'Expect:';
+        }
+
+        // cURL sometimes adds a content-type by default. Prevent this.
+        if (!$request->hasHeader('Content-Type')) {
+            $conf[CURLOPT_HTTPHEADER][] = 'Content-Type:';
+        }
+    }
+
+    private function applyHeaders(EasyHandle $easy, array &$conf)
+    {
+        foreach ($conf['_headers'] as $name => $values) {
+            foreach ($values as $value) {
+                $conf[CURLOPT_HTTPHEADER][] = "$name: $value";
+            }
+        }
+
+        // Remove the Accept header if one was not set
+        if (!$easy->request->hasHeader('Accept')) {
+            $conf[CURLOPT_HTTPHEADER][] = 'Accept:';
+        }
+    }
+
+    /**
+     * Remove a header from the options array.
+     *
+     * @param string $name    Case-insensitive header to remove
+     * @param array  $options Array of options to modify
+     */
+    private function removeHeader($name, array &$options)
+    {
+        foreach (array_keys($options['_headers']) as $key) {
+            if (!strcasecmp($key, $name)) {
+                unset($options['_headers'][$key]);
+                return;
+            }
+        }
+    }
+
+    private function applyHandlerOptions(EasyHandle $easy, array &$conf)
+    {
+        $options = $easy->options;
+        if (isset($options['verify'])) {
+            if ($options['verify'] === false) {
+                unset($conf[CURLOPT_CAINFO]);
+                $conf[CURLOPT_SSL_VERIFYHOST] = 0;
+                $conf[CURLOPT_SSL_VERIFYPEER] = false;
+            } else {
+                $conf[CURLOPT_SSL_VERIFYHOST] = 2;
+                $conf[CURLOPT_SSL_VERIFYPEER] = true;
+                if (is_string($options['verify'])) {
+                    // Throw an error if the file/folder/link path is not valid or doesn't exist.
+                    if (!file_exists($options['verify'])) {
+                        throw new \InvalidArgumentException(
+                            "SSL CA bundle not found: {$options['verify']}"
+                        );
+                    }
+                    // If it's a directory or a link to a directory use CURLOPT_CAPATH.
+                    // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO.
+                    if (is_dir($options['verify']) ||
+                        (is_link($options['verify']) && is_dir(readlink($options['verify'])))) {
+                        $conf[CURLOPT_CAPATH] = $options['verify'];
+                    } else {
+                        $conf[CURLOPT_CAINFO] = $options['verify'];
+                    }
+                }
+            }
+        }
+
+        if (!empty($options['decode_content'])) {
+            $accept = $easy->request->getHeaderLine('Accept-Encoding');
+            if ($accept) {
+                $conf[CURLOPT_ENCODING] = $accept;
+            } else {
+                $conf[CURLOPT_ENCODING] = '';
+                // Don't let curl send the header over the wire
+                $conf[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:';
+            }
+        }
+
+        if (isset($options['sink'])) {
+            $sink = $options['sink'];
+            if (!is_string($sink)) {
+                $sink = \GuzzleHttp\Psr7\stream_for($sink);
+            } elseif (!is_dir(dirname($sink))) {
+                // Ensure that the directory exists before failing in curl.
+                throw new \RuntimeException(sprintf(
+                    'Directory %s does not exist for sink value of %s',
+                    dirname($sink),
+                    $sink
+                ));
+            } else {
+                $sink = new LazyOpenStream($sink, 'w+');
+            }
+            $easy->sink = $sink;
+            $conf[CURLOPT_WRITEFUNCTION] = function ($ch, $write) use ($sink) {
+                return $sink->write($write);
+            };
+        } else {
+            // Use a default temp stream if no sink was set.
+            $conf[CURLOPT_FILE] = fopen('php://temp', 'w+');
+            $easy->sink = Psr7\stream_for($conf[CURLOPT_FILE]);
+        }
+        $timeoutRequiresNoSignal = false;
+        if (isset($options['timeout'])) {
+            $timeoutRequiresNoSignal |= $options['timeout'] < 1;
+            $conf[CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000;
+        }
+
+        // CURL default value is CURL_IPRESOLVE_WHATEVER
+        if (isset($options['force_ip_resolve'])) {
+            if ('v4' === $options['force_ip_resolve']) {
+                $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
+            } else if ('v6' === $options['force_ip_resolve']) {
+                $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V6;
+            }
+        }
+
+        if (isset($options['connect_timeout'])) {
+            $timeoutRequiresNoSignal |= $options['connect_timeout'] < 1;
+            $conf[CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000;
+        }
+
+        if ($timeoutRequiresNoSignal && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
+            $conf[CURLOPT_NOSIGNAL] = true;
+        }
+
+        if (isset($options['proxy'])) {
+            if (!is_array($options['proxy'])) {
+                $conf[CURLOPT_PROXY] = $options['proxy'];
+            } else {
+                $scheme = $easy->request->getUri()->getScheme();
+                if (isset($options['proxy'][$scheme])) {
+                    $host = $easy->request->getUri()->getHost();
+                    if (!isset($options['proxy']['no']) ||
+                        !\GuzzleHttp\is_host_in_noproxy($host, $options['proxy']['no'])
+                    ) {
+                        $conf[CURLOPT_PROXY] = $options['proxy'][$scheme];
+                    }
+                }
+            }
+        }
+
+        if (isset($options['cert'])) {
+            $cert = $options['cert'];
+            if (is_array($cert)) {
+                $conf[CURLOPT_SSLCERTPASSWD] = $cert[1];
+                $cert = $cert[0];
+            }
+            if (!file_exists($cert)) {
+                throw new \InvalidArgumentException(
+                    "SSL certificate not found: {$cert}"
+                );
+            }
+            $conf[CURLOPT_SSLCERT] = $cert;
+        }
+
+        if (isset($options['ssl_key'])) {
+            $sslKey = $options['ssl_key'];
+            if (is_array($sslKey)) {
+                $conf[CURLOPT_SSLKEYPASSWD] = $sslKey[1];
+                $sslKey = $sslKey[0];
+            }
+            if (!file_exists($sslKey)) {
+                throw new \InvalidArgumentException(
+                    "SSL private key not found: {$sslKey}"
+                );
+            }
+            $conf[CURLOPT_SSLKEY] = $sslKey;
+        }
+
+        if (isset($options['progress'])) {
+            $progress = $options['progress'];
+            if (!is_callable($progress)) {
+                throw new \InvalidArgumentException(
+                    'progress client option must be callable'
+                );
+            }
+            $conf[CURLOPT_NOPROGRESS] = false;
+            $conf[CURLOPT_PROGRESSFUNCTION] = function () use ($progress) {
+                $args = func_get_args();
+                // PHP 5.5 pushed the handle onto the start of the args
+                if (is_resource($args[0])) {
+                    array_shift($args);
+                }
+                call_user_func_array($progress, $args);
+            };
+        }
+
+        if (!empty($options['debug'])) {
+            $conf[CURLOPT_STDERR] = \GuzzleHttp\debug_resource($options['debug']);
+            $conf[CURLOPT_VERBOSE] = true;
+        }
+    }
+
+    /**
+     * This function ensures that a response was set on a transaction. If one
+     * was not set, then the request is retried if possible. This error
+     * typically means you are sending a payload, curl encountered a
+     * "Connection died, retrying a fresh connect" error, tried to rewind the
+     * stream, and then encountered a "necessary data rewind wasn't possible"
+     * error, causing the request to be sent through curl_multi_info_read()
+     * without an error status.
+     */
+    private static function retryFailedRewind(
+        callable $handler,
+        EasyHandle $easy,
+        array $ctx
+    ) {
+        try {
+            // Only rewind if the body has been read from.
+            $body = $easy->request->getBody();
+            if ($body->tell() > 0) {
+                $body->rewind();
+            }
+        } catch (\RuntimeException $e) {
+            $ctx['error'] = 'The connection unexpectedly failed without '
+                . 'providing an error. The request would have been retried, '
+                . 'but attempting to rewind the request body failed. '
+                . 'Exception: ' . $e;
+            return self::createRejection($easy, $ctx);
+        }
+
+        // Retry no more than 3 times before giving up.
+        if (!isset($easy->options['_curl_retries'])) {
+            $easy->options['_curl_retries'] = 1;
+        } elseif ($easy->options['_curl_retries'] == 2) {
+            $ctx['error'] = 'The cURL request was retried 3 times '
+                . 'and did not succeed. The most likely reason for the failure '
+                . 'is that cURL was unable to rewind the body of the request '
+                . 'and subsequent retries resulted in the same error. Turn on '
+                . 'the debug option to see what went wrong. See '
+                . 'https://bugs.php.net/bug.php?id=47204 for more information.';
+            return self::createRejection($easy, $ctx);
+        } else {
+            $easy->options['_curl_retries']++;
+        }
+
+        return $handler($easy->request, $easy->options);
+    }
+
+    private function createHeaderFn(EasyHandle $easy)
+    {
+        if (isset($easy->options['on_headers'])) {
+            $onHeaders = $easy->options['on_headers'];
+
+            if (!is_callable($onHeaders)) {
+                throw new \InvalidArgumentException('on_headers must be callable');
+            }
+        } else {
+            $onHeaders = null;
+        }
+
+        return function ($ch, $h) use (
+            $onHeaders,
+            $easy,
+            &$startingResponse
+        ) {
+            $value = trim($h);
+            if ($value === '') {
+                $startingResponse = true;
+                $easy->createResponse();
+                if ($onHeaders !== null) {
+                    try {
+                        $onHeaders($easy->response);
+                    } catch (\Exception $e) {
+                        // Associate the exception with the handle and trigger
+                        // a curl header write error by returning 0.
+                        $easy->onHeadersException = $e;
+                        return -1;
+                    }
+                }
+            } elseif ($startingResponse) {
+                $startingResponse = false;
+                $easy->headers = [$value];
+            } else {
+                $easy->headers[] = $value;
+            }
+            return strlen($h);
+        };
+    }
+}

+ 27 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php

@@ -0,0 +1,27 @@
+<?php
+namespace GuzzleHttp\Handler;
+
+use Psr\Http\Message\RequestInterface;
+
+interface CurlFactoryInterface
+{
+    /**
+     * Creates a cURL handle resource.
+     *
+     * @param RequestInterface $request Request
+     * @param array            $options Transfer options
+     *
+     * @return EasyHandle
+     * @throws \RuntimeException when an option cannot be applied
+     */
+    public function create(RequestInterface $request, array $options);
+
+    /**
+     * Release an easy handle, allowing it to be reused or closed.
+     *
+     * This function must call unset on the easy handle's "handle" property.
+     *
+     * @param EasyHandle $easy
+     */
+    public function release(EasyHandle $easy);
+}

+ 45 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php

@@ -0,0 +1,45 @@
+<?php
+namespace GuzzleHttp\Handler;
+
+use GuzzleHttp\Psr7;
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * HTTP handler that uses cURL easy handles as a transport layer.
+ *
+ * When using the CurlHandler, custom curl options can be specified as an
+ * associative array of curl option constants mapping to values in the
+ * **curl** key of the "client" key of the request.
+ */
+class CurlHandler
+{
+    /** @var CurlFactoryInterface */
+    private $factory;
+
+    /**
+     * Accepts an associative array of options:
+     *
+     * - factory: Optional curl factory used to create cURL handles.
+     *
+     * @param array $options Array of options to use with the handler
+     */
+    public function __construct(array $options = [])
+    {
+        $this->factory = isset($options['handle_factory'])
+            ? $options['handle_factory']
+            : new CurlFactory(3);
+    }
+
+    public function __invoke(RequestInterface $request, array $options)
+    {
+        if (isset($options['delay'])) {
+            usleep($options['delay'] * 1000);
+        }
+
+        $easy = $this->factory->create($request, $options);
+        curl_exec($easy->handle);
+        $easy->errno = curl_errno($easy->handle);
+
+        return CurlFactory::finish($this, $easy, $this->factory);
+    }
+}

+ 197 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php

@@ -0,0 +1,197 @@
+<?php
+namespace GuzzleHttp\Handler;
+
+use GuzzleHttp\Promise as P;
+use GuzzleHttp\Promise\Promise;
+use GuzzleHttp\Psr7;
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Returns an asynchronous response using curl_multi_* functions.
+ *
+ * When using the CurlMultiHandler, custom curl options can be specified as an
+ * associative array of curl option constants mapping to values in the
+ * **curl** key of the provided request options.
+ *
+ * @property resource $_mh Internal use only. Lazy loaded multi-handle.
+ */
+class CurlMultiHandler
+{
+    /** @var CurlFactoryInterface */
+    private $factory;
+    private $selectTimeout;
+    private $active;
+    private $handles = [];
+    private $delays = [];
+
+    /**
+     * This handler accepts the following options:
+     *
+     * - handle_factory: An optional factory  used to create curl handles
+     * - select_timeout: Optional timeout (in seconds) to block before timing
+     *   out while selecting curl handles. Defaults to 1 second.
+     *
+     * @param array $options
+     */
+    public function __construct(array $options = [])
+    {
+        $this->factory = isset($options['handle_factory'])
+            ? $options['handle_factory'] : new CurlFactory(50);
+        $this->selectTimeout = isset($options['select_timeout'])
+            ? $options['select_timeout'] : 1;
+    }
+
+    public function __get($name)
+    {
+        if ($name === '_mh') {
+            return $this->_mh = curl_multi_init();
+        }
+
+        throw new \BadMethodCallException();
+    }
+
+    public function __destruct()
+    {
+        if (isset($this->_mh)) {
+            curl_multi_close($this->_mh);
+            unset($this->_mh);
+        }
+    }
+
+    public function __invoke(RequestInterface $request, array $options)
+    {
+        $easy = $this->factory->create($request, $options);
+        $id = (int) $easy->handle;
+
+        $promise = new Promise(
+            [$this, 'execute'],
+            function () use ($id) { return $this->cancel($id); }
+        );
+
+        $this->addRequest(['easy' => $easy, 'deferred' => $promise]);
+
+        return $promise;
+    }
+
+    /**
+     * Ticks the curl event loop.
+     */
+    public function tick()
+    {
+        // Add any delayed handles if needed.
+        if ($this->delays) {
+            $currentTime = microtime(true);
+            foreach ($this->delays as $id => $delay) {
+                if ($currentTime >= $delay) {
+                    unset($this->delays[$id]);
+                    curl_multi_add_handle(
+                        $this->_mh,
+                        $this->handles[$id]['easy']->handle
+                    );
+                }
+            }
+        }
+
+        // Step through the task queue which may add additional requests.
+        P\queue()->run();
+
+        if ($this->active &&
+            curl_multi_select($this->_mh, $this->selectTimeout) === -1
+        ) {
+            // Perform a usleep if a select returns -1.
+            // See: https://bugs.php.net/bug.php?id=61141
+            usleep(250);
+        }
+
+        while (curl_multi_exec($this->_mh, $this->active) === CURLM_CALL_MULTI_PERFORM);
+
+        $this->processMessages();
+    }
+
+    /**
+     * Runs until all outstanding connections have completed.
+     */
+    public function execute()
+    {
+        $queue = P\queue();
+
+        while ($this->handles || !$queue->isEmpty()) {
+            // If there are no transfers, then sleep for the next delay
+            if (!$this->active && $this->delays) {
+                usleep($this->timeToNext());
+            }
+            $this->tick();
+        }
+    }
+
+    private function addRequest(array $entry)
+    {
+        $easy = $entry['easy'];
+        $id = (int) $easy->handle;
+        $this->handles[$id] = $entry;
+        if (empty($easy->options['delay'])) {
+            curl_multi_add_handle($this->_mh, $easy->handle);
+        } else {
+            $this->delays[$id] = microtime(true) + ($easy->options['delay'] / 1000);
+        }
+    }
+
+    /**
+     * Cancels a handle from sending and removes references to it.
+     *
+     * @param int $id Handle ID to cancel and remove.
+     *
+     * @return bool True on success, false on failure.
+     */
+    private function cancel($id)
+    {
+        // Cannot cancel if it has been processed.
+        if (!isset($this->handles[$id])) {
+            return false;
+        }
+
+        $handle = $this->handles[$id]['easy']->handle;
+        unset($this->delays[$id], $this->handles[$id]);
+        curl_multi_remove_handle($this->_mh, $handle);
+        curl_close($handle);
+
+        return true;
+    }
+
+    private function processMessages()
+    {
+        while ($done = curl_multi_info_read($this->_mh)) {
+            $id = (int) $done['handle'];
+            curl_multi_remove_handle($this->_mh, $done['handle']);
+
+            if (!isset($this->handles[$id])) {
+                // Probably was cancelled.
+                continue;
+            }
+
+            $entry = $this->handles[$id];
+            unset($this->handles[$id], $this->delays[$id]);
+            $entry['easy']->errno = $done['result'];
+            $entry['deferred']->resolve(
+                CurlFactory::finish(
+                    $this,
+                    $entry['easy'],
+                    $this->factory
+                )
+            );
+        }
+    }
+
+    private function timeToNext()
+    {
+        $currentTime = microtime(true);
+        $nextTime = PHP_INT_MAX;
+        foreach ($this->delays as $time) {
+            if ($time < $nextTime) {
+                $nextTime = $time;
+            }
+        }
+
+        return max(0, $nextTime - $currentTime) * 1000000;
+    }
+}

+ 92 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php

@@ -0,0 +1,92 @@
+<?php
+namespace GuzzleHttp\Handler;
+
+use GuzzleHttp\Psr7\Response;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Represents a cURL easy handle and the data it populates.
+ *
+ * @internal
+ */
+final class EasyHandle
+{
+    /** @var resource cURL resource */
+    public $handle;
+
+    /** @var StreamInterface Where data is being written */
+    public $sink;
+
+    /** @var array Received HTTP headers so far */
+    public $headers = [];
+
+    /** @var ResponseInterface Received response (if any) */
+    public $response;
+
+    /** @var RequestInterface Request being sent */
+    public $request;
+
+    /** @var array Request options */
+    public $options = [];
+
+    /** @var int cURL error number (if any) */
+    public $errno = 0;
+
+    /** @var \Exception Exception during on_headers (if any) */
+    public $onHeadersException;
+
+    /**
+     * Attach a response to the easy handle based on the received headers.
+     *
+     * @throws \RuntimeException if no headers have been received.
+     */
+    public function createResponse()
+    {
+        if (empty($this->headers)) {
+            throw new \RuntimeException('No headers have been received');
+        }
+
+        // HTTP-version SP status-code SP reason-phrase
+        $startLine = explode(' ', array_shift($this->headers), 3);
+        $headers = \GuzzleHttp\headers_from_lines($this->headers);
+        $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers);
+
+        if (!empty($this->options['decode_content'])
+            && isset($normalizedKeys['content-encoding'])
+        ) {
+            $headers['x-encoded-content-encoding']
+                = $headers[$normalizedKeys['content-encoding']];
+            unset($headers[$normalizedKeys['content-encoding']]);
+            if (isset($normalizedKeys['content-length'])) {
+                $headers['x-encoded-content-length']
+                    = $headers[$normalizedKeys['content-length']];
+
+                $bodyLength = (int) $this->sink->getSize();
+                if ($bodyLength) {
+                    $headers[$normalizedKeys['content-length']] = $bodyLength;
+                } else {
+                    unset($headers[$normalizedKeys['content-length']]);
+                }
+            }
+        }
+
+        // Attach a response to the easy handle with the parsed headers.
+        $this->response = new Response(
+            $startLine[1],
+            $headers,
+            $this->sink,
+            substr($startLine[0], 5),
+            isset($startLine[2]) ? (string) $startLine[2] : null
+        );
+    }
+
+    public function __get($name)
+    {
+        $msg = $name === 'handle'
+            ? 'The EasyHandle has been released'
+            : 'Invalid property: ' . $name;
+        throw new \BadMethodCallException($msg);
+    }
+}

+ 189 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php

@@ -0,0 +1,189 @@
+<?php
+namespace GuzzleHttp\Handler;
+
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\HandlerStack;
+use GuzzleHttp\Promise\PromiseInterface;
+use GuzzleHttp\Promise\RejectedPromise;
+use GuzzleHttp\TransferStats;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * Handler that returns responses or throw exceptions from a queue.
+ */
+class MockHandler implements \Countable
+{
+    private $queue = [];
+    private $lastRequest;
+    private $lastOptions;
+    private $onFulfilled;
+    private $onRejected;
+
+    /**
+     * Creates a new MockHandler that uses the default handler stack list of
+     * middlewares.
+     *
+     * @param array $queue Array of responses, callables, or exceptions.
+     * @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
+     * @param callable $onRejected  Callback to invoke when the return value is rejected.
+     *
+     * @return HandlerStack
+     */
+    public static function createWithMiddleware(
+        array $queue = null,
+        callable $onFulfilled = null,
+        callable $onRejected = null
+    ) {
+        return HandlerStack::create(new self($queue, $onFulfilled, $onRejected));
+    }
+
+    /**
+     * The passed in value must be an array of
+     * {@see Psr7\Http\Message\ResponseInterface} objects, Exceptions,
+     * callables, or Promises.
+     *
+     * @param array $queue
+     * @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
+     * @param callable $onRejected  Callback to invoke when the return value is rejected.
+     */
+    public function __construct(
+        array $queue = null,
+        callable $onFulfilled = null,
+        callable $onRejected = null
+    ) {
+        $this->onFulfilled = $onFulfilled;
+        $this->onRejected = $onRejected;
+
+        if ($queue) {
+            call_user_func_array([$this, 'append'], $queue);
+        }
+    }
+
+    public function __invoke(RequestInterface $request, array $options)
+    {
+        if (!$this->queue) {
+            throw new \OutOfBoundsException('Mock queue is empty');
+        }
+
+        if (isset($options['delay'])) {
+            usleep($options['delay'] * 1000);
+        }
+
+        $this->lastRequest = $request;
+        $this->lastOptions = $options;
+        $response = array_shift($this->queue);
+
+        if (isset($options['on_headers'])) {
+            if (!is_callable($options['on_headers'])) {
+                throw new \InvalidArgumentException('on_headers must be callable');
+            }
+            try {
+                $options['on_headers']($response);
+            } catch (\Exception $e) {
+                $msg = 'An error was encountered during the on_headers event';
+                $response = new RequestException($msg, $request, $response, $e);
+            }
+        }
+
+        if (is_callable($response)) {
+            $response = call_user_func($response, $request, $options);
+        }
+
+        $response = $response instanceof \Exception
+            ? \GuzzleHttp\Promise\rejection_for($response)
+            : \GuzzleHttp\Promise\promise_for($response);
+
+        return $response->then(
+            function ($value) use ($request, $options) {
+                $this->invokeStats($request, $options, $value);
+                if ($this->onFulfilled) {
+                    call_user_func($this->onFulfilled, $value);
+                }
+                if (isset($options['sink'])) {
+                    $contents = (string) $value->getBody();
+                    $sink = $options['sink'];
+
+                    if (is_resource($sink)) {
+                        fwrite($sink, $contents);
+                    } elseif (is_string($sink)) {
+                        file_put_contents($sink, $contents);
+                    } elseif ($sink instanceof \Psr\Http\Message\StreamInterface) {
+                        $sink->write($contents);
+                    }
+                }
+
+                return $value;
+            },
+            function ($reason) use ($request, $options) {
+                $this->invokeStats($request, $options, null, $reason);
+                if ($this->onRejected) {
+                    call_user_func($this->onRejected, $reason);
+                }
+                return \GuzzleHttp\Promise\rejection_for($reason);
+            }
+        );
+    }
+
+    /**
+     * Adds one or more variadic requests, exceptions, callables, or promises
+     * to the queue.
+     */
+    public function append()
+    {
+        foreach (func_get_args() as $value) {
+            if ($value instanceof ResponseInterface
+                || $value instanceof \Exception
+                || $value instanceof PromiseInterface
+                || is_callable($value)
+            ) {
+                $this->queue[] = $value;
+            } else {
+                throw new \InvalidArgumentException('Expected a response or '
+                    . 'exception. Found ' . \GuzzleHttp\describe_type($value));
+            }
+        }
+    }
+
+    /**
+     * Get the last received request.
+     *
+     * @return RequestInterface
+     */
+    public function getLastRequest()
+    {
+        return $this->lastRequest;
+    }
+
+    /**
+     * Get the last received request options.
+     *
+     * @return array
+     */
+    public function getLastOptions()
+    {
+        return $this->lastOptions;
+    }
+
+    /**
+     * Returns the number of remaining items in the queue.
+     *
+     * @return int
+     */
+    public function count()
+    {
+        return count($this->queue);
+    }
+
+    private function invokeStats(
+        RequestInterface $request,
+        array $options,
+        ResponseInterface $response = null,
+        $reason = null
+    ) {
+        if (isset($options['on_stats'])) {
+            $stats = new TransferStats($request, $response, 0, $reason);
+            call_user_func($options['on_stats'], $stats);
+        }
+    }
+}

+ 55 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php

@@ -0,0 +1,55 @@
+<?php
+namespace GuzzleHttp\Handler;
+
+use GuzzleHttp\RequestOptions;
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Provides basic proxies for handlers.
+ */
+class Proxy
+{
+    /**
+     * Sends synchronous requests to a specific handler while sending all other
+     * requests to another handler.
+     *
+     * @param callable $default Handler used for normal responses
+     * @param callable $sync    Handler used for synchronous responses.
+     *
+     * @return callable Returns the composed handler.
+     */
+    public static function wrapSync(
+        callable $default,
+        callable $sync
+    ) {
+        return function (RequestInterface $request, array $options) use ($default, $sync) {
+            return empty($options[RequestOptions::SYNCHRONOUS])
+                ? $default($request, $options)
+                : $sync($request, $options);
+        };
+    }
+
+    /**
+     * Sends streaming requests to a streaming compatible handler while sending
+     * all other requests to a default handler.
+     *
+     * This, for example, could be useful for taking advantage of the
+     * performance benefits of curl while still supporting true streaming
+     * through the StreamHandler.
+     *
+     * @param callable $default   Handler used for non-streaming responses
+     * @param callable $streaming Handler used for streaming responses
+     *
+     * @return callable Returns the composed handler.
+     */
+    public static function wrapStreaming(
+        callable $default,
+        callable $streaming
+    ) {
+        return function (RequestInterface $request, array $options) use ($default, $streaming) {
+            return empty($options['stream'])
+                ? $default($request, $options)
+                : $streaming($request, $options);
+        };
+    }
+}

+ 533 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php

@@ -0,0 +1,533 @@
+<?php
+namespace GuzzleHttp\Handler;
+
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Exception\ConnectException;
+use GuzzleHttp\Promise\FulfilledPromise;
+use GuzzleHttp\Promise\RejectedPromise;
+use GuzzleHttp\Promise\PromiseInterface;
+use GuzzleHttp\Psr7;
+use GuzzleHttp\TransferStats;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * HTTP handler that uses PHP's HTTP stream wrapper.
+ */
+class StreamHandler
+{
+    private $lastHeaders = [];
+
+    /**
+     * Sends an HTTP request.
+     *
+     * @param RequestInterface $request Request to send.
+     * @param array            $options Request transfer options.
+     *
+     * @return PromiseInterface
+     */
+    public function __invoke(RequestInterface $request, array $options)
+    {
+        // Sleep if there is a delay specified.
+        if (isset($options['delay'])) {
+            usleep($options['delay'] * 1000);
+        }
+
+        $startTime = isset($options['on_stats']) ? microtime(true) : null;
+
+        try {
+            // Does not support the expect header.
+            $request = $request->withoutHeader('Expect');
+
+            // Append a content-length header if body size is zero to match
+            // cURL's behavior.
+            if (0 === $request->getBody()->getSize()) {
+                $request = $request->withHeader('Content-Length', 0);
+            }
+
+            return $this->createResponse(
+                $request,
+                $options,
+                $this->createStream($request, $options),
+                $startTime
+            );
+        } catch (\InvalidArgumentException $e) {
+            throw $e;
+        } catch (\Exception $e) {
+            // Determine if the error was a networking error.
+            $message = $e->getMessage();
+            // This list can probably get more comprehensive.
+            if (strpos($message, 'getaddrinfo') // DNS lookup failed
+                || strpos($message, 'Connection refused')
+                || strpos($message, "couldn't connect to host") // error on HHVM
+            ) {
+                $e = new ConnectException($e->getMessage(), $request, $e);
+            }
+            $e = RequestException::wrapException($request, $e);
+            $this->invokeStats($options, $request, $startTime, null, $e);
+
+            return \GuzzleHttp\Promise\rejection_for($e);
+        }
+    }
+
+    private function invokeStats(
+        array $options,
+        RequestInterface $request,
+        $startTime,
+        ResponseInterface $response = null,
+        $error = null
+    ) {
+        if (isset($options['on_stats'])) {
+            $stats = new TransferStats(
+                $request,
+                $response,
+                microtime(true) - $startTime,
+                $error,
+                []
+            );
+            call_user_func($options['on_stats'], $stats);
+        }
+    }
+
+    private function createResponse(
+        RequestInterface $request,
+        array $options,
+        $stream,
+        $startTime
+    ) {
+        $hdrs = $this->lastHeaders;
+        $this->lastHeaders = [];
+        $parts = explode(' ', array_shift($hdrs), 3);
+        $ver = explode('/', $parts[0])[1];
+        $status = $parts[1];
+        $reason = isset($parts[2]) ? $parts[2] : null;
+        $headers = \GuzzleHttp\headers_from_lines($hdrs);
+        list ($stream, $headers) = $this->checkDecode($options, $headers, $stream);
+        $stream = Psr7\stream_for($stream);
+        $sink = $stream;
+
+        if (strcasecmp('HEAD', $request->getMethod())) {
+            $sink = $this->createSink($stream, $options);
+        }
+
+        $response = new Psr7\Response($status, $headers, $sink, $ver, $reason);
+
+        if (isset($options['on_headers'])) {
+            try {
+                $options['on_headers']($response);
+            } catch (\Exception $e) {
+                $msg = 'An error was encountered during the on_headers event';
+                $ex = new RequestException($msg, $request, $response, $e);
+                return \GuzzleHttp\Promise\rejection_for($ex);
+            }
+        }
+
+        // Do not drain when the request is a HEAD request because they have
+        // no body.
+        if ($sink !== $stream) {
+            $this->drain(
+                $stream,
+                $sink,
+                $response->getHeaderLine('Content-Length')
+            );
+        }
+
+        $this->invokeStats($options, $request, $startTime, $response, null);
+
+        return new FulfilledPromise($response);
+    }
+
+    private function createSink(StreamInterface $stream, array $options)
+    {
+        if (!empty($options['stream'])) {
+            return $stream;
+        }
+
+        $sink = isset($options['sink'])
+            ? $options['sink']
+            : fopen('php://temp', 'r+');
+
+        return is_string($sink)
+            ? new Psr7\LazyOpenStream($sink, 'w+')
+            : Psr7\stream_for($sink);
+    }
+
+    private function checkDecode(array $options, array $headers, $stream)
+    {
+        // Automatically decode responses when instructed.
+        if (!empty($options['decode_content'])) {
+            $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers);
+            if (isset($normalizedKeys['content-encoding'])) {
+                $encoding = $headers[$normalizedKeys['content-encoding']];
+                if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') {
+                    $stream = new Psr7\InflateStream(
+                        Psr7\stream_for($stream)
+                    );
+                    $headers['x-encoded-content-encoding']
+                        = $headers[$normalizedKeys['content-encoding']];
+                    // Remove content-encoding header
+                    unset($headers[$normalizedKeys['content-encoding']]);
+                    // Fix content-length header
+                    if (isset($normalizedKeys['content-length'])) {
+                        $headers['x-encoded-content-length']
+                            = $headers[$normalizedKeys['content-length']];
+
+                        $length = (int) $stream->getSize();
+                        if ($length === 0) {
+                            unset($headers[$normalizedKeys['content-length']]);
+                        } else {
+                            $headers[$normalizedKeys['content-length']] = [$length];
+                        }
+                    }
+                }
+            }
+        }
+
+        return [$stream, $headers];
+    }
+
+    /**
+     * Drains the source stream into the "sink" client option.
+     *
+     * @param StreamInterface $source
+     * @param StreamInterface $sink
+     * @param string          $contentLength Header specifying the amount of
+     *                                       data to read.
+     *
+     * @return StreamInterface
+     * @throws \RuntimeException when the sink option is invalid.
+     */
+    private function drain(
+        StreamInterface $source,
+        StreamInterface $sink,
+        $contentLength
+    ) {
+        // If a content-length header is provided, then stop reading once
+        // that number of bytes has been read. This can prevent infinitely
+        // reading from a stream when dealing with servers that do not honor
+        // Connection: Close headers.
+        Psr7\copy_to_stream(
+            $source,
+            $sink,
+            (strlen($contentLength) > 0 && (int) $contentLength > 0) ? (int) $contentLength : -1
+        );
+
+        $sink->seek(0);
+        $source->close();
+
+        return $sink;
+    }
+
+    /**
+     * Create a resource and check to ensure it was created successfully
+     *
+     * @param callable $callback Callable that returns stream resource
+     *
+     * @return resource
+     * @throws \RuntimeException on error
+     */
+    private function createResource(callable $callback)
+    {
+        $errors = null;
+        set_error_handler(function ($_, $msg, $file, $line) use (&$errors) {
+            $errors[] = [
+                'message' => $msg,
+                'file'    => $file,
+                'line'    => $line
+            ];
+            return true;
+        });
+
+        $resource = $callback();
+        restore_error_handler();
+
+        if (!$resource) {
+            $message = 'Error creating resource: ';
+            foreach ($errors as $err) {
+                foreach ($err as $key => $value) {
+                    $message .= "[$key] $value" . PHP_EOL;
+                }
+            }
+            throw new \RuntimeException(trim($message));
+        }
+
+        return $resource;
+    }
+
+    private function createStream(RequestInterface $request, array $options)
+    {
+        static $methods;
+        if (!$methods) {
+            $methods = array_flip(get_class_methods(__CLASS__));
+        }
+
+        // HTTP/1.1 streams using the PHP stream wrapper require a
+        // Connection: close header
+        if ($request->getProtocolVersion() == '1.1'
+            && !$request->hasHeader('Connection')
+        ) {
+            $request = $request->withHeader('Connection', 'close');
+        }
+
+        // Ensure SSL is verified by default
+        if (!isset($options['verify'])) {
+            $options['verify'] = true;
+        }
+
+        $params = [];
+        $context = $this->getDefaultContext($request, $options);
+
+        if (isset($options['on_headers']) && !is_callable($options['on_headers'])) {
+            throw new \InvalidArgumentException('on_headers must be callable');
+        }
+
+        if (!empty($options)) {
+            foreach ($options as $key => $value) {
+                $method = "add_{$key}";
+                if (isset($methods[$method])) {
+                    $this->{$method}($request, $context, $value, $params);
+                }
+            }
+        }
+
+        if (isset($options['stream_context'])) {
+            if (!is_array($options['stream_context'])) {
+                throw new \InvalidArgumentException('stream_context must be an array');
+            }
+            $context = array_replace_recursive(
+                $context,
+                $options['stream_context']
+            );
+        }
+
+        // Microsoft NTLM authentication only supported with curl handler
+        if (isset($options['auth'])
+            && is_array($options['auth'])
+            && isset($options['auth'][2])
+            && 'ntlm' == $options['auth'][2]
+        ) {
+
+            throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler');
+        }
+
+        $uri = $this->resolveHost($request, $options);
+
+        $context = $this->createResource(
+            function () use ($context, $params) {
+                return stream_context_create($context, $params);
+            }
+        );
+
+        return $this->createResource(
+            function () use ($uri, &$http_response_header, $context, $options) {
+                $resource = fopen((string) $uri, 'r', null, $context);
+                $this->lastHeaders = $http_response_header;
+
+                if (isset($options['read_timeout'])) {
+                    $readTimeout = $options['read_timeout'];
+                    $sec = (int) $readTimeout;
+                    $usec = ($readTimeout - $sec) * 100000;
+                    stream_set_timeout($resource, $sec, $usec);
+                }
+
+                return $resource;
+            }
+        );
+    }
+
+    private function resolveHost(RequestInterface $request, array $options)
+    {
+        $uri = $request->getUri();
+
+        if (isset($options['force_ip_resolve']) && !filter_var($uri->getHost(), FILTER_VALIDATE_IP)) {
+            if ('v4' === $options['force_ip_resolve']) {
+                $records = dns_get_record($uri->getHost(), DNS_A);
+                if (!isset($records[0]['ip'])) {
+                    throw new ConnectException(sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request);
+                }
+                $uri = $uri->withHost($records[0]['ip']);
+            } elseif ('v6' === $options['force_ip_resolve']) {
+                $records = dns_get_record($uri->getHost(), DNS_AAAA);
+                if (!isset($records[0]['ipv6'])) {
+                    throw new ConnectException(sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request);
+                }
+                $uri = $uri->withHost('[' . $records[0]['ipv6'] . ']');
+            }
+        }
+
+        return $uri;
+    }
+
+    private function getDefaultContext(RequestInterface $request)
+    {
+        $headers = '';
+        foreach ($request->getHeaders() as $name => $value) {
+            foreach ($value as $val) {
+                $headers .= "$name: $val\r\n";
+            }
+        }
+
+        $context = [
+            'http' => [
+                'method'           => $request->getMethod(),
+                'header'           => $headers,
+                'protocol_version' => $request->getProtocolVersion(),
+                'ignore_errors'    => true,
+                'follow_location'  => 0,
+            ],
+        ];
+
+        $body = (string) $request->getBody();
+
+        if (!empty($body)) {
+            $context['http']['content'] = $body;
+            // Prevent the HTTP handler from adding a Content-Type header.
+            if (!$request->hasHeader('Content-Type')) {
+                $context['http']['header'] .= "Content-Type:\r\n";
+            }
+        }
+
+        $context['http']['header'] = rtrim($context['http']['header']);
+
+        return $context;
+    }
+
+    private function add_proxy(RequestInterface $request, &$options, $value, &$params)
+    {
+        if (!is_array($value)) {
+            $options['http']['proxy'] = $value;
+        } else {
+            $scheme = $request->getUri()->getScheme();
+            if (isset($value[$scheme])) {
+                if (!isset($value['no'])
+                    || !\GuzzleHttp\is_host_in_noproxy(
+                        $request->getUri()->getHost(),
+                        $value['no']
+                    )
+                ) {
+                    $options['http']['proxy'] = $value[$scheme];
+                }
+            }
+        }
+    }
+
+    private function add_timeout(RequestInterface $request, &$options, $value, &$params)
+    {
+        if ($value > 0) {
+            $options['http']['timeout'] = $value;
+        }
+    }
+
+    private function add_verify(RequestInterface $request, &$options, $value, &$params)
+    {
+        if ($value === true) {
+            // PHP 5.6 or greater will find the system cert by default. When
+            // < 5.6, use the Guzzle bundled cacert.
+            if (PHP_VERSION_ID < 50600) {
+                $options['ssl']['cafile'] = \GuzzleHttp\default_ca_bundle();
+            }
+        } elseif (is_string($value)) {
+            $options['ssl']['cafile'] = $value;
+            if (!file_exists($value)) {
+                throw new \RuntimeException("SSL CA bundle not found: $value");
+            }
+        } elseif ($value === false) {
+            $options['ssl']['verify_peer'] = false;
+            $options['ssl']['verify_peer_name'] = false;
+            return;
+        } else {
+            throw new \InvalidArgumentException('Invalid verify request option');
+        }
+
+        $options['ssl']['verify_peer'] = true;
+        $options['ssl']['verify_peer_name'] = true;
+        $options['ssl']['allow_self_signed'] = false;
+    }
+
+    private function add_cert(RequestInterface $request, &$options, $value, &$params)
+    {
+        if (is_array($value)) {
+            $options['ssl']['passphrase'] = $value[1];
+            $value = $value[0];
+        }
+
+        if (!file_exists($value)) {
+            throw new \RuntimeException("SSL certificate not found: {$value}");
+        }
+
+        $options['ssl']['local_cert'] = $value;
+    }
+
+    private function add_progress(RequestInterface $request, &$options, $value, &$params)
+    {
+        $this->addNotification(
+            $params,
+            function ($code, $a, $b, $c, $transferred, $total) use ($value) {
+                if ($code == STREAM_NOTIFY_PROGRESS) {
+                    $value($total, $transferred, null, null);
+                }
+            }
+        );
+    }
+
+    private function add_debug(RequestInterface $request, &$options, $value, &$params)
+    {
+        if ($value === false) {
+            return;
+        }
+
+        static $map = [
+            STREAM_NOTIFY_CONNECT       => 'CONNECT',
+            STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
+            STREAM_NOTIFY_AUTH_RESULT   => 'AUTH_RESULT',
+            STREAM_NOTIFY_MIME_TYPE_IS  => 'MIME_TYPE_IS',
+            STREAM_NOTIFY_FILE_SIZE_IS  => 'FILE_SIZE_IS',
+            STREAM_NOTIFY_REDIRECTED    => 'REDIRECTED',
+            STREAM_NOTIFY_PROGRESS      => 'PROGRESS',
+            STREAM_NOTIFY_FAILURE       => 'FAILURE',
+            STREAM_NOTIFY_COMPLETED     => 'COMPLETED',
+            STREAM_NOTIFY_RESOLVE       => 'RESOLVE',
+        ];
+        static $args = ['severity', 'message', 'message_code',
+            'bytes_transferred', 'bytes_max'];
+
+        $value = \GuzzleHttp\debug_resource($value);
+        $ident = $request->getMethod() . ' ' . $request->getUri()->withFragment('');
+        $this->addNotification(
+            $params,
+            function () use ($ident, $value, $map, $args) {
+                $passed = func_get_args();
+                $code = array_shift($passed);
+                fprintf($value, '<%s> [%s] ', $ident, $map[$code]);
+                foreach (array_filter($passed) as $i => $v) {
+                    fwrite($value, $args[$i] . ': "' . $v . '" ');
+                }
+                fwrite($value, "\n");
+            }
+        );
+    }
+
+    private function addNotification(array &$params, callable $notify)
+    {
+        // Wrap the existing function if needed.
+        if (!isset($params['notification'])) {
+            $params['notification'] = $notify;
+        } else {
+            $params['notification'] = $this->callArray([
+                $params['notification'],
+                $notify
+            ]);
+        }
+    }
+
+    private function callArray(array $functions)
+    {
+        return function () use ($functions) {
+            $args = func_get_args();
+            foreach ($functions as $fn) {
+                call_user_func_array($fn, $args);
+            }
+        };
+    }
+}

+ 273 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/HandlerStack.php

@@ -0,0 +1,273 @@
+<?php
+namespace GuzzleHttp;
+
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Creates a composed Guzzle handler function by stacking middlewares on top of
+ * an HTTP handler function.
+ */
+class HandlerStack
+{
+    /** @var callable */
+    private $handler;
+
+    /** @var array */
+    private $stack = [];
+
+    /** @var callable|null */
+    private $cached;
+
+    /**
+     * Creates a default handler stack that can be used by clients.
+     *
+     * The returned handler will wrap the provided handler or use the most
+     * appropriate default handler for you system. The returned HandlerStack has
+     * support for cookies, redirects, HTTP error exceptions, and preparing a body
+     * before sending.
+     *
+     * The returned handler stack can be passed to a client in the "handler"
+     * option.
+     *
+     * @param callable $handler HTTP handler function to use with the stack. If no
+     *                          handler is provided, the best handler for your
+     *                          system will be utilized.
+     *
+     * @return HandlerStack
+     */
+    public static function create(callable $handler = null)
+    {
+        $stack = new self($handler ?: choose_handler());
+        $stack->push(Middleware::httpErrors(), 'http_errors');
+        $stack->push(Middleware::redirect(), 'allow_redirects');
+        $stack->push(Middleware::cookies(), 'cookies');
+        $stack->push(Middleware::prepareBody(), 'prepare_body');
+
+        return $stack;
+    }
+
+    /**
+     * @param callable $handler Underlying HTTP handler.
+     */
+    public function __construct(callable $handler = null)
+    {
+        $this->handler = $handler;
+    }
+
+    /**
+     * Invokes the handler stack as a composed handler
+     *
+     * @param RequestInterface $request
+     * @param array            $options
+     */
+    public function __invoke(RequestInterface $request, array $options)
+    {
+        $handler = $this->resolve();
+
+        return $handler($request, $options);
+    }
+
+    /**
+     * Dumps a string representation of the stack.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        $depth = 0;
+        $stack = [];
+        if ($this->handler) {
+            $stack[] = "0) Handler: " . $this->debugCallable($this->handler);
+        }
+
+        $result = '';
+        foreach (array_reverse($this->stack) as $tuple) {
+            $depth++;
+            $str = "{$depth}) Name: '{$tuple[1]}', ";
+            $str .= "Function: " . $this->debugCallable($tuple[0]);
+            $result = "> {$str}\n{$result}";
+            $stack[] = $str;
+        }
+
+        foreach (array_keys($stack) as $k) {
+            $result .= "< {$stack[$k]}\n";
+        }
+
+        return $result;
+    }
+
+    /**
+     * Set the HTTP handler that actually returns a promise.
+     *
+     * @param callable $handler Accepts a request and array of options and
+     *                          returns a Promise.
+     */
+    public function setHandler(callable $handler)
+    {
+        $this->handler = $handler;
+        $this->cached = null;
+    }
+
+    /**
+     * Returns true if the builder has a handler.
+     *
+     * @return bool
+     */
+    public function hasHandler()
+    {
+        return (bool) $this->handler;
+    }
+
+    /**
+     * Unshift a middleware to the bottom of the stack.
+     *
+     * @param callable $middleware Middleware function
+     * @param string   $name       Name to register for this middleware.
+     */
+    public function unshift(callable $middleware, $name = null)
+    {
+        array_unshift($this->stack, [$middleware, $name]);
+        $this->cached = null;
+    }
+
+    /**
+     * Push a middleware to the top of the stack.
+     *
+     * @param callable $middleware Middleware function
+     * @param string   $name       Name to register for this middleware.
+     */
+    public function push(callable $middleware, $name = '')
+    {
+        $this->stack[] = [$middleware, $name];
+        $this->cached = null;
+    }
+
+    /**
+     * Add a middleware before another middleware by name.
+     *
+     * @param string   $findName   Middleware to find
+     * @param callable $middleware Middleware function
+     * @param string   $withName   Name to register for this middleware.
+     */
+    public function before($findName, callable $middleware, $withName = '')
+    {
+        $this->splice($findName, $withName, $middleware, true);
+    }
+
+    /**
+     * Add a middleware after another middleware by name.
+     *
+     * @param string   $findName   Middleware to find
+     * @param callable $middleware Middleware function
+     * @param string   $withName   Name to register for this middleware.
+     */
+    public function after($findName, callable $middleware, $withName = '')
+    {
+        $this->splice($findName, $withName, $middleware, false);
+    }
+
+    /**
+     * Remove a middleware by instance or name from the stack.
+     *
+     * @param callable|string $remove Middleware to remove by instance or name.
+     */
+    public function remove($remove)
+    {
+        $this->cached = null;
+        $idx = is_callable($remove) ? 0 : 1;
+        $this->stack = array_values(array_filter(
+            $this->stack,
+            function ($tuple) use ($idx, $remove) {
+                return $tuple[$idx] !== $remove;
+            }
+        ));
+    }
+
+    /**
+     * Compose the middleware and handler into a single callable function.
+     *
+     * @return callable
+     */
+    public function resolve()
+    {
+        if (!$this->cached) {
+            if (!($prev = $this->handler)) {
+                throw new \LogicException('No handler has been specified');
+            }
+
+            foreach (array_reverse($this->stack) as $fn) {
+                $prev = $fn[0]($prev);
+            }
+
+            $this->cached = $prev;
+        }
+
+        return $this->cached;
+    }
+
+    /**
+     * @param $name
+     * @return int
+     */
+    private function findByName($name)
+    {
+        foreach ($this->stack as $k => $v) {
+            if ($v[1] === $name) {
+                return $k;
+            }
+        }
+
+        throw new \InvalidArgumentException("Middleware not found: $name");
+    }
+
+    /**
+     * Splices a function into the middleware list at a specific position.
+     *
+     * @param          $findName
+     * @param          $withName
+     * @param callable $middleware
+     * @param          $before
+     */
+    private function splice($findName, $withName, callable $middleware, $before)
+    {
+        $this->cached = null;
+        $idx = $this->findByName($findName);
+        $tuple = [$middleware, $withName];
+
+        if ($before) {
+            if ($idx === 0) {
+                array_unshift($this->stack, $tuple);
+            } else {
+                $replacement = [$tuple, $this->stack[$idx]];
+                array_splice($this->stack, $idx, 1, $replacement);
+            }
+        } elseif ($idx === count($this->stack) - 1) {
+            $this->stack[] = $tuple;
+        } else {
+            $replacement = [$this->stack[$idx], $tuple];
+            array_splice($this->stack, $idx, 1, $replacement);
+        }
+    }
+
+    /**
+     * Provides a debug string for a given callable.
+     *
+     * @param array|callable $fn Function to write as a string.
+     *
+     * @return string
+     */
+    private function debugCallable($fn)
+    {
+        if (is_string($fn)) {
+            return "callable({$fn})";
+        }
+
+        if (is_array($fn)) {
+            return is_string($fn[0])
+                ? "callable({$fn[0]}::{$fn[1]})"
+                : "callable(['" . get_class($fn[0]) . "', '{$fn[1]}'])";
+        }
+
+        return 'callable(' . spl_object_hash($fn) . ')';
+    }
+}

+ 182 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/MessageFormatter.php

@@ -0,0 +1,182 @@
+<?php
+namespace GuzzleHttp;
+
+use Psr\Http\Message\MessageInterface;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * Formats log messages using variable substitutions for requests, responses,
+ * and other transactional data.
+ *
+ * The following variable substitutions are supported:
+ *
+ * - {request}:        Full HTTP request message
+ * - {response}:       Full HTTP response message
+ * - {ts}:             ISO 8601 date in GMT
+ * - {date_iso_8601}   ISO 8601 date in GMT
+ * - {date_common_log} Apache common log date using the configured timezone.
+ * - {host}:           Host of the request
+ * - {method}:         Method of the request
+ * - {uri}:            URI of the request
+ * - {host}:           Host of the request
+ * - {version}:        Protocol version
+ * - {target}:         Request target of the request (path + query + fragment)
+ * - {hostname}:       Hostname of the machine that sent the request
+ * - {code}:           Status code of the response (if available)
+ * - {phrase}:         Reason phrase of the response  (if available)
+ * - {error}:          Any error messages (if available)
+ * - {req_header_*}:   Replace `*` with the lowercased name of a request header to add to the message
+ * - {res_header_*}:   Replace `*` with the lowercased name of a response header to add to the message
+ * - {req_headers}:    Request headers
+ * - {res_headers}:    Response headers
+ * - {req_body}:       Request body
+ * - {res_body}:       Response body
+ */
+class MessageFormatter
+{
+    /**
+     * Apache Common Log Format.
+     * @link http://httpd.apache.org/docs/2.4/logs.html#common
+     * @var string
+     */
+    const CLF = "{hostname} {req_header_User-Agent} - [{date_common_log}] \"{method} {target} HTTP/{version}\" {code} {res_header_Content-Length}";
+    const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}";
+    const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}';
+
+    /** @var string Template used to format log messages */
+    private $template;
+
+    /**
+     * @param string $template Log message template
+     */
+    public function __construct($template = self::CLF)
+    {
+        $this->template = $template ?: self::CLF;
+    }
+
+    /**
+     * Returns a formatted message string.
+     *
+     * @param RequestInterface  $request  Request that was sent
+     * @param ResponseInterface $response Response that was received
+     * @param \Exception        $error    Exception that was received
+     *
+     * @return string
+     */
+    public function format(
+        RequestInterface $request,
+        ResponseInterface $response = null,
+        \Exception $error = null
+    ) {
+        $cache = [];
+
+        return preg_replace_callback(
+            '/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
+            function (array $matches) use ($request, $response, $error, &$cache) {
+
+                if (isset($cache[$matches[1]])) {
+                    return $cache[$matches[1]];
+                }
+
+                $result = '';
+                switch ($matches[1]) {
+                    case 'request':
+                        $result = Psr7\str($request);
+                        break;
+                    case 'response':
+                        $result = $response ? Psr7\str($response) : '';
+                        break;
+                    case 'req_headers':
+                        $result = trim($request->getMethod()
+                                . ' ' . $request->getRequestTarget())
+                            . ' HTTP/' . $request->getProtocolVersion() . "\r\n"
+                            . $this->headers($request);
+                        break;
+                    case 'res_headers':
+                        $result = $response ?
+                            sprintf(
+                                'HTTP/%s %d %s',
+                                $response->getProtocolVersion(),
+                                $response->getStatusCode(),
+                                $response->getReasonPhrase()
+                            ) . "\r\n" . $this->headers($response)
+                            : 'NULL';
+                        break;
+                    case 'req_body':
+                        $result = $request->getBody();
+                        break;
+                    case 'res_body':
+                        $result = $response ? $response->getBody() : 'NULL';
+                        break;
+                    case 'ts':
+                    case 'date_iso_8601':
+                        $result = gmdate('c');
+                        break;
+                    case 'date_common_log':
+                        $result = date('d/M/Y:H:i:s O');
+                        break;
+                    case 'method':
+                        $result = $request->getMethod();
+                        break;
+                    case 'version':
+                        $result = $request->getProtocolVersion();
+                        break;
+                    case 'uri':
+                    case 'url':
+                        $result = $request->getUri();
+                        break;
+                    case 'target':
+                        $result = $request->getRequestTarget();
+                        break;
+                    case 'req_version':
+                        $result = $request->getProtocolVersion();
+                        break;
+                    case 'res_version':
+                        $result = $response
+                            ? $response->getProtocolVersion()
+                            : 'NULL';
+                        break;
+                    case 'host':
+                        $result = $request->getHeaderLine('Host');
+                        break;
+                    case 'hostname':
+                        $result = gethostname();
+                        break;
+                    case 'code':
+                        $result = $response ? $response->getStatusCode() : 'NULL';
+                        break;
+                    case 'phrase':
+                        $result = $response ? $response->getReasonPhrase() : 'NULL';
+                        break;
+                    case 'error':
+                        $result = $error ? $error->getMessage() : 'NULL';
+                        break;
+                    default:
+                        // handle prefixed dynamic headers
+                        if (strpos($matches[1], 'req_header_') === 0) {
+                            $result = $request->getHeaderLine(substr($matches[1], 11));
+                        } elseif (strpos($matches[1], 'res_header_') === 0) {
+                            $result = $response
+                                ? $response->getHeaderLine(substr($matches[1], 11))
+                                : 'NULL';
+                        }
+                }
+
+                $cache[$matches[1]] = $result;
+                return $result;
+            },
+            $this->template
+        );
+    }
+
+    private function headers(MessageInterface $message)
+    {
+        $result = '';
+        foreach ($message->getHeaders() as $name => $values) {
+            $result .= $name . ': ' . implode(', ', $values) . "\r\n";
+        }
+
+        return trim($result);
+    }
+}

+ 254 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Middleware.php

@@ -0,0 +1,254 @@
+<?php
+namespace GuzzleHttp;
+
+use GuzzleHttp\Cookie\CookieJarInterface;
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Promise\RejectedPromise;
+use GuzzleHttp\Psr7;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Log\LoggerInterface;
+use Psr\Log\LogLevel;
+
+/**
+ * Functions used to create and wrap handlers with handler middleware.
+ */
+final class Middleware
+{
+    /**
+     * Middleware that adds cookies to requests.
+     *
+     * The options array must be set to a CookieJarInterface in order to use
+     * cookies. This is typically handled for you by a client.
+     *
+     * @return callable Returns a function that accepts the next handler.
+     */
+    public static function cookies()
+    {
+        return function (callable $handler) {
+            return function ($request, array $options) use ($handler) {
+                if (empty($options['cookies'])) {
+                    return $handler($request, $options);
+                } elseif (!($options['cookies'] instanceof CookieJarInterface)) {
+                    throw new \InvalidArgumentException('cookies must be an instance of GuzzleHttp\Cookie\CookieJarInterface');
+                }
+                $cookieJar = $options['cookies'];
+                $request = $cookieJar->withCookieHeader($request);
+                return $handler($request, $options)
+                    ->then(function ($response) use ($cookieJar, $request) {
+                        $cookieJar->extractCookies($request, $response);
+                        return $response;
+                    }
+                );
+            };
+        };
+    }
+
+    /**
+     * Middleware that throws exceptions for 4xx or 5xx responses when the
+     * "http_error" request option is set to true.
+     *
+     * @return callable Returns a function that accepts the next handler.
+     */
+    public static function httpErrors()
+    {
+        return function (callable $handler) {
+            return function ($request, array $options) use ($handler) {
+                if (empty($options['http_errors'])) {
+                    return $handler($request, $options);
+                }
+                return $handler($request, $options)->then(
+                    function (ResponseInterface $response) use ($request, $handler) {
+                        $code = $response->getStatusCode();
+                        if ($code < 400) {
+                            return $response;
+                        }
+                        throw RequestException::create($request, $response);
+                    }
+                );
+            };
+        };
+    }
+
+    /**
+     * Middleware that pushes history data to an ArrayAccess container.
+     *
+     * @param array $container Container to hold the history (by reference).
+     *
+     * @return callable Returns a function that accepts the next handler.
+     * @throws \InvalidArgumentException if container is not an array or ArrayAccess.
+     */
+    public static function history(&$container)
+    {
+        if (!is_array($container) && !$container instanceof \ArrayAccess) {
+            throw new \InvalidArgumentException('history container must be an array or object implementing ArrayAccess');
+        }
+
+        return function (callable $handler) use (&$container) {
+            return function ($request, array $options) use ($handler, &$container) {
+                return $handler($request, $options)->then(
+                    function ($value) use ($request, &$container, $options) {
+                        $container[] = [
+                            'request'  => $request,
+                            'response' => $value,
+                            'error'    => null,
+                            'options'  => $options
+                        ];
+                        return $value;
+                    },
+                    function ($reason) use ($request, &$container, $options) {
+                        $container[] = [
+                            'request'  => $request,
+                            'response' => null,
+                            'error'    => $reason,
+                            'options'  => $options
+                        ];
+                        return \GuzzleHttp\Promise\rejection_for($reason);
+                    }
+                );
+            };
+        };
+    }
+
+    /**
+     * Middleware that invokes a callback before and after sending a request.
+     *
+     * The provided listener cannot modify or alter the response. It simply
+     * "taps" into the chain to be notified before returning the promise. The
+     * before listener accepts a request and options array, and the after
+     * listener accepts a request, options array, and response promise.
+     *
+     * @param callable $before Function to invoke before forwarding the request.
+     * @param callable $after  Function invoked after forwarding.
+     *
+     * @return callable Returns a function that accepts the next handler.
+     */
+    public static function tap(callable $before = null, callable $after = null)
+    {
+        return function (callable $handler) use ($before, $after) {
+            return function ($request, array $options) use ($handler, $before, $after) {
+                if ($before) {
+                    $before($request, $options);
+                }
+                $response = $handler($request, $options);
+                if ($after) {
+                    $after($request, $options, $response);
+                }
+                return $response;
+            };
+        };
+    }
+
+    /**
+     * Middleware that handles request redirects.
+     *
+     * @return callable Returns a function that accepts the next handler.
+     */
+    public static function redirect()
+    {
+        return function (callable $handler) {
+            return new RedirectMiddleware($handler);
+        };
+    }
+
+    /**
+     * Middleware that retries requests based on the boolean result of
+     * invoking the provided "decider" function.
+     *
+     * If no delay function is provided, a simple implementation of exponential
+     * backoff will be utilized.
+     *
+     * @param callable $decider Function that accepts the number of retries,
+     *                          a request, [response], and [exception] and
+     *                          returns true if the request is to be retried.
+     * @param callable $delay   Function that accepts the number of retries and
+     *                          returns the number of milliseconds to delay.
+     *
+     * @return callable Returns a function that accepts the next handler.
+     */
+    public static function retry(callable $decider, callable $delay = null)
+    {
+        return function (callable $handler) use ($decider, $delay) {
+            return new RetryMiddleware($decider, $handler, $delay);
+        };
+    }
+
+    /**
+     * Middleware that logs requests, responses, and errors using a message
+     * formatter.
+     *
+     * @param LoggerInterface  $logger Logs messages.
+     * @param MessageFormatter $formatter Formatter used to create message strings.
+     * @param string           $logLevel Level at which to log requests.
+     *
+     * @return callable Returns a function that accepts the next handler.
+     */
+    public static function log(LoggerInterface $logger, MessageFormatter $formatter, $logLevel = LogLevel::INFO)
+    {
+        return function (callable $handler) use ($logger, $formatter, $logLevel) {
+            return function ($request, array $options) use ($handler, $logger, $formatter, $logLevel) {
+                return $handler($request, $options)->then(
+                    function ($response) use ($logger, $request, $formatter, $logLevel) {
+                        $message = $formatter->format($request, $response);
+                        $logger->log($logLevel, $message);
+                        return $response;
+                    },
+                    function ($reason) use ($logger, $request, $formatter) {
+                        $response = $reason instanceof RequestException
+                            ? $reason->getResponse()
+                            : null;
+                        $message = $formatter->format($request, $response, $reason);
+                        $logger->notice($message);
+                        return \GuzzleHttp\Promise\rejection_for($reason);
+                    }
+                );
+            };
+        };
+    }
+
+    /**
+     * This middleware adds a default content-type if possible, a default
+     * content-length or transfer-encoding header, and the expect header.
+     *
+     * @return callable
+     */
+    public static function prepareBody()
+    {
+        return function (callable $handler) {
+            return new PrepareBodyMiddleware($handler);
+        };
+    }
+
+    /**
+     * Middleware that applies a map function to the request before passing to
+     * the next handler.
+     *
+     * @param callable $fn Function that accepts a RequestInterface and returns
+     *                     a RequestInterface.
+     * @return callable
+     */
+    public static function mapRequest(callable $fn)
+    {
+        return function (callable $handler) use ($fn) {
+            return function ($request, array $options) use ($handler, $fn) {
+                return $handler($fn($request), $options);
+            };
+        };
+    }
+
+    /**
+     * Middleware that applies a map function to the resolved promise's
+     * response.
+     *
+     * @param callable $fn Function that accepts a ResponseInterface and
+     *                     returns a ResponseInterface.
+     * @return callable
+     */
+    public static function mapResponse(callable $fn)
+    {
+        return function (callable $handler) use ($fn) {
+            return function ($request, array $options) use ($handler, $fn) {
+                return $handler($request, $options)->then($fn);
+            };
+        };
+    }
+}

+ 123 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/Pool.php

@@ -0,0 +1,123 @@
+<?php
+namespace GuzzleHttp;
+
+use GuzzleHttp\Promise\PromisorInterface;
+use Psr\Http\Message\RequestInterface;
+use GuzzleHttp\Promise\EachPromise;
+
+/**
+ * Sends and iterator of requests concurrently using a capped pool size.
+ *
+ * The pool will read from an iterator until it is cancelled or until the
+ * iterator is consumed. When a request is yielded, the request is sent after
+ * applying the "request_options" request options (if provided in the ctor).
+ *
+ * When a function is yielded by the iterator, the function is provided the
+ * "request_options" array that should be merged on top of any existing
+ * options, and the function MUST then return a wait-able promise.
+ */
+class Pool implements PromisorInterface
+{
+    /** @var EachPromise */
+    private $each;
+
+    /**
+     * @param ClientInterface $client   Client used to send the requests.
+     * @param array|\Iterator $requests Requests or functions that return
+     *                                  requests to send concurrently.
+     * @param array           $config   Associative array of options
+     *     - concurrency: (int) Maximum number of requests to send concurrently
+     *     - options: Array of request options to apply to each request.
+     *     - fulfilled: (callable) Function to invoke when a request completes.
+     *     - rejected: (callable) Function to invoke when a request is rejected.
+     */
+    public function __construct(
+        ClientInterface $client,
+        $requests,
+        array $config = []
+    ) {
+        // Backwards compatibility.
+        if (isset($config['pool_size'])) {
+            $config['concurrency'] = $config['pool_size'];
+        } elseif (!isset($config['concurrency'])) {
+            $config['concurrency'] = 25;
+        }
+
+        if (isset($config['options'])) {
+            $opts = $config['options'];
+            unset($config['options']);
+        } else {
+            $opts = [];
+        }
+
+        $iterable = \GuzzleHttp\Promise\iter_for($requests);
+        $requests = function () use ($iterable, $client, $opts) {
+            foreach ($iterable as $key => $rfn) {
+                if ($rfn instanceof RequestInterface) {
+                    yield $key => $client->sendAsync($rfn, $opts);
+                } elseif (is_callable($rfn)) {
+                    yield $key => $rfn($opts);
+                } else {
+                    throw new \InvalidArgumentException('Each value yielded by '
+                        . 'the iterator must be a Psr7\Http\Message\RequestInterface '
+                        . 'or a callable that returns a promise that fulfills '
+                        . 'with a Psr7\Message\Http\ResponseInterface object.');
+                }
+            }
+        };
+
+        $this->each = new EachPromise($requests(), $config);
+    }
+
+    public function promise()
+    {
+        return $this->each->promise();
+    }
+
+    /**
+     * Sends multiple requests concurrently and returns an array of responses
+     * and exceptions that uses the same ordering as the provided requests.
+     *
+     * IMPORTANT: This method keeps every request and response in memory, and
+     * as such, is NOT recommended when sending a large number or an
+     * indeterminate number of requests concurrently.
+     *
+     * @param ClientInterface $client   Client used to send the requests
+     * @param array|\Iterator $requests Requests to send concurrently.
+     * @param array           $options  Passes through the options available in
+     *                                  {@see GuzzleHttp\Pool::__construct}
+     *
+     * @return array Returns an array containing the response or an exception
+     *               in the same order that the requests were sent.
+     * @throws \InvalidArgumentException if the event format is incorrect.
+     */
+    public static function batch(
+        ClientInterface $client,
+        $requests,
+        array $options = []
+    ) {
+        $res = [];
+        self::cmpCallback($options, 'fulfilled', $res);
+        self::cmpCallback($options, 'rejected', $res);
+        $pool = new static($client, $requests, $options);
+        $pool->promise()->wait();
+        ksort($res);
+
+        return $res;
+    }
+
+    private static function cmpCallback(array &$options, $name, array &$results)
+    {
+        if (!isset($options[$name])) {
+            $options[$name] = function ($v, $k) use (&$results) {
+                $results[$k] = $v;
+            };
+        } else {
+            $currentFn = $options[$name];
+            $options[$name] = function ($v, $k) use (&$results, $currentFn) {
+                $currentFn($v, $k);
+                $results[$k] = $v;
+            };
+        }
+    }
+}

+ 106 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php

@@ -0,0 +1,106 @@
+<?php
+namespace GuzzleHttp;
+
+use GuzzleHttp\Promise\PromiseInterface;
+use GuzzleHttp\Psr7;
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Prepares requests that contain a body, adding the Content-Length,
+ * Content-Type, and Expect headers.
+ */
+class PrepareBodyMiddleware
+{
+    /** @var callable  */
+    private $nextHandler;
+
+    /**
+     * @param callable $nextHandler Next handler to invoke.
+     */
+    public function __construct(callable $nextHandler)
+    {
+        $this->nextHandler = $nextHandler;
+    }
+
+    /**
+     * @param RequestInterface $request
+     * @param array            $options
+     *
+     * @return PromiseInterface
+     */
+    public function __invoke(RequestInterface $request, array $options)
+    {
+        $fn = $this->nextHandler;
+
+        // Don't do anything if the request has no body.
+        if ($request->getBody()->getSize() === 0) {
+            return $fn($request, $options);
+        }
+
+        $modify = [];
+
+        // Add a default content-type if possible.
+        if (!$request->hasHeader('Content-Type')) {
+            if ($uri = $request->getBody()->getMetadata('uri')) {
+                if ($type = Psr7\mimetype_from_filename($uri)) {
+                    $modify['set_headers']['Content-Type'] = $type;
+                }
+            }
+        }
+
+        // Add a default content-length or transfer-encoding header.
+        if (!$request->hasHeader('Content-Length')
+            && !$request->hasHeader('Transfer-Encoding')
+        ) {
+            $size = $request->getBody()->getSize();
+            if ($size !== null) {
+                $modify['set_headers']['Content-Length'] = $size;
+            } else {
+                $modify['set_headers']['Transfer-Encoding'] = 'chunked';
+            }
+        }
+
+        // Add the expect header if needed.
+        $this->addExpectHeader($request, $options, $modify);
+
+        return $fn(Psr7\modify_request($request, $modify), $options);
+    }
+
+    private function addExpectHeader(
+        RequestInterface $request,
+        array $options,
+        array &$modify
+    ) {
+        // Determine if the Expect header should be used
+        if ($request->hasHeader('Expect')) {
+            return;
+        }
+
+        $expect = isset($options['expect']) ? $options['expect'] : null;
+
+        // Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0
+        if ($expect === false || $request->getProtocolVersion() < 1.1) {
+            return;
+        }
+
+        // The expect header is unconditionally enabled
+        if ($expect === true) {
+            $modify['set_headers']['Expect'] = '100-Continue';
+            return;
+        }
+
+        // By default, send the expect header when the payload is > 1mb
+        if ($expect === null) {
+            $expect = 1048576;
+        }
+
+        // Always add if the body cannot be rewound, the size cannot be
+        // determined, or the size is greater than the cutoff threshold
+        $body = $request->getBody();
+        $size = $body->getSize();
+
+        if ($size === null || $size >= (int) $expect || !$body->isSeekable()) {
+            $modify['set_headers']['Expect'] = '100-Continue';
+        }
+    }
+}

+ 237 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php

@@ -0,0 +1,237 @@
+<?php
+namespace GuzzleHttp;
+
+use GuzzleHttp\Exception\BadResponseException;
+use GuzzleHttp\Exception\TooManyRedirectsException;
+use GuzzleHttp\Promise\PromiseInterface;
+use GuzzleHttp\Psr7;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Request redirect middleware.
+ *
+ * Apply this middleware like other middleware using
+ * {@see GuzzleHttp\Middleware::redirect()}.
+ */
+class RedirectMiddleware
+{
+    const HISTORY_HEADER = 'X-Guzzle-Redirect-History';
+
+    const STATUS_HISTORY_HEADER = 'X-Guzzle-Redirect-Status-History';
+
+    public static $defaultSettings = [
+        'max'             => 5,
+        'protocols'       => ['http', 'https'],
+        'strict'          => false,
+        'referer'         => false,
+        'track_redirects' => false,
+    ];
+
+    /** @var callable  */
+    private $nextHandler;
+
+    /**
+     * @param callable $nextHandler Next handler to invoke.
+     */
+    public function __construct(callable $nextHandler)
+    {
+        $this->nextHandler = $nextHandler;
+    }
+
+    /**
+     * @param RequestInterface $request
+     * @param array            $options
+     *
+     * @return PromiseInterface
+     */
+    public function __invoke(RequestInterface $request, array $options)
+    {
+        $fn = $this->nextHandler;
+
+        if (empty($options['allow_redirects'])) {
+            return $fn($request, $options);
+        }
+
+        if ($options['allow_redirects'] === true) {
+            $options['allow_redirects'] = self::$defaultSettings;
+        } elseif (!is_array($options['allow_redirects'])) {
+            throw new \InvalidArgumentException('allow_redirects must be true, false, or array');
+        } else {
+            // Merge the default settings with the provided settings
+            $options['allow_redirects'] += self::$defaultSettings;
+        }
+
+        if (empty($options['allow_redirects']['max'])) {
+            return $fn($request, $options);
+        }
+
+        return $fn($request, $options)
+            ->then(function (ResponseInterface $response) use ($request, $options) {
+                return $this->checkRedirect($request, $options, $response);
+            });
+    }
+
+    /**
+     * @param RequestInterface  $request
+     * @param array             $options
+     * @param ResponseInterface|PromiseInterface $response
+     *
+     * @return ResponseInterface|PromiseInterface
+     */
+    public function checkRedirect(
+        RequestInterface $request,
+        array $options,
+        ResponseInterface $response
+    ) {
+        if (substr($response->getStatusCode(), 0, 1) != '3'
+            || !$response->hasHeader('Location')
+        ) {
+            return $response;
+        }
+
+        $this->guardMax($request, $options);
+        $nextRequest = $this->modifyRequest($request, $options, $response);
+
+        if (isset($options['allow_redirects']['on_redirect'])) {
+            call_user_func(
+                $options['allow_redirects']['on_redirect'],
+                $request,
+                $response,
+                $nextRequest->getUri()
+            );
+        }
+
+        /** @var PromiseInterface|ResponseInterface $promise */
+        $promise = $this($nextRequest, $options);
+
+        // Add headers to be able to track history of redirects.
+        if (!empty($options['allow_redirects']['track_redirects'])) {
+            return $this->withTracking(
+                $promise,
+                (string) $nextRequest->getUri(),
+                $response->getStatusCode()
+            );
+        }
+
+        return $promise;
+    }
+
+    private function withTracking(PromiseInterface $promise, $uri, $statusCode)
+    {
+        return $promise->then(
+            function (ResponseInterface $response) use ($uri, $statusCode) {
+                // Note that we are pushing to the front of the list as this
+                // would be an earlier response than what is currently present
+                // in the history header.
+                $historyHeader = $response->getHeader(self::HISTORY_HEADER);
+                $statusHeader = $response->getHeader(self::STATUS_HISTORY_HEADER);
+                array_unshift($historyHeader, $uri);
+                array_unshift($statusHeader, $statusCode);
+                return $response->withHeader(self::HISTORY_HEADER, $historyHeader)
+                                ->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader);
+            }
+        );
+    }
+
+    private function guardMax(RequestInterface $request, array &$options)
+    {
+        $current = isset($options['__redirect_count'])
+            ? $options['__redirect_count']
+            : 0;
+        $options['__redirect_count'] = $current + 1;
+        $max = $options['allow_redirects']['max'];
+
+        if ($options['__redirect_count'] > $max) {
+            throw new TooManyRedirectsException(
+                "Will not follow more than {$max} redirects",
+                $request
+            );
+        }
+    }
+
+    /**
+     * @param RequestInterface  $request
+     * @param array             $options
+     * @param ResponseInterface $response
+     *
+     * @return RequestInterface
+     */
+    public function modifyRequest(
+        RequestInterface $request,
+        array $options,
+        ResponseInterface $response
+    ) {
+        // Request modifications to apply.
+        $modify = [];
+        $protocols = $options['allow_redirects']['protocols'];
+
+        // Use a GET request if this is an entity enclosing request and we are
+        // not forcing RFC compliance, but rather emulating what all browsers
+        // would do.
+        $statusCode = $response->getStatusCode();
+        if ($statusCode == 303 ||
+            ($statusCode <= 302 && $request->getBody() && !$options['allow_redirects']['strict'])
+        ) {
+            $modify['method'] = 'GET';
+            $modify['body'] = '';
+        }
+
+        $modify['uri'] = $this->redirectUri($request, $response, $protocols);
+        Psr7\rewind_body($request);
+
+        // Add the Referer header if it is told to do so and only
+        // add the header if we are not redirecting from https to http.
+        if ($options['allow_redirects']['referer']
+            && $modify['uri']->getScheme() === $request->getUri()->getScheme()
+        ) {
+            $uri = $request->getUri()->withUserInfo('', '');
+            $modify['set_headers']['Referer'] = (string) $uri;
+        } else {
+            $modify['remove_headers'][] = 'Referer';
+        }
+
+        // Remove Authorization header if host is different.
+        if ($request->getUri()->getHost() !== $modify['uri']->getHost()) {
+            $modify['remove_headers'][] = 'Authorization';
+        }
+
+        return Psr7\modify_request($request, $modify);
+    }
+
+    /**
+     * Set the appropriate URL on the request based on the location header
+     *
+     * @param RequestInterface  $request
+     * @param ResponseInterface $response
+     * @param array             $protocols
+     *
+     * @return UriInterface
+     */
+    private function redirectUri(
+        RequestInterface $request,
+        ResponseInterface $response,
+        array $protocols
+    ) {
+        $location = Psr7\UriResolver::resolve(
+            $request->getUri(),
+            new Psr7\Uri($response->getHeaderLine('Location'))
+        );
+
+        // Ensure that the redirect URI is allowed based on the protocols.
+        if (!in_array($location->getScheme(), $protocols)) {
+            throw new BadResponseException(
+                sprintf(
+                    'Redirect URI, %s, does not use one of the allowed redirect protocols: %s',
+                    $location,
+                    implode(', ', $protocols)
+                ),
+                $request,
+                $response
+            );
+        }
+
+        return $location;
+    }
+}

+ 255 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/RequestOptions.php

@@ -0,0 +1,255 @@
+<?php
+namespace GuzzleHttp;
+
+/**
+ * This class contains a list of built-in Guzzle request options.
+ *
+ * More documentation for each option can be found at http://guzzlephp.org/.
+ *
+ * @link http://docs.guzzlephp.org/en/v6/request-options.html
+ */
+final class RequestOptions
+{
+    /**
+     * allow_redirects: (bool|array) Controls redirect behavior. Pass false
+     * to disable redirects, pass true to enable redirects, pass an
+     * associative to provide custom redirect settings. Defaults to "false".
+     * This option only works if your handler has the RedirectMiddleware. When
+     * passing an associative array, you can provide the following key value
+     * pairs:
+     *
+     * - max: (int, default=5) maximum number of allowed redirects.
+     * - strict: (bool, default=false) Set to true to use strict redirects
+     *   meaning redirect POST requests with POST requests vs. doing what most
+     *   browsers do which is redirect POST requests with GET requests
+     * - referer: (bool, default=true) Set to false to disable the Referer
+     *   header.
+     * - protocols: (array, default=['http', 'https']) Allowed redirect
+     *   protocols.
+     * - on_redirect: (callable) PHP callable that is invoked when a redirect
+     *   is encountered. The callable is invoked with the request, the redirect
+     *   response that was received, and the effective URI. Any return value
+     *   from the on_redirect function is ignored.
+     */
+    const ALLOW_REDIRECTS = 'allow_redirects';
+
+    /**
+     * auth: (array) Pass an array of HTTP authentication parameters to use
+     * with the request. The array must contain the username in index [0],
+     * the password in index [1], and you can optionally provide a built-in
+     * authentication type in index [2]. Pass null to disable authentication
+     * for a request.
+     */
+    const AUTH = 'auth';
+
+    /**
+     * body: (resource|string|null|int|float|StreamInterface|callable|\Iterator)
+     * Body to send in the request.
+     */
+    const BODY = 'body';
+
+    /**
+     * cert: (string|array) Set to a string to specify the path to a file
+     * containing a PEM formatted SSL client side certificate. If a password
+     * is required, then set cert to an array containing the path to the PEM
+     * file in the first array element followed by the certificate password
+     * in the second array element.
+     */
+    const CERT = 'cert';
+
+    /**
+     * cookies: (bool|GuzzleHttp\Cookie\CookieJarInterface, default=false)
+     * Specifies whether or not cookies are used in a request or what cookie
+     * jar to use or what cookies to send. This option only works if your
+     * handler has the `cookie` middleware. Valid values are `false` and
+     * an instance of {@see GuzzleHttp\Cookie\CookieJarInterface}.
+     */
+    const COOKIES = 'cookies';
+
+    /**
+     * connect_timeout: (float, default=0) Float describing the number of
+     * seconds to wait while trying to connect to a server. Use 0 to wait
+     * indefinitely (the default behavior).
+     */
+    const CONNECT_TIMEOUT = 'connect_timeout';
+
+    /**
+     * debug: (bool|resource) Set to true or set to a PHP stream returned by
+     * fopen()  enable debug output with the HTTP handler used to send a
+     * request.
+     */
+    const DEBUG = 'debug';
+
+    /**
+     * decode_content: (bool, default=true) Specify whether or not
+     * Content-Encoding responses (gzip, deflate, etc.) are automatically
+     * decoded.
+     */
+    const DECODE_CONTENT = 'decode_content';
+
+    /**
+     * delay: (int) The amount of time to delay before sending in milliseconds.
+     */
+    const DELAY = 'delay';
+
+    /**
+     * expect: (bool|integer) Controls the behavior of the
+     * "Expect: 100-Continue" header.
+     *
+     * Set to `true` to enable the "Expect: 100-Continue" header for all
+     * requests that sends a body. Set to `false` to disable the
+     * "Expect: 100-Continue" header for all requests. Set to a number so that
+     * the size of the payload must be greater than the number in order to send
+     * the Expect header. Setting to a number will send the Expect header for
+     * all requests in which the size of the payload cannot be determined or
+     * where the body is not rewindable.
+     *
+     * By default, Guzzle will add the "Expect: 100-Continue" header when the
+     * size of the body of a request is greater than 1 MB and a request is
+     * using HTTP/1.1.
+     */
+    const EXPECT = 'expect';
+
+    /**
+     * form_params: (array) Associative array of form field names to values
+     * where each value is a string or array of strings. Sets the Content-Type
+     * header to application/x-www-form-urlencoded when no Content-Type header
+     * is already present.
+     */
+    const FORM_PARAMS = 'form_params';
+
+    /**
+     * headers: (array) Associative array of HTTP headers. Each value MUST be
+     * a string or array of strings.
+     */
+    const HEADERS = 'headers';
+
+    /**
+     * http_errors: (bool, default=true) Set to false to disable exceptions
+     * when a non- successful HTTP response is received. By default,
+     * exceptions will be thrown for 4xx and 5xx responses. This option only
+     * works if your handler has the `httpErrors` middleware.
+     */
+    const HTTP_ERRORS = 'http_errors';
+
+    /**
+     * json: (mixed) Adds JSON data to a request. The provided value is JSON
+     * encoded and a Content-Type header of application/json will be added to
+     * the request if no Content-Type header is already present.
+     */
+    const JSON = 'json';
+
+    /**
+     * multipart: (array) Array of associative arrays, each containing a
+     * required "name" key mapping to the form field, name, a required
+     * "contents" key mapping to a StreamInterface|resource|string, an
+     * optional "headers" associative array of custom headers, and an
+     * optional "filename" key mapping to a string to send as the filename in
+     * the part. If no "filename" key is present, then no "filename" attribute
+     * will be added to the part.
+     */
+    const MULTIPART = 'multipart';
+
+    /**
+     * on_headers: (callable) A callable that is invoked when the HTTP headers
+     * of the response have been received but the body has not yet begun to
+     * download.
+     */
+    const ON_HEADERS = 'on_headers';
+
+    /**
+     * on_stats: (callable) allows you to get access to transfer statistics of
+     * a request and access the lower level transfer details of the handler
+     * associated with your client. ``on_stats`` is a callable that is invoked
+     * when a handler has finished sending a request. The callback is invoked
+     * with transfer statistics about the request, the response received, or
+     * the error encountered. Included in the data is the total amount of time
+     * taken to send the request.
+     */
+    const ON_STATS = 'on_stats';
+
+    /**
+     * progress: (callable) Defines a function to invoke when transfer
+     * progress is made. The function accepts the following positional
+     * arguments: the total number of bytes expected to be downloaded, the
+     * number of bytes downloaded so far, the number of bytes expected to be
+     * uploaded, the number of bytes uploaded so far.
+     */
+    const PROGRESS = 'progress';
+
+    /**
+     * proxy: (string|array) Pass a string to specify an HTTP proxy, or an
+     * array to specify different proxies for different protocols (where the
+     * key is the protocol and the value is a proxy string).
+     */
+    const PROXY = 'proxy';
+
+    /**
+     * query: (array|string) Associative array of query string values to add
+     * to the request. This option uses PHP's http_build_query() to create
+     * the string representation. Pass a string value if you need more
+     * control than what this method provides
+     */
+    const QUERY = 'query';
+
+    /**
+     * sink: (resource|string|StreamInterface) Where the data of the
+     * response is written to. Defaults to a PHP temp stream. Providing a
+     * string will write data to a file by the given name.
+     */
+    const SINK = 'sink';
+
+    /**
+     * synchronous: (bool) Set to true to inform HTTP handlers that you intend
+     * on waiting on the response. This can be useful for optimizations. Note
+     * that a promise is still returned if you are using one of the async
+     * client methods.
+     */
+    const SYNCHRONOUS = 'synchronous';
+
+    /**
+     * ssl_key: (array|string) Specify the path to a file containing a private
+     * SSL key in PEM format. If a password is required, then set to an array
+     * containing the path to the SSL key in the first array element followed
+     * by the password required for the certificate in the second element.
+     */
+    const SSL_KEY = 'ssl_key';
+
+    /**
+     * stream: Set to true to attempt to stream a response rather than
+     * download it all up-front.
+     */
+    const STREAM = 'stream';
+
+    /**
+     * verify: (bool|string, default=true) Describes the SSL certificate
+     * verification behavior of a request. Set to true to enable SSL
+     * certificate verification using the system CA bundle when available
+     * (the default). Set to false to disable certificate verification (this
+     * is insecure!). Set to a string to provide the path to a CA bundle on
+     * disk to enable verification using a custom certificate.
+     */
+    const VERIFY = 'verify';
+
+    /**
+     * timeout: (float, default=0) Float describing the timeout of the
+     * request in seconds. Use 0 to wait indefinitely (the default behavior).
+     */
+    const TIMEOUT = 'timeout';
+
+    /**
+     * read_timeout: (float, default=default_socket_timeout ini setting) Float describing
+     * the body read timeout, for stream requests.
+     */
+    const READ_TIMEOUT = 'read_timeout';
+
+    /**
+     * version: (float) Specifies the HTTP protocol version to attempt to use.
+     */
+    const VERSION = 'version';
+
+    /**
+     * force_ip_resolve: (bool) Force client to use only ipv4 or ipv6 protocol
+     */
+    const FORCE_IP_RESOLVE = 'force_ip_resolve';
+}

+ 112 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/RetryMiddleware.php

@@ -0,0 +1,112 @@
+<?php
+namespace GuzzleHttp;
+
+use GuzzleHttp\Promise\PromiseInterface;
+use GuzzleHttp\Promise\RejectedPromise;
+use GuzzleHttp\Psr7;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * Middleware that retries requests based on the boolean result of
+ * invoking the provided "decider" function.
+ */
+class RetryMiddleware
+{
+    /** @var callable  */
+    private $nextHandler;
+
+    /** @var callable */
+    private $decider;
+
+    /**
+     * @param callable $decider     Function that accepts the number of retries,
+     *                              a request, [response], and [exception] and
+     *                              returns true if the request is to be
+     *                              retried.
+     * @param callable $nextHandler Next handler to invoke.
+     * @param callable $delay       Function that accepts the number of retries
+     *                              and [response] and returns the number of
+     *                              milliseconds to delay.
+     */
+    public function __construct(
+        callable $decider,
+        callable $nextHandler,
+        callable $delay = null
+    ) {
+        $this->decider = $decider;
+        $this->nextHandler = $nextHandler;
+        $this->delay = $delay ?: __CLASS__ . '::exponentialDelay';
+    }
+
+    /**
+     * Default exponential backoff delay function.
+     *
+     * @param $retries
+     *
+     * @return int
+     */
+    public static function exponentialDelay($retries)
+    {
+        return (int) pow(2, $retries - 1);
+    }
+
+    /**
+     * @param RequestInterface $request
+     * @param array            $options
+     *
+     * @return PromiseInterface
+     */
+    public function __invoke(RequestInterface $request, array $options)
+    {
+        if (!isset($options['retries'])) {
+            $options['retries'] = 0;
+        }
+
+        $fn = $this->nextHandler;
+        return $fn($request, $options)
+            ->then(
+                $this->onFulfilled($request, $options),
+                $this->onRejected($request, $options)
+            );
+    }
+
+    private function onFulfilled(RequestInterface $req, array $options)
+    {
+        return function ($value) use ($req, $options) {
+            if (!call_user_func(
+                $this->decider,
+                $options['retries'],
+                $req,
+                $value,
+                null
+            )) {
+                return $value;
+            }
+            return $this->doRetry($req, $options, $value);
+        };
+    }
+
+    private function onRejected(RequestInterface $req, array $options)
+    {
+        return function ($reason) use ($req, $options) {
+            if (!call_user_func(
+                $this->decider,
+                $options['retries'],
+                $req,
+                null,
+                $reason
+            )) {
+                return \GuzzleHttp\Promise\rejection_for($reason);
+            }
+            return $this->doRetry($req, $options);
+        };
+    }
+
+    private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null)
+    {
+        $options['delay'] = call_user_func($this->delay, ++$options['retries'], $response);
+
+        return $this($request, $options);
+    }
+}

+ 126 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/TransferStats.php

@@ -0,0 +1,126 @@
+<?php
+namespace GuzzleHttp;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Represents data at the point after it was transferred either successfully
+ * or after a network error.
+ */
+final class TransferStats
+{
+    private $request;
+    private $response;
+    private $transferTime;
+    private $handlerStats;
+    private $handlerErrorData;
+
+    /**
+     * @param RequestInterface  $request          Request that was sent.
+     * @param ResponseInterface $response         Response received (if any)
+     * @param null              $transferTime     Total handler transfer time.
+     * @param mixed             $handlerErrorData Handler error data.
+     * @param array             $handlerStats     Handler specific stats.
+     */
+    public function __construct(
+        RequestInterface $request,
+        ResponseInterface $response = null,
+        $transferTime = null,
+        $handlerErrorData = null,
+        $handlerStats = []
+    ) {
+        $this->request = $request;
+        $this->response = $response;
+        $this->transferTime = $transferTime;
+        $this->handlerErrorData = $handlerErrorData;
+        $this->handlerStats = $handlerStats;
+    }
+
+    /**
+     * @return RequestInterface
+     */
+    public function getRequest()
+    {
+        return $this->request;
+    }
+
+    /**
+     * Returns the response that was received (if any).
+     *
+     * @return ResponseInterface|null
+     */
+    public function getResponse()
+    {
+        return $this->response;
+    }
+
+    /**
+     * Returns true if a response was received.
+     *
+     * @return bool
+     */
+    public function hasResponse()
+    {
+        return $this->response !== null;
+    }
+
+    /**
+     * Gets handler specific error data.
+     *
+     * This might be an exception, a integer representing an error code, or
+     * anything else. Relying on this value assumes that you know what handler
+     * you are using.
+     *
+     * @return mixed
+     */
+    public function getHandlerErrorData()
+    {
+        return $this->handlerErrorData;
+    }
+
+    /**
+     * Get the effective URI the request was sent to.
+     *
+     * @return UriInterface
+     */
+    public function getEffectiveUri()
+    {
+        return $this->request->getUri();
+    }
+
+    /**
+     * Get the estimated time the request was being transferred by the handler.
+     *
+     * @return float Time in seconds.
+     */
+    public function getTransferTime()
+    {
+        return $this->transferTime;
+    }
+
+    /**
+     * Gets an array of all of the handler specific transfer data.
+     *
+     * @return array
+     */
+    public function getHandlerStats()
+    {
+        return $this->handlerStats;
+    }
+
+    /**
+     * Get a specific handler statistic from the handler by name.
+     *
+     * @param string $stat Handler specific transfer stat to retrieve.
+     *
+     * @return mixed|null
+     */
+    public function getHandlerStat($stat)
+    {
+        return isset($this->handlerStats[$stat])
+            ? $this->handlerStats[$stat]
+            : null;
+    }
+}

+ 241 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/UriTemplate.php

@@ -0,0 +1,241 @@
+<?php
+namespace GuzzleHttp;
+
+/**
+ * Expands URI templates. Userland implementation of PECL uri_template.
+ *
+ * @link http://tools.ietf.org/html/rfc6570
+ */
+class UriTemplate
+{
+    /** @var string URI template */
+    private $template;
+
+    /** @var array Variables to use in the template expansion */
+    private $variables;
+
+    /** @var array Hash for quick operator lookups */
+    private static $operatorHash = [
+        ''  => ['prefix' => '',  'joiner' => ',', 'query' => false],
+        '+' => ['prefix' => '',  'joiner' => ',', 'query' => false],
+        '#' => ['prefix' => '#', 'joiner' => ',', 'query' => false],
+        '.' => ['prefix' => '.', 'joiner' => '.', 'query' => false],
+        '/' => ['prefix' => '/', 'joiner' => '/', 'query' => false],
+        ';' => ['prefix' => ';', 'joiner' => ';', 'query' => true],
+        '?' => ['prefix' => '?', 'joiner' => '&', 'query' => true],
+        '&' => ['prefix' => '&', 'joiner' => '&', 'query' => true]
+    ];
+
+    /** @var array Delimiters */
+    private static $delims = [':', '/', '?', '#', '[', ']', '@', '!', '$',
+        '&', '\'', '(', ')', '*', '+', ',', ';', '='];
+
+    /** @var array Percent encoded delimiters */
+    private static $delimsPct = ['%3A', '%2F', '%3F', '%23', '%5B', '%5D',
+        '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C',
+        '%3B', '%3D'];
+
+    public function expand($template, array $variables)
+    {
+        if (false === strpos($template, '{')) {
+            return $template;
+        }
+
+        $this->template = $template;
+        $this->variables = $variables;
+
+        return preg_replace_callback(
+            '/\{([^\}]+)\}/',
+            [$this, 'expandMatch'],
+            $this->template
+        );
+    }
+
+    /**
+     * Parse an expression into parts
+     *
+     * @param string $expression Expression to parse
+     *
+     * @return array Returns an associative array of parts
+     */
+    private function parseExpression($expression)
+    {
+        $result = [];
+
+        if (isset(self::$operatorHash[$expression[0]])) {
+            $result['operator'] = $expression[0];
+            $expression = substr($expression, 1);
+        } else {
+            $result['operator'] = '';
+        }
+
+        foreach (explode(',', $expression) as $value) {
+            $value = trim($value);
+            $varspec = [];
+            if ($colonPos = strpos($value, ':')) {
+                $varspec['value'] = substr($value, 0, $colonPos);
+                $varspec['modifier'] = ':';
+                $varspec['position'] = (int) substr($value, $colonPos + 1);
+            } elseif (substr($value, -1) === '*') {
+                $varspec['modifier'] = '*';
+                $varspec['value'] = substr($value, 0, -1);
+            } else {
+                $varspec['value'] = (string) $value;
+                $varspec['modifier'] = '';
+            }
+            $result['values'][] = $varspec;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Process an expansion
+     *
+     * @param array $matches Matches met in the preg_replace_callback
+     *
+     * @return string Returns the replacement string
+     */
+    private function expandMatch(array $matches)
+    {
+        static $rfc1738to3986 = ['+' => '%20', '%7e' => '~'];
+
+        $replacements = [];
+        $parsed = self::parseExpression($matches[1]);
+        $prefix = self::$operatorHash[$parsed['operator']]['prefix'];
+        $joiner = self::$operatorHash[$parsed['operator']]['joiner'];
+        $useQuery = self::$operatorHash[$parsed['operator']]['query'];
+
+        foreach ($parsed['values'] as $value) {
+
+            if (!isset($this->variables[$value['value']])) {
+                continue;
+            }
+
+            $variable = $this->variables[$value['value']];
+            $actuallyUseQuery = $useQuery;
+            $expanded = '';
+
+            if (is_array($variable)) {
+
+                $isAssoc = $this->isAssoc($variable);
+                $kvp = [];
+                foreach ($variable as $key => $var) {
+
+                    if ($isAssoc) {
+                        $key = rawurlencode($key);
+                        $isNestedArray = is_array($var);
+                    } else {
+                        $isNestedArray = false;
+                    }
+
+                    if (!$isNestedArray) {
+                        $var = rawurlencode($var);
+                        if ($parsed['operator'] === '+' ||
+                            $parsed['operator'] === '#'
+                        ) {
+                            $var = $this->decodeReserved($var);
+                        }
+                    }
+
+                    if ($value['modifier'] === '*') {
+                        if ($isAssoc) {
+                            if ($isNestedArray) {
+                                // Nested arrays must allow for deeply nested
+                                // structures.
+                                $var = strtr(
+                                    http_build_query([$key => $var]),
+                                    $rfc1738to3986
+                                );
+                            } else {
+                                $var = $key . '=' . $var;
+                            }
+                        } elseif ($key > 0 && $actuallyUseQuery) {
+                            $var = $value['value'] . '=' . $var;
+                        }
+                    }
+
+                    $kvp[$key] = $var;
+                }
+
+                if (empty($variable)) {
+                    $actuallyUseQuery = false;
+                } elseif ($value['modifier'] === '*') {
+                    $expanded = implode($joiner, $kvp);
+                    if ($isAssoc) {
+                        // Don't prepend the value name when using the explode
+                        // modifier with an associative array.
+                        $actuallyUseQuery = false;
+                    }
+                } else {
+                    if ($isAssoc) {
+                        // When an associative array is encountered and the
+                        // explode modifier is not set, then the result must be
+                        // a comma separated list of keys followed by their
+                        // respective values.
+                        foreach ($kvp as $k => &$v) {
+                            $v = $k . ',' . $v;
+                        }
+                    }
+                    $expanded = implode(',', $kvp);
+                }
+
+            } else {
+                if ($value['modifier'] === ':') {
+                    $variable = substr($variable, 0, $value['position']);
+                }
+                $expanded = rawurlencode($variable);
+                if ($parsed['operator'] === '+' || $parsed['operator'] === '#') {
+                    $expanded = $this->decodeReserved($expanded);
+                }
+            }
+
+            if ($actuallyUseQuery) {
+                if (!$expanded && $joiner !== '&') {
+                    $expanded = $value['value'];
+                } else {
+                    $expanded = $value['value'] . '=' . $expanded;
+                }
+            }
+
+            $replacements[] = $expanded;
+        }
+
+        $ret = implode($joiner, $replacements);
+        if ($ret && $prefix) {
+            return $prefix . $ret;
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Determines if an array is associative.
+     *
+     * This makes the assumption that input arrays are sequences or hashes.
+     * This assumption is a tradeoff for accuracy in favor of speed, but it
+     * should work in almost every case where input is supplied for a URI
+     * template.
+     *
+     * @param array $array Array to check
+     *
+     * @return bool
+     */
+    private function isAssoc(array $array)
+    {
+        return $array && array_keys($array)[0] !== 0;
+    }
+
+    /**
+     * Removes percent encoding on reserved characters (used with + and #
+     * modifiers).
+     *
+     * @param string $string String to fix
+     *
+     * @return string
+     */
+    private function decodeReserved($string)
+    {
+        return str_replace(self::$delimsPct, self::$delims, $string);
+    }
+}

+ 331 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/functions.php

@@ -0,0 +1,331 @@
+<?php
+namespace GuzzleHttp;
+
+use GuzzleHttp\Handler\CurlHandler;
+use GuzzleHttp\Handler\CurlMultiHandler;
+use GuzzleHttp\Handler\Proxy;
+use GuzzleHttp\Handler\StreamHandler;
+
+/**
+ * Expands a URI template
+ *
+ * @param string $template  URI template
+ * @param array  $variables Template variables
+ *
+ * @return string
+ */
+function uri_template($template, array $variables)
+{
+    if (extension_loaded('uri_template')) {
+        // @codeCoverageIgnoreStart
+        return \uri_template($template, $variables);
+        // @codeCoverageIgnoreEnd
+    }
+
+    static $uriTemplate;
+    if (!$uriTemplate) {
+        $uriTemplate = new UriTemplate();
+    }
+
+    return $uriTemplate->expand($template, $variables);
+}
+
+/**
+ * Debug function used to describe the provided value type and class.
+ *
+ * @param mixed $input
+ *
+ * @return string Returns a string containing the type of the variable and
+ *                if a class is provided, the class name.
+ */
+function describe_type($input)
+{
+    switch (gettype($input)) {
+        case 'object':
+            return 'object(' . get_class($input) . ')';
+        case 'array':
+            return 'array(' . count($input) . ')';
+        default:
+            ob_start();
+            var_dump($input);
+            // normalize float vs double
+            return str_replace('double(', 'float(', rtrim(ob_get_clean()));
+    }
+}
+
+/**
+ * Parses an array of header lines into an associative array of headers.
+ *
+ * @param array $lines Header lines array of strings in the following
+ *                     format: "Name: Value"
+ * @return array
+ */
+function headers_from_lines($lines)
+{
+    $headers = [];
+
+    foreach ($lines as $line) {
+        $parts = explode(':', $line, 2);
+        $headers[trim($parts[0])][] = isset($parts[1])
+            ? trim($parts[1])
+            : null;
+    }
+
+    return $headers;
+}
+
+/**
+ * Returns a debug stream based on the provided variable.
+ *
+ * @param mixed $value Optional value
+ *
+ * @return resource
+ */
+function debug_resource($value = null)
+{
+    if (is_resource($value)) {
+        return $value;
+    } elseif (defined('STDOUT')) {
+        return STDOUT;
+    }
+
+    return fopen('php://output', 'w');
+}
+
+/**
+ * Chooses and creates a default handler to use based on the environment.
+ *
+ * The returned handler is not wrapped by any default middlewares.
+ *
+ * @throws \RuntimeException if no viable Handler is available.
+ * @return callable Returns the best handler for the given system.
+ */
+function choose_handler()
+{
+    $handler = null;
+    if (function_exists('curl_multi_exec') && function_exists('curl_exec')) {
+        $handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler());
+    } elseif (function_exists('curl_exec')) {
+        $handler = new CurlHandler();
+    } elseif (function_exists('curl_multi_exec')) {
+        $handler = new CurlMultiHandler();
+    }
+
+    if (ini_get('allow_url_fopen')) {
+        $handler = $handler
+            ? Proxy::wrapStreaming($handler, new StreamHandler())
+            : new StreamHandler();
+    } elseif (!$handler) {
+        throw new \RuntimeException('GuzzleHttp requires cURL, the '
+            . 'allow_url_fopen ini setting, or a custom HTTP handler.');
+    }
+
+    return $handler;
+}
+
+/**
+ * Get the default User-Agent string to use with Guzzle
+ *
+ * @return string
+ */
+function default_user_agent()
+{
+    static $defaultAgent = '';
+
+    if (!$defaultAgent) {
+        $defaultAgent = 'GuzzleHttp/' . Client::VERSION;
+        if (extension_loaded('curl') && function_exists('curl_version')) {
+            $defaultAgent .= ' curl/' . \curl_version()['version'];
+        }
+        $defaultAgent .= ' PHP/' . PHP_VERSION;
+    }
+
+    return $defaultAgent;
+}
+
+/**
+ * Returns the default cacert bundle for the current system.
+ *
+ * First, the openssl.cafile and curl.cainfo php.ini settings are checked.
+ * If those settings are not configured, then the common locations for
+ * bundles found on Red Hat, CentOS, Fedora, Ubuntu, Debian, FreeBSD, OS X
+ * and Windows are checked. If any of these file locations are found on
+ * disk, they will be utilized.
+ *
+ * Note: the result of this function is cached for subsequent calls.
+ *
+ * @return string
+ * @throws \RuntimeException if no bundle can be found.
+ */
+function default_ca_bundle()
+{
+    static $cached = null;
+    static $cafiles = [
+        // Red Hat, CentOS, Fedora (provided by the ca-certificates package)
+        '/etc/pki/tls/certs/ca-bundle.crt',
+        // Ubuntu, Debian (provided by the ca-certificates package)
+        '/etc/ssl/certs/ca-certificates.crt',
+        // FreeBSD (provided by the ca_root_nss package)
+        '/usr/local/share/certs/ca-root-nss.crt',
+        // SLES 12 (provided by the ca-certificates package)
+        '/var/lib/ca-certificates/ca-bundle.pem',
+        // OS X provided by homebrew (using the default path)
+        '/usr/local/etc/openssl/cert.pem',
+        // Google app engine
+        '/etc/ca-certificates.crt',
+        // Windows?
+        'C:\\windows\\system32\\curl-ca-bundle.crt',
+        'C:\\windows\\curl-ca-bundle.crt',
+    ];
+
+    if ($cached) {
+        return $cached;
+    }
+
+    if ($ca = ini_get('openssl.cafile')) {
+        return $cached = $ca;
+    }
+
+    if ($ca = ini_get('curl.cainfo')) {
+        return $cached = $ca;
+    }
+
+    foreach ($cafiles as $filename) {
+        if (file_exists($filename)) {
+            return $cached = $filename;
+        }
+    }
+
+    throw new \RuntimeException(<<< EOT
+No system CA bundle could be found in any of the the common system locations.
+PHP versions earlier than 5.6 are not properly configured to use the system's
+CA bundle by default. In order to verify peer certificates, you will need to
+supply the path on disk to a certificate bundle to the 'verify' request
+option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not
+need a specific certificate bundle, then Mozilla provides a commonly used CA
+bundle which can be downloaded here (provided by the maintainer of cURL):
+https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt. Once
+you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP
+ini setting to point to the path to the file, allowing you to omit the 'verify'
+request option. See http://curl.haxx.se/docs/sslcerts.html for more
+information.
+EOT
+    );
+}
+
+/**
+ * Creates an associative array of lowercase header names to the actual
+ * header casing.
+ *
+ * @param array $headers
+ *
+ * @return array
+ */
+function normalize_header_keys(array $headers)
+{
+    $result = [];
+    foreach (array_keys($headers) as $key) {
+        $result[strtolower($key)] = $key;
+    }
+
+    return $result;
+}
+
+/**
+ * Returns true if the provided host matches any of the no proxy areas.
+ *
+ * This method will strip a port from the host if it is present. Each pattern
+ * can be matched with an exact match (e.g., "foo.com" == "foo.com") or a
+ * partial match: (e.g., "foo.com" == "baz.foo.com" and ".foo.com" ==
+ * "baz.foo.com", but ".foo.com" != "foo.com").
+ *
+ * Areas are matched in the following cases:
+ * 1. "*" (without quotes) always matches any hosts.
+ * 2. An exact match.
+ * 3. The area starts with "." and the area is the last part of the host. e.g.
+ *    '.mit.edu' will match any host that ends with '.mit.edu'.
+ *
+ * @param string $host         Host to check against the patterns.
+ * @param array  $noProxyArray An array of host patterns.
+ *
+ * @return bool
+ */
+function is_host_in_noproxy($host, array $noProxyArray)
+{
+    if (strlen($host) === 0) {
+        throw new \InvalidArgumentException('Empty host provided');
+    }
+
+    // Strip port if present.
+    if (strpos($host, ':')) {
+        $host = explode($host, ':', 2)[0];
+    }
+
+    foreach ($noProxyArray as $area) {
+        // Always match on wildcards.
+        if ($area === '*') {
+            return true;
+        } elseif (empty($area)) {
+            // Don't match on empty values.
+            continue;
+        } elseif ($area === $host) {
+            // Exact matches.
+            return true;
+        } else {
+            // Special match if the area when prefixed with ".". Remove any
+            // existing leading "." and add a new leading ".".
+            $area = '.' . ltrim($area, '.');
+            if (substr($host, -(strlen($area))) === $area) {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+/**
+ * Wrapper for json_decode that throws when an error occurs.
+ *
+ * @param string $json    JSON data to parse
+ * @param bool $assoc     When true, returned objects will be converted
+ *                        into associative arrays.
+ * @param int    $depth   User specified recursion depth.
+ * @param int    $options Bitmask of JSON decode options.
+ *
+ * @return mixed
+ * @throws \InvalidArgumentException if the JSON cannot be decoded.
+ * @link http://www.php.net/manual/en/function.json-decode.php
+ */
+function json_decode($json, $assoc = false, $depth = 512, $options = 0)
+{
+    $data = \json_decode($json, $assoc, $depth, $options);
+    if (JSON_ERROR_NONE !== json_last_error()) {
+        throw new \InvalidArgumentException(
+            'json_decode error: ' . json_last_error_msg());
+    }
+
+    return $data;
+}
+
+/**
+ * Wrapper for JSON encoding that throws when an error occurs.
+ *
+ * @param mixed $value   The value being encoded
+ * @param int    $options JSON encode option bitmask
+ * @param int    $depth   Set the maximum depth. Must be greater than zero.
+ *
+ * @return string
+ * @throws \InvalidArgumentException if the JSON cannot be encoded.
+ * @link http://www.php.net/manual/en/function.json-encode.php
+ */
+function json_encode($value, $options = 0, $depth = 512)
+{
+    $json = \json_encode($value, $options, $depth);
+    if (JSON_ERROR_NONE !== json_last_error()) {
+        throw new \InvalidArgumentException(
+            'json_encode error: ' . json_last_error_msg());
+    }
+
+    return $json;
+}

+ 6 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/guzzle/src/functions_include.php

@@ -0,0 +1,6 @@
+<?php
+
+// Don't redefine the functions if included multiple times.
+if (!function_exists('GuzzleHttp\uri_template')) {
+    require __DIR__ . '/functions.php';
+}

+ 65 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/CHANGELOG.md

@@ -0,0 +1,65 @@
+# CHANGELOG
+
+
+## 1.3.1 - 2016-12-20
+
+### Fixed
+
+- `wait()` foreign promise compatibility
+
+
+## 1.3.0 - 2016-11-18
+
+### Added
+
+- Adds support for custom task queues.
+
+### Fixed
+
+- Fixed coroutine promise memory leak.
+
+
+## 1.2.0 - 2016-05-18
+
+### Changed
+
+- Update to now catch `\Throwable` on PHP 7+
+
+
+## 1.1.0 - 2016-03-07
+
+### Changed
+
+- Update EachPromise to prevent recurring on a iterator when advancing, as this
+  could trigger fatal generator errors.
+- Update Promise to allow recursive waiting without unwrapping exceptions.
+
+
+## 1.0.3 - 2015-10-15
+
+### Changed
+
+- Update EachPromise to immediately resolve when the underlying promise iterator
+  is empty. Previously, such a promise would throw an exception when its `wait`
+  function was called.
+
+
+## 1.0.2 - 2015-05-15
+
+### Changed
+
+- Conditionally require functions.php.
+
+
+## 1.0.1 - 2015-06-24
+
+### Changed
+
+- Updating EachPromise to call next on the underlying promise iterator as late
+  as possible to ensure that generators that generate new requests based on
+  callbacks are not iterated until after callbacks are invoked.
+
+
+## 1.0.0 - 2015-05-12
+
+- Initial release

+ 19 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2015-2016 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 13 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/Makefile

@@ -0,0 +1,13 @@
+all: clean test
+
+test:
+	vendor/bin/phpunit
+
+coverage:
+	vendor/bin/phpunit --coverage-html=artifacts/coverage
+
+view-coverage:
+	open artifacts/coverage/index.html
+
+clean:
+	rm -rf artifacts/*

+ 504 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/README.md

@@ -0,0 +1,504 @@
+# Guzzle Promises
+
+[Promises/A+](https://promisesaplus.com/) implementation that handles promise
+chaining and resolution iteratively, allowing for "infinite" promise chaining
+while keeping the stack size constant. Read [this blog post](https://blog.domenic.me/youre-missing-the-point-of-promises/)
+for a general introduction to promises.
+
+- [Features](#features)
+- [Quick start](#quick-start)
+- [Synchronous wait](#synchronous-wait)
+- [Cancellation](#cancellation)
+- [API](#api)
+  - [Promise](#promise)
+  - [FulfilledPromise](#fulfilledpromise)
+  - [RejectedPromise](#rejectedpromise)
+- [Promise interop](#promise-interop)
+- [Implementation notes](#implementation-notes)
+
+
+# Features
+
+- [Promises/A+](https://promisesaplus.com/) implementation.
+- Promise resolution and chaining is handled iteratively, allowing for
+  "infinite" promise chaining.
+- Promises have a synchronous `wait` method.
+- Promises can be cancelled.
+- Works with any object that has a `then` function.
+- C# style async/await coroutine promises using
+  `GuzzleHttp\Promise\coroutine()`.
+
+
+# Quick start
+
+A *promise* represents the eventual result of an asynchronous operation. The
+primary way of interacting with a promise is through its `then` method, which
+registers callbacks to receive either a promise's eventual value or the reason
+why the promise cannot be fulfilled.
+
+
+## Callbacks
+
+Callbacks are registered with the `then` method by providing an optional 
+`$onFulfilled` followed by an optional `$onRejected` function.
+
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$promise->then(
+    // $onFulfilled
+    function ($value) {
+        echo 'The promise was fulfilled.';
+    },
+    // $onRejected
+    function ($reason) {
+        echo 'The promise was rejected.';
+    }
+);
+```
+
+*Resolving* a promise means that you either fulfill a promise with a *value* or
+reject a promise with a *reason*. Resolving a promises triggers callbacks
+registered with the promises's `then` method. These callbacks are triggered
+only once and in the order in which they were added.
+
+
+## Resolving a promise
+
+Promises are fulfilled using the `resolve($value)` method. Resolving a promise
+with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger
+all of the onFulfilled callbacks (resolving a promise with a rejected promise
+will reject the promise and trigger the `$onRejected` callbacks).
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$promise
+    ->then(function ($value) {
+        // Return a value and don't break the chain
+        return "Hello, " . $value;
+    })
+    // This then is executed after the first then and receives the value
+    // returned from the first then.
+    ->then(function ($value) {
+        echo $value;
+    });
+
+// Resolving the promise triggers the $onFulfilled callbacks and outputs
+// "Hello, reader".
+$promise->resolve('reader.');
+```
+
+
+## Promise forwarding
+
+Promises can be chained one after the other. Each then in the chain is a new
+promise. The return value of a promise is what's forwarded to the next
+promise in the chain. Returning a promise in a `then` callback will cause the
+subsequent promises in the chain to only be fulfilled when the returned promise
+has been fulfilled. The next promise in the chain will be invoked with the
+resolved value of the promise.
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$nextPromise = new Promise();
+
+$promise
+    ->then(function ($value) use ($nextPromise) {
+        echo $value;
+        return $nextPromise;
+    })
+    ->then(function ($value) {
+        echo $value;
+    });
+
+// Triggers the first callback and outputs "A"
+$promise->resolve('A');
+// Triggers the second callback and outputs "B"
+$nextPromise->resolve('B');
+```
+
+## Promise rejection
+
+When a promise is rejected, the `$onRejected` callbacks are invoked with the
+rejection reason.
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$promise->then(null, function ($reason) {
+    echo $reason;
+});
+
+$promise->reject('Error!');
+// Outputs "Error!"
+```
+
+## Rejection forwarding
+
+If an exception is thrown in an `$onRejected` callback, subsequent
+`$onRejected` callbacks are invoked with the thrown exception as the reason.
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$promise->then(null, function ($reason) {
+    throw new \Exception($reason);
+})->then(null, function ($reason) {
+    assert($reason->getMessage() === 'Error!');
+});
+
+$promise->reject('Error!');
+```
+
+You can also forward a rejection down the promise chain by returning a
+`GuzzleHttp\Promise\RejectedPromise` in either an `$onFulfilled` or
+`$onRejected` callback.
+
+```php
+use GuzzleHttp\Promise\Promise;
+use GuzzleHttp\Promise\RejectedPromise;
+
+$promise = new Promise();
+$promise->then(null, function ($reason) {
+    return new RejectedPromise($reason);
+})->then(null, function ($reason) {
+    assert($reason === 'Error!');
+});
+
+$promise->reject('Error!');
+```
+
+If an exception is not thrown in a `$onRejected` callback and the callback
+does not return a rejected promise, downstream `$onFulfilled` callbacks are
+invoked using the value returned from the `$onRejected` callback.
+
+```php
+use GuzzleHttp\Promise\Promise;
+use GuzzleHttp\Promise\RejectedPromise;
+
+$promise = new Promise();
+$promise
+    ->then(null, function ($reason) {
+        return "It's ok";
+    })
+    ->then(function ($value) {
+        assert($value === "It's ok");
+    });
+
+$promise->reject('Error!');
+```
+
+# Synchronous wait
+
+You can synchronously force promises to complete using a promise's `wait`
+method. When creating a promise, you can provide a wait function that is used
+to synchronously force a promise to complete. When a wait function is invoked
+it is expected to deliver a value to the promise or reject the promise. If the
+wait function does not deliver a value, then an exception is thrown. The wait
+function provided to a promise constructor is invoked when the `wait` function
+of the promise is called.
+
+```php
+$promise = new Promise(function () use (&$promise) {
+    $promise->resolve('foo');
+});
+
+// Calling wait will return the value of the promise.
+echo $promise->wait(); // outputs "foo"
+```
+
+If an exception is encountered while invoking the wait function of a promise,
+the promise is rejected with the exception and the exception is thrown.
+
+```php
+$promise = new Promise(function () use (&$promise) {
+    throw new \Exception('foo');
+});
+
+$promise->wait(); // throws the exception.
+```
+
+Calling `wait` on a promise that has been fulfilled will not trigger the wait
+function. It will simply return the previously resolved value.
+
+```php
+$promise = new Promise(function () { die('this is not called!'); });
+$promise->resolve('foo');
+echo $promise->wait(); // outputs "foo"
+```
+
+Calling `wait` on a promise that has been rejected will throw an exception. If
+the rejection reason is an instance of `\Exception` the reason is thrown.
+Otherwise, a `GuzzleHttp\Promise\RejectionException` is thrown and the reason
+can be obtained by calling the `getReason` method of the exception.
+
+```php
+$promise = new Promise();
+$promise->reject('foo');
+$promise->wait();
+```
+
+> PHP Fatal error:  Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo'
+
+
+## Unwrapping a promise
+
+When synchronously waiting on a promise, you are joining the state of the
+promise into the current state of execution (i.e., return the value of the
+promise if it was fulfilled or throw an exception if it was rejected). This is
+called "unwrapping" the promise. Waiting on a promise will by default unwrap
+the promise state.
+
+You can force a promise to resolve and *not* unwrap the state of the promise
+by passing `false` to the first argument of the `wait` function:
+
+```php
+$promise = new Promise();
+$promise->reject('foo');
+// This will not throw an exception. It simply ensures the promise has
+// been resolved.
+$promise->wait(false);
+```
+
+When unwrapping a promise, the resolved value of the promise will be waited
+upon until the unwrapped value is not a promise. This means that if you resolve
+promise A with a promise B and unwrap promise A, the value returned by the
+wait function will be the value delivered to promise B.
+
+**Note**: when you do not unwrap the promise, no value is returned.
+
+
+# Cancellation
+
+You can cancel a promise that has not yet been fulfilled using the `cancel()`
+method of a promise. When creating a promise you can provide an optional
+cancel function that when invoked cancels the action of computing a resolution
+of the promise.
+
+
+# API
+
+
+## Promise
+
+When creating a promise object, you can provide an optional `$waitFn` and
+`$cancelFn`. `$waitFn` is a function that is invoked with no arguments and is
+expected to resolve the promise. `$cancelFn` is a function with no arguments
+that is expected to cancel the computation of a promise. It is invoked when the
+`cancel()` method of a promise is called.
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise(
+    function () use (&$promise) {
+        $promise->resolve('waited');
+    },
+    function () {
+        // do something that will cancel the promise computation (e.g., close
+        // a socket, cancel a database query, etc...)
+    }
+);
+
+assert('waited' === $promise->wait());
+```
+
+A promise has the following methods:
+
+- `then(callable $onFulfilled, callable $onRejected) : PromiseInterface`
+  
+  Appends fulfillment and rejection handlers to the promise, and returns a new promise resolving to the return value of the called handler.
+
+- `otherwise(callable $onRejected) : PromiseInterface`
+  
+  Appends a rejection handler callback to the promise, and returns a new promise resolving to the return value of the callback if it is called, or to its original fulfillment value if the promise is instead fulfilled.
+
+- `wait($unwrap = true) : mixed`
+
+  Synchronously waits on the promise to complete.
+  
+  `$unwrap` controls whether or not the value of the promise is returned for a
+  fulfilled promise or if an exception is thrown if the promise is rejected.
+  This is set to `true` by default.
+
+- `cancel()`
+
+  Attempts to cancel the promise if possible. The promise being cancelled and
+  the parent most ancestor that has not yet been resolved will also be
+  cancelled. Any promises waiting on the cancelled promise to resolve will also
+  be cancelled.
+
+- `getState() : string`
+
+  Returns the state of the promise. One of `pending`, `fulfilled`, or
+  `rejected`.
+
+- `resolve($value)`
+
+  Fulfills the promise with the given `$value`.
+
+- `reject($reason)`
+
+  Rejects the promise with the given `$reason`.
+
+
+## FulfilledPromise
+
+A fulfilled promise can be created to represent a promise that has been
+fulfilled.
+
+```php
+use GuzzleHttp\Promise\FulfilledPromise;
+
+$promise = new FulfilledPromise('value');
+
+// Fulfilled callbacks are immediately invoked.
+$promise->then(function ($value) {
+    echo $value;
+});
+```
+
+
+## RejectedPromise
+
+A rejected promise can be created to represent a promise that has been
+rejected.
+
+```php
+use GuzzleHttp\Promise\RejectedPromise;
+
+$promise = new RejectedPromise('Error');
+
+// Rejected callbacks are immediately invoked.
+$promise->then(null, function ($reason) {
+    echo $reason;
+});
+```
+
+
+# Promise interop
+
+This library works with foreign promises that have a `then` method. This means
+you can use Guzzle promises with [React promises](https://github.com/reactphp/promise)
+for example. When a foreign promise is returned inside of a then method
+callback, promise resolution will occur recursively.
+
+```php
+// Create a React promise
+$deferred = new React\Promise\Deferred();
+$reactPromise = $deferred->promise();
+
+// Create a Guzzle promise that is fulfilled with a React promise.
+$guzzlePromise = new \GuzzleHttp\Promise\Promise();
+$guzzlePromise->then(function ($value) use ($reactPromise) {
+    // Do something something with the value...
+    // Return the React promise
+    return $reactPromise;
+});
+```
+
+Please note that wait and cancel chaining is no longer possible when forwarding
+a foreign promise. You will need to wrap a third-party promise with a Guzzle
+promise in order to utilize wait and cancel functions with foreign promises.
+
+
+## Event Loop Integration
+
+In order to keep the stack size constant, Guzzle promises are resolved
+asynchronously using a task queue. When waiting on promises synchronously, the
+task queue will be automatically run to ensure that the blocking promise and
+any forwarded promises are resolved. When using promises asynchronously in an
+event loop, you will need to run the task queue on each tick of the loop. If
+you do not run the task queue, then promises will not be resolved.
+
+You can run the task queue using the `run()` method of the global task queue
+instance.
+
+```php
+// Get the global task queue
+$queue = \GuzzleHttp\Promise\queue();
+$queue->run();
+```
+
+For example, you could use Guzzle promises with React using a periodic timer:
+
+```php
+$loop = React\EventLoop\Factory::create();
+$loop->addPeriodicTimer(0, [$queue, 'run']);
+```
+
+*TODO*: Perhaps adding a `futureTick()` on each tick would be faster?
+
+
+# Implementation notes
+
+
+## Promise resolution and chaining is handled iteratively
+
+By shuffling pending handlers from one owner to another, promises are
+resolved iteratively, allowing for "infinite" then chaining.
+
+```php
+<?php
+require 'vendor/autoload.php';
+
+use GuzzleHttp\Promise\Promise;
+
+$parent = new Promise();
+$p = $parent;
+
+for ($i = 0; $i < 1000; $i++) {
+    $p = $p->then(function ($v) {
+        // The stack size remains constant (a good thing)
+        echo xdebug_get_stack_depth() . ', ';
+        return $v + 1;
+    });
+}
+
+$parent->resolve(0);
+var_dump($p->wait()); // int(1000)
+
+```
+
+When a promise is fulfilled or rejected with a non-promise value, the promise
+then takes ownership of the handlers of each child promise and delivers values
+down the chain without using recursion.
+
+When a promise is resolved with another promise, the original promise transfers
+all of its pending handlers to the new promise. When the new promise is
+eventually resolved, all of the pending handlers are delivered the forwarded
+value.
+
+
+## A promise is the deferred.
+
+Some promise libraries implement promises using a deferred object to represent
+a computation and a promise object to represent the delivery of the result of
+the computation. This is a nice separation of computation and delivery because
+consumers of the promise cannot modify the value that will be eventually
+delivered.
+
+One side effect of being able to implement promise resolution and chaining
+iteratively is that you need to be able for one promise to reach into the state
+of another promise to shuffle around ownership of handlers. In order to achieve
+this without making the handlers of a promise publicly mutable, a promise is
+also the deferred value, allowing promises of the same parent class to reach
+into and modify the private properties of promises of the same type. While this
+does allow consumers of the value to modify the resolution or rejection of the
+deferred, it is a small price to pay for keeping the stack size constant.
+
+```php
+$promise = new Promise();
+$promise->then(function ($value) { echo $value; });
+// The promise is the deferred value, so you can deliver a value to it.
+$promise->resolve('foo');
+// prints "foo"
+```

+ 34 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/composer.json

@@ -0,0 +1,34 @@
+{
+    "name": "guzzlehttp/promises",
+    "description": "Guzzle promises library",
+    "keywords": ["promise"],
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Michael Dowling",
+            "email": "mtdowling@gmail.com",
+            "homepage": "https://github.com/mtdowling"
+        }
+    ],
+    "require": {
+        "php": ">=5.5.0"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "^4.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "GuzzleHttp\\Promise\\": "src/"
+        },
+        "files": ["src/functions_include.php"]
+    },
+    "scripts": {
+        "test": "vendor/bin/phpunit",
+        "test-ci": "vendor/bin/phpunit --coverage-text"
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.4-dev"
+        }
+    }
+}

+ 16 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/AggregateException.php

@@ -0,0 +1,16 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Exception thrown when too many errors occur in the some() or any() methods.
+ */
+class AggregateException extends RejectionException
+{
+    public function __construct($msg, array $reasons)
+    {
+        parent::__construct(
+            $reasons,
+            sprintf('%s; %d rejected promises', $msg, count($reasons))
+        );
+    }
+}

+ 9 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/CancellationException.php

@@ -0,0 +1,9 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Exception that is set as the reason for a promise that has been cancelled.
+ */
+class CancellationException extends RejectionException
+{
+}

+ 151 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/Coroutine.php

@@ -0,0 +1,151 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+use Exception;
+use Generator;
+use Throwable;
+
+/**
+ * Creates a promise that is resolved using a generator that yields values or
+ * promises (somewhat similar to C#'s async keyword).
+ *
+ * When called, the coroutine function will start an instance of the generator
+ * and returns a promise that is fulfilled with its final yielded value.
+ *
+ * Control is returned back to the generator when the yielded promise settles.
+ * This can lead to less verbose code when doing lots of sequential async calls
+ * with minimal processing in between.
+ *
+ *     use GuzzleHttp\Promise;
+ *
+ *     function createPromise($value) {
+ *         return new Promise\FulfilledPromise($value);
+ *     }
+ *
+ *     $promise = Promise\coroutine(function () {
+ *         $value = (yield createPromise('a'));
+ *         try {
+ *             $value = (yield createPromise($value . 'b'));
+ *         } catch (\Exception $e) {
+ *             // The promise was rejected.
+ *         }
+ *         yield $value . 'c';
+ *     });
+ *
+ *     // Outputs "abc"
+ *     $promise->then(function ($v) { echo $v; });
+ *
+ * @param callable $generatorFn Generator function to wrap into a promise.
+ *
+ * @return Promise
+ * @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
+ */
+final class Coroutine implements PromiseInterface
+{
+    /**
+     * @var PromiseInterface|null
+     */
+    private $currentPromise;
+
+    /**
+     * @var Generator
+     */
+    private $generator;
+
+    /**
+     * @var Promise
+     */
+    private $result;
+
+    public function __construct(callable $generatorFn)
+    {
+        $this->generator = $generatorFn();
+        $this->result = new Promise(function () {
+            while (isset($this->currentPromise)) {
+                $this->currentPromise->wait();
+            }
+        });
+        $this->nextCoroutine($this->generator->current());
+    }
+
+    public function then(
+        callable $onFulfilled = null,
+        callable $onRejected = null
+    ) {
+        return $this->result->then($onFulfilled, $onRejected);
+    }
+
+    public function otherwise(callable $onRejected)
+    {
+        return $this->result->otherwise($onRejected);
+    }
+
+    public function wait($unwrap = true)
+    {
+        return $this->result->wait($unwrap);
+    }
+
+    public function getState()
+    {
+        return $this->result->getState();
+    }
+
+    public function resolve($value)
+    {
+        $this->result->resolve($value);
+    }
+
+    public function reject($reason)
+    {
+        $this->result->reject($reason);
+    }
+
+    public function cancel()
+    {
+        $this->currentPromise->cancel();
+        $this->result->cancel();
+    }
+
+    private function nextCoroutine($yielded)
+    {
+        $this->currentPromise = promise_for($yielded)
+            ->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
+    }
+
+    /**
+     * @internal
+     */
+    public function _handleSuccess($value)
+    {
+        unset($this->currentPromise);
+        try {
+            $next = $this->generator->send($value);
+            if ($this->generator->valid()) {
+                $this->nextCoroutine($next);
+            } else {
+                $this->result->resolve($value);
+            }
+        } catch (Exception $exception) {
+            $this->result->reject($exception);
+        } catch (Throwable $throwable) {
+            $this->result->reject($throwable);
+        }
+    }
+
+    /**
+     * @internal
+     */
+    public function _handleFailure($reason)
+    {
+        unset($this->currentPromise);
+        try {
+            $nextYield = $this->generator->throw(exception_for($reason));
+            // The throw was caught, so keep iterating on the coroutine
+            $this->nextCoroutine($nextYield);
+        } catch (Exception $exception) {
+            $this->result->reject($exception);
+        } catch (Throwable $throwable) {
+            $this->result->reject($throwable);
+        }
+    }
+}

+ 229 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/EachPromise.php

@@ -0,0 +1,229 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Represents a promise that iterates over many promises and invokes
+ * side-effect functions in the process.
+ */
+class EachPromise implements PromisorInterface
+{
+    private $pending = [];
+
+    /** @var \Iterator */
+    private $iterable;
+
+    /** @var callable|int */
+    private $concurrency;
+
+    /** @var callable */
+    private $onFulfilled;
+
+    /** @var callable */
+    private $onRejected;
+
+    /** @var Promise */
+    private $aggregate;
+
+    /** @var bool */
+    private $mutex;
+
+    /**
+     * Configuration hash can include the following key value pairs:
+     *
+     * - fulfilled: (callable) Invoked when a promise fulfills. The function
+     *   is invoked with three arguments: the fulfillment value, the index
+     *   position from the iterable list of the promise, and the aggregate
+     *   promise that manages all of the promises. The aggregate promise may
+     *   be resolved from within the callback to short-circuit the promise.
+     * - rejected: (callable) Invoked when a promise is rejected. The
+     *   function is invoked with three arguments: the rejection reason, the
+     *   index position from the iterable list of the promise, and the
+     *   aggregate promise that manages all of the promises. The aggregate
+     *   promise may be resolved from within the callback to short-circuit
+     *   the promise.
+     * - concurrency: (integer) Pass this configuration option to limit the
+     *   allowed number of outstanding concurrently executing promises,
+     *   creating a capped pool of promises. There is no limit by default.
+     *
+     * @param mixed    $iterable Promises or values to iterate.
+     * @param array    $config   Configuration options
+     */
+    public function __construct($iterable, array $config = [])
+    {
+        $this->iterable = iter_for($iterable);
+
+        if (isset($config['concurrency'])) {
+            $this->concurrency = $config['concurrency'];
+        }
+
+        if (isset($config['fulfilled'])) {
+            $this->onFulfilled = $config['fulfilled'];
+        }
+
+        if (isset($config['rejected'])) {
+            $this->onRejected = $config['rejected'];
+        }
+    }
+
+    public function promise()
+    {
+        if ($this->aggregate) {
+            return $this->aggregate;
+        }
+
+        try {
+            $this->createPromise();
+            $this->iterable->rewind();
+            $this->refillPending();
+        } catch (\Throwable $e) {
+            $this->aggregate->reject($e);
+        } catch (\Exception $e) {
+            $this->aggregate->reject($e);
+        }
+
+        return $this->aggregate;
+    }
+
+    private function createPromise()
+    {
+        $this->mutex = false;
+        $this->aggregate = new Promise(function () {
+            reset($this->pending);
+            if (empty($this->pending) && !$this->iterable->valid()) {
+                $this->aggregate->resolve(null);
+                return;
+            }
+
+            // Consume a potentially fluctuating list of promises while
+            // ensuring that indexes are maintained (precluding array_shift).
+            while ($promise = current($this->pending)) {
+                next($this->pending);
+                $promise->wait();
+                if ($this->aggregate->getState() !== PromiseInterface::PENDING) {
+                    return;
+                }
+            }
+        });
+
+        // Clear the references when the promise is resolved.
+        $clearFn = function () {
+            $this->iterable = $this->concurrency = $this->pending = null;
+            $this->onFulfilled = $this->onRejected = null;
+        };
+
+        $this->aggregate->then($clearFn, $clearFn);
+    }
+
+    private function refillPending()
+    {
+        if (!$this->concurrency) {
+            // Add all pending promises.
+            while ($this->addPending() && $this->advanceIterator());
+            return;
+        }
+
+        // Add only up to N pending promises.
+        $concurrency = is_callable($this->concurrency)
+            ? call_user_func($this->concurrency, count($this->pending))
+            : $this->concurrency;
+        $concurrency = max($concurrency - count($this->pending), 0);
+        // Concurrency may be set to 0 to disallow new promises.
+        if (!$concurrency) {
+            return;
+        }
+        // Add the first pending promise.
+        $this->addPending();
+        // Note this is special handling for concurrency=1 so that we do
+        // not advance the iterator after adding the first promise. This
+        // helps work around issues with generators that might not have the
+        // next value to yield until promise callbacks are called.
+        while (--$concurrency
+            && $this->advanceIterator()
+            && $this->addPending());
+    }
+
+    private function addPending()
+    {
+        if (!$this->iterable || !$this->iterable->valid()) {
+            return false;
+        }
+
+        $promise = promise_for($this->iterable->current());
+        $idx = $this->iterable->key();
+
+        $this->pending[$idx] = $promise->then(
+            function ($value) use ($idx) {
+                if ($this->onFulfilled) {
+                    call_user_func(
+                        $this->onFulfilled, $value, $idx, $this->aggregate
+                    );
+                }
+                $this->step($idx);
+            },
+            function ($reason) use ($idx) {
+                if ($this->onRejected) {
+                    call_user_func(
+                        $this->onRejected, $reason, $idx, $this->aggregate
+                    );
+                }
+                $this->step($idx);
+            }
+        );
+
+        return true;
+    }
+
+    private function advanceIterator()
+    {
+        // Place a lock on the iterator so that we ensure to not recurse,
+        // preventing fatal generator errors.
+        if ($this->mutex) {
+            return false;
+        }
+
+        $this->mutex = true;
+
+        try {
+            $this->iterable->next();
+            $this->mutex = false;
+            return true;
+        } catch (\Throwable $e) {
+            $this->aggregate->reject($e);
+            $this->mutex = false;
+            return false;
+        } catch (\Exception $e) {
+            $this->aggregate->reject($e);
+            $this->mutex = false;
+            return false;
+        }
+    }
+
+    private function step($idx)
+    {
+        // If the promise was already resolved, then ignore this step.
+        if ($this->aggregate->getState() !== PromiseInterface::PENDING) {
+            return;
+        }
+
+        unset($this->pending[$idx]);
+
+        // Only refill pending promises if we are not locked, preventing the
+        // EachPromise to recursively invoke the provided iterator, which
+        // cause a fatal error: "Cannot resume an already running generator"
+        if ($this->advanceIterator() && !$this->checkIfFinished()) {
+            // Add more pending promises if possible.
+            $this->refillPending();
+        }
+    }
+
+    private function checkIfFinished()
+    {
+        if (!$this->pending && !$this->iterable->valid()) {
+            // Resolve the promise if there's nothing left to do.
+            $this->aggregate->resolve(null);
+            return true;
+        }
+
+        return false;
+    }
+}

+ 82 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/FulfilledPromise.php

@@ -0,0 +1,82 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * A promise that has been fulfilled.
+ *
+ * Thenning off of this promise will invoke the onFulfilled callback
+ * immediately and ignore other callbacks.
+ */
+class FulfilledPromise implements PromiseInterface
+{
+    private $value;
+
+    public function __construct($value)
+    {
+        if (method_exists($value, 'then')) {
+            throw new \InvalidArgumentException(
+                'You cannot create a FulfilledPromise with a promise.');
+        }
+
+        $this->value = $value;
+    }
+
+    public function then(
+        callable $onFulfilled = null,
+        callable $onRejected = null
+    ) {
+        // Return itself if there is no onFulfilled function.
+        if (!$onFulfilled) {
+            return $this;
+        }
+
+        $queue = queue();
+        $p = new Promise([$queue, 'run']);
+        $value = $this->value;
+        $queue->add(static function () use ($p, $value, $onFulfilled) {
+            if ($p->getState() === self::PENDING) {
+                try {
+                    $p->resolve($onFulfilled($value));
+                } catch (\Throwable $e) {
+                    $p->reject($e);
+                } catch (\Exception $e) {
+                    $p->reject($e);
+                }
+            }
+        });
+
+        return $p;
+    }
+
+    public function otherwise(callable $onRejected)
+    {
+        return $this->then(null, $onRejected);
+    }
+
+    public function wait($unwrap = true, $defaultDelivery = null)
+    {
+        return $unwrap ? $this->value : null;
+    }
+
+    public function getState()
+    {
+        return self::FULFILLED;
+    }
+
+    public function resolve($value)
+    {
+        if ($value !== $this->value) {
+            throw new \LogicException("Cannot resolve a fulfilled promise");
+        }
+    }
+
+    public function reject($reason)
+    {
+        throw new \LogicException("Cannot reject a fulfilled promise");
+    }
+
+    public function cancel()
+    {
+        // pass
+    }
+}

+ 280 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/Promise.php

@@ -0,0 +1,280 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Promises/A+ implementation that avoids recursion when possible.
+ *
+ * @link https://promisesaplus.com/
+ */
+class Promise implements PromiseInterface
+{
+    private $state = self::PENDING;
+    private $result;
+    private $cancelFn;
+    private $waitFn;
+    private $waitList;
+    private $handlers = [];
+
+    /**
+     * @param callable $waitFn   Fn that when invoked resolves the promise.
+     * @param callable $cancelFn Fn that when invoked cancels the promise.
+     */
+    public function __construct(
+        callable $waitFn = null,
+        callable $cancelFn = null
+    ) {
+        $this->waitFn = $waitFn;
+        $this->cancelFn = $cancelFn;
+    }
+
+    public function then(
+        callable $onFulfilled = null,
+        callable $onRejected = null
+    ) {
+        if ($this->state === self::PENDING) {
+            $p = new Promise(null, [$this, 'cancel']);
+            $this->handlers[] = [$p, $onFulfilled, $onRejected];
+            $p->waitList = $this->waitList;
+            $p->waitList[] = $this;
+            return $p;
+        }
+
+        // Return a fulfilled promise and immediately invoke any callbacks.
+        if ($this->state === self::FULFILLED) {
+            return $onFulfilled
+                ? promise_for($this->result)->then($onFulfilled)
+                : promise_for($this->result);
+        }
+
+        // It's either cancelled or rejected, so return a rejected promise
+        // and immediately invoke any callbacks.
+        $rejection = rejection_for($this->result);
+        return $onRejected ? $rejection->then(null, $onRejected) : $rejection;
+    }
+
+    public function otherwise(callable $onRejected)
+    {
+        return $this->then(null, $onRejected);
+    }
+
+    public function wait($unwrap = true)
+    {
+        $this->waitIfPending();
+
+        $inner = $this->result instanceof PromiseInterface
+            ? $this->result->wait($unwrap)
+            : $this->result;
+
+        if ($unwrap) {
+            if ($this->result instanceof PromiseInterface
+                || $this->state === self::FULFILLED
+            ) {
+                return $inner;
+            } else {
+                // It's rejected so "unwrap" and throw an exception.
+                throw exception_for($inner);
+            }
+        }
+    }
+
+    public function getState()
+    {
+        return $this->state;
+    }
+
+    public function cancel()
+    {
+        if ($this->state !== self::PENDING) {
+            return;
+        }
+
+        $this->waitFn = $this->waitList = null;
+
+        if ($this->cancelFn) {
+            $fn = $this->cancelFn;
+            $this->cancelFn = null;
+            try {
+                $fn();
+            } catch (\Throwable $e) {
+                $this->reject($e);
+            } catch (\Exception $e) {
+                $this->reject($e);
+            }
+        }
+
+        // Reject the promise only if it wasn't rejected in a then callback.
+        if ($this->state === self::PENDING) {
+            $this->reject(new CancellationException('Promise has been cancelled'));
+        }
+    }
+
+    public function resolve($value)
+    {
+        $this->settle(self::FULFILLED, $value);
+    }
+
+    public function reject($reason)
+    {
+        $this->settle(self::REJECTED, $reason);
+    }
+
+    private function settle($state, $value)
+    {
+        if ($this->state !== self::PENDING) {
+            // Ignore calls with the same resolution.
+            if ($state === $this->state && $value === $this->result) {
+                return;
+            }
+            throw $this->state === $state
+                ? new \LogicException("The promise is already {$state}.")
+                : new \LogicException("Cannot change a {$this->state} promise to {$state}");
+        }
+
+        if ($value === $this) {
+            throw new \LogicException('Cannot fulfill or reject a promise with itself');
+        }
+
+        // Clear out the state of the promise but stash the handlers.
+        $this->state = $state;
+        $this->result = $value;
+        $handlers = $this->handlers;
+        $this->handlers = null;
+        $this->waitList = $this->waitFn = null;
+        $this->cancelFn = null;
+
+        if (!$handlers) {
+            return;
+        }
+
+        // If the value was not a settled promise or a thenable, then resolve
+        // it in the task queue using the correct ID.
+        if (!method_exists($value, 'then')) {
+            $id = $state === self::FULFILLED ? 1 : 2;
+            // It's a success, so resolve the handlers in the queue.
+            queue()->add(static function () use ($id, $value, $handlers) {
+                foreach ($handlers as $handler) {
+                    self::callHandler($id, $value, $handler);
+                }
+            });
+        } elseif ($value instanceof Promise
+            && $value->getState() === self::PENDING
+        ) {
+            // We can just merge our handlers onto the next promise.
+            $value->handlers = array_merge($value->handlers, $handlers);
+        } else {
+            // Resolve the handlers when the forwarded promise is resolved.
+            $value->then(
+                static function ($value) use ($handlers) {
+                    foreach ($handlers as $handler) {
+                        self::callHandler(1, $value, $handler);
+                    }
+                },
+                static function ($reason) use ($handlers) {
+                    foreach ($handlers as $handler) {
+                        self::callHandler(2, $reason, $handler);
+                    }
+                }
+            );
+        }
+    }
+
+    /**
+     * Call a stack of handlers using a specific callback index and value.
+     *
+     * @param int   $index   1 (resolve) or 2 (reject).
+     * @param mixed $value   Value to pass to the callback.
+     * @param array $handler Array of handler data (promise and callbacks).
+     *
+     * @return array Returns the next group to resolve.
+     */
+    private static function callHandler($index, $value, array $handler)
+    {
+        /** @var PromiseInterface $promise */
+        $promise = $handler[0];
+
+        // The promise may have been cancelled or resolved before placing
+        // this thunk in the queue.
+        if ($promise->getState() !== self::PENDING) {
+            return;
+        }
+
+        try {
+            if (isset($handler[$index])) {
+                $promise->resolve($handler[$index]($value));
+            } elseif ($index === 1) {
+                // Forward resolution values as-is.
+                $promise->resolve($value);
+            } else {
+                // Forward rejections down the chain.
+                $promise->reject($value);
+            }
+        } catch (\Throwable $reason) {
+            $promise->reject($reason);
+        } catch (\Exception $reason) {
+            $promise->reject($reason);
+        }
+    }
+
+    private function waitIfPending()
+    {
+        if ($this->state !== self::PENDING) {
+            return;
+        } elseif ($this->waitFn) {
+            $this->invokeWaitFn();
+        } elseif ($this->waitList) {
+            $this->invokeWaitList();
+        } else {
+            // If there's not wait function, then reject the promise.
+            $this->reject('Cannot wait on a promise that has '
+                . 'no internal wait function. You must provide a wait '
+                . 'function when constructing the promise to be able to '
+                . 'wait on a promise.');
+        }
+
+        queue()->run();
+
+        if ($this->state === self::PENDING) {
+            $this->reject('Invoking the wait callback did not resolve the promise');
+        }
+    }
+
+    private function invokeWaitFn()
+    {
+        try {
+            $wfn = $this->waitFn;
+            $this->waitFn = null;
+            $wfn(true);
+        } catch (\Exception $reason) {
+            if ($this->state === self::PENDING) {
+                // The promise has not been resolved yet, so reject the promise
+                // with the exception.
+                $this->reject($reason);
+            } else {
+                // The promise was already resolved, so there's a problem in
+                // the application.
+                throw $reason;
+            }
+        }
+    }
+
+    private function invokeWaitList()
+    {
+        $waitList = $this->waitList;
+        $this->waitList = null;
+
+        foreach ($waitList as $result) {
+            while (true) {
+                $result->waitIfPending();
+
+                if ($result->result instanceof Promise) {
+                    $result = $result->result;
+                } else {
+                    if ($result->result instanceof PromiseInterface) {
+                        $result->result->wait(false);
+                    }
+                    break;
+                }
+            }
+        }
+    }
+}

+ 93 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/PromiseInterface.php

@@ -0,0 +1,93 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * A promise represents the eventual result of an asynchronous operation.
+ *
+ * The primary way of interacting with a promise is through its then method,
+ * which registers callbacks to receive either a promise’s eventual value or
+ * the reason why the promise cannot be fulfilled.
+ *
+ * @link https://promisesaplus.com/
+ */
+interface PromiseInterface
+{
+    const PENDING = 'pending';
+    const FULFILLED = 'fulfilled';
+    const REJECTED = 'rejected';
+
+    /**
+     * Appends fulfillment and rejection handlers to the promise, and returns
+     * a new promise resolving to the return value of the called handler.
+     *
+     * @param callable $onFulfilled Invoked when the promise fulfills.
+     * @param callable $onRejected  Invoked when the promise is rejected.
+     *
+     * @return PromiseInterface
+     */
+    public function then(
+        callable $onFulfilled = null,
+        callable $onRejected = null
+    );
+
+    /**
+     * Appends a rejection handler callback to the promise, and returns a new
+     * promise resolving to the return value of the callback if it is called,
+     * or to its original fulfillment value if the promise is instead
+     * fulfilled.
+     *
+     * @param callable $onRejected Invoked when the promise is rejected.
+     *
+     * @return PromiseInterface
+     */
+    public function otherwise(callable $onRejected);
+
+    /**
+     * Get the state of the promise ("pending", "rejected", or "fulfilled").
+     *
+     * The three states can be checked against the constants defined on
+     * PromiseInterface: PENDING, FULFILLED, and REJECTED.
+     *
+     * @return string
+     */
+    public function getState();
+
+    /**
+     * Resolve the promise with the given value.
+     *
+     * @param mixed $value
+     * @throws \RuntimeException if the promise is already resolved.
+     */
+    public function resolve($value);
+
+    /**
+     * Reject the promise with the given reason.
+     *
+     * @param mixed $reason
+     * @throws \RuntimeException if the promise is already resolved.
+     */
+    public function reject($reason);
+
+    /**
+     * Cancels the promise if possible.
+     *
+     * @link https://github.com/promises-aplus/cancellation-spec/issues/7
+     */
+    public function cancel();
+
+    /**
+     * Waits until the promise completes if possible.
+     *
+     * Pass $unwrap as true to unwrap the result of the promise, either
+     * returning the resolved value or throwing the rejected exception.
+     *
+     * If the promise cannot be waited on, then the promise will be rejected.
+     *
+     * @param bool $unwrap
+     *
+     * @return mixed
+     * @throws \LogicException if the promise has no wait function or if the
+     *                         promise does not settle after waiting.
+     */
+    public function wait($unwrap = true);
+}

+ 15 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/PromisorInterface.php

@@ -0,0 +1,15 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Interface used with classes that return a promise.
+ */
+interface PromisorInterface
+{
+    /**
+     * Returns a promise.
+     *
+     * @return PromiseInterface
+     */
+    public function promise();
+}

+ 87 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/RejectedPromise.php

@@ -0,0 +1,87 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * A promise that has been rejected.
+ *
+ * Thenning off of this promise will invoke the onRejected callback
+ * immediately and ignore other callbacks.
+ */
+class RejectedPromise implements PromiseInterface
+{
+    private $reason;
+
+    public function __construct($reason)
+    {
+        if (method_exists($reason, 'then')) {
+            throw new \InvalidArgumentException(
+                'You cannot create a RejectedPromise with a promise.');
+        }
+
+        $this->reason = $reason;
+    }
+
+    public function then(
+        callable $onFulfilled = null,
+        callable $onRejected = null
+    ) {
+        // If there's no onRejected callback then just return self.
+        if (!$onRejected) {
+            return $this;
+        }
+
+        $queue = queue();
+        $reason = $this->reason;
+        $p = new Promise([$queue, 'run']);
+        $queue->add(static function () use ($p, $reason, $onRejected) {
+            if ($p->getState() === self::PENDING) {
+                try {
+                    // Return a resolved promise if onRejected does not throw.
+                    $p->resolve($onRejected($reason));
+                } catch (\Throwable $e) {
+                    // onRejected threw, so return a rejected promise.
+                    $p->reject($e);
+                } catch (\Exception $e) {
+                    // onRejected threw, so return a rejected promise.
+                    $p->reject($e);
+                }
+            }
+        });
+
+        return $p;
+    }
+
+    public function otherwise(callable $onRejected)
+    {
+        return $this->then(null, $onRejected);
+    }
+
+    public function wait($unwrap = true, $defaultDelivery = null)
+    {
+        if ($unwrap) {
+            throw exception_for($this->reason);
+        }
+    }
+
+    public function getState()
+    {
+        return self::REJECTED;
+    }
+
+    public function resolve($value)
+    {
+        throw new \LogicException("Cannot resolve a rejected promise");
+    }
+
+    public function reject($reason)
+    {
+        if ($reason !== $this->reason) {
+            throw new \LogicException("Cannot reject a rejected promise");
+        }
+    }
+
+    public function cancel()
+    {
+        // pass
+    }
+}

+ 47 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/RejectionException.php

@@ -0,0 +1,47 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * A special exception that is thrown when waiting on a rejected promise.
+ *
+ * The reason value is available via the getReason() method.
+ */
+class RejectionException extends \RuntimeException
+{
+    /** @var mixed Rejection reason. */
+    private $reason;
+
+    /**
+     * @param mixed $reason       Rejection reason.
+     * @param string $description Optional description
+     */
+    public function __construct($reason, $description = null)
+    {
+        $this->reason = $reason;
+
+        $message = 'The promise was rejected';
+
+        if ($description) {
+            $message .= ' with reason: ' . $description;
+        } elseif (is_string($reason)
+            || (is_object($reason) && method_exists($reason, '__toString'))
+        ) {
+            $message .= ' with reason: ' . $this->reason;
+        } elseif ($reason instanceof \JsonSerializable) {
+            $message .= ' with reason: '
+                . json_encode($this->reason, JSON_PRETTY_PRINT);
+        }
+
+        parent::__construct($message);
+    }
+
+    /**
+     * Returns the rejection reason.
+     *
+     * @return mixed
+     */
+    public function getReason()
+    {
+        return $this->reason;
+    }
+}

+ 66 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/TaskQueue.php

@@ -0,0 +1,66 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * A task queue that executes tasks in a FIFO order.
+ *
+ * This task queue class is used to settle promises asynchronously and
+ * maintains a constant stack size. You can use the task queue asynchronously
+ * by calling the `run()` function of the global task queue in an event loop.
+ *
+ *     GuzzleHttp\Promise\queue()->run();
+ */
+class TaskQueue implements TaskQueueInterface
+{
+    private $enableShutdown = true;
+    private $queue = [];
+
+    public function __construct($withShutdown = true)
+    {
+        if ($withShutdown) {
+            register_shutdown_function(function () {
+                if ($this->enableShutdown) {
+                    // Only run the tasks if an E_ERROR didn't occur.
+                    $err = error_get_last();
+                    if (!$err || ($err['type'] ^ E_ERROR)) {
+                        $this->run();
+                    }
+                }
+            });
+        }
+    }
+
+    public function isEmpty()
+    {
+        return !$this->queue;
+    }
+
+    public function add(callable $task)
+    {
+        $this->queue[] = $task;
+    }
+
+    public function run()
+    {
+        /** @var callable $task */
+        while ($task = array_shift($this->queue)) {
+            $task();
+        }
+    }
+
+    /**
+     * The task queue will be run and exhausted by default when the process
+     * exits IFF the exit is not the result of a PHP E_ERROR error.
+     *
+     * You can disable running the automatic shutdown of the queue by calling
+     * this function. If you disable the task queue shutdown process, then you
+     * MUST either run the task queue (as a result of running your event loop
+     * or manually using the run() method) or wait on each outstanding promise.
+     *
+     * Note: This shutdown will occur before any destructors are triggered.
+     */
+    public function disableShutdown()
+    {
+        $this->enableShutdown = false;
+    }
+}

+ 25 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/TaskQueueInterface.php

@@ -0,0 +1,25 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+interface TaskQueueInterface
+{
+    /**
+     * Returns true if the queue is empty.
+     *
+     * @return bool
+     */
+    public function isEmpty();
+
+    /**
+     * Adds a task to the queue that will be executed the next time run is
+     * called.
+     *
+     * @param callable $task
+     */
+    public function add(callable $task);
+
+    /**
+     * Execute all of the pending task in the queue.
+     */
+    public function run();
+}

+ 457 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/functions.php

@@ -0,0 +1,457 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Get the global task queue used for promise resolution.
+ *
+ * This task queue MUST be run in an event loop in order for promises to be
+ * settled asynchronously. It will be automatically run when synchronously
+ * waiting on a promise.
+ *
+ * <code>
+ * while ($eventLoop->isRunning()) {
+ *     GuzzleHttp\Promise\queue()->run();
+ * }
+ * </code>
+ *
+ * @param TaskQueueInterface $assign Optionally specify a new queue instance.
+ *
+ * @return TaskQueueInterface
+ */
+function queue(TaskQueueInterface $assign = null)
+{
+    static $queue;
+
+    if ($assign) {
+        $queue = $assign;
+    } elseif (!$queue) {
+        $queue = new TaskQueue();
+    }
+
+    return $queue;
+}
+
+/**
+ * Adds a function to run in the task queue when it is next `run()` and returns
+ * a promise that is fulfilled or rejected with the result.
+ *
+ * @param callable $task Task function to run.
+ *
+ * @return PromiseInterface
+ */
+function task(callable $task)
+{
+    $queue = queue();
+    $promise = new Promise([$queue, 'run']);
+    $queue->add(function () use ($task, $promise) {
+        try {
+            $promise->resolve($task());
+        } catch (\Throwable $e) {
+            $promise->reject($e);
+        } catch (\Exception $e) {
+            $promise->reject($e);
+        }
+    });
+
+    return $promise;
+}
+
+/**
+ * Creates a promise for a value if the value is not a promise.
+ *
+ * @param mixed $value Promise or value.
+ *
+ * @return PromiseInterface
+ */
+function promise_for($value)
+{
+    if ($value instanceof PromiseInterface) {
+        return $value;
+    }
+
+    // Return a Guzzle promise that shadows the given promise.
+    if (method_exists($value, 'then')) {
+        $wfn = method_exists($value, 'wait') ? [$value, 'wait'] : null;
+        $cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null;
+        $promise = new Promise($wfn, $cfn);
+        $value->then([$promise, 'resolve'], [$promise, 'reject']);
+        return $promise;
+    }
+
+    return new FulfilledPromise($value);
+}
+
+/**
+ * Creates a rejected promise for a reason if the reason is not a promise. If
+ * the provided reason is a promise, then it is returned as-is.
+ *
+ * @param mixed $reason Promise or reason.
+ *
+ * @return PromiseInterface
+ */
+function rejection_for($reason)
+{
+    if ($reason instanceof PromiseInterface) {
+        return $reason;
+    }
+
+    return new RejectedPromise($reason);
+}
+
+/**
+ * Create an exception for a rejected promise value.
+ *
+ * @param mixed $reason
+ *
+ * @return \Exception|\Throwable
+ */
+function exception_for($reason)
+{
+    return $reason instanceof \Exception || $reason instanceof \Throwable
+        ? $reason
+        : new RejectionException($reason);
+}
+
+/**
+ * Returns an iterator for the given value.
+ *
+ * @param mixed $value
+ *
+ * @return \Iterator
+ */
+function iter_for($value)
+{
+    if ($value instanceof \Iterator) {
+        return $value;
+    } elseif (is_array($value)) {
+        return new \ArrayIterator($value);
+    } else {
+        return new \ArrayIterator([$value]);
+    }
+}
+
+/**
+ * Synchronously waits on a promise to resolve and returns an inspection state
+ * array.
+ *
+ * Returns a state associative array containing a "state" key mapping to a
+ * valid promise state. If the state of the promise is "fulfilled", the array
+ * will contain a "value" key mapping to the fulfilled value of the promise. If
+ * the promise is rejected, the array will contain a "reason" key mapping to
+ * the rejection reason of the promise.
+ *
+ * @param PromiseInterface $promise Promise or value.
+ *
+ * @return array
+ */
+function inspect(PromiseInterface $promise)
+{
+    try {
+        return [
+            'state' => PromiseInterface::FULFILLED,
+            'value' => $promise->wait()
+        ];
+    } catch (RejectionException $e) {
+        return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
+    } catch (\Throwable $e) {
+        return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
+    } catch (\Exception $e) {
+        return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
+    }
+}
+
+/**
+ * Waits on all of the provided promises, but does not unwrap rejected promises
+ * as thrown exception.
+ *
+ * Returns an array of inspection state arrays.
+ *
+ * @param PromiseInterface[] $promises Traversable of promises to wait upon.
+ *
+ * @return array
+ * @see GuzzleHttp\Promise\inspect for the inspection state array format.
+ */
+function inspect_all($promises)
+{
+    $results = [];
+    foreach ($promises as $key => $promise) {
+        $results[$key] = inspect($promise);
+    }
+
+    return $results;
+}
+
+/**
+ * Waits on all of the provided promises and returns the fulfilled values.
+ *
+ * Returns an array that contains the value of each promise (in the same order
+ * the promises were provided). An exception is thrown if any of the promises
+ * are rejected.
+ *
+ * @param mixed $promises Iterable of PromiseInterface objects to wait on.
+ *
+ * @return array
+ * @throws \Exception on error
+ * @throws \Throwable on error in PHP >=7
+ */
+function unwrap($promises)
+{
+    $results = [];
+    foreach ($promises as $key => $promise) {
+        $results[$key] = $promise->wait();
+    }
+
+    return $results;
+}
+
+/**
+ * Given an array of promises, return a promise that is fulfilled when all the
+ * items in the array are fulfilled.
+ *
+ * The promise's fulfillment value is an array with fulfillment values at
+ * respective positions to the original array. If any promise in the array
+ * rejects, the returned promise is rejected with the rejection reason.
+ *
+ * @param mixed $promises Promises or values.
+ *
+ * @return PromiseInterface
+ */
+function all($promises)
+{
+    $results = [];
+    return each(
+        $promises,
+        function ($value, $idx) use (&$results) {
+            $results[$idx] = $value;
+        },
+        function ($reason, $idx, Promise $aggregate) {
+            $aggregate->reject($reason);
+        }
+    )->then(function () use (&$results) {
+        ksort($results);
+        return $results;
+    });
+}
+
+/**
+ * Initiate a competitive race between multiple promises or values (values will
+ * become immediately fulfilled promises).
+ *
+ * When count amount of promises have been fulfilled, the returned promise is
+ * fulfilled with an array that contains the fulfillment values of the winners
+ * in order of resolution.
+ *
+ * This prommise is rejected with a {@see GuzzleHttp\Promise\AggregateException}
+ * if the number of fulfilled promises is less than the desired $count.
+ *
+ * @param int   $count    Total number of promises.
+ * @param mixed $promises Promises or values.
+ *
+ * @return PromiseInterface
+ */
+function some($count, $promises)
+{
+    $results = [];
+    $rejections = [];
+
+    return each(
+        $promises,
+        function ($value, $idx, PromiseInterface $p) use (&$results, $count) {
+            if ($p->getState() !== PromiseInterface::PENDING) {
+                return;
+            }
+            $results[$idx] = $value;
+            if (count($results) >= $count) {
+                $p->resolve(null);
+            }
+        },
+        function ($reason) use (&$rejections) {
+            $rejections[] = $reason;
+        }
+    )->then(
+        function () use (&$results, &$rejections, $count) {
+            if (count($results) !== $count) {
+                throw new AggregateException(
+                    'Not enough promises to fulfill count',
+                    $rejections
+                );
+            }
+            ksort($results);
+            return array_values($results);
+        }
+    );
+}
+
+/**
+ * Like some(), with 1 as count. However, if the promise fulfills, the
+ * fulfillment value is not an array of 1 but the value directly.
+ *
+ * @param mixed $promises Promises or values.
+ *
+ * @return PromiseInterface
+ */
+function any($promises)
+{
+    return some(1, $promises)->then(function ($values) { return $values[0]; });
+}
+
+/**
+ * Returns a promise that is fulfilled when all of the provided promises have
+ * been fulfilled or rejected.
+ *
+ * The returned promise is fulfilled with an array of inspection state arrays.
+ *
+ * @param mixed $promises Promises or values.
+ *
+ * @return PromiseInterface
+ * @see GuzzleHttp\Promise\inspect for the inspection state array format.
+ */
+function settle($promises)
+{
+    $results = [];
+
+    return each(
+        $promises,
+        function ($value, $idx) use (&$results) {
+            $results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
+        },
+        function ($reason, $idx) use (&$results) {
+            $results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
+        }
+    )->then(function () use (&$results) {
+        ksort($results);
+        return $results;
+    });
+}
+
+/**
+ * Given an iterator that yields promises or values, returns a promise that is
+ * fulfilled with a null value when the iterator has been consumed or the
+ * aggregate promise has been fulfilled or rejected.
+ *
+ * $onFulfilled is a function that accepts the fulfilled value, iterator
+ * index, and the aggregate promise. The callback can invoke any necessary side
+ * effects and choose to resolve or reject the aggregate promise if needed.
+ *
+ * $onRejected is a function that accepts the rejection reason, iterator
+ * index, and the aggregate promise. The callback can invoke any necessary side
+ * effects and choose to resolve or reject the aggregate promise if needed.
+ *
+ * @param mixed    $iterable    Iterator or array to iterate over.
+ * @param callable $onFulfilled
+ * @param callable $onRejected
+ *
+ * @return PromiseInterface
+ */
+function each(
+    $iterable,
+    callable $onFulfilled = null,
+    callable $onRejected = null
+) {
+    return (new EachPromise($iterable, [
+        'fulfilled' => $onFulfilled,
+        'rejected'  => $onRejected
+    ]))->promise();
+}
+
+/**
+ * Like each, but only allows a certain number of outstanding promises at any
+ * given time.
+ *
+ * $concurrency may be an integer or a function that accepts the number of
+ * pending promises and returns a numeric concurrency limit value to allow for
+ * dynamic a concurrency size.
+ *
+ * @param mixed        $iterable
+ * @param int|callable $concurrency
+ * @param callable     $onFulfilled
+ * @param callable     $onRejected
+ *
+ * @return PromiseInterface
+ */
+function each_limit(
+    $iterable,
+    $concurrency,
+    callable $onFulfilled = null,
+    callable $onRejected = null
+) {
+    return (new EachPromise($iterable, [
+        'fulfilled'   => $onFulfilled,
+        'rejected'    => $onRejected,
+        'concurrency' => $concurrency
+    ]))->promise();
+}
+
+/**
+ * Like each_limit, but ensures that no promise in the given $iterable argument
+ * is rejected. If any promise is rejected, then the aggregate promise is
+ * rejected with the encountered rejection.
+ *
+ * @param mixed        $iterable
+ * @param int|callable $concurrency
+ * @param callable     $onFulfilled
+ *
+ * @return PromiseInterface
+ */
+function each_limit_all(
+    $iterable,
+    $concurrency,
+    callable $onFulfilled = null
+) {
+    return each_limit(
+        $iterable,
+        $concurrency,
+        $onFulfilled,
+        function ($reason, $idx, PromiseInterface $aggregate) {
+            $aggregate->reject($reason);
+        }
+    );
+}
+
+/**
+ * Returns true if a promise is fulfilled.
+ *
+ * @param PromiseInterface $promise
+ *
+ * @return bool
+ */
+function is_fulfilled(PromiseInterface $promise)
+{
+    return $promise->getState() === PromiseInterface::FULFILLED;
+}
+
+/**
+ * Returns true if a promise is rejected.
+ *
+ * @param PromiseInterface $promise
+ *
+ * @return bool
+ */
+function is_rejected(PromiseInterface $promise)
+{
+    return $promise->getState() === PromiseInterface::REJECTED;
+}
+
+/**
+ * Returns true if a promise is fulfilled or rejected.
+ *
+ * @param PromiseInterface $promise
+ *
+ * @return bool
+ */
+function is_settled(PromiseInterface $promise)
+{
+    return $promise->getState() !== PromiseInterface::PENDING;
+}
+
+/**
+ * @see Coroutine
+ *
+ * @param callable $generatorFn
+ *
+ * @return PromiseInterface
+ */
+function coroutine(callable $generatorFn)
+{
+    return new Coroutine($generatorFn);
+}

+ 6 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/promises/src/functions_include.php

@@ -0,0 +1,6 @@
+<?php
+
+// Don't redefine the functions if included multiple times.
+if (!function_exists('GuzzleHttp\Promise\promise_for')) {
+    require __DIR__ . '/functions.php';
+}

+ 110 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/CHANGELOG.md

@@ -0,0 +1,110 @@
+# CHANGELOG
+
+## 1.4.2 - 2017-03-20
+
+* Reverted BC break to `Uri::resolve` and `Uri::removeDotSegments` by removing 
+  calls to `trigger_error` when deprecated methods are invoked.
+
+## 1.4.1 - 2017-02-27
+
+* Reverted BC break by reintroducing behavior to automagically fix a URI with a
+  relative path and an authority by adding a leading slash to the path. It's only
+  deprecated now.
+* Added triggering of silenced deprecation warnings.
+
+## 1.4.0 - 2017-02-21
+
+* Fix `Stream::read` when length parameter <= 0.
+* `copy_to_stream` reads bytes in chunks instead of `maxLen` into memory.
+* Fix `ServerRequest::getUriFromGlobals` when `Host` header contains port.
+* Ensure `ServerRequest::getUriFromGlobals` returns a URI in absolute form.
+* Allow `parse_response` to parse a response without delimiting space and reason.
+* Ensure each URI modification results in a valid URI according to PSR-7 discussions.
+  Invalid modifications will throw an exception instead of returning a wrong URI or
+  doing some magic.
+  - `(new Uri)->withPath('foo')->withHost('example.com')` will throw an exception
+    because the path of a URI with an authority must start with a slash "/" or be empty
+  - `(new Uri())->withScheme('http')` will return `'http://localhost'`
+* Fix compatibility of URIs with `file` scheme and empty host.
+* Added common URI utility methods based on RFC 3986 (see documentation in the readme):
+  - `Uri::isDefaultPort`
+  - `Uri::isAbsolute`
+  - `Uri::isNetworkPathReference`
+  - `Uri::isAbsolutePathReference`
+  - `Uri::isRelativePathReference`
+  - `Uri::isSameDocumentReference`
+  - `Uri::composeComponents`
+  - `UriNormalizer::normalize`
+  - `UriNormalizer::isEquivalent`
+  - `UriResolver::relativize`
+* Deprecated `Uri::resolve` in favor of `UriResolver::resolve`
+* Deprecated `Uri::removeDotSegments` in favor of `UriResolver::removeDotSegments`
+
+## 1.3.1 - 2016-06-25
+
+* Fix `Uri::__toString` for network path references, e.g. `//example.org`.
+* Fix missing lowercase normalization for host.
+* Fix handling of URI components in case they are `'0'` in a lot of places,
+  e.g. as a user info password.
+* Fix `Uri::withAddedHeader` to correctly merge headers with different case.
+* Fix trimming of header values in `Uri::withAddedHeader`. Header values may
+  be surrounded by whitespace which should be ignored according to RFC 7230
+  Section 3.2.4. This does not apply to header names.
+* Fix `Uri::withAddedHeader` with an array of header values.
+* Fix `Uri::resolve` when base path has no slash and handling of fragment.
+* Fix handling of encoding in `Uri::with(out)QueryValue` so one can pass the
+  key/value both in encoded as well as decoded form to those methods. This is
+  consistent with withPath, withQuery etc.
+* Fix `ServerRequest::withoutAttribute` when attribute value is null.
+
+## 1.3.0 - 2016-04-13
+
+* Added remaining interfaces needed for full PSR7 compatibility
+  (ServerRequestInterface, UploadedFileInterface, etc.).
+* Added support for stream_for from scalars.
+* Can now extend Uri.
+* Fixed a bug in validating request methods by making it more permissive.
+
+## 1.2.3 - 2016-02-18
+
+* Fixed support in `GuzzleHttp\Psr7\CachingStream` for seeking forward on remote
+  streams, which can sometimes return fewer bytes than requested with `fread`.
+* Fixed handling of gzipped responses with FNAME headers.
+
+## 1.2.2 - 2016-01-22
+
+* Added support for URIs without any authority.
+* Added support for HTTP 451 'Unavailable For Legal Reasons.'
+* Added support for using '0' as a filename.
+* Added support for including non-standard ports in Host headers.
+
+## 1.2.1 - 2015-11-02
+
+* Now supporting negative offsets when seeking to SEEK_END.
+
+## 1.2.0 - 2015-08-15
+
+* Body as `"0"` is now properly added to a response.
+* Now allowing forward seeking in CachingStream.
+* Now properly parsing HTTP requests that contain proxy targets in
+  `parse_request`.
+* functions.php is now conditionally required.
+* user-info is no longer dropped when resolving URIs.
+
+## 1.1.0 - 2015-06-24
+
+* URIs can now be relative.
+* `multipart/form-data` headers are now overridden case-insensitively.
+* URI paths no longer encode the following characters because they are allowed
+  in URIs: "(", ")", "*", "!", "'"
+* A port is no longer added to a URI when the scheme is missing and no port is
+  present.
+
+## 1.0.0 - 2015-05-19
+
+Initial release.
+
+Currently unsupported:
+
+- `Psr\Http\Message\ServerRequestInterface`
+- `Psr\Http\Message\UploadedFileInterface`

+ 19 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2015 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 739 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/README.md

@@ -0,0 +1,739 @@
+# PSR-7 Message Implementation
+
+This repository contains a full [PSR-7](http://www.php-fig.org/psr/psr-7/)
+message implementation, several stream decorators, and some helpful
+functionality like query string parsing.
+
+
+[![Build Status](https://travis-ci.org/guzzle/psr7.svg?branch=master)](https://travis-ci.org/guzzle/psr7)
+
+
+# Stream implementation
+
+This package comes with a number of stream implementations and stream
+decorators.
+
+
+## AppendStream
+
+`GuzzleHttp\Psr7\AppendStream`
+
+Reads from multiple streams, one after the other.
+
+```php
+use GuzzleHttp\Psr7;
+
+$a = Psr7\stream_for('abc, ');
+$b = Psr7\stream_for('123.');
+$composed = new Psr7\AppendStream([$a, $b]);
+
+$composed->addStream(Psr7\stream_for(' Above all listen to me'));
+
+echo $composed; // abc, 123. Above all listen to me.
+```
+
+
+## BufferStream
+
+`GuzzleHttp\Psr7\BufferStream`
+
+Provides a buffer stream that can be written to fill a buffer, and read
+from to remove bytes from the buffer.
+
+This stream returns a "hwm" metadata value that tells upstream consumers
+what the configured high water mark of the stream is, or the maximum
+preferred size of the buffer.
+
+```php
+use GuzzleHttp\Psr7;
+
+// When more than 1024 bytes are in the buffer, it will begin returning
+// false to writes. This is an indication that writers should slow down.
+$buffer = new Psr7\BufferStream(1024);
+```
+
+
+## CachingStream
+
+The CachingStream is used to allow seeking over previously read bytes on
+non-seekable streams. This can be useful when transferring a non-seekable
+entity body fails due to needing to rewind the stream (for example, resulting
+from a redirect). Data that is read from the remote stream will be buffered in
+a PHP temp stream so that previously read bytes are cached first in memory,
+then on disk.
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\stream_for(fopen('http://www.google.com', 'r'));
+$stream = new Psr7\CachingStream($original);
+
+$stream->read(1024);
+echo $stream->tell();
+// 1024
+
+$stream->seek(0);
+echo $stream->tell();
+// 0
+```
+
+
+## DroppingStream
+
+`GuzzleHttp\Psr7\DroppingStream`
+
+Stream decorator that begins dropping data once the size of the underlying
+stream becomes too full.
+
+```php
+use GuzzleHttp\Psr7;
+
+// Create an empty stream
+$stream = Psr7\stream_for();
+
+// Start dropping data when the stream has more than 10 bytes
+$dropping = new Psr7\DroppingStream($stream, 10);
+
+$dropping->write('01234567890123456789');
+echo $stream; // 0123456789
+```
+
+
+## FnStream
+
+`GuzzleHttp\Psr7\FnStream`
+
+Compose stream implementations based on a hash of functions.
+
+Allows for easy testing and extension of a provided stream without needing
+to create a concrete class for a simple extension point.
+
+```php
+
+use GuzzleHttp\Psr7;
+
+$stream = Psr7\stream_for('hi');
+$fnStream = Psr7\FnStream::decorate($stream, [
+    'rewind' => function () use ($stream) {
+        echo 'About to rewind - ';
+        $stream->rewind();
+        echo 'rewound!';
+    }
+]);
+
+$fnStream->rewind();
+// Outputs: About to rewind - rewound!
+```
+
+
+## InflateStream
+
+`GuzzleHttp\Psr7\InflateStream`
+
+Uses PHP's zlib.inflate filter to inflate deflate or gzipped content.
+
+This stream decorator skips the first 10 bytes of the given stream to remove
+the gzip header, converts the provided stream to a PHP stream resource,
+then appends the zlib.inflate filter. The stream is then converted back
+to a Guzzle stream resource to be used as a Guzzle stream.
+
+
+## LazyOpenStream
+
+`GuzzleHttp\Psr7\LazyOpenStream`
+
+Lazily reads or writes to a file that is opened only after an IO operation
+take place on the stream.
+
+```php
+use GuzzleHttp\Psr7;
+
+$stream = new Psr7\LazyOpenStream('/path/to/file', 'r');
+// The file has not yet been opened...
+
+echo $stream->read(10);
+// The file is opened and read from only when needed.
+```
+
+
+## LimitStream
+
+`GuzzleHttp\Psr7\LimitStream`
+
+LimitStream can be used to read a subset or slice of an existing stream object.
+This can be useful for breaking a large file into smaller pieces to be sent in
+chunks (e.g. Amazon S3's multipart upload API).
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\stream_for(fopen('/tmp/test.txt', 'r+'));
+echo $original->getSize();
+// >>> 1048576
+
+// Limit the size of the body to 1024 bytes and start reading from byte 2048
+$stream = new Psr7\LimitStream($original, 1024, 2048);
+echo $stream->getSize();
+// >>> 1024
+echo $stream->tell();
+// >>> 0
+```
+
+
+## MultipartStream
+
+`GuzzleHttp\Psr7\MultipartStream`
+
+Stream that when read returns bytes for a streaming multipart or
+multipart/form-data stream.
+
+
+## NoSeekStream
+
+`GuzzleHttp\Psr7\NoSeekStream`
+
+NoSeekStream wraps a stream and does not allow seeking.
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\stream_for('foo');
+$noSeek = new Psr7\NoSeekStream($original);
+
+echo $noSeek->read(3);
+// foo
+var_export($noSeek->isSeekable());
+// false
+$noSeek->seek(0);
+var_export($noSeek->read(3));
+// NULL
+```
+
+
+## PumpStream
+
+`GuzzleHttp\Psr7\PumpStream`
+
+Provides a read only stream that pumps data from a PHP callable.
+
+When invoking the provided callable, the PumpStream will pass the amount of
+data requested to read to the callable. The callable can choose to ignore
+this value and return fewer or more bytes than requested. Any extra data
+returned by the provided callable is buffered internally until drained using
+the read() function of the PumpStream. The provided callable MUST return
+false when there is no more data to read.
+
+
+## Implementing stream decorators
+
+Creating a stream decorator is very easy thanks to the
+`GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that
+implement `Psr\Http\Message\StreamInterface` by proxying to an underlying
+stream. Just `use` the `StreamDecoratorTrait` and implement your custom
+methods.
+
+For example, let's say we wanted to call a specific function each time the last
+byte is read from a stream. This could be implemented by overriding the
+`read()` method.
+
+```php
+use Psr\Http\Message\StreamInterface;
+use GuzzleHttp\Psr7\StreamDecoratorTrait;
+
+class EofCallbackStream implements StreamInterface
+{
+    use StreamDecoratorTrait;
+
+    private $callback;
+
+    public function __construct(StreamInterface $stream, callable $cb)
+    {
+        $this->stream = $stream;
+        $this->callback = $cb;
+    }
+
+    public function read($length)
+    {
+        $result = $this->stream->read($length);
+
+        // Invoke the callback when EOF is hit.
+        if ($this->eof()) {
+            call_user_func($this->callback);
+        }
+
+        return $result;
+    }
+}
+```
+
+This decorator could be added to any existing stream and used like so:
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\stream_for('foo');
+
+$eofStream = new EofCallbackStream($original, function () {
+    echo 'EOF!';
+});
+
+$eofStream->read(2);
+$eofStream->read(1);
+// echoes "EOF!"
+$eofStream->seek(0);
+$eofStream->read(3);
+// echoes "EOF!"
+```
+
+
+## PHP StreamWrapper
+
+You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a
+PSR-7 stream as a PHP stream resource.
+
+Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP
+stream from a PSR-7 stream.
+
+```php
+use GuzzleHttp\Psr7\StreamWrapper;
+
+$stream = GuzzleHttp\Psr7\stream_for('hello!');
+$resource = StreamWrapper::getResource($stream);
+echo fread($resource, 6); // outputs hello!
+```
+
+
+# Function API
+
+There are various functions available under the `GuzzleHttp\Psr7` namespace.
+
+
+## `function str`
+
+`function str(MessageInterface $message)`
+
+Returns the string representation of an HTTP message.
+
+```php
+$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com');
+echo GuzzleHttp\Psr7\str($request);
+```
+
+
+## `function uri_for`
+
+`function uri_for($uri)`
+
+This function accepts a string or `Psr\Http\Message\UriInterface` and returns a
+UriInterface for the given value. If the value is already a `UriInterface`, it
+is returned as-is.
+
+```php
+$uri = GuzzleHttp\Psr7\uri_for('http://example.com');
+assert($uri === GuzzleHttp\Psr7\uri_for($uri));
+```
+
+
+## `function stream_for`
+
+`function stream_for($resource = '', array $options = [])`
+
+Create a new stream based on the input type.
+
+Options is an associative array that can contain the following keys:
+
+* - metadata: Array of custom metadata.
+* - size: Size of the stream.
+
+This method accepts the following `$resource` types:
+
+- `Psr\Http\Message\StreamInterface`: Returns the value as-is.
+- `string`: Creates a stream object that uses the given string as the contents.
+- `resource`: Creates a stream object that wraps the given PHP stream resource.
+- `Iterator`: If the provided value implements `Iterator`, then a read-only
+  stream object will be created that wraps the given iterable. Each time the
+  stream is read from, data from the iterator will fill a buffer and will be
+  continuously called until the buffer is equal to the requested read size.
+  Subsequent read calls will first read from the buffer and then call `next`
+  on the underlying iterator until it is exhausted.
+- `object` with `__toString()`: If the object has the `__toString()` method,
+  the object will be cast to a string and then a stream will be returned that
+  uses the string value.
+- `NULL`: When `null` is passed, an empty stream object is returned.
+- `callable` When a callable is passed, a read-only stream object will be
+  created that invokes the given callable. The callable is invoked with the
+  number of suggested bytes to read. The callable can return any number of
+  bytes, but MUST return `false` when there is no more data to return. The
+  stream object that wraps the callable will invoke the callable until the
+  number of requested bytes are available. Any additional bytes will be
+  buffered and used in subsequent reads.
+
+```php
+$stream = GuzzleHttp\Psr7\stream_for('foo');
+$stream = GuzzleHttp\Psr7\stream_for(fopen('/path/to/file', 'r'));
+
+$generator function ($bytes) {
+    for ($i = 0; $i < $bytes; $i++) {
+        yield ' ';
+    }
+}
+
+$stream = GuzzleHttp\Psr7\stream_for($generator(100));
+```
+
+
+## `function parse_header`
+
+`function parse_header($header)`
+
+Parse an array of header values containing ";" separated data into an array of
+associative arrays representing the header key value pair data of the header.
+When a parameter does not contain a value, but just contains a key, this
+function will inject a key with a '' string value.
+
+
+## `function normalize_header`
+
+`function normalize_header($header)`
+
+Converts an array of header values that may contain comma separated headers
+into an array of headers with no comma separated values.
+
+
+## `function modify_request`
+
+`function modify_request(RequestInterface $request, array $changes)`
+
+Clone and modify a request with the given changes. This method is useful for
+reducing the number of clones needed to mutate a message.
+
+The changes can be one of:
+
+- method: (string) Changes the HTTP method.
+- set_headers: (array) Sets the given headers.
+- remove_headers: (array) Remove the given headers.
+- body: (mixed) Sets the given body.
+- uri: (UriInterface) Set the URI.
+- query: (string) Set the query string value of the URI.
+- version: (string) Set the protocol version.
+
+
+## `function rewind_body`
+
+`function rewind_body(MessageInterface $message)`
+
+Attempts to rewind a message body and throws an exception on failure. The body
+of the message will only be rewound if a call to `tell()` returns a value other
+than `0`.
+
+
+## `function try_fopen`
+
+`function try_fopen($filename, $mode)`
+
+Safely opens a PHP stream resource using a filename.
+
+When fopen fails, PHP normally raises a warning. This function adds an error
+handler that checks for errors and throws an exception instead.
+
+
+## `function copy_to_string`
+
+`function copy_to_string(StreamInterface $stream, $maxLen = -1)`
+
+Copy the contents of a stream into a string until the given number of bytes
+have been read.
+
+
+## `function copy_to_stream`
+
+`function copy_to_stream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)`
+
+Copy the contents of a stream into another stream until the given number of
+bytes have been read.
+
+
+## `function hash`
+
+`function hash(StreamInterface $stream, $algo, $rawOutput = false)`
+
+Calculate a hash of a Stream. This method reads the entire stream to calculate
+a rolling hash (based on PHP's hash_init functions).
+
+
+## `function readline`
+
+`function readline(StreamInterface $stream, $maxLength = null)`
+
+Read a line from the stream up to the maximum allowed buffer length.
+
+
+## `function parse_request`
+
+`function parse_request($message)`
+
+Parses a request message string into a request object.
+
+
+## `function parse_response`
+
+`function parse_response($message)`
+
+Parses a response message string into a response object.
+
+
+## `function parse_query`
+
+`function parse_query($str, $urlEncoding = true)`
+
+Parse a query string into an associative array.
+
+If multiple values are found for the same key, the value of that key value pair
+will become an array. This function does not parse nested PHP style arrays into
+an associative array (e.g., `foo[a]=1&foo[b]=2` will be parsed into
+`['foo[a]' => '1', 'foo[b]' => '2']`).
+
+
+## `function build_query`
+
+`function build_query(array $params, $encoding = PHP_QUERY_RFC3986)`
+
+Build a query string from an array of key value pairs.
+
+This function can use the return value of parse_query() to build a query string.
+This function does not modify the provided keys when an array is encountered
+(like http_build_query would).
+
+
+## `function mimetype_from_filename`
+
+`function mimetype_from_filename($filename)`
+
+Determines the mimetype of a file by looking at its extension.
+
+
+## `function mimetype_from_extension`
+
+`function mimetype_from_extension($extension)`
+
+Maps a file extensions to a mimetype.
+
+
+# Additional URI Methods
+
+Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class,
+this library also provides additional functionality when working with URIs as static methods.
+
+## URI Types
+
+An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference.
+An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI,
+the base URI. Relative references can be divided into several forms according to
+[RFC 3986 Section 4.2](https://tools.ietf.org/html/rfc3986#section-4.2):
+
+- network-path references, e.g. `//example.com/path`
+- absolute-path references, e.g. `/path`
+- relative-path references, e.g. `subpath`
+
+The following methods can be used to identify the type of the URI.
+
+### `GuzzleHttp\Psr7\Uri::isAbsolute`
+
+`public static function isAbsolute(UriInterface $uri): bool`
+
+Whether the URI is absolute, i.e. it has a scheme.
+
+### `GuzzleHttp\Psr7\Uri::isNetworkPathReference`
+
+`public static function isNetworkPathReference(UriInterface $uri): bool`
+
+Whether the URI is a network-path reference. A relative reference that begins with two slash characters is
+termed an network-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isAbsolutePathReference`
+
+`public static function isAbsolutePathReference(UriInterface $uri): bool`
+
+Whether the URI is a absolute-path reference. A relative reference that begins with a single slash character is
+termed an absolute-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isRelativePathReference`
+
+`public static function isRelativePathReference(UriInterface $uri): bool`
+
+Whether the URI is a relative-path reference. A relative reference that does not begin with a slash character is
+termed a relative-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isSameDocumentReference`
+
+`public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool`
+
+Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its
+fragment component, identical to the base URI. When no base URI is given, only an empty URI reference
+(apart from its fragment) is considered a same-document reference.
+
+## URI Components
+
+Additional methods to work with URI components.
+
+### `GuzzleHttp\Psr7\Uri::isDefaultPort`
+
+`public static function isDefaultPort(UriInterface $uri): bool`
+
+Whether the URI has the default port of the current scheme. `Psr\Http\Message\UriInterface::getPort` may return null
+or the standard port. This method can be used independently of the implementation.
+
+### `GuzzleHttp\Psr7\Uri::composeComponents`
+
+`public static function composeComponents($scheme, $authority, $path, $query, $fragment): string`
+
+Composes a URI reference string from its various components according to
+[RFC 3986 Section 5.3](https://tools.ietf.org/html/rfc3986#section-5.3). Usually this method does not need to be called
+manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`.
+
+### `GuzzleHttp\Psr7\Uri::fromParts`
+
+`public static function fromParts(array $parts): UriInterface`
+
+Creates a URI from a hash of [`parse_url`](http://php.net/manual/en/function.parse-url.php) components.
+
+
+### `GuzzleHttp\Psr7\Uri::withQueryValue`
+
+`public static function withQueryValue(UriInterface $uri, $key, $value): UriInterface`
+
+Creates a new URI with a specific query string value. Any existing query string values that exactly match the
+provided key are removed and replaced with the given key value pair. A value of null will set the query string
+key without a value, e.g. "key" instead of "key=value".
+
+
+### `GuzzleHttp\Psr7\Uri::withoutQueryValue`
+
+`public static function withoutQueryValue(UriInterface $uri, $key): UriInterface`
+
+Creates a new URI with a specific query string value removed. Any existing query string values that exactly match the
+provided key are removed.
+
+## Reference Resolution
+
+`GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according
+to [RFC 3986 Section 5](https://tools.ietf.org/html/rfc3986#section-5). This is for example also what web browsers
+do when resolving a link in a website based on the current request URI.
+
+### `GuzzleHttp\Psr7\UriResolver::resolve`
+
+`public static function resolve(UriInterface $base, UriInterface $rel): UriInterface`
+
+Converts the relative URI into a new URI that is resolved against the base URI.
+
+### `GuzzleHttp\Psr7\UriResolver::removeDotSegments`
+
+`public static function removeDotSegments(string $path): string`
+
+Removes dot segments from a path and returns the new path according to
+[RFC 3986 Section 5.2.4](https://tools.ietf.org/html/rfc3986#section-5.2.4).
+
+### `GuzzleHttp\Psr7\UriResolver::relativize`
+
+`public static function relativize(UriInterface $base, UriInterface $target): UriInterface`
+
+Returns the target URI as a relative reference from the base URI. This method is the counterpart to resolve():
+
+```php
+(string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
+```
+
+One use-case is to use the current request URI as base URI and then generate relative links in your documents
+to reduce the document size or offer self-contained downloadable document archives.
+
+```php
+$base = new Uri('http://example.com/a/b/');
+echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c'));  // prints 'c'.
+echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y'));  // prints '../x/y'.
+echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
+echo UriResolver::relativize($base, new Uri('http://example.org/a/b/'));   // prints '//example.org/a/b/'.
+```
+
+## Normalization and Comparison
+
+`GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to
+[RFC 3986 Section 6](https://tools.ietf.org/html/rfc3986#section-6).
+
+### `GuzzleHttp\Psr7\UriNormalizer::normalize`
+
+`public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS): UriInterface`
+
+Returns a normalized URI. The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
+This methods adds additional normalizations that can be configured with the `$flags` parameter which is a bitmask
+of normalizations to apply. The following normalizations are available:
+
+- `UriNormalizer::PRESERVING_NORMALIZATIONS`
+
+    Default normalizations which only include the ones that preserve semantics.
+
+- `UriNormalizer::CAPITALIZE_PERCENT_ENCODING`
+
+    All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.
+
+    Example: `http://example.org/a%c2%b1b` → `http://example.org/a%C2%B1b`
+
+- `UriNormalizer::DECODE_UNRESERVED_CHARACTERS`
+
+    Decodes percent-encoded octets of unreserved characters. For consistency, percent-encoded octets in the ranges of
+    ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should
+    not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved
+    characters by URI normalizers.
+
+    Example: `http://example.org/%7Eusern%61me/` → `http://example.org/~username/`
+
+- `UriNormalizer::CONVERT_EMPTY_PATH`
+
+    Converts the empty path to "/" for http and https URIs.
+
+    Example: `http://example.org` → `http://example.org/`
+
+- `UriNormalizer::REMOVE_DEFAULT_HOST`
+
+    Removes the default host of the given URI scheme from the URI. Only the "file" scheme defines the default host
+    "localhost". All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile` are equivalent according to
+    RFC 3986.
+
+    Example: `file://localhost/myfile` → `file:///myfile`
+
+- `UriNormalizer::REMOVE_DEFAULT_PORT`
+
+    Removes the default port of the given URI scheme from the URI.
+
+    Example: `http://example.org:80/` → `http://example.org/`
+
+- `UriNormalizer::REMOVE_DOT_SEGMENTS`
+
+    Removes unnecessary dot-segments. Dot-segments in relative-path references are not removed as it would
+    change the semantics of the URI reference.
+
+    Example: `http://example.org/../a/b/../c/./d.html` → `http://example.org/a/c/d.html`
+
+- `UriNormalizer::REMOVE_DUPLICATE_SLASHES`
+
+    Paths which include two or more adjacent slashes are converted to one. Webservers usually ignore duplicate slashes
+    and treat those URIs equivalent. But in theory those URIs do not need to be equivalent. So this normalization
+    may change the semantics. Encoded slashes (%2F) are not removed.
+
+    Example: `http://example.org//foo///bar.html` → `http://example.org/foo/bar.html`
+
+- `UriNormalizer::SORT_QUERY_PARAMETERS`
+
+    Sort query parameters with their values in alphabetical order. However, the order of parameters in a URI may be
+    significant (this is not defined by the standard). So this normalization is not safe and may change the semantics
+    of the URI.
+
+    Example: `?lang=en&article=fred` → `?article=fred&lang=en`
+
+### `GuzzleHttp\Psr7\UriNormalizer::isEquivalent`
+
+`public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS): bool`
+
+Whether two URIs can be considered equivalent. Both URIs are normalized automatically before comparison with the given
+`$normalizations` bitmask. The method also accepts relative URI references and returns true when they are equivalent.
+This of course assumes they will be resolved against the same base URI. If this is not the case, determination of
+equivalence or difference of relative references does not mean anything.

+ 39 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/composer.json

@@ -0,0 +1,39 @@
+{
+    "name": "guzzlehttp/psr7",
+    "type": "library",
+    "description": "PSR-7 message implementation that also provides common utility methods",
+    "keywords": ["request", "response", "message", "stream", "http", "uri", "url"],
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Michael Dowling",
+            "email": "mtdowling@gmail.com",
+            "homepage": "https://github.com/mtdowling"
+        },
+        {
+            "name": "Tobias Schultze",
+            "homepage": "https://github.com/Tobion"
+        }
+    ],
+    "require": {
+        "php": ">=5.4.0",
+        "psr/http-message": "~1.0"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "~4.0"
+    },
+    "provide": {
+        "psr/http-message-implementation": "1.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "GuzzleHttp\\Psr7\\": "src/"
+        },
+        "files": ["src/functions_include.php"]
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.4-dev"
+        }
+    }
+}

+ 233 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/AppendStream.php

@@ -0,0 +1,233 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Reads from multiple streams, one after the other.
+ *
+ * This is a read-only stream decorator.
+ */
+class AppendStream implements StreamInterface
+{
+    /** @var StreamInterface[] Streams being decorated */
+    private $streams = [];
+
+    private $seekable = true;
+    private $current = 0;
+    private $pos = 0;
+    private $detached = false;
+
+    /**
+     * @param StreamInterface[] $streams Streams to decorate. Each stream must
+     *                                   be readable.
+     */
+    public function __construct(array $streams = [])
+    {
+        foreach ($streams as $stream) {
+            $this->addStream($stream);
+        }
+    }
+
+    public function __toString()
+    {
+        try {
+            $this->rewind();
+            return $this->getContents();
+        } catch (\Exception $e) {
+            return '';
+        }
+    }
+
+    /**
+     * Add a stream to the AppendStream
+     *
+     * @param StreamInterface $stream Stream to append. Must be readable.
+     *
+     * @throws \InvalidArgumentException if the stream is not readable
+     */
+    public function addStream(StreamInterface $stream)
+    {
+        if (!$stream->isReadable()) {
+            throw new \InvalidArgumentException('Each stream must be readable');
+        }
+
+        // The stream is only seekable if all streams are seekable
+        if (!$stream->isSeekable()) {
+            $this->seekable = false;
+        }
+
+        $this->streams[] = $stream;
+    }
+
+    public function getContents()
+    {
+        return copy_to_string($this);
+    }
+
+    /**
+     * Closes each attached stream.
+     *
+     * {@inheritdoc}
+     */
+    public function close()
+    {
+        $this->pos = $this->current = 0;
+
+        foreach ($this->streams as $stream) {
+            $stream->close();
+        }
+
+        $this->streams = [];
+    }
+
+    /**
+     * Detaches each attached stream
+     *
+     * {@inheritdoc}
+     */
+    public function detach()
+    {
+        $this->close();
+        $this->detached = true;
+    }
+
+    public function tell()
+    {
+        return $this->pos;
+    }
+
+    /**
+     * Tries to calculate the size by adding the size of each stream.
+     *
+     * If any of the streams do not return a valid number, then the size of the
+     * append stream cannot be determined and null is returned.
+     *
+     * {@inheritdoc}
+     */
+    public function getSize()
+    {
+        $size = 0;
+
+        foreach ($this->streams as $stream) {
+            $s = $stream->getSize();
+            if ($s === null) {
+                return null;
+            }
+            $size += $s;
+        }
+
+        return $size;
+    }
+
+    public function eof()
+    {
+        return !$this->streams ||
+            ($this->current >= count($this->streams) - 1 &&
+             $this->streams[$this->current]->eof());
+    }
+
+    public function rewind()
+    {
+        $this->seek(0);
+    }
+
+    /**
+     * Attempts to seek to the given position. Only supports SEEK_SET.
+     *
+     * {@inheritdoc}
+     */
+    public function seek($offset, $whence = SEEK_SET)
+    {
+        if (!$this->seekable) {
+            throw new \RuntimeException('This AppendStream is not seekable');
+        } elseif ($whence !== SEEK_SET) {
+            throw new \RuntimeException('The AppendStream can only seek with SEEK_SET');
+        }
+
+        $this->pos = $this->current = 0;
+
+        // Rewind each stream
+        foreach ($this->streams as $i => $stream) {
+            try {
+                $stream->rewind();
+            } catch (\Exception $e) {
+                throw new \RuntimeException('Unable to seek stream '
+                    . $i . ' of the AppendStream', 0, $e);
+            }
+        }
+
+        // Seek to the actual position by reading from each stream
+        while ($this->pos < $offset && !$this->eof()) {
+            $result = $this->read(min(8096, $offset - $this->pos));
+            if ($result === '') {
+                break;
+            }
+        }
+    }
+
+    /**
+     * Reads from all of the appended streams until the length is met or EOF.
+     *
+     * {@inheritdoc}
+     */
+    public function read($length)
+    {
+        $buffer = '';
+        $total = count($this->streams) - 1;
+        $remaining = $length;
+        $progressToNext = false;
+
+        while ($remaining > 0) {
+
+            // Progress to the next stream if needed.
+            if ($progressToNext || $this->streams[$this->current]->eof()) {
+                $progressToNext = false;
+                if ($this->current === $total) {
+                    break;
+                }
+                $this->current++;
+            }
+
+            $result = $this->streams[$this->current]->read($remaining);
+
+            // Using a loose comparison here to match on '', false, and null
+            if ($result == null) {
+                $progressToNext = true;
+                continue;
+            }
+
+            $buffer .= $result;
+            $remaining = $length - strlen($buffer);
+        }
+
+        $this->pos += strlen($buffer);
+
+        return $buffer;
+    }
+
+    public function isReadable()
+    {
+        return true;
+    }
+
+    public function isWritable()
+    {
+        return false;
+    }
+
+    public function isSeekable()
+    {
+        return $this->seekable;
+    }
+
+    public function write($string)
+    {
+        throw new \RuntimeException('Cannot write to an AppendStream');
+    }
+
+    public function getMetadata($key = null)
+    {
+        return $key ? null : [];
+    }
+}

+ 137 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/BufferStream.php

@@ -0,0 +1,137 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Provides a buffer stream that can be written to to fill a buffer, and read
+ * from to remove bytes from the buffer.
+ *
+ * This stream returns a "hwm" metadata value that tells upstream consumers
+ * what the configured high water mark of the stream is, or the maximum
+ * preferred size of the buffer.
+ */
+class BufferStream implements StreamInterface
+{
+    private $hwm;
+    private $buffer = '';
+
+    /**
+     * @param int $hwm High water mark, representing the preferred maximum
+     *                 buffer size. If the size of the buffer exceeds the high
+     *                 water mark, then calls to write will continue to succeed
+     *                 but will return false to inform writers to slow down
+     *                 until the buffer has been drained by reading from it.
+     */
+    public function __construct($hwm = 16384)
+    {
+        $this->hwm = $hwm;
+    }
+
+    public function __toString()
+    {
+        return $this->getContents();
+    }
+
+    public function getContents()
+    {
+        $buffer = $this->buffer;
+        $this->buffer = '';
+
+        return $buffer;
+    }
+
+    public function close()
+    {
+        $this->buffer = '';
+    }
+
+    public function detach()
+    {
+        $this->close();
+    }
+
+    public function getSize()
+    {
+        return strlen($this->buffer);
+    }
+
+    public function isReadable()
+    {
+        return true;
+    }
+
+    public function isWritable()
+    {
+        return true;
+    }
+
+    public function isSeekable()
+    {
+        return false;
+    }
+
+    public function rewind()
+    {
+        $this->seek(0);
+    }
+
+    public function seek($offset, $whence = SEEK_SET)
+    {
+        throw new \RuntimeException('Cannot seek a BufferStream');
+    }
+
+    public function eof()
+    {
+        return strlen($this->buffer) === 0;
+    }
+
+    public function tell()
+    {
+        throw new \RuntimeException('Cannot determine the position of a BufferStream');
+    }
+
+    /**
+     * Reads data from the buffer.
+     */
+    public function read($length)
+    {
+        $currentLength = strlen($this->buffer);
+
+        if ($length >= $currentLength) {
+            // No need to slice the buffer because we don't have enough data.
+            $result = $this->buffer;
+            $this->buffer = '';
+        } else {
+            // Slice up the result to provide a subset of the buffer.
+            $result = substr($this->buffer, 0, $length);
+            $this->buffer = substr($this->buffer, $length);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Writes data to the buffer.
+     */
+    public function write($string)
+    {
+        $this->buffer .= $string;
+
+        // TODO: What should happen here?
+        if (strlen($this->buffer) >= $this->hwm) {
+            return false;
+        }
+
+        return strlen($string);
+    }
+
+    public function getMetadata($key = null)
+    {
+        if ($key == 'hwm') {
+            return $this->hwm;
+        }
+
+        return $key ? null : [];
+    }
+}

+ 138 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/CachingStream.php

@@ -0,0 +1,138 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream decorator that can cache previously read bytes from a sequentially
+ * read stream.
+ */
+class CachingStream implements StreamInterface
+{
+    use StreamDecoratorTrait;
+
+    /** @var StreamInterface Stream being wrapped */
+    private $remoteStream;
+
+    /** @var int Number of bytes to skip reading due to a write on the buffer */
+    private $skipReadBytes = 0;
+
+    /**
+     * We will treat the buffer object as the body of the stream
+     *
+     * @param StreamInterface $stream Stream to cache
+     * @param StreamInterface $target Optionally specify where data is cached
+     */
+    public function __construct(
+        StreamInterface $stream,
+        StreamInterface $target = null
+    ) {
+        $this->remoteStream = $stream;
+        $this->stream = $target ?: new Stream(fopen('php://temp', 'r+'));
+    }
+
+    public function getSize()
+    {
+        return max($this->stream->getSize(), $this->remoteStream->getSize());
+    }
+
+    public function rewind()
+    {
+        $this->seek(0);
+    }
+
+    public function seek($offset, $whence = SEEK_SET)
+    {
+        if ($whence == SEEK_SET) {
+            $byte = $offset;
+        } elseif ($whence == SEEK_CUR) {
+            $byte = $offset + $this->tell();
+        } elseif ($whence == SEEK_END) {
+            $size = $this->remoteStream->getSize();
+            if ($size === null) {
+                $size = $this->cacheEntireStream();
+            }
+            $byte = $size + $offset;
+        } else {
+            throw new \InvalidArgumentException('Invalid whence');
+        }
+
+        $diff = $byte - $this->stream->getSize();
+
+        if ($diff > 0) {
+            // Read the remoteStream until we have read in at least the amount
+            // of bytes requested, or we reach the end of the file.
+            while ($diff > 0 && !$this->remoteStream->eof()) {
+                $this->read($diff);
+                $diff = $byte - $this->stream->getSize();
+            }
+        } else {
+            // We can just do a normal seek since we've already seen this byte.
+            $this->stream->seek($byte);
+        }
+    }
+
+    public function read($length)
+    {
+        // Perform a regular read on any previously read data from the buffer
+        $data = $this->stream->read($length);
+        $remaining = $length - strlen($data);
+
+        // More data was requested so read from the remote stream
+        if ($remaining) {
+            // If data was written to the buffer in a position that would have
+            // been filled from the remote stream, then we must skip bytes on
+            // the remote stream to emulate overwriting bytes from that
+            // position. This mimics the behavior of other PHP stream wrappers.
+            $remoteData = $this->remoteStream->read(
+                $remaining + $this->skipReadBytes
+            );
+
+            if ($this->skipReadBytes) {
+                $len = strlen($remoteData);
+                $remoteData = substr($remoteData, $this->skipReadBytes);
+                $this->skipReadBytes = max(0, $this->skipReadBytes - $len);
+            }
+
+            $data .= $remoteData;
+            $this->stream->write($remoteData);
+        }
+
+        return $data;
+    }
+
+    public function write($string)
+    {
+        // When appending to the end of the currently read stream, you'll want
+        // to skip bytes from being read from the remote stream to emulate
+        // other stream wrappers. Basically replacing bytes of data of a fixed
+        // length.
+        $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell();
+        if ($overflow > 0) {
+            $this->skipReadBytes += $overflow;
+        }
+
+        return $this->stream->write($string);
+    }
+
+    public function eof()
+    {
+        return $this->stream->eof() && $this->remoteStream->eof();
+    }
+
+    /**
+     * Close both the remote stream and buffer stream
+     */
+    public function close()
+    {
+        $this->remoteStream->close() && $this->stream->close();
+    }
+
+    private function cacheEntireStream()
+    {
+        $target = new FnStream(['write' => 'strlen']);
+        copy_to_stream($this, $target);
+
+        return $this->tell();
+    }
+}

+ 42 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/DroppingStream.php

@@ -0,0 +1,42 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream decorator that begins dropping data once the size of the underlying
+ * stream becomes too full.
+ */
+class DroppingStream implements StreamInterface
+{
+    use StreamDecoratorTrait;
+
+    private $maxLength;
+
+    /**
+     * @param StreamInterface $stream    Underlying stream to decorate.
+     * @param int             $maxLength Maximum size before dropping data.
+     */
+    public function __construct(StreamInterface $stream, $maxLength)
+    {
+        $this->stream = $stream;
+        $this->maxLength = $maxLength;
+    }
+
+    public function write($string)
+    {
+        $diff = $this->maxLength - $this->stream->getSize();
+
+        // Begin returning 0 when the underlying stream is too large.
+        if ($diff <= 0) {
+            return 0;
+        }
+
+        // Write the stream or a subset of the stream if needed.
+        if (strlen($string) < $diff) {
+            return $this->stream->write($string);
+        }
+
+        return $this->stream->write(substr($string, 0, $diff));
+    }
+}

+ 149 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/FnStream.php

@@ -0,0 +1,149 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Compose stream implementations based on a hash of functions.
+ *
+ * Allows for easy testing and extension of a provided stream without needing
+ * to create a concrete class for a simple extension point.
+ */
+class FnStream implements StreamInterface
+{
+    /** @var array */
+    private $methods;
+
+    /** @var array Methods that must be implemented in the given array */
+    private static $slots = ['__toString', 'close', 'detach', 'rewind',
+        'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write',
+        'isReadable', 'read', 'getContents', 'getMetadata'];
+
+    /**
+     * @param array $methods Hash of method name to a callable.
+     */
+    public function __construct(array $methods)
+    {
+        $this->methods = $methods;
+
+        // Create the functions on the class
+        foreach ($methods as $name => $fn) {
+            $this->{'_fn_' . $name} = $fn;
+        }
+    }
+
+    /**
+     * Lazily determine which methods are not implemented.
+     * @throws \BadMethodCallException
+     */
+    public function __get($name)
+    {
+        throw new \BadMethodCallException(str_replace('_fn_', '', $name)
+            . '() is not implemented in the FnStream');
+    }
+
+    /**
+     * The close method is called on the underlying stream only if possible.
+     */
+    public function __destruct()
+    {
+        if (isset($this->_fn_close)) {
+            call_user_func($this->_fn_close);
+        }
+    }
+
+    /**
+     * Adds custom functionality to an underlying stream by intercepting
+     * specific method calls.
+     *
+     * @param StreamInterface $stream  Stream to decorate
+     * @param array           $methods Hash of method name to a closure
+     *
+     * @return FnStream
+     */
+    public static function decorate(StreamInterface $stream, array $methods)
+    {
+        // If any of the required methods were not provided, then simply
+        // proxy to the decorated stream.
+        foreach (array_diff(self::$slots, array_keys($methods)) as $diff) {
+            $methods[$diff] = [$stream, $diff];
+        }
+
+        return new self($methods);
+    }
+
+    public function __toString()
+    {
+        return call_user_func($this->_fn___toString);
+    }
+
+    public function close()
+    {
+        return call_user_func($this->_fn_close);
+    }
+
+    public function detach()
+    {
+        return call_user_func($this->_fn_detach);
+    }
+
+    public function getSize()
+    {
+        return call_user_func($this->_fn_getSize);
+    }
+
+    public function tell()
+    {
+        return call_user_func($this->_fn_tell);
+    }
+
+    public function eof()
+    {
+        return call_user_func($this->_fn_eof);
+    }
+
+    public function isSeekable()
+    {
+        return call_user_func($this->_fn_isSeekable);
+    }
+
+    public function rewind()
+    {
+        call_user_func($this->_fn_rewind);
+    }
+
+    public function seek($offset, $whence = SEEK_SET)
+    {
+        call_user_func($this->_fn_seek, $offset, $whence);
+    }
+
+    public function isWritable()
+    {
+        return call_user_func($this->_fn_isWritable);
+    }
+
+    public function write($string)
+    {
+        return call_user_func($this->_fn_write, $string);
+    }
+
+    public function isReadable()
+    {
+        return call_user_func($this->_fn_isReadable);
+    }
+
+    public function read($length)
+    {
+        return call_user_func($this->_fn_read, $length);
+    }
+
+    public function getContents()
+    {
+        return call_user_func($this->_fn_getContents);
+    }
+
+    public function getMetadata($key = null)
+    {
+        return call_user_func($this->_fn_getMetadata, $key);
+    }
+}

+ 52 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/InflateStream.php

@@ -0,0 +1,52 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Uses PHP's zlib.inflate filter to inflate deflate or gzipped content.
+ *
+ * This stream decorator skips the first 10 bytes of the given stream to remove
+ * the gzip header, converts the provided stream to a PHP stream resource,
+ * then appends the zlib.inflate filter. The stream is then converted back
+ * to a Guzzle stream resource to be used as a Guzzle stream.
+ *
+ * @link http://tools.ietf.org/html/rfc1952
+ * @link http://php.net/manual/en/filters.compression.php
+ */
+class InflateStream implements StreamInterface
+{
+    use StreamDecoratorTrait;
+
+    public function __construct(StreamInterface $stream)
+    {
+        // read the first 10 bytes, ie. gzip header
+        $header = $stream->read(10);
+        $filenameHeaderLength = $this->getLengthOfPossibleFilenameHeader($stream, $header);
+        // Skip the header, that is 10 + length of filename + 1 (nil) bytes
+        $stream = new LimitStream($stream, -1, 10 + $filenameHeaderLength);
+        $resource = StreamWrapper::getResource($stream);
+        stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ);
+        $this->stream = new Stream($resource);
+    }
+
+    /**
+     * @param StreamInterface $stream
+     * @param $header
+     * @return int
+     */
+    private function getLengthOfPossibleFilenameHeader(StreamInterface $stream, $header)
+    {
+        $filename_header_length = 0;
+
+        if (substr(bin2hex($header), 6, 2) === '08') {
+            // we have a filename, read until nil
+            $filename_header_length = 1;
+            while ($stream->read(1) !== chr(0)) {
+                $filename_header_length++;
+            }
+        }
+
+        return $filename_header_length;
+    }
+}

+ 39 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/LazyOpenStream.php

@@ -0,0 +1,39 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Lazily reads or writes to a file that is opened only after an IO operation
+ * take place on the stream.
+ */
+class LazyOpenStream implements StreamInterface
+{
+    use StreamDecoratorTrait;
+
+    /** @var string File to open */
+    private $filename;
+
+    /** @var string $mode */
+    private $mode;
+
+    /**
+     * @param string $filename File to lazily open
+     * @param string $mode     fopen mode to use when opening the stream
+     */
+    public function __construct($filename, $mode)
+    {
+        $this->filename = $filename;
+        $this->mode = $mode;
+    }
+
+    /**
+     * Creates the underlying stream lazily when required.
+     *
+     * @return StreamInterface
+     */
+    protected function createStream()
+    {
+        return stream_for(try_fopen($this->filename, $this->mode));
+    }
+}

+ 155 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/LimitStream.php

@@ -0,0 +1,155 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+
+/**
+ * Decorator used to return only a subset of a stream
+ */
+class LimitStream implements StreamInterface
+{
+    use StreamDecoratorTrait;
+
+    /** @var int Offset to start reading from */
+    private $offset;
+
+    /** @var int Limit the number of bytes that can be read */
+    private $limit;
+
+    /**
+     * @param StreamInterface $stream Stream to wrap
+     * @param int             $limit  Total number of bytes to allow to be read
+     *                                from the stream. Pass -1 for no limit.
+     * @param int             $offset Position to seek to before reading (only
+     *                                works on seekable streams).
+     */
+    public function __construct(
+        StreamInterface $stream,
+        $limit = -1,
+        $offset = 0
+    ) {
+        $this->stream = $stream;
+        $this->setLimit($limit);
+        $this->setOffset($offset);
+    }
+
+    public function eof()
+    {
+        // Always return true if the underlying stream is EOF
+        if ($this->stream->eof()) {
+            return true;
+        }
+
+        // No limit and the underlying stream is not at EOF
+        if ($this->limit == -1) {
+            return false;
+        }
+
+        return $this->stream->tell() >= $this->offset + $this->limit;
+    }
+
+    /**
+     * Returns the size of the limited subset of data
+     * {@inheritdoc}
+     */
+    public function getSize()
+    {
+        if (null === ($length = $this->stream->getSize())) {
+            return null;
+        } elseif ($this->limit == -1) {
+            return $length - $this->offset;
+        } else {
+            return min($this->limit, $length - $this->offset);
+        }
+    }
+
+    /**
+     * Allow for a bounded seek on the read limited stream
+     * {@inheritdoc}
+     */
+    public function seek($offset, $whence = SEEK_SET)
+    {
+        if ($whence !== SEEK_SET || $offset < 0) {
+            throw new \RuntimeException(sprintf(
+                'Cannot seek to offset % with whence %s',
+                $offset,
+                $whence
+            ));
+        }
+
+        $offset += $this->offset;
+
+        if ($this->limit !== -1) {
+            if ($offset > $this->offset + $this->limit) {
+                $offset = $this->offset + $this->limit;
+            }
+        }
+
+        $this->stream->seek($offset);
+    }
+
+    /**
+     * Give a relative tell()
+     * {@inheritdoc}
+     */
+    public function tell()
+    {
+        return $this->stream->tell() - $this->offset;
+    }
+
+    /**
+     * Set the offset to start limiting from
+     *
+     * @param int $offset Offset to seek to and begin byte limiting from
+     *
+     * @throws \RuntimeException if the stream cannot be seeked.
+     */
+    public function setOffset($offset)
+    {
+        $current = $this->stream->tell();
+
+        if ($current !== $offset) {
+            // If the stream cannot seek to the offset position, then read to it
+            if ($this->stream->isSeekable()) {
+                $this->stream->seek($offset);
+            } elseif ($current > $offset) {
+                throw new \RuntimeException("Could not seek to stream offset $offset");
+            } else {
+                $this->stream->read($offset - $current);
+            }
+        }
+
+        $this->offset = $offset;
+    }
+
+    /**
+     * Set the limit of bytes that the decorator allows to be read from the
+     * stream.
+     *
+     * @param int $limit Number of bytes to allow to be read from the stream.
+     *                   Use -1 for no limit.
+     */
+    public function setLimit($limit)
+    {
+        $this->limit = $limit;
+    }
+
+    public function read($length)
+    {
+        if ($this->limit == -1) {
+            return $this->stream->read($length);
+        }
+
+        // Check if the current position is less than the total allowed
+        // bytes + original offset
+        $remaining = ($this->offset + $this->limit) - $this->stream->tell();
+        if ($remaining > 0) {
+            // Only return the amount of requested data, ensuring that the byte
+            // limit is not exceeded
+            return $this->stream->read(min($remaining, $length));
+        }
+
+        return '';
+    }
+}

+ 183 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/MessageTrait.php

@@ -0,0 +1,183 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Trait implementing functionality common to requests and responses.
+ */
+trait MessageTrait
+{
+    /** @var array Map of all registered headers, as original name => array of values */
+    private $headers = [];
+
+    /** @var array Map of lowercase header name => original name at registration */
+    private $headerNames  = [];
+
+    /** @var string */
+    private $protocol = '1.1';
+
+    /** @var StreamInterface */
+    private $stream;
+
+    public function getProtocolVersion()
+    {
+        return $this->protocol;
+    }
+
+    public function withProtocolVersion($version)
+    {
+        if ($this->protocol === $version) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->protocol = $version;
+        return $new;
+    }
+
+    public function getHeaders()
+    {
+        return $this->headers;
+    }
+
+    public function hasHeader($header)
+    {
+        return isset($this->headerNames[strtolower($header)]);
+    }
+
+    public function getHeader($header)
+    {
+        $header = strtolower($header);
+
+        if (!isset($this->headerNames[$header])) {
+            return [];
+        }
+
+        $header = $this->headerNames[$header];
+
+        return $this->headers[$header];
+    }
+
+    public function getHeaderLine($header)
+    {
+        return implode(', ', $this->getHeader($header));
+    }
+
+    public function withHeader($header, $value)
+    {
+        if (!is_array($value)) {
+            $value = [$value];
+        }
+
+        $value = $this->trimHeaderValues($value);
+        $normalized = strtolower($header);
+
+        $new = clone $this;
+        if (isset($new->headerNames[$normalized])) {
+            unset($new->headers[$new->headerNames[$normalized]]);
+        }
+        $new->headerNames[$normalized] = $header;
+        $new->headers[$header] = $value;
+
+        return $new;
+    }
+
+    public function withAddedHeader($header, $value)
+    {
+        if (!is_array($value)) {
+            $value = [$value];
+        }
+
+        $value = $this->trimHeaderValues($value);
+        $normalized = strtolower($header);
+
+        $new = clone $this;
+        if (isset($new->headerNames[$normalized])) {
+            $header = $this->headerNames[$normalized];
+            $new->headers[$header] = array_merge($this->headers[$header], $value);
+        } else {
+            $new->headerNames[$normalized] = $header;
+            $new->headers[$header] = $value;
+        }
+
+        return $new;
+    }
+
+    public function withoutHeader($header)
+    {
+        $normalized = strtolower($header);
+
+        if (!isset($this->headerNames[$normalized])) {
+            return $this;
+        }
+
+        $header = $this->headerNames[$normalized];
+
+        $new = clone $this;
+        unset($new->headers[$header], $new->headerNames[$normalized]);
+
+        return $new;
+    }
+
+    public function getBody()
+    {
+        if (!$this->stream) {
+            $this->stream = stream_for('');
+        }
+
+        return $this->stream;
+    }
+
+    public function withBody(StreamInterface $body)
+    {
+        if ($body === $this->stream) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->stream = $body;
+        return $new;
+    }
+
+    private function setHeaders(array $headers)
+    {
+        $this->headerNames = $this->headers = [];
+        foreach ($headers as $header => $value) {
+            if (!is_array($value)) {
+                $value = [$value];
+            }
+
+            $value = $this->trimHeaderValues($value);
+            $normalized = strtolower($header);
+            if (isset($this->headerNames[$normalized])) {
+                $header = $this->headerNames[$normalized];
+                $this->headers[$header] = array_merge($this->headers[$header], $value);
+            } else {
+                $this->headerNames[$normalized] = $header;
+                $this->headers[$header] = $value;
+            }
+        }
+    }
+
+    /**
+     * Trims whitespace from the header values.
+     *
+     * Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field.
+     *
+     * header-field = field-name ":" OWS field-value OWS
+     * OWS          = *( SP / HTAB )
+     *
+     * @param string[] $values Header values
+     *
+     * @return string[] Trimmed header values
+     *
+     * @see https://tools.ietf.org/html/rfc7230#section-3.2.4
+     */
+    private function trimHeaderValues(array $values)
+    {
+        return array_map(function ($value) {
+            return trim($value, " \t");
+        }, $values);
+    }
+}

+ 153 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/MultipartStream.php

@@ -0,0 +1,153 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream that when read returns bytes for a streaming multipart or
+ * multipart/form-data stream.
+ */
+class MultipartStream implements StreamInterface
+{
+    use StreamDecoratorTrait;
+
+    private $boundary;
+
+    /**
+     * @param array  $elements Array of associative arrays, each containing a
+     *                         required "name" key mapping to the form field,
+     *                         name, a required "contents" key mapping to a
+     *                         StreamInterface/resource/string, an optional
+     *                         "headers" associative array of custom headers,
+     *                         and an optional "filename" key mapping to a
+     *                         string to send as the filename in the part.
+     * @param string $boundary You can optionally provide a specific boundary
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function __construct(array $elements = [], $boundary = null)
+    {
+        $this->boundary = $boundary ?: sha1(uniqid('', true));
+        $this->stream = $this->createStream($elements);
+    }
+
+    /**
+     * Get the boundary
+     *
+     * @return string
+     */
+    public function getBoundary()
+    {
+        return $this->boundary;
+    }
+
+    public function isWritable()
+    {
+        return false;
+    }
+
+    /**
+     * Get the headers needed before transferring the content of a POST file
+     */
+    private function getHeaders(array $headers)
+    {
+        $str = '';
+        foreach ($headers as $key => $value) {
+            $str .= "{$key}: {$value}\r\n";
+        }
+
+        return "--{$this->boundary}\r\n" . trim($str) . "\r\n\r\n";
+    }
+
+    /**
+     * Create the aggregate stream that will be used to upload the POST data
+     */
+    protected function createStream(array $elements)
+    {
+        $stream = new AppendStream();
+
+        foreach ($elements as $element) {
+            $this->addElement($stream, $element);
+        }
+
+        // Add the trailing boundary with CRLF
+        $stream->addStream(stream_for("--{$this->boundary}--\r\n"));
+
+        return $stream;
+    }
+
+    private function addElement(AppendStream $stream, array $element)
+    {
+        foreach (['contents', 'name'] as $key) {
+            if (!array_key_exists($key, $element)) {
+                throw new \InvalidArgumentException("A '{$key}' key is required");
+            }
+        }
+
+        $element['contents'] = stream_for($element['contents']);
+
+        if (empty($element['filename'])) {
+            $uri = $element['contents']->getMetadata('uri');
+            if (substr($uri, 0, 6) !== 'php://') {
+                $element['filename'] = $uri;
+            }
+        }
+
+        list($body, $headers) = $this->createElement(
+            $element['name'],
+            $element['contents'],
+            isset($element['filename']) ? $element['filename'] : null,
+            isset($element['headers']) ? $element['headers'] : []
+        );
+
+        $stream->addStream(stream_for($this->getHeaders($headers)));
+        $stream->addStream($body);
+        $stream->addStream(stream_for("\r\n"));
+    }
+
+    /**
+     * @return array
+     */
+    private function createElement($name, StreamInterface $stream, $filename, array $headers)
+    {
+        // Set a default content-disposition header if one was no provided
+        $disposition = $this->getHeader($headers, 'content-disposition');
+        if (!$disposition) {
+            $headers['Content-Disposition'] = ($filename === '0' || $filename)
+                ? sprintf('form-data; name="%s"; filename="%s"',
+                    $name,
+                    basename($filename))
+                : "form-data; name=\"{$name}\"";
+        }
+
+        // Set a default content-length header if one was no provided
+        $length = $this->getHeader($headers, 'content-length');
+        if (!$length) {
+            if ($length = $stream->getSize()) {
+                $headers['Content-Length'] = (string) $length;
+            }
+        }
+
+        // Set a default Content-Type if one was not supplied
+        $type = $this->getHeader($headers, 'content-type');
+        if (!$type && ($filename === '0' || $filename)) {
+            if ($type = mimetype_from_filename($filename)) {
+                $headers['Content-Type'] = $type;
+            }
+        }
+
+        return [$stream, $headers];
+    }
+
+    private function getHeader(array $headers, $key)
+    {
+        $lowercaseHeader = strtolower($key);
+        foreach ($headers as $k => $v) {
+            if (strtolower($k) === $lowercaseHeader) {
+                return $v;
+            }
+        }
+
+        return null;
+    }
+}

+ 22 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/NoSeekStream.php

@@ -0,0 +1,22 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream decorator that prevents a stream from being seeked
+ */
+class NoSeekStream implements StreamInterface
+{
+    use StreamDecoratorTrait;
+
+    public function seek($offset, $whence = SEEK_SET)
+    {
+        throw new \RuntimeException('Cannot seek a NoSeekStream');
+    }
+
+    public function isSeekable()
+    {
+        return false;
+    }
+}

+ 165 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/PumpStream.php

@@ -0,0 +1,165 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Provides a read only stream that pumps data from a PHP callable.
+ *
+ * When invoking the provided callable, the PumpStream will pass the amount of
+ * data requested to read to the callable. The callable can choose to ignore
+ * this value and return fewer or more bytes than requested. Any extra data
+ * returned by the provided callable is buffered internally until drained using
+ * the read() function of the PumpStream. The provided callable MUST return
+ * false when there is no more data to read.
+ */
+class PumpStream implements StreamInterface
+{
+    /** @var callable */
+    private $source;
+
+    /** @var int */
+    private $size;
+
+    /** @var int */
+    private $tellPos = 0;
+
+    /** @var array */
+    private $metadata;
+
+    /** @var BufferStream */
+    private $buffer;
+
+    /**
+     * @param callable $source Source of the stream data. The callable MAY
+     *                         accept an integer argument used to control the
+     *                         amount of data to return. The callable MUST
+     *                         return a string when called, or false on error
+     *                         or EOF.
+     * @param array $options   Stream options:
+     *                         - metadata: Hash of metadata to use with stream.
+     *                         - size: Size of the stream, if known.
+     */
+    public function __construct(callable $source, array $options = [])
+    {
+        $this->source = $source;
+        $this->size = isset($options['size']) ? $options['size'] : null;
+        $this->metadata = isset($options['metadata']) ? $options['metadata'] : [];
+        $this->buffer = new BufferStream();
+    }
+
+    public function __toString()
+    {
+        try {
+            return copy_to_string($this);
+        } catch (\Exception $e) {
+            return '';
+        }
+    }
+
+    public function close()
+    {
+        $this->detach();
+    }
+
+    public function detach()
+    {
+        $this->tellPos = false;
+        $this->source = null;
+    }
+
+    public function getSize()
+    {
+        return $this->size;
+    }
+
+    public function tell()
+    {
+        return $this->tellPos;
+    }
+
+    public function eof()
+    {
+        return !$this->source;
+    }
+
+    public function isSeekable()
+    {
+        return false;
+    }
+
+    public function rewind()
+    {
+        $this->seek(0);
+    }
+
+    public function seek($offset, $whence = SEEK_SET)
+    {
+        throw new \RuntimeException('Cannot seek a PumpStream');
+    }
+
+    public function isWritable()
+    {
+        return false;
+    }
+
+    public function write($string)
+    {
+        throw new \RuntimeException('Cannot write to a PumpStream');
+    }
+
+    public function isReadable()
+    {
+        return true;
+    }
+
+    public function read($length)
+    {
+        $data = $this->buffer->read($length);
+        $readLen = strlen($data);
+        $this->tellPos += $readLen;
+        $remaining = $length - $readLen;
+
+        if ($remaining) {
+            $this->pump($remaining);
+            $data .= $this->buffer->read($remaining);
+            $this->tellPos += strlen($data) - $readLen;
+        }
+
+        return $data;
+    }
+
+    public function getContents()
+    {
+        $result = '';
+        while (!$this->eof()) {
+            $result .= $this->read(1000000);
+        }
+
+        return $result;
+    }
+
+    public function getMetadata($key = null)
+    {
+        if (!$key) {
+            return $this->metadata;
+        }
+
+        return isset($this->metadata[$key]) ? $this->metadata[$key] : null;
+    }
+
+    private function pump($length)
+    {
+        if ($this->source) {
+            do {
+                $data = call_user_func($this->source, $length);
+                if ($data === false || $data === null) {
+                    $this->source = null;
+                    return;
+                }
+                $this->buffer->write($data);
+                $length -= strlen($data);
+            } while ($length > 0);
+        }
+    }
+}

+ 142 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/Request.php

@@ -0,0 +1,142 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use InvalidArgumentException;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * PSR-7 request implementation.
+ */
+class Request implements RequestInterface
+{
+    use MessageTrait;
+
+    /** @var string */
+    private $method;
+
+    /** @var null|string */
+    private $requestTarget;
+
+    /** @var UriInterface */
+    private $uri;
+
+    /**
+     * @param string                               $method  HTTP method
+     * @param string|UriInterface                  $uri     URI
+     * @param array                                $headers Request headers
+     * @param string|null|resource|StreamInterface $body    Request body
+     * @param string                               $version Protocol version
+     */
+    public function __construct(
+        $method,
+        $uri,
+        array $headers = [],
+        $body = null,
+        $version = '1.1'
+    ) {
+        if (!($uri instanceof UriInterface)) {
+            $uri = new Uri($uri);
+        }
+
+        $this->method = strtoupper($method);
+        $this->uri = $uri;
+        $this->setHeaders($headers);
+        $this->protocol = $version;
+
+        if (!$this->hasHeader('Host')) {
+            $this->updateHostFromUri();
+        }
+
+        if ($body !== '' && $body !== null) {
+            $this->stream = stream_for($body);
+        }
+    }
+
+    public function getRequestTarget()
+    {
+        if ($this->requestTarget !== null) {
+            return $this->requestTarget;
+        }
+
+        $target = $this->uri->getPath();
+        if ($target == '') {
+            $target = '/';
+        }
+        if ($this->uri->getQuery() != '') {
+            $target .= '?' . $this->uri->getQuery();
+        }
+
+        return $target;
+    }
+
+    public function withRequestTarget($requestTarget)
+    {
+        if (preg_match('#\s#', $requestTarget)) {
+            throw new InvalidArgumentException(
+                'Invalid request target provided; cannot contain whitespace'
+            );
+        }
+
+        $new = clone $this;
+        $new->requestTarget = $requestTarget;
+        return $new;
+    }
+
+    public function getMethod()
+    {
+        return $this->method;
+    }
+
+    public function withMethod($method)
+    {
+        $new = clone $this;
+        $new->method = strtoupper($method);
+        return $new;
+    }
+
+    public function getUri()
+    {
+        return $this->uri;
+    }
+
+    public function withUri(UriInterface $uri, $preserveHost = false)
+    {
+        if ($uri === $this->uri) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->uri = $uri;
+
+        if (!$preserveHost) {
+            $new->updateHostFromUri();
+        }
+
+        return $new;
+    }
+
+    private function updateHostFromUri()
+    {
+        $host = $this->uri->getHost();
+
+        if ($host == '') {
+            return;
+        }
+
+        if (($port = $this->uri->getPort()) !== null) {
+            $host .= ':' . $port;
+        }
+
+        if (isset($this->headerNames['host'])) {
+            $header = $this->headerNames['host'];
+        } else {
+            $header = 'Host';
+            $this->headerNames['host'] = 'Host';
+        }
+        // Ensure Host is the first header.
+        // See: http://tools.ietf.org/html/rfc7230#section-5.4
+        $this->headers = [$header => [$host]] + $this->headers;
+    }
+}

+ 132 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/Response.php

@@ -0,0 +1,132 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * PSR-7 response implementation.
+ */
+class Response implements ResponseInterface
+{
+    use MessageTrait;
+
+    /** @var array Map of standard HTTP status code/reason phrases */
+    private static $phrases = [
+        100 => 'Continue',
+        101 => 'Switching Protocols',
+        102 => 'Processing',
+        200 => 'OK',
+        201 => 'Created',
+        202 => 'Accepted',
+        203 => 'Non-Authoritative Information',
+        204 => 'No Content',
+        205 => 'Reset Content',
+        206 => 'Partial Content',
+        207 => 'Multi-status',
+        208 => 'Already Reported',
+        300 => 'Multiple Choices',
+        301 => 'Moved Permanently',
+        302 => 'Found',
+        303 => 'See Other',
+        304 => 'Not Modified',
+        305 => 'Use Proxy',
+        306 => 'Switch Proxy',
+        307 => 'Temporary Redirect',
+        400 => 'Bad Request',
+        401 => 'Unauthorized',
+        402 => 'Payment Required',
+        403 => 'Forbidden',
+        404 => 'Not Found',
+        405 => 'Method Not Allowed',
+        406 => 'Not Acceptable',
+        407 => 'Proxy Authentication Required',
+        408 => 'Request Time-out',
+        409 => 'Conflict',
+        410 => 'Gone',
+        411 => 'Length Required',
+        412 => 'Precondition Failed',
+        413 => 'Request Entity Too Large',
+        414 => 'Request-URI Too Large',
+        415 => 'Unsupported Media Type',
+        416 => 'Requested range not satisfiable',
+        417 => 'Expectation Failed',
+        418 => 'I\'m a teapot',
+        422 => 'Unprocessable Entity',
+        423 => 'Locked',
+        424 => 'Failed Dependency',
+        425 => 'Unordered Collection',
+        426 => 'Upgrade Required',
+        428 => 'Precondition Required',
+        429 => 'Too Many Requests',
+        431 => 'Request Header Fields Too Large',
+        451 => 'Unavailable For Legal Reasons',
+        500 => 'Internal Server Error',
+        501 => 'Not Implemented',
+        502 => 'Bad Gateway',
+        503 => 'Service Unavailable',
+        504 => 'Gateway Time-out',
+        505 => 'HTTP Version not supported',
+        506 => 'Variant Also Negotiates',
+        507 => 'Insufficient Storage',
+        508 => 'Loop Detected',
+        511 => 'Network Authentication Required',
+    ];
+
+    /** @var string */
+    private $reasonPhrase = '';
+
+    /** @var int */
+    private $statusCode = 200;
+
+    /**
+     * @param int                                  $status  Status code
+     * @param array                                $headers Response headers
+     * @param string|null|resource|StreamInterface $body    Response body
+     * @param string                               $version Protocol version
+     * @param string|null                          $reason  Reason phrase (when empty a default will be used based on the status code)
+     */
+    public function __construct(
+        $status = 200,
+        array $headers = [],
+        $body = null,
+        $version = '1.1',
+        $reason = null
+    ) {
+        $this->statusCode = (int) $status;
+
+        if ($body !== '' && $body !== null) {
+            $this->stream = stream_for($body);
+        }
+
+        $this->setHeaders($headers);
+        if ($reason == '' && isset(self::$phrases[$this->statusCode])) {
+            $this->reasonPhrase = self::$phrases[$this->statusCode];
+        } else {
+            $this->reasonPhrase = (string) $reason;
+        }
+
+        $this->protocol = $version;
+    }
+
+    public function getStatusCode()
+    {
+        return $this->statusCode;
+    }
+
+    public function getReasonPhrase()
+    {
+        return $this->reasonPhrase;
+    }
+
+    public function withStatus($code, $reasonPhrase = '')
+    {
+        $new = clone $this;
+        $new->statusCode = (int) $code;
+        if ($reasonPhrase == '' && isset(self::$phrases[$new->statusCode])) {
+            $reasonPhrase = self::$phrases[$new->statusCode];
+        }
+        $new->reasonPhrase = $reasonPhrase;
+        return $new;
+    }
+}

+ 358 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/ServerRequest.php

@@ -0,0 +1,358 @@
+<?php
+
+namespace GuzzleHttp\Psr7;
+
+use InvalidArgumentException;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\UriInterface;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UploadedFileInterface;
+
+/**
+ * Server-side HTTP request
+ *
+ * Extends the Request definition to add methods for accessing incoming data,
+ * specifically server parameters, cookies, matched path parameters, query
+ * string arguments, body parameters, and upload file information.
+ *
+ * "Attributes" are discovered via decomposing the request (and usually
+ * specifically the URI path), and typically will be injected by the application.
+ *
+ * Requests are considered immutable; all methods that might change state are
+ * implemented such that they retain the internal state of the current
+ * message and return a new instance that contains the changed state.
+ */
+class ServerRequest extends Request implements ServerRequestInterface
+{
+    /**
+     * @var array
+     */
+    private $attributes = [];
+
+    /**
+     * @var array
+     */
+    private $cookieParams = [];
+
+    /**
+     * @var null|array|object
+     */
+    private $parsedBody;
+
+    /**
+     * @var array
+     */
+    private $queryParams = [];
+
+    /**
+     * @var array
+     */
+    private $serverParams;
+
+    /**
+     * @var array
+     */
+    private $uploadedFiles = [];
+
+    /**
+     * @param string                               $method       HTTP method
+     * @param string|UriInterface                  $uri          URI
+     * @param array                                $headers      Request headers
+     * @param string|null|resource|StreamInterface $body         Request body
+     * @param string                               $version      Protocol version
+     * @param array                                $serverParams Typically the $_SERVER superglobal
+     */
+    public function __construct(
+        $method,
+        $uri,
+        array $headers = [],
+        $body = null,
+        $version = '1.1',
+        array $serverParams = []
+    ) {
+        $this->serverParams = $serverParams;
+
+        parent::__construct($method, $uri, $headers, $body, $version);
+    }
+
+    /**
+     * Return an UploadedFile instance array.
+     *
+     * @param array $files A array which respect $_FILES structure
+     * @throws InvalidArgumentException for unrecognized values
+     * @return array
+     */
+    public static function normalizeFiles(array $files)
+    {
+        $normalized = [];
+
+        foreach ($files as $key => $value) {
+            if ($value instanceof UploadedFileInterface) {
+                $normalized[$key] = $value;
+            } elseif (is_array($value) && isset($value['tmp_name'])) {
+                $normalized[$key] = self::createUploadedFileFromSpec($value);
+            } elseif (is_array($value)) {
+                $normalized[$key] = self::normalizeFiles($value);
+                continue;
+            } else {
+                throw new InvalidArgumentException('Invalid value in files specification');
+            }
+        }
+
+        return $normalized;
+    }
+
+    /**
+     * Create and return an UploadedFile instance from a $_FILES specification.
+     *
+     * If the specification represents an array of values, this method will
+     * delegate to normalizeNestedFileSpec() and return that return value.
+     *
+     * @param array $value $_FILES struct
+     * @return array|UploadedFileInterface
+     */
+    private static function createUploadedFileFromSpec(array $value)
+    {
+        if (is_array($value['tmp_name'])) {
+            return self::normalizeNestedFileSpec($value);
+        }
+
+        return new UploadedFile(
+            $value['tmp_name'],
+            (int) $value['size'],
+            (int) $value['error'],
+            $value['name'],
+            $value['type']
+        );
+    }
+
+    /**
+     * Normalize an array of file specifications.
+     *
+     * Loops through all nested files and returns a normalized array of
+     * UploadedFileInterface instances.
+     *
+     * @param array $files
+     * @return UploadedFileInterface[]
+     */
+    private static function normalizeNestedFileSpec(array $files = [])
+    {
+        $normalizedFiles = [];
+
+        foreach (array_keys($files['tmp_name']) as $key) {
+            $spec = [
+                'tmp_name' => $files['tmp_name'][$key],
+                'size'     => $files['size'][$key],
+                'error'    => $files['error'][$key],
+                'name'     => $files['name'][$key],
+                'type'     => $files['type'][$key],
+            ];
+            $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
+        }
+
+        return $normalizedFiles;
+    }
+
+    /**
+     * Return a ServerRequest populated with superglobals:
+     * $_GET
+     * $_POST
+     * $_COOKIE
+     * $_FILES
+     * $_SERVER
+     *
+     * @return ServerRequestInterface
+     */
+    public static function fromGlobals()
+    {
+        $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
+        $headers = function_exists('getallheaders') ? getallheaders() : [];
+        $uri = self::getUriFromGlobals();
+        $body = new LazyOpenStream('php://input', 'r+');
+        $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1';
+
+        $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER);
+
+        return $serverRequest
+            ->withCookieParams($_COOKIE)
+            ->withQueryParams($_GET)
+            ->withParsedBody($_POST)
+            ->withUploadedFiles(self::normalizeFiles($_FILES));
+    }
+
+    /**
+     * Get a Uri populated with values from $_SERVER.
+     *
+     * @return UriInterface
+     */
+    public static function getUriFromGlobals() {
+        $uri = new Uri('');
+
+        $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http');
+
+        $hasPort = false;
+        if (isset($_SERVER['HTTP_HOST'])) {
+            $hostHeaderParts = explode(':', $_SERVER['HTTP_HOST']);
+            $uri = $uri->withHost($hostHeaderParts[0]);
+            if (isset($hostHeaderParts[1])) {
+                $hasPort = true;
+                $uri = $uri->withPort($hostHeaderParts[1]);
+            }
+        } elseif (isset($_SERVER['SERVER_NAME'])) {
+            $uri = $uri->withHost($_SERVER['SERVER_NAME']);
+        } elseif (isset($_SERVER['SERVER_ADDR'])) {
+            $uri = $uri->withHost($_SERVER['SERVER_ADDR']);
+        }
+
+        if (!$hasPort && isset($_SERVER['SERVER_PORT'])) {
+            $uri = $uri->withPort($_SERVER['SERVER_PORT']);
+        }
+
+        $hasQuery = false;
+        if (isset($_SERVER['REQUEST_URI'])) {
+            $requestUriParts = explode('?', $_SERVER['REQUEST_URI']);
+            $uri = $uri->withPath($requestUriParts[0]);
+            if (isset($requestUriParts[1])) {
+                $hasQuery = true;
+                $uri = $uri->withQuery($requestUriParts[1]);
+            }
+        }
+
+        if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) {
+            $uri = $uri->withQuery($_SERVER['QUERY_STRING']);
+        }
+
+        return $uri;
+    }
+
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getServerParams()
+    {
+        return $this->serverParams;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getUploadedFiles()
+    {
+        return $this->uploadedFiles;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function withUploadedFiles(array $uploadedFiles)
+    {
+        $new = clone $this;
+        $new->uploadedFiles = $uploadedFiles;
+
+        return $new;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCookieParams()
+    {
+        return $this->cookieParams;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function withCookieParams(array $cookies)
+    {
+        $new = clone $this;
+        $new->cookieParams = $cookies;
+
+        return $new;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getQueryParams()
+    {
+        return $this->queryParams;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function withQueryParams(array $query)
+    {
+        $new = clone $this;
+        $new->queryParams = $query;
+
+        return $new;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getParsedBody()
+    {
+        return $this->parsedBody;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function withParsedBody($data)
+    {
+        $new = clone $this;
+        $new->parsedBody = $data;
+
+        return $new;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getAttributes()
+    {
+        return $this->attributes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getAttribute($attribute, $default = null)
+    {
+        if (false === array_key_exists($attribute, $this->attributes)) {
+            return $default;
+        }
+
+        return $this->attributes[$attribute];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function withAttribute($attribute, $value)
+    {
+        $new = clone $this;
+        $new->attributes[$attribute] = $value;
+
+        return $new;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function withoutAttribute($attribute)
+    {
+        if (false === array_key_exists($attribute, $this->attributes)) {
+            return $this;
+        }
+
+        $new = clone $this;
+        unset($new->attributes[$attribute]);
+
+        return $new;
+    }
+}

+ 257 - 0
usr/plugins/UpyunFile/Upyun/vendor/guzzlehttp/psr7/src/Stream.php

@@ -0,0 +1,257 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * PHP stream implementation.
+ *
+ * @var $stream
+ */
+class Stream implements StreamInterface
+{
+    private $stream;
+    private $size;
+    private $seekable;
+    private $readable;
+    private $writable;
+    private $uri;
+    private $customMetadata;
+
+    /** @var array Hash of readable and writable stream types */
+    private static $readWriteHash = [
+        'read' => [
+            'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true,
+            'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true,
+            'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true,
+            'x+t' => true, 'c+t' => true, 'a+' => true
+        ],
+        'write' => [
+            'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true,
+            'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true,
+            'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true,
+            'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true
+        ]
+    ];
+
+    /**
+     * This constructor accepts an associative array of options.
+     *
+     * - size: (int) If a read stream would otherwise have an indeterminate
+     *   size, but the size is known due to foreknowledge, then you can
+     *   provide that size, in bytes.
+     * - metadata: (array) Any additional metadata to return when the metadata
+     *   of the stream is accessed.
+     *
+     * @param resource $stream  Stream resource to wrap.
+     * @param array    $options Associative array of options.
+     *
+     * @throws \InvalidArgumentException if the stream is not a stream resource
+     */
+    public function __construct($stream, $options = [])
+    {
+        if (!is_resource($stream)) {
+            throw new \InvalidArgumentException('Stream must be a resource');
+        }
+
+        if (isset($options['size'])) {
+            $this->size = $options['size'];
+        }
+
+        $this->customMetadata = isset($options['metadata'])
+            ? $options['metadata']
+            : [];
+
+        $this->stream = $stream;
+        $meta = stream_get_meta_data($this->stream);
+        $this->seekable = $meta['seekable'];
+        $this->readable = isset(self::$readWriteHash['read'][$meta['mode']]);
+        $this->writable = isset(self::$readWriteHash['write'][$meta['mode']]);
+        $this->uri = $this->getMetadata('uri');
+    }
+
+    public function __get($name)
+    {
+        if ($name == 'stream') {
+            throw new \RuntimeException('The stream is detached');
+        }
+
+        throw new \BadMethodCallException('No value for ' . $name);
+    }
+
+    /**
+     * Closes the stream when the destructed
+     */
+    public function __destruct()
+    {
+        $this->close();
+    }
+
+    public function __toString()
+    {
+        try {
+            $this->seek(0);
+            return (string) stream_get_contents($this->stream);
+        } catch (\Exception $e) {
+            return '';
+        }
+    }
+
+    public function getContents()
+    {
+        $contents = stream_get_contents($this->stream);
+
+        if ($contents === false) {
+            throw new \RuntimeException('Unable to read stream contents');
+        }
+
+        return $contents;
+    }
+
+    public function close()
+    {
+        if (isset($this->stream)) {
+            if (is_resource($this->stream)) {
+                fclose($this->stream);
+            }
+            $this->detach();
+        }
+    }
+
+    public function detach()
+    {
+        if (!isset($this->stream)) {
+            return null;
+        }
+
+        $result = $this->stream;
+        unset($this->stream);
+        $this->size = $this->uri = null;
+        $this->readable = $this->writable = $this->seekable = false;
+
+        return $result;
+    }
+
+    public function getSize()
+    {
+        if ($this->size !== null) {
+            return $this->size;
+        }
+
+        if (!isset($this->stream)) {
+            return null;
+        }
+
+        // Clear the stat cache if the stream has a URI
+        if ($this->uri) {
+            clearstatcache(true, $this->uri);
+        }
+
+        $stats = fstat($this->stream);
+        if (isset($stats['size'])) {
+            $this->size = $stats['size'];
+            return $this->size;
+        }
+
+        return null;
+    }
+
+    public function isReadable()
+    {
+        return $this->readable;
+    }
+
+    public function isWritable()
+    {
+        return $this->writable;
+    }
+
+    public function isSeekable()
+    {
+        return $this->seekable;
+    }
+
+    public function eof()
+    {
+        return !$this->stream || feof($this->stream);
+    }
+
+    public function tell()
+    {
+        $result = ftell($this->stream);
+
+        if ($result === false) {
+            throw new \RuntimeException('Unable to determine stream position');
+        }
+
+        return $result;
+    }
+
+    public function rewind()
+    {
+        $this->seek(0);
+    }
+
+    public function seek($offset, $whence = SEEK_SET)
+    {
+        if (!$this->seekable) {
+            throw new \RuntimeException('Stream is not seekable');
+        } elseif (fseek($this->stream, $offset, $whence) === -1) {
+            throw new \RuntimeException('Unable to seek to stream position '
+                . $offset . ' with whence ' . var_export($whence, true));
+        }
+    }
+
+    public function read($length)
+    {
+        if (!$this->readable) {
+            throw new \RuntimeException('Cannot read from non-readable stream');
+        }
+        if ($length < 0) {
+            throw new \RuntimeException('Length parameter cannot be negative');
+        }
+
+        if (0 === $length) {
+            return '';
+        }
+
+        $string = fread($this->stream, $length);
+        if (false === $string) {
+            throw new \RuntimeException('Unable to read from stream');
+        }
+
+        return $string;
+    }
+
+    public function write($string)
+    {
+        if (!$this->writable) {
+            throw new \RuntimeException('Cannot write to a non-writable stream');
+        }
+
+        // We can't know the size after writing anything
+        $this->size = null;
+        $result = fwrite($this->stream, $string);
+
+        if ($result === false) {
+            throw new \RuntimeException('Unable to write to stream');
+        }
+
+        return $result;
+    }
+
+    public function getMetadata($key = null)
+    {
+        if (!isset($this->stream)) {
+            return $key ? null : [];
+        } elseif (!$key) {
+            return $this->customMetadata + stream_get_meta_data($this->stream);
+        } elseif (isset($this->customMetadata[$key])) {
+            return $this->customMetadata[$key];
+        }
+
+        $meta = stream_get_meta_data($this->stream);
+
+        return isset($meta[$key]) ? $meta[$key] : null;
+    }
+}

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels