feat(upload): 添加华为 OBS 对象存储支持
- 在 addons.php 中添加了与华为 OBS 相关的钩子 - 新增了对华为 OBS 上传功能的实现,包括分片上传和合并 - 优化了上传参数处理和错误处理 - 支持客户端和服务端两种上传模式
This commit is contained in:
37
addons/hwobs/library/Auth.php
Normal file
37
addons/hwobs/library/Auth.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace addons\hwobs\library;
|
||||
|
||||
class Auth
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static function isModuleAllow()
|
||||
{
|
||||
$config = get_addon_config('hwobs');
|
||||
$module = request()->module();
|
||||
$module = $module ? strtolower($module) : 'index';
|
||||
$noNeedLogin = array_filter(explode(',', $config['noneedlogin'] ?? ''));
|
||||
$isModuleLogin = false;
|
||||
$tagName = 'upload_config_checklogin';
|
||||
foreach (\think\Hook::get($tagName) as $index => $name) {
|
||||
if (\think\Hook::exec($name, $tagName)) {
|
||||
$isModuleLogin = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (in_array($module, $noNeedLogin)
|
||||
|| ($module == 'admin' && \app\admin\library\Auth::instance()->id)
|
||||
|| ($module != 'admin' && \app\common\library\Auth::instance()->id)
|
||||
|| $isModuleLogin) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
63
addons/hwobs/library/Obs/Internal/Common/CheckoutStream.php
Normal file
63
addons/hwobs/library/Obs/Internal/Common/CheckoutStream.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs\Internal\Common;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use GuzzleHttp\Psr7\StreamDecoratorTrait;
|
||||
use Obs\ObsException;
|
||||
|
||||
class CheckoutStream implements StreamInterface {
|
||||
use StreamDecoratorTrait;
|
||||
|
||||
private $expectedLength;
|
||||
private $readedCount = 0;
|
||||
|
||||
public function __construct(StreamInterface $stream, $expectedLength) {
|
||||
$this->stream = $stream;
|
||||
$this->expectedLength = $expectedLength;
|
||||
}
|
||||
|
||||
public function getContents() {
|
||||
$contents = $this->stream->getContents();
|
||||
$length = strlen($contents);
|
||||
if ($this->expectedLength !== null && floatval($length) !== $this->expectedLength) {
|
||||
$this -> throwObsException($this->expectedLength, $length);
|
||||
}
|
||||
return $contents;
|
||||
}
|
||||
|
||||
public function read($length) {
|
||||
$string = $this->stream->read($length);
|
||||
if ($this->expectedLength !== null) {
|
||||
$this->readedCount += strlen($string);
|
||||
if ($this->stream->eof()) {
|
||||
if (floatval($this->readedCount) !== $this->expectedLength) {
|
||||
$this -> throwObsException($this->expectedLength, $this->readedCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
public function throwObsException($expectedLength, $reaLength) {
|
||||
$obsException = new ObsException('premature end of Content-Length delimiter message body (expected:' . $expectedLength . '; received:' . $reaLength . ')');
|
||||
$obsException->setExceptionType('server');
|
||||
throw $obsException;
|
||||
}
|
||||
}
|
||||
|
||||
23
addons/hwobs/library/Obs/Internal/Common/ITransform.php
Normal file
23
addons/hwobs/library/Obs/Internal/Common/ITransform.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs\Internal\Common;
|
||||
|
||||
interface ITransform {
|
||||
public function transform($sign, $para);
|
||||
}
|
||||
|
||||
257
addons/hwobs/library/Obs/Internal/Common/Model.php
Normal file
257
addons/hwobs/library/Obs/Internal/Common/Model.php
Normal file
@@ -0,0 +1,257 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (c) 2011-2018 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.
|
||||
*/
|
||||
|
||||
namespace Obs\Internal\Common;
|
||||
|
||||
class Model implements \ArrayAccess, \IteratorAggregate, \Countable, ToArrayInterface
|
||||
{
|
||||
protected $data;
|
||||
|
||||
public function __construct(array $data = [])
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->data);
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->data);
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
$this->data = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAll(array $keys = null)
|
||||
{
|
||||
return $keys ? array_intersect_key($this->data, array_flip($keys)) : $this->data;
|
||||
}
|
||||
|
||||
public function get($key)
|
||||
{
|
||||
return isset($this->data[$key]) ? $this->data[$key] : null;
|
||||
}
|
||||
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->data[$key] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function add($key, $value)
|
||||
{
|
||||
if (!array_key_exists($key, $this->data)) {
|
||||
$this->data[$key] = $value;
|
||||
} elseif (is_array($this->data[$key])) {
|
||||
$this->data[$key][] = $value;
|
||||
} else {
|
||||
$this->data[$key] = [$this->data[$key], $value];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function remove($key)
|
||||
{
|
||||
unset($this->data[$key]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getKeys()
|
||||
{
|
||||
return array_keys($this->data);
|
||||
}
|
||||
|
||||
public function hasKey($key)
|
||||
{
|
||||
return array_key_exists($key, $this->data);
|
||||
}
|
||||
|
||||
public function keySearch($key)
|
||||
{
|
||||
foreach (array_keys($this->data) as $k) {
|
||||
if (!strcasecmp($k, $key)) {
|
||||
return $k;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public function hasValue($value)
|
||||
{
|
||||
return array_search($value, $this->data);
|
||||
}
|
||||
|
||||
public function replace(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function merge($data)
|
||||
{
|
||||
foreach ($data as $key => $value) {
|
||||
$this->add($key, $value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function overwriteWith($data)
|
||||
{
|
||||
if (is_array($data)) {
|
||||
$this->data = $data + $this->data;
|
||||
} else {
|
||||
foreach ($data as $key => $value) {
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function map(\Closure $closure, array $context = [], $static = true)
|
||||
{
|
||||
$collection = $static ? new static() : new self();
|
||||
foreach ($this as $key => $value) {
|
||||
$collection->add($key, $closure($key, $value, $context));
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
public function filter(\Closure $closure, $static = true)
|
||||
{
|
||||
$collection = ($static) ? new static() : new self();
|
||||
foreach ($this->data as $key => $value) {
|
||||
if ($closure($key, $value)) {
|
||||
$collection->add($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->data[$offset]);
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return isset($this->data[$offset]) ? $this->data[$offset] : null;
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->data[$offset] = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->data[$offset]);
|
||||
}
|
||||
|
||||
public function setPath($path, $value)
|
||||
{
|
||||
$current =& $this->data;
|
||||
$queue = explode('/', $path);
|
||||
while (null !== ($key = array_shift($queue))) {
|
||||
if (!is_array($current)) {
|
||||
throw new \RuntimeException("Trying to setPath {$path}, but {$key} is set and is not an array");
|
||||
} elseif (!$queue) {
|
||||
$current[$key] = $value;
|
||||
} elseif (isset($current[$key])) {
|
||||
$current =& $current[$key];
|
||||
} else {
|
||||
$current[$key] = [];
|
||||
$current =& $current[$key];
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPath($path, $separator = '/', $data = null)
|
||||
{
|
||||
if ($data === null) {
|
||||
$data =& $this->data;
|
||||
}
|
||||
|
||||
$path = is_array($path) ? $path : explode($separator, $path);
|
||||
while (null !== ($part = array_shift($path))) {
|
||||
if (!is_array($data)) {
|
||||
return null;
|
||||
} elseif (isset($data[$part])) {
|
||||
$data =& $data[$part];
|
||||
} elseif ($part != '*') {
|
||||
return null;
|
||||
} else {
|
||||
// Perform a wildcard search by diverging and merging paths
|
||||
$result = [];
|
||||
foreach ($data as $value) {
|
||||
if (!$path) {
|
||||
$result = array_merge_recursive($result, (array) $value);
|
||||
} elseif (null !== ($test = $this->getPath($path, $separator, $value))) {
|
||||
$result = array_merge_recursive($result, (array) $test);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$output = 'Debug output of ';
|
||||
$output .= 'model';
|
||||
$output = str_repeat('=', strlen($output)) . "\n" . $output . "\n" . str_repeat('=', strlen($output)) . "\n\n";
|
||||
$output .= "Model data\n-----------\n\n";
|
||||
$output .= "This data can be retrieved from the model object using the get() method of the model "
|
||||
. "(e.g. \$model->get(\$key)) or accessing the model like an associative array (e.g. \$model['key']).\n\n";
|
||||
$lines = array_slice(explode("\n", trim(print_r($this->toArray(), true))), 2, -1);
|
||||
$output .= implode("\n", $lines);
|
||||
|
||||
return $output . "\n";
|
||||
}
|
||||
}
|
||||
78
addons/hwobs/library/Obs/Internal/Common/ObsTransform.php
Normal file
78
addons/hwobs/library/Obs/Internal/Common/ObsTransform.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs\Internal\Common;
|
||||
|
||||
use Obs\ObsClient;
|
||||
|
||||
class ObsTransform implements ITransform {
|
||||
private static $instance;
|
||||
|
||||
private function __construct(){}
|
||||
|
||||
public static function getInstance() {
|
||||
if (!(self::$instance instanceof ObsTransform)) {
|
||||
self::$instance = new ObsTransform();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
|
||||
public function transform($sign, $para) {
|
||||
if ($sign === 'aclHeader') {
|
||||
$para = $this->transAclHeader($para);
|
||||
} else if ($sign === 'aclUri') {
|
||||
$para = $this->transAclGroupUri($para);
|
||||
} else if ($sign == 'event') {
|
||||
$para = $this->transNotificationEvent($para);
|
||||
} else if ($sign == 'storageClass') {
|
||||
$para = $this->transStorageClass($para);
|
||||
}
|
||||
return $para;
|
||||
}
|
||||
|
||||
private function transAclHeader($para) {
|
||||
if ($para === ObsClient::AclAuthenticatedRead || $para === ObsClient::AclBucketOwnerRead ||
|
||||
$para === ObsClient::AclBucketOwnerFullControl || $para === ObsClient::AclLogDeliveryWrite) {
|
||||
$para = null;
|
||||
}
|
||||
return $para;
|
||||
}
|
||||
|
||||
private function transAclGroupUri($para) {
|
||||
if ($para === ObsClient::GroupAllUsers) {
|
||||
$para = ObsClient::AllUsers;
|
||||
}
|
||||
return $para;
|
||||
}
|
||||
|
||||
private function transNotificationEvent($para) {
|
||||
$pos = strpos($para, 's3:');
|
||||
if ($pos !== false && $pos === 0) {
|
||||
$para = substr($para, 3);
|
||||
}
|
||||
return $para;
|
||||
}
|
||||
|
||||
private function transStorageClass($para) {
|
||||
$search = array('STANDARD', 'STANDARD_IA', 'GLACIER');
|
||||
$repalce = array(ObsClient::StorageClassStandard, ObsClient::StorageClassWarm, ObsClient::StorageClassCold);
|
||||
$para = str_replace($search, $repalce, $para);
|
||||
return $para;
|
||||
}
|
||||
}
|
||||
|
||||
116
addons/hwobs/library/Obs/Internal/Common/SchemaFormatter.php
Normal file
116
addons/hwobs/library/Obs/Internal/Common/SchemaFormatter.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs\Internal\Common;
|
||||
|
||||
|
||||
class SchemaFormatter
|
||||
{
|
||||
protected static $utcTimeZone;
|
||||
|
||||
public static function format($fmt, $value)
|
||||
{
|
||||
if($fmt === 'date-time'){
|
||||
return self::formatDateTime($value);
|
||||
}
|
||||
|
||||
if($fmt === 'data-time-http'){
|
||||
return self::formatDateTimeHttp($value);
|
||||
}
|
||||
|
||||
if($fmt === 'data-time-middle'){
|
||||
return self::formatDateTimeMiddle($value);
|
||||
}
|
||||
|
||||
if($fmt === 'date'){
|
||||
return self::formatDate($value);
|
||||
}
|
||||
|
||||
if($fmt === 'timestamp'){
|
||||
return self::formatTimestamp($value);
|
||||
}
|
||||
|
||||
if($fmt === 'boolean-string'){
|
||||
return self::formatBooleanAsString($value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public static function formatDateTimeMiddle($dateTime)
|
||||
{
|
||||
if (is_string($dateTime)) {
|
||||
$dateTime = new \DateTime($dateTime);
|
||||
}
|
||||
|
||||
if ($dateTime instanceof \DateTime) {
|
||||
return $dateTime -> format('Y-m-d\T00:00:00\Z');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function formatDateTime($value)
|
||||
{
|
||||
return self::dateFormatter($value, 'Y-m-d\TH:i:s\Z');
|
||||
}
|
||||
|
||||
public static function formatDateTimeHttp($value)
|
||||
{
|
||||
return self::dateFormatter($value, 'D, d M Y H:i:s \G\M\T');
|
||||
}
|
||||
|
||||
public static function formatDate($value)
|
||||
{
|
||||
return self::dateFormatter($value, 'Y-m-d');
|
||||
}
|
||||
|
||||
public static function formatTime($value)
|
||||
{
|
||||
return self::dateFormatter($value, 'H:i:s');
|
||||
}
|
||||
|
||||
public static function formatBooleanAsString($value)
|
||||
{
|
||||
return filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 'true' : 'false';
|
||||
}
|
||||
|
||||
public static function formatTimestamp($value)
|
||||
{
|
||||
return (int) self::dateFormatter($value, 'U');
|
||||
}
|
||||
|
||||
private static function dateFormatter($dt, $fmt)
|
||||
{
|
||||
if (is_numeric($dt)) {
|
||||
return gmdate($fmt, (int) $dt);
|
||||
}
|
||||
|
||||
if (is_string($dt)) {
|
||||
$dt = new \DateTime($dt);
|
||||
}
|
||||
|
||||
if ($dt instanceof \DateTime) {
|
||||
if (!self::$utcTimeZone) {
|
||||
self::$utcTimeZone = new \DateTimeZone('UTC');
|
||||
}
|
||||
|
||||
return $dt->setTimezone(self::$utcTimeZone)->format($fmt);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
422
addons/hwobs/library/Obs/Internal/Common/SdkCurlFactory.php
Normal file
422
addons/hwobs/library/Obs/Internal/Common/SdkCurlFactory.php
Normal file
@@ -0,0 +1,422 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (c) 2011-2018 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.
|
||||
*/
|
||||
|
||||
namespace Obs\Internal\Common;
|
||||
|
||||
use GuzzleHttp\Psr7;
|
||||
use GuzzleHttp\Psr7\LazyOpenStream;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use GuzzleHttp\Handler\CurlFactoryInterface;
|
||||
use GuzzleHttp\Handler\EasyHandle;
|
||||
|
||||
class SdkCurlFactory implements CurlFactoryInterface
|
||||
{
|
||||
private $handles = [];
|
||||
|
||||
private $maxHandles;
|
||||
|
||||
public function __construct($maxHandles)
|
||||
{
|
||||
$this->maxHandles = $maxHandles;
|
||||
}
|
||||
|
||||
public function create(RequestInterface $request, array $options): EasyHandle
|
||||
{
|
||||
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']);
|
||||
|
||||
if (isset($options['curl'])) {
|
||||
$conf = array_replace($conf, $options['curl']);
|
||||
}
|
||||
|
||||
$conf[CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy);
|
||||
if($this->handles){
|
||||
$easy->handle = array_pop($this->handles);
|
||||
}else{
|
||||
$easy->handle = curl_init();
|
||||
}
|
||||
curl_setopt_array($easy->handle, $conf);
|
||||
|
||||
return $easy;
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
if($this->handles){
|
||||
foreach ($this->handles as $handle){
|
||||
curl_close($handle);
|
||||
}
|
||||
unset($this->handles);
|
||||
$this->handles = [];
|
||||
}
|
||||
}
|
||||
|
||||
public function release(EasyHandle $easy): void
|
||||
{
|
||||
$resource = $easy->handle;
|
||||
unset($easy->handle);
|
||||
|
||||
if (count($this->handles) >= $this->maxHandles) {
|
||||
curl_close($resource);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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') {
|
||||
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')
|
||||
: $request->getBody()->getSize();
|
||||
|
||||
if($request->getBody()->getSize() === $size && $request -> getBody() ->tell() <= 0){
|
||||
if (($size !== null && $size < 1000000) ||
|
||||
!empty($options['_body_as_string'])
|
||||
) {
|
||||
$conf[CURLOPT_POSTFIELDS] = (string) $request->getBody();
|
||||
$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);
|
||||
};
|
||||
}
|
||||
}else{
|
||||
$body = $request->getBody();
|
||||
$conf[CURLOPT_UPLOAD] = true;
|
||||
$conf[CURLOPT_INFILESIZE] = $size;
|
||||
$readCount = 0;
|
||||
$conf[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body, $readCount, $size) {
|
||||
if($readCount >= $size){
|
||||
$body -> close();
|
||||
return '';
|
||||
}
|
||||
$readCountOnce = $length <= $size ? $length : $size;
|
||||
$readCount += $readCountOnce;
|
||||
return $body->read($readCountOnce);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!$request->hasHeader('Expect')) {
|
||||
$conf[CURLOPT_HTTPHEADER][] = 'Expect:';
|
||||
}
|
||||
|
||||
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:';
|
||||
}
|
||||
}
|
||||
|
||||
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'])) {
|
||||
$conf[CURLOPT_SSL_VERIFYHOST] = 0;
|
||||
if ($options['verify'] === false) {
|
||||
unset($conf[CURLOPT_CAINFO]);
|
||||
$conf[CURLOPT_SSL_VERIFYPEER] = false;
|
||||
} else {
|
||||
$conf[CURLOPT_SSL_VERIFYPEER] = true;
|
||||
if (is_string($options['verify'])) {
|
||||
if (!file_exists($options['verify'])) {
|
||||
throw new \InvalidArgumentException(
|
||||
"SSL CA bundle not found: {$options['verify']}"
|
||||
);
|
||||
}
|
||||
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] = '';
|
||||
$conf[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:';
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($options['sink'])) {
|
||||
$sink = $options['sink'];
|
||||
if (!is_string($sink)) {
|
||||
$sink = \GuzzleHttp\Psr7\Utils::streamFor($sink);
|
||||
} elseif (!is_dir(dirname($sink))) {
|
||||
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 {
|
||||
$conf[CURLOPT_FILE] = fopen('php://temp', 'w+');
|
||||
$easy->sink = \GuzzleHttp\Psr7\Utils::streamFor($conf[CURLOPT_FILE]);
|
||||
}
|
||||
$timeoutRequiresNoSignal = false;
|
||||
if (isset($options['timeout'])) {
|
||||
$timeoutRequiresNoSignal |= $options['timeout'] < 1;
|
||||
$conf[CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000;
|
||||
}
|
||||
|
||||
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();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
$easy->onHeadersException = $e;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} elseif ($startingResponse) {
|
||||
$startingResponse = false;
|
||||
$easy->headers = [$value];
|
||||
} else {
|
||||
$easy->headers[] = $value;
|
||||
}
|
||||
return strlen($h);
|
||||
};
|
||||
}
|
||||
}
|
||||
502
addons/hwobs/library/Obs/Internal/Common/SdkStreamHandler.php
Normal file
502
addons/hwobs/library/Obs/Internal/Common/SdkStreamHandler.php
Normal file
@@ -0,0 +1,502 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (c) 2011-2018 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.
|
||||
*/
|
||||
|
||||
namespace Obs\Internal\Common;
|
||||
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use GuzzleHttp\Promise\FulfilledPromise;
|
||||
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;
|
||||
|
||||
class SdkStreamHandler
|
||||
{
|
||||
private $lastHeaders = [];
|
||||
|
||||
public function __invoke(RequestInterface $request, array $options)
|
||||
{
|
||||
if (isset($options['delay'])) {
|
||||
usleep($options['delay'] * 1000);
|
||||
}
|
||||
|
||||
$startTime = isset($options['on_stats']) ? microtime(true) : null;
|
||||
|
||||
try {
|
||||
$request = $request->withoutHeader('Expect');
|
||||
|
||||
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) {
|
||||
$message = $e->getMessage();
|
||||
if (strpos($message, 'getaddrinfo')
|
||||
|| strpos($message, 'Connection refused')
|
||||
|| strpos($message, "couldn't connect to host")
|
||||
) {
|
||||
$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 = \GuzzleHttp\Psr7\Utils::streamFor($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);
|
||||
}
|
||||
}
|
||||
|
||||
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+')
|
||||
: \GuzzleHttp\Psr7\Utils::streamFor($sink);
|
||||
}
|
||||
|
||||
private function checkDecode(array $options, array $headers, $stream)
|
||||
{
|
||||
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(
|
||||
\GuzzleHttp\Psr7\Utils::streamFor($stream)
|
||||
);
|
||||
$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']];
|
||||
|
||||
$length = (int) $stream->getSize();
|
||||
if ($length === 0) {
|
||||
unset($headers[$normalizedKeys['content-length']]);
|
||||
} else {
|
||||
$headers[$normalizedKeys['content-length']] = [$length];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [$stream, $headers];
|
||||
}
|
||||
|
||||
private function drain(
|
||||
StreamInterface $source,
|
||||
StreamInterface $sink,
|
||||
$contentLength
|
||||
) {
|
||||
\GuzzleHttp\Psr7\Utils::copyToStream(
|
||||
$source,
|
||||
$sink,
|
||||
(strlen($contentLength) > 0 && (int) $contentLength > 0) ? (int) $contentLength : -1
|
||||
);
|
||||
|
||||
$sink->seek(0);
|
||||
$source->close();
|
||||
|
||||
return $sink;
|
||||
}
|
||||
|
||||
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__));
|
||||
}
|
||||
|
||||
if ($request->getProtocolVersion() == '1.1'
|
||||
&& !$request->hasHeader('Connection')
|
||||
) {
|
||||
$request = $request->withHeader('Connection', 'close');
|
||||
}
|
||||
|
||||
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']
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
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) {
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs\Internal\Common;
|
||||
|
||||
interface ToArrayInterface
|
||||
{
|
||||
public function toArray();
|
||||
}
|
||||
83
addons/hwobs/library/Obs/Internal/Common/V2Transform.php
Normal file
83
addons/hwobs/library/Obs/Internal/Common/V2Transform.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs\Internal\Common;
|
||||
|
||||
use Obs\ObsClient;
|
||||
use Obs\Internal\Resource\V2Constants;
|
||||
|
||||
class V2Transform implements ITransform{
|
||||
private static $instance;
|
||||
|
||||
private function __construct(){}
|
||||
|
||||
public static function getInstance() {
|
||||
if (!(self::$instance instanceof V2Transform)) {
|
||||
self::$instance = new V2Transform();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function transform($sign, $para) {
|
||||
if ($sign === 'storageClass') {
|
||||
$para = $this->transStorageClass($para);
|
||||
} else if ($sign === 'aclHeader') {
|
||||
$para = $this->transAclHeader($para);
|
||||
} else if ($sign === 'aclUri') {
|
||||
$para = $this->transAclGroupUri($para);
|
||||
} else if ($sign == 'event') {
|
||||
$para = $this->transNotificationEvent($para);
|
||||
}
|
||||
return $para;
|
||||
}
|
||||
|
||||
private function transStorageClass($para) {
|
||||
$search = array(ObsClient::StorageClassStandard, ObsClient::StorageClassWarm, ObsClient::StorageClassCold);
|
||||
$repalce = array('STANDARD', 'STANDARD_IA', 'GLACIER');
|
||||
$para = str_replace($search, $repalce, $para);
|
||||
return $para;
|
||||
}
|
||||
|
||||
private function transAclHeader($para) {
|
||||
if ($para === ObsClient::AclPublicReadDelivered || $para === ObsClient::AclPublicReadWriteDelivered) {
|
||||
$para = null;
|
||||
}
|
||||
return $para;
|
||||
}
|
||||
|
||||
private function transAclGroupUri($para) {
|
||||
if ($para === ObsClient::GroupAllUsers) {
|
||||
$para = V2Constants::GROUP_ALL_USERS_PREFIX . $para;
|
||||
} else if ($para === ObsClient::GroupAuthenticatedUsers) {
|
||||
$para = V2Constants::GROUP_AUTHENTICATED_USERS_PREFIX . $para;
|
||||
} else if ($para === ObsClient::GroupLogDelivery) {
|
||||
$para = V2Constants::GROUP_LOG_DELIVERY_PREFIX . $para;
|
||||
} else if ($para === ObsClient::AllUsers) {
|
||||
$para = V2Constants::GROUP_ALL_USERS_PREFIX . ObsClient::GroupAllUsers;
|
||||
}
|
||||
return $para;
|
||||
}
|
||||
|
||||
private function transNotificationEvent($para) {
|
||||
$pos = strpos($para, 's3:');
|
||||
if ($pos === false || $pos !== 0) {
|
||||
$para = 's3:' . $para;
|
||||
}
|
||||
return $para;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
380
addons/hwobs/library/Obs/Internal/GetResponseTrait.php
Normal file
380
addons/hwobs/library/Obs/Internal/GetResponseTrait.php
Normal file
@@ -0,0 +1,380 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs\Internal;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Obs\ObsException;
|
||||
use Obs\Internal\Common\Model;
|
||||
use Obs\Internal\Resource\Constants;
|
||||
use Obs\Log\ObsLog;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Obs\Internal\Common\CheckoutStream;
|
||||
|
||||
trait GetResponseTrait
|
||||
{
|
||||
|
||||
protected $exceptionResponseMode = true;
|
||||
|
||||
protected $chunkSize = 65536;
|
||||
|
||||
protected function isClientError(Response $response)
|
||||
{
|
||||
return $response -> getStatusCode() >= 400 && $response -> getStatusCode() < 500;
|
||||
}
|
||||
|
||||
protected function parseXmlByType($searchPath, $key, &$value, $xml, $prefix)
|
||||
{
|
||||
$type = 'string';
|
||||
|
||||
if(isset($value['sentAs'])){
|
||||
$key = $value['sentAs'];
|
||||
}
|
||||
|
||||
if($searchPath === null){
|
||||
$searchPath = '//'.$prefix.$key;
|
||||
}
|
||||
|
||||
if(isset($value['type'])){
|
||||
$type = $value['type'];
|
||||
if($type === 'array'){
|
||||
$items = $value['items'];
|
||||
if(isset($value['wrapper'])){
|
||||
$paths = explode('/', $searchPath);
|
||||
$size = count($paths);
|
||||
if ($size > 1) {
|
||||
$end = $paths[$size - 1];
|
||||
$paths[$size - 1] = $value['wrapper'];
|
||||
$paths[] = $end;
|
||||
$searchPath = implode('/', $paths) .'/' . $prefix;
|
||||
}
|
||||
}
|
||||
|
||||
$array = [];
|
||||
if(!isset($value['data']['xmlFlattened'])){
|
||||
$pkey = isset($items['sentAs']) ? $items['sentAs'] : $items['name'];
|
||||
$_searchPath = $searchPath .'/' . $prefix .$pkey;
|
||||
}else{
|
||||
$pkey = $key;
|
||||
$_searchPath = $searchPath;
|
||||
}
|
||||
if($result = $xml -> xpath($_searchPath)){
|
||||
if(is_array($result)){
|
||||
foreach ($result as $subXml){
|
||||
$subXml = simplexml_load_string($subXml -> asXML());
|
||||
$subPrefix = $this->getXpathPrefix($subXml);
|
||||
$array[] = $this->parseXmlByType('//'.$subPrefix. $pkey, $pkey, $items, $subXml, $subPrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $array;
|
||||
}else if($type === 'object'){
|
||||
$properties = $value['properties'];
|
||||
$array = [];
|
||||
foreach ($properties as $pkey => $pvalue){
|
||||
$name = isset($pvalue['sentAs']) ? $pvalue['sentAs'] : $pkey;
|
||||
$array[$pkey] = $this->parseXmlByType($searchPath.'/' . $prefix .$name, $name, $pvalue, $xml, $prefix);
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
|
||||
if($result = $xml -> xpath($searchPath)){
|
||||
if($type === 'boolean'){
|
||||
return strval($result[0]) !== 'false';
|
||||
}else if($type === 'numeric' || $type === 'float'){
|
||||
return floatval($result[0]);
|
||||
}else if($type === 'int' || $type === 'integer'){
|
||||
return intval($result[0]);
|
||||
}else{
|
||||
return strval($result[0]);
|
||||
}
|
||||
}else{
|
||||
if($type === 'boolean'){
|
||||
return false;
|
||||
}else if($type === 'numeric' || $type === 'float' || $type === 'int' || $type === 'integer'){
|
||||
return null;
|
||||
}else{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function parseCommonHeaders($model, $response){
|
||||
$constants = Constants::selectConstants($this -> signature);
|
||||
foreach($constants::COMMON_HEADERS as $key => $value){
|
||||
$model[$value] = $response -> getHeaderLine($key);
|
||||
}
|
||||
}
|
||||
|
||||
protected function parseItems($responseParameters, $model, $response, $body)
|
||||
{
|
||||
$prefix = '';
|
||||
|
||||
$this->parseCommonHeaders($model, $response);
|
||||
|
||||
$closeBody = false;
|
||||
try{
|
||||
foreach ($responseParameters as $key => $value){
|
||||
if(isset($value['location'])){
|
||||
$location = $value['location'];
|
||||
if($location === 'header'){
|
||||
$name = isset($value['sentAs']) ? $value['sentAs'] : $key;
|
||||
$isSet = false;
|
||||
if(isset($value['type'])){
|
||||
$type = $value['type'];
|
||||
if($type === 'object'){
|
||||
$headers = $response -> getHeaders();
|
||||
$temp = [];
|
||||
foreach ($headers as $headerName => $headerValue){
|
||||
if(stripos($headerName, $name) === 0){
|
||||
$metaKey = rawurldecode(substr($headerName, strlen($name)));
|
||||
$temp[$metaKey] = rawurldecode($response -> getHeaderLine($headerName));
|
||||
}
|
||||
}
|
||||
$model[$key] = $temp;
|
||||
$isSet = true;
|
||||
}else{
|
||||
if($response -> hasHeader($name)){
|
||||
if($type === 'boolean'){
|
||||
$model[$key] = ($response -> getHeaderLine($name)) !== 'false';
|
||||
$isSet = true;
|
||||
}else if($type === 'numeric' || $type === 'float'){
|
||||
$model[$key] = floatval($response -> getHeaderLine($name));
|
||||
$isSet = true;
|
||||
}else if($type === 'int' || $type === 'integer'){
|
||||
$model[$key] = intval($response -> getHeaderLine($name));
|
||||
$isSet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!$isSet){
|
||||
$model[$key] = rawurldecode($response -> getHeaderLine($name));
|
||||
}
|
||||
}else if($location === 'xml' && $body !== null){
|
||||
if(!isset($xml) && ($xml = simplexml_load_string($body -> getContents()))){
|
||||
$prefix = $this ->getXpathPrefix($xml);
|
||||
}
|
||||
$closeBody = true;
|
||||
$model[$key] = $this -> parseXmlByType(null, $key,$value, $xml, $prefix);
|
||||
}else if($location === 'body' && $body !== null){
|
||||
if(isset($value['type']) && $value['type'] === 'stream'){
|
||||
$model[$key] = $body;
|
||||
}else{
|
||||
$model[$key] = $body -> getContents();
|
||||
$closeBody = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}finally {
|
||||
if($closeBody && $body !== null){
|
||||
$body -> close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function writeFile($filePath, StreamInterface &$body, $contentLength)
|
||||
{
|
||||
$filePath = iconv('UTF-8', 'GBK', $filePath);
|
||||
if(is_string($filePath) && $filePath !== '')
|
||||
{
|
||||
$fp = null;
|
||||
$dir = dirname($filePath);
|
||||
try{
|
||||
if(!is_dir($dir))
|
||||
{
|
||||
mkdir($dir,0755,true);
|
||||
}
|
||||
|
||||
if(($fp = fopen($filePath, 'w')))
|
||||
{
|
||||
while(!$body->eof())
|
||||
{
|
||||
$str = $body->read($this->chunkSize);
|
||||
fwrite($fp, $str);
|
||||
}
|
||||
fflush($fp);
|
||||
ObsLog::commonLog(DEBUG, "write file %s ok",$filePath);
|
||||
}
|
||||
else{
|
||||
ObsLog::commonLog(ERROR, "open file error,file path:%s",$filePath);
|
||||
}
|
||||
}finally{
|
||||
if($fp){
|
||||
fclose($fp);
|
||||
}
|
||||
$body->close();
|
||||
$body = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function parseXmlToException($body, $obsException){
|
||||
try{
|
||||
$xmlErrorBody = trim($body -> getContents());
|
||||
if($xmlErrorBody && ($xml = simplexml_load_string($xmlErrorBody))){
|
||||
$prefix = $this->getXpathPrefix($xml);
|
||||
if ($tempXml = $xml->xpath('//'.$prefix . 'Code')) {
|
||||
$obsException-> setExceptionCode(strval($tempXml[0]));
|
||||
}
|
||||
if ($tempXml = $xml->xpath('//'.$prefix . 'RequestId')) {
|
||||
$obsException-> setRequestId(strval($tempXml[0]));
|
||||
}
|
||||
if ($tempXml = $xml->xpath('//'.$prefix . 'Message')) {
|
||||
$obsException-> setExceptionMessage(strval($tempXml[0]));
|
||||
}
|
||||
if ($tempXml = $xml->xpath('//'.$prefix . 'HostId')) {
|
||||
$obsException -> setHostId(strval($tempXml[0]));
|
||||
}
|
||||
}
|
||||
}finally{
|
||||
$body -> close();
|
||||
}
|
||||
}
|
||||
|
||||
private function parseXmlToModel($body, $model){
|
||||
try{
|
||||
$xmlErrorBody = trim($body -> getContents());
|
||||
if($xmlErrorBody && ($xml = simplexml_load_string($xmlErrorBody))){
|
||||
$prefix = $this->getXpathPrefix($xml);
|
||||
if ($tempXml = $xml->xpath('//'.$prefix . 'Code')) {
|
||||
$model['Code'] = strval($tempXml[0]);
|
||||
}
|
||||
if ($tempXml = $xml->xpath('//'.$prefix . 'RequestId')) {
|
||||
$model['RequestId'] = strval($tempXml[0]);
|
||||
}
|
||||
|
||||
if ($tempXml = $xml->xpath('//'.$prefix . 'HostId')) {
|
||||
$model['HostId'] = strval($tempXml[0]);
|
||||
}
|
||||
if ($tempXml = $xml->xpath('//'.$prefix . 'Resource')) {
|
||||
$model['Resource'] = strval($tempXml[0]);
|
||||
}
|
||||
|
||||
if ($tempXml = $xml->xpath('//'.$prefix . 'Message')) {
|
||||
$model['Message'] = strval($tempXml[0]);
|
||||
}
|
||||
}
|
||||
}finally {
|
||||
$body -> close();
|
||||
}
|
||||
}
|
||||
|
||||
protected function parseResponse(Model $model, Request $request, Response $response, array $requestConfig)
|
||||
{
|
||||
$statusCode = $response -> getStatusCode();
|
||||
$expectedLength = $response -> getHeaderLine('content-length');
|
||||
$expectedLength = is_numeric($expectedLength) ? floatval($expectedLength) : null;
|
||||
|
||||
$body = new CheckoutStream($response->getBody(), $expectedLength);
|
||||
|
||||
if($statusCode >= 300){
|
||||
if($this-> exceptionResponseMode){
|
||||
$obsException= new ObsException();
|
||||
$obsException-> setRequest($request);
|
||||
$obsException-> setResponse($response);
|
||||
$obsException-> setExceptionType($this->isClientError($response) ? 'client' : 'server');
|
||||
$this->parseXmlToException($body, $obsException);
|
||||
throw $obsException;
|
||||
}else{
|
||||
$this->parseCommonHeaders($model, $response);
|
||||
$this->parseXmlToModel($body, $model);
|
||||
}
|
||||
|
||||
}else{
|
||||
if(!empty($model)){
|
||||
foreach ($model as $key => $value){
|
||||
if($key === 'method'){
|
||||
continue;
|
||||
}
|
||||
if(isset($value['type']) && $value['type'] === 'file'){
|
||||
$this->writeFile($value['value'], $body, $expectedLength);
|
||||
}
|
||||
$model[$key] = $value['value'];
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($requestConfig['responseParameters'])){
|
||||
$responseParameters = $requestConfig['responseParameters'];
|
||||
if(isset($responseParameters['type']) && $responseParameters['type'] === 'object'){
|
||||
$responseParameters = $responseParameters['properties'];
|
||||
}
|
||||
$this->parseItems($responseParameters, $model, $response, $body);
|
||||
}
|
||||
}
|
||||
|
||||
$model['HttpStatusCode'] = $statusCode;
|
||||
$model['Reason'] = $response -> getReasonPhrase();
|
||||
}
|
||||
|
||||
protected function getXpathPrefix($xml)
|
||||
{
|
||||
$namespaces = $xml -> getDocNamespaces();
|
||||
if (isset($namespaces[''])) {
|
||||
$xml->registerXPathNamespace('ns', $namespaces['']);
|
||||
$prefix = 'ns:';
|
||||
} else {
|
||||
$prefix = '';
|
||||
}
|
||||
return $prefix;
|
||||
}
|
||||
|
||||
protected function buildException(Request $request, RequestException $exception, $message)
|
||||
{
|
||||
$response = $exception-> hasResponse() ? $exception-> getResponse() : null;
|
||||
$obsException= new ObsException($message ? $message : $exception-> getMessage());
|
||||
$obsException-> setExceptionType('client');
|
||||
$obsException-> setRequest($request);
|
||||
if($response){
|
||||
$obsException-> setResponse($response);
|
||||
$obsException-> setExceptionType($this->isClientError($response) ? 'client' : 'server');
|
||||
$this->parseXmlToException($response -> getBody(), $obsException);
|
||||
if ($obsException->getRequestId() === null) {
|
||||
$prefix = strcasecmp($this->signature, 'obs' ) === 0 ? 'x-obs-' : 'x-amz-';
|
||||
$requestId = $response->getHeaderLine($prefix . 'request-id');
|
||||
$obsException->setRequestId($requestId);
|
||||
}
|
||||
}
|
||||
return $obsException;
|
||||
}
|
||||
|
||||
protected function parseExceptionAsync(Request $request, RequestException $exception, $message=null)
|
||||
{
|
||||
return $this->buildException($request, $exception, $message);
|
||||
}
|
||||
|
||||
protected function parseException(Model $model, Request $request, RequestException $exception, $message=null)
|
||||
{
|
||||
$response = $exception-> hasResponse() ? $exception-> getResponse() : null;
|
||||
|
||||
if($this-> exceptionResponseMode){
|
||||
throw $this->buildException($request, $exception, $message);
|
||||
}else{
|
||||
if($response){
|
||||
$model['HttpStatusCode'] = $response -> getStatusCode();
|
||||
$model['Reason'] = $response -> getReasonPhrase();
|
||||
$this->parseXmlToModel($response -> getBody(), $model);
|
||||
}else{
|
||||
$model['HttpStatusCode'] = -1;
|
||||
$model['Message'] = $exception -> getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
123
addons/hwobs/library/Obs/Internal/Resource/Constants.php
Normal file
123
addons/hwobs/library/Obs/Internal/Resource/Constants.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs\Internal\Resource;
|
||||
|
||||
class Constants {
|
||||
const ALLOWED_RESOURCE_PARAMTER_NAMES = [
|
||||
'acl',
|
||||
'policy',
|
||||
'torrent',
|
||||
'logging',
|
||||
'location',
|
||||
'storageinfo',
|
||||
'quota',
|
||||
'storagepolicy',
|
||||
'requestpayment',
|
||||
'versions',
|
||||
'versioning',
|
||||
'versionid',
|
||||
'uploads',
|
||||
'uploadid',
|
||||
'partnumber',
|
||||
'website',
|
||||
'notification',
|
||||
'lifecycle',
|
||||
'deletebucket',
|
||||
'delete',
|
||||
'cors',
|
||||
'restore',
|
||||
'tagging',
|
||||
'response-content-type',
|
||||
'response-content-language',
|
||||
'response-expires',
|
||||
'response-cache-control',
|
||||
'response-content-disposition',
|
||||
'response-content-encoding',
|
||||
'x-image-process',
|
||||
|
||||
'backtosource',
|
||||
'storageclass',
|
||||
'replication',
|
||||
'append',
|
||||
'position',
|
||||
'x-oss-process'
|
||||
];
|
||||
const ALLOWED_REQUEST_HTTP_HEADER_METADATA_NAMES = [
|
||||
'content-type',
|
||||
'content-md5',
|
||||
'content-length',
|
||||
'content-language',
|
||||
'expires',
|
||||
'origin',
|
||||
'cache-control',
|
||||
'content-disposition',
|
||||
'content-encoding',
|
||||
'access-control-request-method',
|
||||
'access-control-request-headers',
|
||||
'x-default-storage-class',
|
||||
'location',
|
||||
'date',
|
||||
'etag',
|
||||
'range',
|
||||
'host',
|
||||
'if-modified-since',
|
||||
'if-unmodified-since',
|
||||
'if-match',
|
||||
'if-none-match',
|
||||
'last-modified',
|
||||
'content-range',
|
||||
|
||||
'success-action-redirect'
|
||||
];
|
||||
const ALLOWED_RESPONSE_HTTP_HEADER_METADATA_NAMES = [
|
||||
'content-type',
|
||||
'content-md5',
|
||||
'content-length',
|
||||
'content-language',
|
||||
'expires',
|
||||
'origin',
|
||||
'cache-control',
|
||||
'content-disposition',
|
||||
'content-encoding',
|
||||
'x-default-storage-class',
|
||||
'location',
|
||||
'date',
|
||||
'etag',
|
||||
'host',
|
||||
'last-modified',
|
||||
'content-range',
|
||||
'x-reserved',
|
||||
'access-control-allow-origin',
|
||||
'access-control-allow-headers',
|
||||
'access-control-max-age',
|
||||
'access-control-allow-methods',
|
||||
'access-control-expose-headers',
|
||||
'connection'
|
||||
];
|
||||
|
||||
public static function selectConstants($signature) {
|
||||
$signature = (strcasecmp ( $signature, 'obs' ) === 0) ? 'OBS' : 'V2';
|
||||
return __NAMESPACE__ . '\\' . $signature . 'Constants';
|
||||
}
|
||||
|
||||
public static function selectRequestResource($signature) {
|
||||
$signature = (strcasecmp ( $signature, 'obs' ) === 0) ? 'OBS' : 'V2';
|
||||
return (__NAMESPACE__ . '\\' . $signature . 'RequestResource');
|
||||
}
|
||||
|
||||
}
|
||||
35
addons/hwobs/library/Obs/Internal/Resource/OBSConstants.php
Normal file
35
addons/hwobs/library/Obs/Internal/Resource/OBSConstants.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs\Internal\Resource;
|
||||
|
||||
class OBSConstants extends Constants {
|
||||
const FLAG = 'OBS';
|
||||
const METADATA_PREFIX = 'x-obs-meta-';
|
||||
const HEADER_PREFIX = 'x-obs-';
|
||||
const ALTERNATIVE_DATE_HEADER = 'x-obs-date';
|
||||
const SECURITY_TOKEN_HEAD = 'x-obs-security-token';
|
||||
const TEMPURL_AK_HEAD = 'AccessKeyId';
|
||||
|
||||
const COMMON_HEADERS = [
|
||||
'content-length' => 'ContentLength',
|
||||
'date' => 'Date',
|
||||
'x-obs-request-id' => 'RequestId',
|
||||
'x-obs-id-2' => 'Id2',
|
||||
'x-reserved' => 'Reserved'
|
||||
];
|
||||
}
|
||||
4071
addons/hwobs/library/Obs/Internal/Resource/OBSRequestResource.php
Normal file
4071
addons/hwobs/library/Obs/Internal/Resource/OBSRequestResource.php
Normal file
File diff suppressed because it is too large
Load Diff
38
addons/hwobs/library/Obs/Internal/Resource/V2Constants.php
Normal file
38
addons/hwobs/library/Obs/Internal/Resource/V2Constants.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs\Internal\Resource;
|
||||
|
||||
class V2Constants extends Constants {
|
||||
const FLAG = 'AWS';
|
||||
const METADATA_PREFIX = 'x-amz-meta-';
|
||||
const HEADER_PREFIX = 'x-amz-';
|
||||
const ALTERNATIVE_DATE_HEADER = 'x-amz-date';
|
||||
const SECURITY_TOKEN_HEAD = 'x-amz-security-token';
|
||||
const TEMPURL_AK_HEAD = 'AWSAccessKeyId';
|
||||
|
||||
const GROUP_ALL_USERS_PREFIX = 'http://acs.amazonaws.com/groups/global/';
|
||||
const GROUP_AUTHENTICATED_USERS_PREFIX = 'http://acs.amazonaws.com/groups/global/';
|
||||
const GROUP_LOG_DELIVERY_PREFIX = 'http://acs.amazonaws.com/groups/s3/';
|
||||
|
||||
const COMMON_HEADERS = [
|
||||
'content-length' => 'ContentLength',
|
||||
'date' => 'Date',
|
||||
'x-amz-request-id' => 'RequestId',
|
||||
'x-amz-id-2' => 'Id2',
|
||||
'x-reserved' => 'Reserved'
|
||||
];
|
||||
}
|
||||
3890
addons/hwobs/library/Obs/Internal/Resource/V2RequestResource.php
Normal file
3890
addons/hwobs/library/Obs/Internal/Resource/V2RequestResource.php
Normal file
File diff suppressed because it is too large
Load Diff
718
addons/hwobs/library/Obs/Internal/SendRequestTrait.php
Normal file
718
addons/hwobs/library/Obs/Internal/SendRequestTrait.php
Normal file
@@ -0,0 +1,718 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs\Internal;
|
||||
|
||||
use GuzzleHttp\Psr7;
|
||||
use Obs\Log\ObsLog;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use Obs\Internal\Common\Model;
|
||||
use Obs\Internal\Resource\V2Constants;
|
||||
use Obs\ObsException;
|
||||
use Obs\Internal\Signature\V4Signature;
|
||||
use Obs\Internal\Signature\DefaultSignature;
|
||||
use GuzzleHttp\Client;
|
||||
use Obs\Internal\Resource\Constants;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Obs\Internal\Resource\V2RequestResource;
|
||||
|
||||
trait SendRequestTrait
|
||||
{
|
||||
protected $ak;
|
||||
|
||||
protected $sk;
|
||||
|
||||
protected $securityToken = false;
|
||||
|
||||
protected $endpoint = '';
|
||||
|
||||
protected $pathStyle = false;
|
||||
|
||||
protected $region = 'region';
|
||||
|
||||
protected $signature = 'v2';
|
||||
|
||||
protected $sslVerify = false;
|
||||
|
||||
protected $maxRetryCount = 3;
|
||||
|
||||
protected $timeout = 0;
|
||||
|
||||
protected $socketTimeout = 60;
|
||||
|
||||
protected $connectTimeout = 60;
|
||||
|
||||
protected $isCname = false;
|
||||
|
||||
/** @var Client */
|
||||
protected $httpClient;
|
||||
|
||||
public function createSignedUrl(array $args=[]){
|
||||
if (strcasecmp($this -> signature, 'v4') === 0) {
|
||||
return $this -> createV4SignedUrl($args);
|
||||
}
|
||||
return $this->createCommonSignedUrl($args, $this->signature);
|
||||
}
|
||||
|
||||
public function createV2SignedUrl(array $args=[]) {
|
||||
return $this->createCommonSignedUrl($args, 'v2');
|
||||
}
|
||||
|
||||
private function createCommonSignedUrl(array $args=[], $signature = '') {
|
||||
if(!isset($args['Method'])){
|
||||
$obsException = new ObsException('Method param must be specified, allowed values: GET | PUT | HEAD | POST | DELETE | OPTIONS');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
$method = strval($args['Method']);
|
||||
$bucketName = isset($args['Bucket'])? strval($args['Bucket']): null;
|
||||
$objectKey = isset($args['Key'])? strval($args['Key']): null;
|
||||
$specialParam = isset($args['SpecialParam'])? strval($args['SpecialParam']): null;
|
||||
$expires = isset($args['Expires']) && is_numeric($args['Expires']) ? intval($args['Expires']): 300;
|
||||
|
||||
$headers = [];
|
||||
if(isset($args['Headers']) && is_array($args['Headers']) ){
|
||||
foreach ($args['Headers'] as $key => $val){
|
||||
if(is_string($key) && $key !== ''){
|
||||
$headers[$key] = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
$queryParams = [];
|
||||
if(isset($args['QueryParams']) && is_array($args['QueryParams']) ){
|
||||
foreach ($args['QueryParams'] as $key => $val){
|
||||
if(is_string($key) && $key !== ''){
|
||||
$queryParams[$key] = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$constants = Constants::selectConstants($signature);
|
||||
if($this->securityToken && !isset($queryParams[$constants::SECURITY_TOKEN_HEAD])){
|
||||
$queryParams[$constants::SECURITY_TOKEN_HEAD] = $this->securityToken;
|
||||
}
|
||||
|
||||
$sign = new DefaultSignature($this->ak, $this->sk, $this->pathStyle, $this->endpoint, $method, $this->signature, $this->securityToken, $this->isCname);
|
||||
|
||||
$url = parse_url($this->endpoint);
|
||||
$host = $url['host'];
|
||||
|
||||
$result = '';
|
||||
|
||||
if($bucketName){
|
||||
if($this-> pathStyle){
|
||||
$result = '/' . $bucketName;
|
||||
}else{
|
||||
$host = $this->isCname ? $host : $bucketName . '.' . $host;
|
||||
}
|
||||
}
|
||||
|
||||
$headers['Host'] = $host;
|
||||
|
||||
if($objectKey){
|
||||
$objectKey = $sign ->urlencodeWithSafe($objectKey);
|
||||
$result .= '/' . $objectKey;
|
||||
}
|
||||
|
||||
$result .= '?';
|
||||
|
||||
if($specialParam){
|
||||
$queryParams[$specialParam] = '';
|
||||
}
|
||||
|
||||
$queryParams[$constants::TEMPURL_AK_HEAD] = $this->ak;
|
||||
|
||||
|
||||
if(!is_numeric($expires) || $expires < 0){
|
||||
$expires = 300;
|
||||
}
|
||||
$expires = intval($expires) + intval(microtime(true));
|
||||
|
||||
$queryParams['Expires'] = strval($expires);
|
||||
|
||||
$_queryParams = [];
|
||||
|
||||
foreach ($queryParams as $key => $val){
|
||||
$key = $sign -> urlencodeWithSafe($key);
|
||||
$val = $sign -> urlencodeWithSafe($val);
|
||||
$_queryParams[$key] = $val;
|
||||
$result .= $key;
|
||||
if($val){
|
||||
$result .= '=' . $val;
|
||||
}
|
||||
$result .= '&';
|
||||
}
|
||||
|
||||
$canonicalstring = $sign ->makeCanonicalstring($method, $headers, $_queryParams, $bucketName, $objectKey, $expires);
|
||||
$signatureContent = base64_encode(hash_hmac('sha1', $canonicalstring, $this->sk, true));
|
||||
|
||||
$result .= 'Signature=' . $sign->urlencodeWithSafe($signatureContent);
|
||||
|
||||
$model = new Model();
|
||||
$model['ActualSignedRequestHeaders'] = $headers;
|
||||
$model['SignedUrl'] = $url['scheme'] . '://' . $host . ':' . (isset($url['port']) ? $url['port'] : (strtolower($url['scheme']) === 'https' ? '443' : '80')) . $result;
|
||||
return $model;
|
||||
}
|
||||
|
||||
public function createV4SignedUrl(array $args=[]){
|
||||
if(!isset($args['Method'])){
|
||||
$obsException= new ObsException('Method param must be specified, allowed values: GET | PUT | HEAD | POST | DELETE | OPTIONS');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
$method = strval($args['Method']);
|
||||
$bucketName = isset($args['Bucket'])? strval($args['Bucket']): null;
|
||||
$objectKey = isset($args['Key'])? strval($args['Key']): null;
|
||||
$specialParam = isset($args['SpecialParam'])? strval($args['SpecialParam']): null;
|
||||
$expires = isset($args['Expires']) && is_numeric($args['Expires']) ? intval($args['Expires']): 300;
|
||||
$headers = [];
|
||||
if(isset($args['Headers']) && is_array($args['Headers']) ){
|
||||
foreach ($args['Headers'] as $key => $val){
|
||||
if(is_string($key) && $key !== ''){
|
||||
$headers[$key] = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$queryParams = [];
|
||||
if(isset($args['QueryParams']) && is_array($args['QueryParams']) ){
|
||||
foreach ($args['QueryParams'] as $key => $val){
|
||||
if(is_string($key) && $key !== ''){
|
||||
$queryParams[$key] = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($this->securityToken && !isset($queryParams['x-amz-security-token'])){
|
||||
$queryParams['x-amz-security-token'] = $this->securityToken;
|
||||
}
|
||||
|
||||
$v4 = new V4Signature($this->ak, $this->sk, $this->pathStyle, $this->endpoint, $this->region, $method, $this->signature, $this->securityToken, $this->isCname);
|
||||
|
||||
$url = parse_url($this->endpoint);
|
||||
$host = $url['host'];
|
||||
|
||||
$result = '';
|
||||
|
||||
if($bucketName){
|
||||
if($this-> pathStyle){
|
||||
$result = '/' . $bucketName;
|
||||
}else{
|
||||
$host = $this->isCname ? $host : $bucketName . '.' . $host;
|
||||
}
|
||||
}
|
||||
|
||||
$headers['Host'] = $host;
|
||||
|
||||
if($objectKey){
|
||||
$objectKey = $v4 -> urlencodeWithSafe($objectKey);
|
||||
$result .= '/' . $objectKey;
|
||||
}
|
||||
|
||||
$result .= '?';
|
||||
|
||||
if($specialParam){
|
||||
$queryParams[$specialParam] = '';
|
||||
}
|
||||
|
||||
if(!is_numeric($expires) || $expires < 0){
|
||||
$expires = 300;
|
||||
}
|
||||
|
||||
$expires = strval($expires);
|
||||
|
||||
$date = isset($headers['date']) ? $headers['date'] : (isset($headers['Date']) ? $headers['Date'] : null);
|
||||
|
||||
$timestamp = $date ? date_create_from_format('D, d M Y H:i:s \G\M\T', $date, new \DateTimeZone ('UTC')) -> getTimestamp()
|
||||
:time();
|
||||
|
||||
$longDate = gmdate('Ymd\THis\Z', $timestamp);
|
||||
$shortDate = substr($longDate, 0, 8);
|
||||
|
||||
$headers['host'] = $host;
|
||||
if(isset($url['port'])){
|
||||
$port = $url['port'];
|
||||
if($port !== 443 && $port !== 80){
|
||||
$headers['host'] = $headers['host'] . ':' . $port;
|
||||
}
|
||||
}
|
||||
|
||||
$signedHeaders = $v4 -> getSignedHeaders($headers);
|
||||
|
||||
$queryParams['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256';
|
||||
$queryParams['X-Amz-Credential'] = $v4 -> getCredential($shortDate);
|
||||
$queryParams['X-Amz-Date'] = $longDate;
|
||||
$queryParams['X-Amz-Expires'] = $expires;
|
||||
$queryParams['X-Amz-SignedHeaders'] = $signedHeaders;
|
||||
|
||||
$_queryParams = [];
|
||||
|
||||
foreach ($queryParams as $key => $val){
|
||||
$key = rawurlencode($key);
|
||||
$val = rawurlencode($val);
|
||||
$_queryParams[$key] = $val;
|
||||
$result .= $key;
|
||||
if($val){
|
||||
$result .= '=' . $val;
|
||||
}
|
||||
$result .= '&';
|
||||
}
|
||||
|
||||
$canonicalstring = $v4 -> makeCanonicalstring($method, $headers, $_queryParams, $bucketName, $objectKey, $signedHeaders, 'UNSIGNED-PAYLOAD');
|
||||
|
||||
$signatureContent = $v4 -> getSignature($canonicalstring, $longDate, $shortDate);
|
||||
|
||||
$result .= 'X-Amz-Signature=' . $v4 -> urlencodeWithSafe($signatureContent);
|
||||
|
||||
$model = new Model();
|
||||
$model['ActualSignedRequestHeaders'] = $headers;
|
||||
$model['SignedUrl'] = $url['scheme'] . '://' . $host . ':' . (isset($url['port']) ? $url['port'] : (strtolower($url['scheme']) === 'https' ? '443' : '80')) . $result;
|
||||
return $model;
|
||||
}
|
||||
|
||||
public function createPostSignature(array $args=[]) {
|
||||
if (strcasecmp($this -> signature, 'v4') === 0) {
|
||||
return $this -> createV4PostSignature($args);
|
||||
}
|
||||
|
||||
$bucketName = isset($args['Bucket'])? strval($args['Bucket']): null;
|
||||
$objectKey = isset($args['Key'])? strval($args['Key']): null;
|
||||
$expires = isset($args['Expires']) && is_numeric($args['Expires']) ? intval($args['Expires']): 300;
|
||||
|
||||
$formParams = [];
|
||||
|
||||
if(isset($args['FormParams']) && is_array($args['FormParams'])){
|
||||
foreach ($args['FormParams'] as $key => $val){
|
||||
$formParams[$key] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
$constants = Constants::selectConstants($this -> signature);
|
||||
if($this->securityToken && !isset($formParams[$constants::SECURITY_TOKEN_HEAD])){
|
||||
$formParams[$constants::SECURITY_TOKEN_HEAD] = $this->securityToken;
|
||||
}
|
||||
|
||||
$timestamp = time();
|
||||
$expires = gmdate('Y-m-d\TH:i:s\Z', $timestamp + $expires);
|
||||
|
||||
if($bucketName){
|
||||
$formParams['bucket'] = $bucketName;
|
||||
}
|
||||
|
||||
if($objectKey){
|
||||
$formParams['key'] = $objectKey;
|
||||
}
|
||||
|
||||
$policy = [];
|
||||
|
||||
$policy[] = '{"expiration":"';
|
||||
$policy[] = $expires;
|
||||
$policy[] = '", "conditions":[';
|
||||
|
||||
$matchAnyBucket = true;
|
||||
$matchAnyKey = true;
|
||||
|
||||
$conditionAllowKeys = ['acl', 'bucket', 'key', 'success_action_redirect', 'redirect', 'success_action_status'];
|
||||
|
||||
foreach($formParams as $key => $val){
|
||||
if($key){
|
||||
$key = strtolower(strval($key));
|
||||
|
||||
if($key === 'bucket'){
|
||||
$matchAnyBucket = false;
|
||||
}else if($key === 'key'){
|
||||
$matchAnyKey = false;
|
||||
}
|
||||
|
||||
if(!in_array($key, Constants::ALLOWED_REQUEST_HTTP_HEADER_METADATA_NAMES) && strpos($key, $constants::HEADER_PREFIX) !== 0 && !in_array($key, $conditionAllowKeys)){
|
||||
$key = $constants::METADATA_PREFIX . $key;
|
||||
}
|
||||
|
||||
$policy[] = '{"';
|
||||
$policy[] = $key;
|
||||
$policy[] = '":"';
|
||||
$policy[] = $val !== null ? strval($val) : '';
|
||||
$policy[] = '"},';
|
||||
}
|
||||
}
|
||||
|
||||
if($matchAnyBucket){
|
||||
$policy[] = '["starts-with", "$bucket", ""],';
|
||||
}
|
||||
|
||||
if($matchAnyKey){
|
||||
$policy[] = '["starts-with", "$key", ""],';
|
||||
}
|
||||
|
||||
$policy[] = ']}';
|
||||
|
||||
$originPolicy = implode('', $policy);
|
||||
|
||||
$policy = base64_encode($originPolicy);
|
||||
|
||||
$signatureContent = base64_encode(hash_hmac('sha1', $policy, $this->sk, true));
|
||||
|
||||
$model = new Model();
|
||||
$model['OriginPolicy'] = $originPolicy;
|
||||
$model['Policy'] = $policy;
|
||||
$model['Signature'] = $signatureContent;
|
||||
return $model;
|
||||
}
|
||||
|
||||
public function createV4PostSignature(array $args=[]){
|
||||
$bucketName = isset($args['Bucket'])? strval($args['Bucket']): null;
|
||||
$objectKey = isset($args['Key'])? strval($args['Key']): null;
|
||||
$expires = isset($args['Expires']) && is_numeric($args['Expires']) ? intval($args['Expires']): 300;
|
||||
|
||||
$formParams = [];
|
||||
|
||||
if(isset($args['FormParams']) && is_array($args['FormParams'])){
|
||||
foreach ($args['FormParams'] as $key => $val){
|
||||
$formParams[$key] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
if($this->securityToken && !isset($formParams['x-amz-security-token'])){
|
||||
$formParams['x-amz-security-token'] = $this->securityToken;
|
||||
}
|
||||
|
||||
$timestamp = time();
|
||||
$longDate = gmdate('Ymd\THis\Z', $timestamp);
|
||||
$shortDate = substr($longDate, 0, 8);
|
||||
|
||||
$credential = sprintf('%s/%s/%s/s3/aws4_request', $this->ak, $shortDate, $this->region);
|
||||
|
||||
$expires = gmdate('Y-m-d\TH:i:s\Z', $timestamp + $expires);
|
||||
|
||||
$formParams['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256';
|
||||
$formParams['X-Amz-Date'] = $longDate;
|
||||
$formParams['X-Amz-Credential'] = $credential;
|
||||
|
||||
if($bucketName){
|
||||
$formParams['bucket'] = $bucketName;
|
||||
}
|
||||
|
||||
if($objectKey){
|
||||
$formParams['key'] = $objectKey;
|
||||
}
|
||||
|
||||
$policy = [];
|
||||
|
||||
$policy[] = '{"expiration":"';
|
||||
$policy[] = $expires;
|
||||
$policy[] = '", "conditions":[';
|
||||
|
||||
$matchAnyBucket = true;
|
||||
$matchAnyKey = true;
|
||||
|
||||
$conditionAllowKeys = ['acl', 'bucket', 'key', 'success_action_redirect', 'redirect', 'success_action_status'];
|
||||
|
||||
foreach($formParams as $key => $val){
|
||||
if($key){
|
||||
$key = strtolower(strval($key));
|
||||
|
||||
if($key === 'bucket'){
|
||||
$matchAnyBucket = false;
|
||||
}else if($key === 'key'){
|
||||
$matchAnyKey = false;
|
||||
}
|
||||
|
||||
if(!in_array($key, Constants::ALLOWED_REQUEST_HTTP_HEADER_METADATA_NAMES) && strpos($key, V2Constants::HEADER_PREFIX) !== 0 && !in_array($key, $conditionAllowKeys)){
|
||||
$key = V2Constants::METADATA_PREFIX . $key;
|
||||
}
|
||||
|
||||
$policy[] = '{"';
|
||||
$policy[] = $key;
|
||||
$policy[] = '":"';
|
||||
$policy[] = $val !== null ? strval($val) : '';
|
||||
$policy[] = '"},';
|
||||
}
|
||||
}
|
||||
|
||||
if($matchAnyBucket){
|
||||
$policy[] = '["starts-with", "$bucket", ""],';
|
||||
}
|
||||
|
||||
if($matchAnyKey){
|
||||
$policy[] = '["starts-with", "$key", ""],';
|
||||
}
|
||||
|
||||
$policy[] = ']}';
|
||||
|
||||
$originPolicy = implode('', $policy);
|
||||
|
||||
$policy = base64_encode($originPolicy);
|
||||
|
||||
$dateKey = hash_hmac('sha256', $shortDate, 'AWS4' . $this -> sk, true);
|
||||
$regionKey = hash_hmac('sha256', $this->region, $dateKey, true);
|
||||
$serviceKey = hash_hmac('sha256', 's3', $regionKey, true);
|
||||
$signingKey = hash_hmac('sha256', 'aws4_request', $serviceKey, true);
|
||||
$signatureContent = hash_hmac('sha256', $policy, $signingKey);
|
||||
|
||||
$model = new Model();
|
||||
$model['OriginPolicy'] = $originPolicy;
|
||||
$model['Policy'] = $policy;
|
||||
$model['Algorithm'] = $formParams['X-Amz-Algorithm'];
|
||||
$model['Credential'] = $formParams['X-Amz-Credential'];
|
||||
$model['Date'] = $formParams['X-Amz-Date'];
|
||||
$model['Signature'] = $signatureContent;
|
||||
return $model;
|
||||
}
|
||||
|
||||
public function __call($originMethod, $args)
|
||||
{
|
||||
$method = $originMethod;
|
||||
|
||||
$contents = Constants::selectRequestResource($this->signature);
|
||||
$resource = &$contents::$RESOURCE_ARRAY;
|
||||
$async = false;
|
||||
if(strpos($method, 'Async') === (strlen($method) - 5)){
|
||||
$method = substr($method, 0, strlen($method) - 5);
|
||||
$async = true;
|
||||
}
|
||||
|
||||
if(isset($resource['aliases'][$method])){
|
||||
$method = $resource['aliases'][$method];
|
||||
}
|
||||
|
||||
$method = lcfirst($method);
|
||||
|
||||
|
||||
$operation = isset($resource['operations'][$method]) ?
|
||||
$resource['operations'][$method] : null;
|
||||
|
||||
if(!$operation){
|
||||
ObsLog::commonLog(WARNING, 'unknow method ' . $originMethod);
|
||||
$obsException= new ObsException('unknow method '. $originMethod);
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
|
||||
$start = microtime(true);
|
||||
if(!$async){
|
||||
ObsLog::commonLog(INFO, 'enter method '. $originMethod. '...');
|
||||
$model = new Model();
|
||||
$model['method'] = $method;
|
||||
$params = empty($args) ? [] : $args[0];
|
||||
$this->checkMimeType($method, $params);
|
||||
$this->doRequest($model, $operation, $params);
|
||||
ObsLog::commonLog(INFO, 'obsclient cost ' . round(microtime(true) - $start, 3) * 1000 . ' ms to execute '. $originMethod);
|
||||
unset($model['method']);
|
||||
return $model;
|
||||
}else{
|
||||
if(empty($args) || !(is_callable($callback = $args[count($args) -1]))){
|
||||
ObsLog::commonLog(WARNING, 'async method ' . $originMethod . ' must pass a CallbackInterface as param');
|
||||
$obsException= new ObsException('async method ' . $originMethod . ' must pass a CallbackInterface as param');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
ObsLog::commonLog(INFO, 'enter method '. $originMethod. '...');
|
||||
$params = count($args) === 1 ? [] : $args[0];
|
||||
$this->checkMimeType($method, $params);
|
||||
$model = new Model();
|
||||
$model['method'] = $method;
|
||||
return $this->doRequestAsync($model, $operation, $params, $callback, $start, $originMethod);
|
||||
}
|
||||
}
|
||||
|
||||
private function checkMimeType($method, &$params){
|
||||
// fix bug that guzzlehttp lib will add the content-type if not set
|
||||
if(($method === 'putObject' || $method === 'initiateMultipartUpload' || $method === 'uploadPart') && (!isset($params['ContentType']) || $params['ContentType'] === null)){
|
||||
if(isset($params['Key'])){
|
||||
$params['ContentType'] = \GuzzleHttp\Psr7\MimeType::fromFilename($params['Key']);
|
||||
}
|
||||
|
||||
if((!isset($params['ContentType']) || $params['ContentType'] === null) && isset($params['SourceFile'])){
|
||||
$params['ContentType'] = \GuzzleHttp\Psr7\MimeType::fromFilename($params['SourceFile']);
|
||||
}
|
||||
|
||||
if(!isset($params['ContentType']) || $params['ContentType'] === null){
|
||||
$params['ContentType'] = 'binary/octet-stream';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function makeRequest($model, &$operation, $params, $endpoint = null)
|
||||
{
|
||||
if($endpoint === null){
|
||||
$endpoint = $this->endpoint;
|
||||
}
|
||||
$signatureInterface = strcasecmp($this-> signature, 'v4') === 0 ?
|
||||
new V4Signature($this->ak, $this->sk, $this->pathStyle, $endpoint, $this->region, $model['method'], $this->signature, $this->securityToken, $this->isCname) :
|
||||
new DefaultSignature($this->ak, $this->sk, $this->pathStyle, $endpoint, $model['method'], $this->signature, $this->securityToken, $this->isCname);
|
||||
$authResult = $signatureInterface -> doAuth($operation, $params, $model);
|
||||
$httpMethod = $authResult['method'];
|
||||
ObsLog::commonLog(DEBUG, 'perform '. strtolower($httpMethod) . ' request with url ' . $authResult['requestUrl']);
|
||||
ObsLog::commonLog(DEBUG, 'cannonicalRequest:' . $authResult['cannonicalRequest']);
|
||||
ObsLog::commonLog(DEBUG, 'request headers ' . var_export($authResult['headers'],true));
|
||||
$authResult['headers']['User-Agent'] = self::default_user_agent();
|
||||
if($model['method'] === 'putObject'){
|
||||
$model['ObjectURL'] = ['value' => $authResult['requestUrl']];
|
||||
}
|
||||
return new Request($httpMethod, $authResult['requestUrl'], $authResult['headers'], $authResult['body']);
|
||||
}
|
||||
|
||||
|
||||
protected function doRequest($model, &$operation, $params, $endpoint = null)
|
||||
{
|
||||
$request = $this -> makeRequest($model, $operation, $params, $endpoint);
|
||||
$this->sendRequest($model, $operation, $params, $request);
|
||||
}
|
||||
|
||||
protected function sendRequest($model, &$operation, $params, $request, $requestCount = 1)
|
||||
{
|
||||
$start = microtime(true);
|
||||
$saveAsStream = false;
|
||||
if(isset($operation['stream']) && $operation['stream']){
|
||||
$saveAsStream = isset($params['SaveAsStream']) ? $params['SaveAsStream'] : false;
|
||||
|
||||
if(isset($params['SaveAsFile'])){
|
||||
if($saveAsStream){
|
||||
$obsException = new ObsException('SaveAsStream cannot be used with SaveAsFile together');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
$saveAsStream = true;
|
||||
}
|
||||
if(isset($params['FilePath'])){
|
||||
if($saveAsStream){
|
||||
$obsException = new ObsException('SaveAsStream cannot be used with FilePath together');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
$saveAsStream = true;
|
||||
}
|
||||
|
||||
if(isset($params['SaveAsFile']) && isset($params['FilePath'])){
|
||||
$obsException = new ObsException('SaveAsFile cannot be used with FilePath together');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
}
|
||||
|
||||
$promise = $this->httpClient->sendAsync($request, ['stream' => $saveAsStream])->then(
|
||||
function(Response $response) use ($model, $operation, $params, $request, $requestCount, $start){
|
||||
|
||||
ObsLog::commonLog(INFO, 'http request cost ' . round(microtime(true) - $start, 3) * 1000 . ' ms');
|
||||
$statusCode = $response -> getStatusCode();
|
||||
$readable = isset($params['Body']) && ($params['Body'] instanceof StreamInterface || is_resource($params['Body']));
|
||||
if($statusCode >= 300 && $statusCode <400 && $statusCode !== 304 && !$readable && $requestCount <= $this->maxRetryCount){
|
||||
if($location = $response -> getHeaderLine('location')){
|
||||
$url = parse_url($this->endpoint);
|
||||
$newUrl = parse_url($location);
|
||||
$scheme = (isset($newUrl['scheme']) ? $newUrl['scheme'] : $url['scheme']);
|
||||
$defaultPort = strtolower($scheme) === 'https' ? '443' : '80';
|
||||
$this->doRequest($model, $operation, $params, $scheme. '://' . $newUrl['host'] .
|
||||
':' . (isset($newUrl['port']) ? $newUrl['port'] : $defaultPort));
|
||||
return;
|
||||
}
|
||||
}
|
||||
$this -> parseResponse($model, $request, $response, $operation);
|
||||
},
|
||||
function ($exception) use ($model, $operation, $params, $request, $requestCount, $start) {
|
||||
|
||||
ObsLog::commonLog(INFO, 'http request cost ' . round(microtime(true) - $start, 3) * 1000 . ' ms');
|
||||
$message = null;
|
||||
if($exception instanceof ConnectException){
|
||||
if($requestCount <= $this->maxRetryCount){
|
||||
$this -> sendRequest($model, $operation, $params, $request, $requestCount + 1);
|
||||
return;
|
||||
}else{
|
||||
$message = 'Exceeded retry limitation, max retry count:'. $this->maxRetryCount . ', error message:' . $exception -> getMessage();
|
||||
}
|
||||
}
|
||||
$this -> parseException($model, $request, $exception, $message);
|
||||
});
|
||||
$promise -> wait();
|
||||
}
|
||||
|
||||
|
||||
protected function doRequestAsync($model, &$operation, $params, $callback, $startAsync, $originMethod, $endpoint = null){
|
||||
$request = $this -> makeRequest($model, $operation, $params, $endpoint);
|
||||
return $this->sendRequestAsync($model, $operation, $params, $callback, $startAsync, $originMethod, $request);
|
||||
}
|
||||
|
||||
protected function sendRequestAsync($model, &$operation, $params, $callback, $startAsync, $originMethod, $request, $requestCount = 1)
|
||||
{
|
||||
$start = microtime(true);
|
||||
|
||||
$saveAsStream = false;
|
||||
if(isset($operation['stream']) && $operation['stream']){
|
||||
$saveAsStream = isset($params['SaveAsStream']) ? $params['SaveAsStream'] : false;
|
||||
|
||||
if($saveAsStream){
|
||||
if(isset($params['SaveAsFile'])){
|
||||
$obsException = new ObsException('SaveAsStream cannot be used with SaveAsFile together');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
if(isset($params['FilePath'])){
|
||||
$obsException = new ObsException('SaveAsStream cannot be used with FilePath together');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($params['SaveAsFile']) && isset($params['FilePath'])){
|
||||
$obsException = new ObsException('SaveAsFile cannot be used with FilePath together');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
}
|
||||
return $this->httpClient->sendAsync($request, ['stream' => $saveAsStream])->then(
|
||||
function(Response $response) use ($model, $operation, $params, $callback, $startAsync, $originMethod, $request, $start){
|
||||
ObsLog::commonLog(INFO, 'http request cost ' . round(microtime(true) - $start, 3) * 1000 . ' ms');
|
||||
$statusCode = $response -> getStatusCode();
|
||||
$readable = isset($params['Body']) && ($params['Body'] instanceof StreamInterface || is_resource($params['Body']));
|
||||
if($statusCode === 307 && !$readable){
|
||||
if($location = $response -> getHeaderLine('location')){
|
||||
$url = parse_url($this->endpoint);
|
||||
$newUrl = parse_url($location);
|
||||
$scheme = (isset($newUrl['scheme']) ? $newUrl['scheme'] : $url['scheme']);
|
||||
$defaultPort = strtolower($scheme) === 'https' ? '443' : '80';
|
||||
return $this->doRequestAsync($model, $operation, $params, $callback, $startAsync, $originMethod, $scheme. '://' . $newUrl['host'] .
|
||||
':' . (isset($newUrl['port']) ? $newUrl['port'] : $defaultPort));
|
||||
}
|
||||
}
|
||||
$this -> parseResponse($model, $request, $response, $operation);
|
||||
ObsLog::commonLog(INFO, 'obsclient cost ' . round(microtime(true) - $startAsync, 3) * 1000 . ' ms to execute '. $originMethod);
|
||||
unset($model['method']);
|
||||
$callback(null, $model);
|
||||
},
|
||||
function ($exception) use ($model, $operation, $params, $callback, $startAsync, $originMethod, $request, $start, $requestCount){
|
||||
ObsLog::commonLog(INFO, 'http request cost ' . round(microtime(true) - $start, 3) * 1000 . ' ms');
|
||||
$message = null;
|
||||
if($exception instanceof ConnectException){
|
||||
if($requestCount <= $this->maxRetryCount){
|
||||
return $this -> sendRequestAsync($model, $operation, $params, $callback, $startAsync, $originMethod, $request, $requestCount + 1);
|
||||
}else{
|
||||
$message = 'Exceeded retry limitation, max retry count:'. $this->maxRetryCount . ', error message:' . $exception -> getMessage();
|
||||
}
|
||||
}
|
||||
$obsException = $this -> parseExceptionAsync($request, $exception, $message);
|
||||
ObsLog::commonLog(INFO, 'obsclient cost ' . round(microtime(true) - $startAsync, 3) * 1000 . ' ms to execute '. $originMethod);
|
||||
$callback($obsException, null);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,462 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs\Internal\Signature;
|
||||
|
||||
use Obs\Log\ObsLog;
|
||||
use Obs\Internal\Resource\Constants;
|
||||
use Obs\ObsException;
|
||||
use Obs\Internal\Common\SchemaFormatter;
|
||||
use GuzzleHttp\Psr7\Stream;
|
||||
use Obs\Internal\Common\Model;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Obs\Internal\Common\ObsTransform;
|
||||
use Obs\Internal\Common\V2Transform;
|
||||
|
||||
abstract class AbstractSignature implements SignatureInterface
|
||||
{
|
||||
|
||||
protected $ak;
|
||||
|
||||
protected $sk;
|
||||
|
||||
protected $pathStyle;
|
||||
|
||||
protected $endpoint;
|
||||
|
||||
protected $methodName;
|
||||
|
||||
protected $securityToken;
|
||||
|
||||
protected $signature;
|
||||
|
||||
protected $isCname;
|
||||
|
||||
public static function urlencodeWithSafe($val, $safe='/'){
|
||||
if(($len = strlen($val)) === 0){
|
||||
return '';
|
||||
}
|
||||
$buffer = [];
|
||||
for ($index=0;$index<$len;$index++){
|
||||
$str = $val[$index];
|
||||
$buffer[] = !($pos = strpos($safe, $str)) && $pos !== 0 ? rawurlencode($str) : $str;
|
||||
}
|
||||
return implode('', $buffer);
|
||||
}
|
||||
|
||||
protected function __construct($ak, $sk, $pathStyle, $endpoint, $methodName, $signature, $securityToken=false, $isCname=false)
|
||||
{
|
||||
$this -> ak = $ak;
|
||||
$this -> sk = $sk;
|
||||
$this -> pathStyle = $pathStyle;
|
||||
$this -> endpoint = $endpoint;
|
||||
$this -> methodName = $methodName;
|
||||
$this -> signature = $signature;
|
||||
$this -> securityToken = $securityToken;
|
||||
$this -> isCname = $isCname;
|
||||
}
|
||||
|
||||
protected function transXmlByType($key, &$value, &$subParams, $transHolder)
|
||||
{
|
||||
$xml = [];
|
||||
$treatAsString = false;
|
||||
if(isset($value['type'])){
|
||||
$type = $value['type'];
|
||||
if($type === 'array'){
|
||||
$name = isset($value['sentAs']) ? $value['sentAs'] : $key;
|
||||
$subXml = [];
|
||||
foreach($subParams as $item){
|
||||
$temp = $this->transXmlByType($key, $value['items'], $item, $transHolder);
|
||||
if($temp !== ''){
|
||||
$subXml[] = $temp;
|
||||
}
|
||||
}
|
||||
if(!empty($subXml)){
|
||||
if(!isset($value['data']['xmlFlattened'])){
|
||||
$xml[] = '<' . $name . '>';
|
||||
$xml[] = implode('', $subXml);
|
||||
$xml[] = '</' . $name . '>';
|
||||
}else{
|
||||
$xml[] = implode('', $subXml);
|
||||
}
|
||||
}
|
||||
}else if($type === 'object'){
|
||||
$name = isset($value['sentAs']) ? $value['sentAs'] : (isset($value['name']) ? $value['name'] : $key);
|
||||
$properties = $value['properties'];
|
||||
$subXml = [];
|
||||
$attr = [];
|
||||
foreach ($properties as $pkey => $pvalue){
|
||||
if(isset($pvalue['required']) && $pvalue['required'] && !isset($subParams[$pkey])){
|
||||
$obsException= new ObsException('param:' .$pkey. ' is required');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
if(isset($subParams[$pkey])){
|
||||
if(isset($pvalue['data']) && isset($pvalue['data']['xmlAttribute']) && $pvalue['data']['xmlAttribute']){
|
||||
$attrValue = $this->xml_tansfer(trim(strval($subParams[$pkey])));
|
||||
$attr[$pvalue['sentAs']] = '"' . $attrValue . '"';
|
||||
if(isset($pvalue['data']['xmlNamespace'])){
|
||||
$ns = substr($pvalue['sentAs'], 0, strpos($pvalue['sentAs'], ':'));
|
||||
$attr['xmlns:' . $ns] = '"' . $pvalue['data']['xmlNamespace'] . '"';
|
||||
}
|
||||
}else{
|
||||
$subXml[] = $this -> transXmlByType($pkey, $pvalue, $subParams[$pkey], $transHolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
$val = implode('', $subXml);
|
||||
if($val !== ''){
|
||||
$_name = $name;
|
||||
if(!empty($attr)){
|
||||
foreach ($attr as $akey => $avalue){
|
||||
$_name .= ' ' . $akey . '=' . $avalue;
|
||||
}
|
||||
}
|
||||
if(!isset($value['data']['xmlFlattened'])){
|
||||
$xml[] = '<' . $_name . '>';
|
||||
$xml[] = $val;
|
||||
$xml[] = '</' . $name . '>';
|
||||
} else {
|
||||
$xml[] = $val;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$treatAsString = true;
|
||||
}
|
||||
}else{
|
||||
$treatAsString = true;
|
||||
$type = null;
|
||||
}
|
||||
|
||||
if($treatAsString){
|
||||
if($type === 'boolean'){
|
||||
if(!is_bool($subParams) && strval($subParams) !== 'false' && strval($subParams) !== 'true'){
|
||||
$obsException= new ObsException('param:' .$key. ' is not a boolean value');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
}else if($type === 'numeric'){
|
||||
if(!is_numeric($subParams)){
|
||||
$obsException= new ObsException('param:' .$key. ' is not a numeric value');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
}else if($type === 'float'){
|
||||
if(!is_float($subParams)){
|
||||
$obsException= new ObsException('param:' .$key. ' is not a float value');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
}else if($type === 'int' || $type === 'integer'){
|
||||
if(!is_int($subParams)){
|
||||
$obsException= new ObsException('param:' .$key. ' is not a int value');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
}
|
||||
|
||||
$name = isset($value['sentAs']) ? $value['sentAs'] : $key;
|
||||
if(is_bool($subParams)){
|
||||
$val = $subParams ? 'true' : 'false';
|
||||
}else{
|
||||
$val = strval($subParams);
|
||||
}
|
||||
if(isset($value['format'])){
|
||||
$val = SchemaFormatter::format($value['format'], $val);
|
||||
}
|
||||
if (isset($value['transform'])) {
|
||||
$val = $transHolder->transform($value['transform'], $val);
|
||||
}
|
||||
if(isset($val) && $val !== ''){
|
||||
$val = $this->xml_tansfer($val);
|
||||
if(!isset($value['data']['xmlFlattened'])){
|
||||
$xml[] = '<' . $name . '>';
|
||||
$xml[] = $val;
|
||||
$xml[] = '</' . $name . '>';
|
||||
} else {
|
||||
$xml[] = $val;
|
||||
}
|
||||
}else if(isset($value['canEmpty']) && $value['canEmpty']){
|
||||
$xml[] = '<' . $name . '>';
|
||||
$xml[] = $val;
|
||||
$xml[] = '</' . $name . '>';
|
||||
}
|
||||
}
|
||||
$ret = implode('', $xml);
|
||||
|
||||
if(isset($value['wrapper'])){
|
||||
$ret = '<'. $value['wrapper'] . '>' . $ret . '</'. $value['wrapper'] . '>';
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
private function xml_tansfer($tag) {
|
||||
$search = array('&', '<', '>', '\'', '"');
|
||||
$repalce = array('&', '<', '>', ''', '"');
|
||||
$transferXml = str_replace($search, $repalce, $tag);
|
||||
return $transferXml;
|
||||
}
|
||||
|
||||
protected function prepareAuth(array &$requestConfig, array &$params, Model $model)
|
||||
{
|
||||
$transHolder = strcasecmp($this-> signature, 'obs') === 0 ? ObsTransform::getInstance() : V2Transform::getInstance();
|
||||
$method = $requestConfig['httpMethod'];
|
||||
$requestUrl = $this->endpoint;
|
||||
$headers = [];
|
||||
$pathArgs = [];
|
||||
$dnsParam = null;
|
||||
$uriParam = null;
|
||||
$body = [];
|
||||
$xml = [];
|
||||
|
||||
if(isset($requestConfig['specialParam'])){
|
||||
$pathArgs[$requestConfig['specialParam']] = '';
|
||||
}
|
||||
|
||||
$result = ['body' => null];
|
||||
$url = parse_url($requestUrl);
|
||||
$host = $url['host'];
|
||||
|
||||
$fileFlag = false;
|
||||
|
||||
if(isset($requestConfig['requestParameters'])){
|
||||
$paramsMetadata = $requestConfig['requestParameters'];
|
||||
foreach ($paramsMetadata as $key => $value){
|
||||
if(isset($value['required']) && $value['required'] && !isset($params[$key])){
|
||||
$obsException= new ObsException('param:' .$key. ' is required');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
if(isset($params[$key]) && isset($value['location'])){
|
||||
$location = $value['location'];
|
||||
$val = $params[$key];
|
||||
$type = 'string';
|
||||
if($val !== '' && isset($value['type'])){
|
||||
$type = $value['type'];
|
||||
if($type === 'boolean'){
|
||||
if(!is_bool($val) && strval($val) !== 'false' && strval($val) !== 'true'){
|
||||
$obsException= new ObsException('param:' .$key. ' is not a boolean value');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
}else if($type === 'numeric'){
|
||||
if(!is_numeric($val)){
|
||||
$obsException= new ObsException('param:' .$key. ' is not a numeric value');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
}else if($type === 'float'){
|
||||
if(!is_float($val)){
|
||||
$obsException= new ObsException('param:' .$key. ' is not a float value');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
}else if($type === 'int' || $type === 'integer'){
|
||||
if(!is_int($val)){
|
||||
$obsException= new ObsException('param:' .$key. ' is not a int value');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($location === 'header'){
|
||||
if($type === 'object'){
|
||||
if(is_array($val)){
|
||||
$sentAs = strtolower($value['sentAs']);
|
||||
foreach ($val as $k => $v){
|
||||
$k = self::urlencodeWithSafe(strtolower($k), ' ;/?:@&=+$,');
|
||||
$name = strpos($k, $sentAs) === 0 ? $k : $sentAs . $k;
|
||||
$headers[$name] = self::urlencodeWithSafe($v, ' ;/?:@&=+$,\'*');
|
||||
}
|
||||
}
|
||||
}else if($type === 'array'){
|
||||
if(is_array($val)){
|
||||
$name = isset($value['sentAs']) ? $value['sentAs'] : (isset($value['items']['sentAs']) ? $value['items']['sentAs'] : $key);
|
||||
$temp = [];
|
||||
foreach ($val as $v){
|
||||
if(($v = strval($v)) !== ''){
|
||||
$temp[] = self::urlencodeWithSafe($val, ' ;/?:@&=+$,\'*');
|
||||
}
|
||||
}
|
||||
|
||||
$headers[$name] = $temp;
|
||||
}
|
||||
}else if($type === 'password'){
|
||||
if(($val = strval($val)) !== ''){
|
||||
$name = isset($value['sentAs']) ? $value['sentAs'] : $key;
|
||||
$pwdName = isset($value['pwdSentAs']) ? $value['pwdSentAs'] : $name . '-MD5';
|
||||
$val1 = base64_encode($val);
|
||||
$val2 = base64_encode(md5($val, true));
|
||||
$headers[$name] = $val1;
|
||||
$headers[$pwdName] = $val2;
|
||||
}
|
||||
}else{
|
||||
if (isset($value['transform'])) {
|
||||
$val = $transHolder->transform($value['transform'], strval($val));
|
||||
}
|
||||
if(isset($val)){
|
||||
if(is_bool($val)){
|
||||
$val = $val ? 'true' : 'false';
|
||||
}else{
|
||||
$val = strval($val);
|
||||
}
|
||||
if($val !== ''){
|
||||
$name = isset($value['sentAs']) ? $value['sentAs'] : $key;
|
||||
if(isset($value['format'])){
|
||||
$val = SchemaFormatter::format($value['format'], $val);
|
||||
}
|
||||
$headers[$name] = self::urlencodeWithSafe($val, ' ;/?:@&=+$,\'*');
|
||||
}
|
||||
}
|
||||
}
|
||||
}else if($location === 'uri' && $uriParam === null){
|
||||
$uriParam = self::urlencodeWithSafe($val);
|
||||
}else if($location === 'dns' && $dnsParam === null){
|
||||
$dnsParam = $val;
|
||||
}else if($location === 'query'){
|
||||
$name = isset($value['sentAs']) ? $value['sentAs'] : $key;
|
||||
if(strval($val) !== ''){
|
||||
if (strcasecmp ( $this->signature, 'v4' ) === 0) {
|
||||
$pathArgs[rawurlencode($name)] = rawurlencode(strval($val));
|
||||
} else {
|
||||
$pathArgs[self::urlencodeWithSafe($name)] = self::urlencodeWithSafe(strval($val));
|
||||
}
|
||||
}
|
||||
}else if($location === 'xml'){
|
||||
$val = $this->transXmlByType($key, $value, $val, $transHolder);
|
||||
if($val !== ''){
|
||||
$xml[] = $val;
|
||||
}
|
||||
}else if($location === 'body'){
|
||||
|
||||
if(isset($result['body'])){
|
||||
$obsException= new ObsException('duplicated body provided');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
|
||||
if($type === 'file'){
|
||||
if(!file_exists($val)){
|
||||
$obsException= new ObsException('file[' .$val. '] does not exist');
|
||||
$obsException-> setExceptionType('client');
|
||||
throw $obsException;
|
||||
}
|
||||
$result['body'] = new Stream(fopen($val, 'r'));
|
||||
$fileFlag = true;
|
||||
}else if($type === 'stream'){
|
||||
$result['body'] = $val;
|
||||
}else{
|
||||
$result['body'] = strval($val);
|
||||
}
|
||||
}else if($location === 'response'){
|
||||
$model[$key] = ['value' => $val, 'type' => $type];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if($dnsParam){
|
||||
if($this -> pathStyle){
|
||||
$requestUrl = $requestUrl . '/' . $dnsParam;
|
||||
}else{
|
||||
$defaultPort = strtolower($url['scheme']) === 'https' ? '443' : '80';
|
||||
$host = $this -> isCname ? $host : $dnsParam. '.' . $host;
|
||||
$requestUrl = $url['scheme'] . '://' . $host . ':' . (isset($url['port']) ? $url['port'] : $defaultPort);
|
||||
}
|
||||
}
|
||||
if($uriParam){
|
||||
$requestUrl = $requestUrl . '/' . $uriParam;
|
||||
}
|
||||
|
||||
if(!empty($pathArgs)){
|
||||
$requestUrl .= '?';
|
||||
$_pathArgs = [];
|
||||
foreach ($pathArgs as $key => $value){
|
||||
$_pathArgs[] = $value === null || $value === '' ? $key : $key . '=' . $value;
|
||||
}
|
||||
$requestUrl .= implode('&', $_pathArgs);
|
||||
}
|
||||
}
|
||||
|
||||
if($xml || (isset($requestConfig['data']['xmlAllowEmpty']) && $requestConfig['data']['xmlAllowEmpty'])){
|
||||
$body[] = '<';
|
||||
$xmlRoot = $requestConfig['data']['xmlRoot']['name'];
|
||||
|
||||
$body[] = $xmlRoot;
|
||||
$body[] = '>';
|
||||
$body[] = implode('', $xml);
|
||||
$body[] = '</';
|
||||
$body[] = $xmlRoot;
|
||||
$body[] = '>';
|
||||
$headers['Content-Type'] = 'application/xml';
|
||||
$result['body'] = implode('', $body);
|
||||
|
||||
ObsLog::commonLog(DEBUG, 'request content ' . $result['body']);
|
||||
|
||||
if(isset($requestConfig['data']['contentMd5']) && $requestConfig['data']['contentMd5']){
|
||||
$headers['Content-MD5'] = base64_encode(md5($result['body'],true));
|
||||
}
|
||||
}
|
||||
|
||||
if($fileFlag && ($result['body'] instanceof StreamInterface)){
|
||||
if($this->methodName === 'uploadPart' && (isset($model['Offset']) || isset($model['PartSize']))){
|
||||
$bodySize = $result['body'] ->getSize();
|
||||
if(isset($model['Offset'])){
|
||||
$offset = intval($model['Offset']['value']);
|
||||
$offset = $offset >= 0 && $offset < $bodySize ? $offset : 0;
|
||||
}else{
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
if(isset($model['PartSize'])){
|
||||
$partSize = intval($model['PartSize']['value']);
|
||||
$partSize = $partSize > 0 && $partSize <= ($bodySize - $offset) ? $partSize : $bodySize - $offset;
|
||||
}else{
|
||||
$partSize = $bodySize - $offset;
|
||||
}
|
||||
$result['body'] -> rewind();
|
||||
$result['body'] -> seek($offset);
|
||||
$headers['Content-Length'] = $partSize;
|
||||
}else if(isset($headers['Content-Length'])){
|
||||
$bodySize = $result['body'] -> getSize();
|
||||
if(intval($headers['Content-Length']) > $bodySize){
|
||||
$headers['Content-Length'] = $bodySize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$constants = Constants::selectConstants($this -> signature);
|
||||
|
||||
if($this->securityToken){
|
||||
$headers[$constants::SECURITY_TOKEN_HEAD] = $this->securityToken;
|
||||
}
|
||||
|
||||
$headers['Host'] = $host;
|
||||
|
||||
$result['host'] = $host;
|
||||
$result['method'] = $method;
|
||||
$result['headers'] = $headers;
|
||||
$result['pathArgs'] = $pathArgs;
|
||||
$result['dnsParam'] = $dnsParam;
|
||||
$result['uriParam'] = $uriParam;
|
||||
$result['requestUrl'] = $requestUrl;
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
138
addons/hwobs/library/Obs/Internal/Signature/DefaultSignature.php
Normal file
138
addons/hwobs/library/Obs/Internal/Signature/DefaultSignature.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs\Internal\Signature;
|
||||
|
||||
|
||||
use Obs\Internal\Resource\Constants;
|
||||
use Obs\Internal\Common\Model;
|
||||
use Obs\Internal\Resource\V2Constants;
|
||||
|
||||
class DefaultSignature extends AbstractSignature
|
||||
{
|
||||
const INTEREST_HEADER_KEY_LIST = array('content-type', 'content-md5', 'date');
|
||||
|
||||
|
||||
public function __construct($ak, $sk, $pathStyle, $endpoint, $methodName, $signature, $securityToken=false, $isCname=false)
|
||||
{
|
||||
|
||||
parent::__construct($ak, $sk, $pathStyle, $endpoint, $methodName, $signature, $securityToken, $isCname);
|
||||
}
|
||||
|
||||
public function doAuth(array &$requestConfig, array &$params, Model $model)
|
||||
{
|
||||
$result = $this -> prepareAuth($requestConfig, $params, $model);
|
||||
|
||||
$result['headers']['Date'] = gmdate('D, d M Y H:i:s \G\M\T');
|
||||
$canonicalstring = $this-> makeCanonicalstring($result['method'], $result['headers'], $result['pathArgs'], $result['dnsParam'], $result['uriParam']);
|
||||
|
||||
$result['cannonicalRequest'] = $canonicalstring;
|
||||
//print_r($result);
|
||||
$signature = base64_encode(hash_hmac('sha1', $canonicalstring, $this->sk, true));
|
||||
|
||||
$constants = Constants::selectConstants($this -> signature);
|
||||
$signatureFlag = $constants::FLAG;
|
||||
|
||||
$authorization = $signatureFlag . ' ' . $this->ak . ':' . $signature;
|
||||
|
||||
$result['headers']['Authorization'] = $authorization;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function makeCanonicalstring($method, $headers, $pathArgs, $bucketName, $objectKey, $expires = null)
|
||||
{
|
||||
$buffer = [];
|
||||
$buffer[] = $method;
|
||||
$buffer[] = "\n";
|
||||
$interestHeaders = [];
|
||||
$constants = Constants::selectConstants($this -> signature);
|
||||
|
||||
foreach ($headers as $key => $value){
|
||||
$key = strtolower($key);
|
||||
if(in_array($key, self::INTEREST_HEADER_KEY_LIST) || strpos($key, $constants::HEADER_PREFIX) === 0){
|
||||
$interestHeaders[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if(array_key_exists($constants::ALTERNATIVE_DATE_HEADER, $interestHeaders)){
|
||||
$interestHeaders['date'] = '';
|
||||
}
|
||||
|
||||
if($expires !== null){
|
||||
$interestHeaders['date'] = strval($expires);
|
||||
}
|
||||
|
||||
if(!array_key_exists('content-type', $interestHeaders)){
|
||||
$interestHeaders['content-type'] = '';
|
||||
}
|
||||
|
||||
if(!array_key_exists('content-md5', $interestHeaders)){
|
||||
$interestHeaders['content-md5'] = '';
|
||||
}
|
||||
|
||||
ksort($interestHeaders);
|
||||
|
||||
foreach ($interestHeaders as $key => $value){
|
||||
if(strpos($key, $constants::HEADER_PREFIX) === 0){
|
||||
$buffer[] = $key . ':' . $value;
|
||||
}else{
|
||||
$buffer[] = $value;
|
||||
}
|
||||
$buffer[] = "\n";
|
||||
}
|
||||
|
||||
$uri = '';
|
||||
|
||||
$bucketName = $this->isCname ? $headers['Host'] : $bucketName;
|
||||
|
||||
if($bucketName){
|
||||
$uri .= '/';
|
||||
$uri .= $bucketName;
|
||||
if(!$this->pathStyle){
|
||||
$uri .= '/';
|
||||
}
|
||||
}
|
||||
|
||||
if($objectKey){
|
||||
if(!($pos=strripos($uri, '/')) || strlen($uri)-1 !== $pos){
|
||||
$uri .= '/';
|
||||
}
|
||||
$uri .= $objectKey;
|
||||
}
|
||||
|
||||
$buffer[] = $uri === ''? '/' : $uri;
|
||||
|
||||
|
||||
if(!empty($pathArgs)){
|
||||
ksort($pathArgs);
|
||||
$_pathArgs = [];
|
||||
foreach ($pathArgs as $key => $value){
|
||||
if(in_array(strtolower($key), $constants::ALLOWED_RESOURCE_PARAMTER_NAMES) || strpos($key, $constants::HEADER_PREFIX) === 0){
|
||||
$_pathArgs[] = $value === null || $value === '' ? $key : $key . '=' . urldecode($value);
|
||||
}
|
||||
}
|
||||
if(!empty($_pathArgs)){
|
||||
$buffer[] = '?';
|
||||
$buffer[] = implode('&', $_pathArgs);
|
||||
}
|
||||
}
|
||||
|
||||
return implode('', $buffer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs\Internal\Signature;
|
||||
|
||||
use Obs\Internal\Common\Model;
|
||||
|
||||
interface SignatureInterface
|
||||
{
|
||||
function doAuth(array &$requestConfig, array &$params, Model $model);
|
||||
}
|
||||
199
addons/hwobs/library/Obs/Internal/Signature/V4Signature.php
Normal file
199
addons/hwobs/library/Obs/Internal/Signature/V4Signature.php
Normal file
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs\Internal\Signature;
|
||||
|
||||
use Obs\Internal\Common\Model;
|
||||
|
||||
class V4Signature extends AbstractSignature
|
||||
{
|
||||
const CONTENT_SHA256 = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
|
||||
|
||||
protected $region;
|
||||
|
||||
protected $utcTimeZone;
|
||||
|
||||
public function __construct($ak, $sk, $pathStyle, $endpoint, $region, $methodName, $signature, $securityToken=false, $isCname=false)
|
||||
{
|
||||
parent::__construct($ak, $sk, $pathStyle, $endpoint, $methodName, $signature, $securityToken, $isCname);
|
||||
$this->region = $region;
|
||||
$this->utcTimeZone = new \DateTimeZone ('UTC');
|
||||
}
|
||||
|
||||
public function doAuth(array &$requestConfig, array &$params, Model $model)
|
||||
{
|
||||
$result = $this -> prepareAuth($requestConfig, $params, $model);
|
||||
|
||||
$result['headers']['x-amz-content-sha256'] = self::CONTENT_SHA256;
|
||||
|
||||
$bucketName = $result['dnsParam'];
|
||||
|
||||
$result['headers']['Host'] = $result['host'];
|
||||
|
||||
$time = null;
|
||||
if(array_key_exists('x-amz-date', $result['headers'])){
|
||||
$time = $result['headers']['x-amz-date'];
|
||||
}else if(array_key_exists('X-Amz-Date', $result['headers'])){
|
||||
$time = $result['headers']['X-Amz-Date'];
|
||||
}
|
||||
$timestamp = $time ? date_create_from_format('Ymd\THis\Z', $time, $this->utcTimeZone) -> getTimestamp()
|
||||
:time();
|
||||
|
||||
$result['headers']['Date'] = gmdate('D, d M Y H:i:s \G\M\T', $timestamp);
|
||||
|
||||
$longDate = gmdate('Ymd\THis\Z', $timestamp);
|
||||
$shortDate = substr($longDate, 0, 8);
|
||||
|
||||
$credential = $this-> getCredential($shortDate);
|
||||
|
||||
$signedHeaders = $this->getSignedHeaders($result['headers']);
|
||||
|
||||
$canonicalstring = $this-> makeCanonicalstring($result['method'], $result['headers'], $result['pathArgs'], $bucketName, $result['uriParam'], $signedHeaders);
|
||||
|
||||
$result['cannonicalRequest'] = $canonicalstring;
|
||||
|
||||
$signature = $this -> getSignature($canonicalstring, $longDate, $shortDate);
|
||||
|
||||
$authorization = 'AWS4-HMAC-SHA256 ' . 'Credential=' . $credential. ',' . 'SignedHeaders=' . $signedHeaders . ',' . 'Signature=' . $signature;
|
||||
|
||||
$result['headers']['Authorization'] = $authorization;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getSignature($canonicalstring, $longDate, $shortDate)
|
||||
{
|
||||
$stringToSign = [];
|
||||
$stringToSign[] = 'AWS4-HMAC-SHA256';
|
||||
|
||||
$stringToSign[] = "\n";
|
||||
|
||||
$stringToSign[] = $longDate;
|
||||
|
||||
$stringToSign[] = "\n";
|
||||
$stringToSign[] = $this -> getScope($shortDate);
|
||||
$stringToSign[] = "\n";
|
||||
|
||||
$stringToSign[] = hash('sha256', $canonicalstring);
|
||||
|
||||
$dateKey = hash_hmac('sha256', $shortDate, 'AWS4' . $this -> sk, true);
|
||||
$regionKey = hash_hmac('sha256', $this->region, $dateKey, true);
|
||||
$serviceKey = hash_hmac('sha256', 's3', $regionKey, true);
|
||||
$signingKey = hash_hmac('sha256', 'aws4_request', $serviceKey, true);
|
||||
$signature = hash_hmac('sha256', implode('', $stringToSign), $signingKey);
|
||||
return $signature;
|
||||
}
|
||||
|
||||
public function getCanonicalQueryString($pathArgs)
|
||||
{
|
||||
$queryStr = '';
|
||||
|
||||
ksort($pathArgs);
|
||||
$index = 0;
|
||||
foreach ($pathArgs as $key => $value){
|
||||
$queryStr .= $key . '=' . $value;
|
||||
if($index++ !== count($pathArgs) - 1){
|
||||
$queryStr .= '&';
|
||||
}
|
||||
}
|
||||
return $queryStr;
|
||||
}
|
||||
|
||||
public function getCanonicalHeaders($headers)
|
||||
{
|
||||
$_headers = [];
|
||||
foreach ($headers as $key => $value) {
|
||||
$_headers[strtolower($key)] = $value;
|
||||
}
|
||||
ksort($_headers);
|
||||
|
||||
$canonicalHeaderStr = '';
|
||||
|
||||
foreach ($_headers as $key => $value){
|
||||
$value = is_array($value) ? implode(',', $value) : $value;
|
||||
$canonicalHeaderStr .= $key . ':' . $value;
|
||||
$canonicalHeaderStr .= "\n";
|
||||
}
|
||||
return $canonicalHeaderStr;
|
||||
}
|
||||
|
||||
public function getCanonicalURI($bucketName, $objectKey)
|
||||
{
|
||||
$uri = '';
|
||||
if($this -> pathStyle && $bucketName){
|
||||
$uri .= '/' . $bucketName;
|
||||
}
|
||||
|
||||
if($objectKey){
|
||||
$uri .= '/' . $objectKey;
|
||||
}
|
||||
|
||||
if($uri === ''){
|
||||
$uri = '/';
|
||||
}
|
||||
return $uri;
|
||||
}
|
||||
|
||||
public function makeCanonicalstring($method, $headers, $pathArgs, $bucketName, $objectKey, $signedHeaders=null, $payload=null)
|
||||
{
|
||||
$buffer = [];
|
||||
$buffer[] = $method;
|
||||
$buffer[] = "\n";
|
||||
$buffer[] = $this->getCanonicalURI($bucketName, $objectKey);
|
||||
$buffer[] = "\n";
|
||||
$buffer[] = $this->getCanonicalQueryString($pathArgs);
|
||||
$buffer[] = "\n";
|
||||
$buffer[] = $this->getCanonicalHeaders($headers);
|
||||
$buffer[] = "\n";
|
||||
$buffer[] = $signedHeaders ? $signedHeaders : $this->getSignedHeaders($headers);
|
||||
$buffer[] = "\n";
|
||||
$buffer[] = $payload ? strval($payload) : self::CONTENT_SHA256;
|
||||
|
||||
return implode('', $buffer);
|
||||
}
|
||||
|
||||
public function getSignedHeaders($headers)
|
||||
{
|
||||
$_headers = [];
|
||||
|
||||
foreach ($headers as $key => $value) {
|
||||
$_headers[] = strtolower($key);
|
||||
}
|
||||
|
||||
sort($_headers);
|
||||
|
||||
$signedHeaders = '';
|
||||
|
||||
foreach ($_headers as $key => $value){
|
||||
$signedHeaders .= $value;
|
||||
if($key !== count($_headers) - 1){
|
||||
$signedHeaders .= ';';
|
||||
}
|
||||
}
|
||||
return $signedHeaders;
|
||||
}
|
||||
|
||||
public function getScope($shortDate)
|
||||
{
|
||||
return $shortDate . '/' . $this->region . '/s3/aws4_request';
|
||||
}
|
||||
|
||||
public function getCredential($shortDate)
|
||||
{
|
||||
return $this->ak . '/' . $this->getScope($shortDate);
|
||||
}
|
||||
}
|
||||
28
addons/hwobs/library/Obs/Log/ObsConfig.php
Normal file
28
addons/hwobs/library/Obs/Log/ObsConfig.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs\Log;
|
||||
|
||||
class ObsConfig
|
||||
{
|
||||
const LOG_FILE_CONFIG = [
|
||||
'FilePath'=>'./logs',
|
||||
'FileName'=>'eSDK-OBS-PHP.log',
|
||||
'MaxFiles'=>10,
|
||||
'Level'=>INFO
|
||||
];
|
||||
}
|
||||
126
addons/hwobs/library/Obs/Log/ObsLog.php
Normal file
126
addons/hwobs/library/Obs/Log/ObsLog.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs\Log;
|
||||
|
||||
use Monolog\Formatter\LineFormatter;
|
||||
use Monolog\Handler\RotatingFileHandler;
|
||||
use Monolog\Logger;
|
||||
|
||||
class ObsLog extends Logger
|
||||
{
|
||||
public static $log = null;
|
||||
|
||||
protected $log_path = './';
|
||||
protected $log_name = null;
|
||||
protected $log_level = Logger::DEBUG;
|
||||
protected $log_maxFiles = 0;
|
||||
|
||||
private $formatter = null;
|
||||
private $handler = null;
|
||||
private $filepath = '';
|
||||
|
||||
public static function initLog($logConfig= [])
|
||||
{
|
||||
$s3log = new ObsLog('');
|
||||
$s3log->setConfig($logConfig);
|
||||
$s3log->cheakDir();
|
||||
$s3log->setFilePath();
|
||||
$s3log->setFormat();
|
||||
$s3log->setHande();
|
||||
}
|
||||
private function setFormat()
|
||||
{
|
||||
$output = '[%datetime%][%level_name%]'.'%message%' . "\n";
|
||||
$this->formatter = new LineFormatter($output);
|
||||
|
||||
}
|
||||
private function setHande()
|
||||
{
|
||||
self::$log = new Logger('obs_logger');
|
||||
$rotating = new RotatingFileHandler($this->filepath, $this->log_maxFiles, $this->log_level);
|
||||
$rotating->setFormatter($this->formatter);
|
||||
self::$log->pushHandler($rotating);
|
||||
}
|
||||
private function setConfig($logConfig= [])
|
||||
{
|
||||
$arr = empty($logConfig) ? ObsConfig::LOG_FILE_CONFIG : $logConfig;
|
||||
$this->log_path = iconv('UTF-8', 'GBK',$arr['FilePath']);
|
||||
$this->log_name = iconv('UTF-8', 'GBK',$arr['FileName']);
|
||||
$this->log_maxFiles = is_numeric($arr['MaxFiles']) ? 0 : intval($arr['MaxFiles']);
|
||||
$this->log_level = $arr['Level'];
|
||||
}
|
||||
private function cheakDir()
|
||||
{
|
||||
if (!is_dir($this->log_path)){
|
||||
mkdir($this->log_path, 0755, true);
|
||||
}
|
||||
}
|
||||
private function setFilePath()
|
||||
{
|
||||
$this->filepath = $this->log_path.'/'.$this->log_name;
|
||||
}
|
||||
private static function writeLog($level, $msg)
|
||||
{
|
||||
switch ($level) {
|
||||
case DEBUG:
|
||||
self::$log->debug($msg);
|
||||
break;
|
||||
case INFO:
|
||||
self::$log->info($msg);
|
||||
break;
|
||||
case NOTICE:
|
||||
self::$log->notice($msg);
|
||||
break;
|
||||
case WARNING:
|
||||
self::$log->warning($msg);
|
||||
break;
|
||||
case ERROR:
|
||||
self::$log->error($msg);
|
||||
break;
|
||||
case CRITICAL:
|
||||
self::$log->critical($msg);
|
||||
break;
|
||||
case ALERT:
|
||||
self::$log->alert($msg);
|
||||
break;
|
||||
case EMERGENCY:
|
||||
self::$log->emergency($msg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function commonLog($level, $format, $args1 = null, $arg2 = null)
|
||||
{
|
||||
if(ObsLog::$log){
|
||||
if ($args1 === null && $arg2 === null) {
|
||||
$msg = urldecode($format);
|
||||
} else {
|
||||
$msg = sprintf($format, $args1, $arg2);
|
||||
}
|
||||
$back = debug_backtrace();
|
||||
$line = $back[0]['line'];
|
||||
$funcname = $back[1]['function'];
|
||||
$filename = basename($back[0]['file']);
|
||||
$message = '['.$filename.':'.$line.']: '.$msg;
|
||||
ObsLog::writeLog($level, $message);
|
||||
}
|
||||
}
|
||||
}
|
||||
405
addons/hwobs/library/Obs/ObsClient.php
Normal file
405
addons/hwobs/library/Obs/ObsClient.php
Normal file
@@ -0,0 +1,405 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs;
|
||||
|
||||
use Obs\Log\ObsLog;
|
||||
use Obs\Internal\Common\SdkCurlFactory;
|
||||
use Obs\Internal\Common\SdkStreamHandler;
|
||||
use Obs\Internal\Common\Model;
|
||||
use Monolog\Logger;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Handler\CurlHandler;
|
||||
use GuzzleHttp\Handler\CurlMultiHandler;
|
||||
use GuzzleHttp\Handler\Proxy;
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
use Obs\Internal\Resource\Constants;
|
||||
|
||||
|
||||
define('DEBUG', Logger::DEBUG);
|
||||
define('INFO', Logger::INFO);
|
||||
define('NOTICE', Logger::NOTICE);
|
||||
define('WARNING', Logger::WARNING);
|
||||
define('WARN', Logger::WARNING);
|
||||
define('ERROR', Logger::ERROR);
|
||||
define('CRITICAL', Logger::CRITICAL);
|
||||
define('ALERT', Logger::ALERT);
|
||||
define('EMERGENCY', Logger::EMERGENCY);
|
||||
|
||||
/**
|
||||
* @method Model createPostSignature(array $args=[]);
|
||||
* @method Model createSignedUrl(array $args=[]);
|
||||
* @method Model createBucket(array $args = []);
|
||||
* @method Model listBuckets();
|
||||
* @method Model deleteBucket(array $args = []);
|
||||
* @method Model listObjects(array $args = []);
|
||||
* @method Model listVersions(array $args = []);
|
||||
* @method Model headBucket(array $args = []);
|
||||
* @method Model getBucketMetadata(array $args = []);
|
||||
* @method Model getBucketLocation(array $args = []);
|
||||
* @method Model getBucketStorageInfo(array $args = []);
|
||||
* @method Model setBucketQuota(array $args = []);
|
||||
* @method Model getBucketQuota(array $args = []);
|
||||
* @method Model setBucketStoragePolicy(array $args = []);
|
||||
* @method Model getBucketStoragePolicy(array $args = []);
|
||||
* @method Model setBucketAcl(array $args = []);
|
||||
* @method Model getBucketAcl(array $args = []);
|
||||
* @method Model setBucketLogging(array $args = []);
|
||||
* @method Model getBucketLogging(array $args = []);
|
||||
* @method Model setBucketPolicy(array $args = []);
|
||||
* @method Model getBucketPolicy(array $args = []);
|
||||
* @method Model deleteBucketPolicy(array $args = []);
|
||||
* @method Model setBucketLifecycle(array $args = []);
|
||||
* @method Model getBucketLifecycle(array $args = []);
|
||||
* @method Model deleteBucketLifecycle(array $args = []);
|
||||
* @method Model setBucketWebsite(array $args = []);
|
||||
* @method Model getBucketWebsite(array $args = []);
|
||||
* @method Model deleteBucketWebsite(array $args = []);
|
||||
* @method Model setBucketVersioning(array $args = []);
|
||||
* @method Model getBucketVersioning(array $args = []);
|
||||
* @method Model setBucketCors(array $args = []);
|
||||
* @method Model getBucketCors(array $args = []);
|
||||
* @method Model deleteBucketCors(array $args = []);
|
||||
* @method Model setBucketNotification(array $args = []);
|
||||
* @method Model getBucketNotification(array $args = []);
|
||||
* @method Model setBucketTagging(array $args = []);
|
||||
* @method Model getBucketTagging(array $args = []);
|
||||
* @method Model deleteBucketTagging(array $args = []);
|
||||
* @method Model optionsBucket(array $args = []);
|
||||
*
|
||||
* @method Model putObject(array $args = []);
|
||||
* @method Model getObject(array $args = []);
|
||||
* @method Model copyObject(array $args = []);
|
||||
* @method Model deleteObject(array $args = []);
|
||||
* @method Model deleteObjects(array $args = []);
|
||||
* @method Model getObjectMetadata(array $args = []);
|
||||
* @method Model setObjectAcl(array $args = []);
|
||||
* @method Model getObjectAcl(array $args = []);
|
||||
* @method Model initiateMultipartUpload(array $args = []);
|
||||
* @method Model uploadPart(array $args = []);
|
||||
* @method Model copyPart(array $args = []);
|
||||
* @method Model listParts(array $args = []);
|
||||
* @method Model completeMultipartUpload(array $args = []);
|
||||
* @method Model abortMultipartUpload(array $args = []);
|
||||
* @method Model listMultipartUploads(array $args = []);
|
||||
* @method Model optionsObject(array $args = []);
|
||||
* @method Model restoreObject(array $args = []);
|
||||
*
|
||||
* @method Promise createBucketAsync(array $args = [], callable $callback);
|
||||
* @method Promise listBucketsAsync(callable $callback);
|
||||
* @method Promise deleteBucketAsync(array $args = [], callable $callback);
|
||||
* @method Promise listObjectsAsync(array $args = [], callable $callback);
|
||||
* @method Promise listVersionsAsync(array $args = [], callable $callback);
|
||||
* @method Promise headBucketAsync(array $args = [], callable $callback);
|
||||
* @method Promise getBucketMetadataAsync(array $args = [], callable $callback);
|
||||
* @method Promise getBucketLocationAsync(array $args = [], callable $callback);
|
||||
* @method Promise getBucketStorageInfoAsync(array $args = [], callable $callback);
|
||||
* @method Promise setBucketQuotaAsync(array $args = [], callable $callback);
|
||||
* @method Promise getBucketQuotaAsync(array $args = [], callable $callback);
|
||||
* @method Promise setBucketStoragePolicyAsync(array $args = [], callable $callback);
|
||||
* @method Promise getBucketStoragePolicyAsync(array $args = [], callable $callback);
|
||||
* @method Promise setBucketAclAsync(array $args = [], callable $callback);
|
||||
* @method Promise getBucketAclAsync(array $args = [], callable $callback);
|
||||
* @method Promise setBucketLoggingAsync(array $args = [], callable $callback);
|
||||
* @method Promise getBucketLoggingAsync(array $args = [], callable $callback);
|
||||
* @method Promise setBucketPolicyAsync(array $args = [], callable $callback);
|
||||
* @method Promise getBucketPolicyAsync(array $args = [], callable $callback);
|
||||
* @method Promise deleteBucketPolicyAsync(array $args = [], callable $callback);
|
||||
* @method Promise setBucketLifecycleAsync(array $args = [], callable $callback);
|
||||
* @method Promise getBucketLifecycleAsync(array $args = [], callable $callback);
|
||||
* @method Promise deleteBucketLifecycleAsync(array $args = [], callable $callback);
|
||||
* @method Promise setBucketWebsiteAsync(array $args = [], callable $callback);
|
||||
* @method Promise getBucketWebsiteAsync(array $args = [], callable $callback);
|
||||
* @method Promise deleteBucketWebsiteAsync(array $args = [], callable $callback);
|
||||
* @method Promise setBucketVersioningAsync(array $args = [], callable $callback);
|
||||
* @method Promise getBucketVersioningAsync(array $args = [], callable $callback);
|
||||
* @method Promise setBucketCorsAsync(array $args = [], callable $callback);
|
||||
* @method Promise getBucketCorsAsync(array $args = [], callable $callback);
|
||||
* @method Promise deleteBucketCorsAsync(array $args = [], callable $callback);
|
||||
* @method Promise setBucketNotificationAsync(array $args = [], callable $callback);
|
||||
* @method Promise getBucketNotificationAsync(array $args = [], callable $callback);
|
||||
* @method Promise setBucketTaggingAsync(array $args = [], callable $callback);
|
||||
* @method Promise getBucketTaggingAsync(array $args = [], callable $callback);
|
||||
* @method Promise deleteBucketTaggingAsync(array $args = [], callable $callback);
|
||||
* @method Promise optionsBucketAsync(array $args = [], callable $callback);
|
||||
*
|
||||
* @method Promise putObjectAsync(array $args = [], callable $callback);
|
||||
* @method Promise getObjectAsync(array $args = [], callable $callback);
|
||||
* @method Promise copyObjectAsync(array $args = [], callable $callback);
|
||||
* @method Promise deleteObjectAsync(array $args = [], callable $callback);
|
||||
* @method Promise deleteObjectsAsync(array $args = [], callable $callback);
|
||||
* @method Promise getObjectMetadataAsync(array $args = [], callable $callback);
|
||||
* @method Promise setObjectAclAsync(array $args = [], callable $callback);
|
||||
* @method Promise getObjectAclAsync(array $args = [], callable $callback);
|
||||
* @method Promise initiateMultipartUploadAsync(array $args = [], callable $callback);
|
||||
* @method Promise uploadPartAsync(array $args = [], callable $callback);
|
||||
* @method Promise copyPartAsync(array $args = [], callable $callback);
|
||||
* @method Promise listPartsAsync(array $args = [], callable $callback);
|
||||
* @method Promise completeMultipartUploadAsync(array $args = [], callable $callback);
|
||||
* @method Promise abortMultipartUploadAsync(array $args = [], callable $callback);
|
||||
* @method Promise listMultipartUploadsAsync(array $args = [], callable $callback);
|
||||
* @method Promise optionsObjectAsync(array $args = [], callable $callback);
|
||||
* @method Promise restoreObjectAsync(array $args = [], callable $callback);
|
||||
*
|
||||
*/
|
||||
class ObsClient
|
||||
{
|
||||
|
||||
const SDK_VERSION = '3.20.5';
|
||||
|
||||
const AclPrivate = 'private';
|
||||
const AclPublicRead = 'public-read';
|
||||
const AclPublicReadWrite = 'public-read-write';
|
||||
const AclPublicReadDelivered = 'public-read-delivered';
|
||||
const AclPublicReadWriteDelivered = 'public-read-write-delivered';
|
||||
|
||||
const AclAuthenticatedRead = 'authenticated-read';
|
||||
const AclBucketOwnerRead = 'bucket-owner-read';
|
||||
const AclBucketOwnerFullControl = 'bucket-owner-full-control';
|
||||
const AclLogDeliveryWrite = 'log-delivery-write';
|
||||
|
||||
const StorageClassStandard = 'STANDARD';
|
||||
const StorageClassWarm = 'WARM';
|
||||
const StorageClassCold = 'COLD';
|
||||
|
||||
const PermissionRead = 'READ';
|
||||
const PermissionWrite = 'WRITE';
|
||||
const PermissionReadAcp = 'READ_ACP';
|
||||
const PermissionWriteAcp = 'WRITE_ACP';
|
||||
const PermissionFullControl = 'FULL_CONTROL';
|
||||
|
||||
const AllUsers = 'Everyone';
|
||||
|
||||
const GroupAllUsers = 'AllUsers';
|
||||
const GroupAuthenticatedUsers = 'AuthenticatedUsers';
|
||||
const GroupLogDelivery = 'LogDelivery';
|
||||
|
||||
const RestoreTierExpedited = 'Expedited';
|
||||
const RestoreTierStandard = 'Standard';
|
||||
const RestoreTierBulk = 'Bulk';
|
||||
|
||||
const GranteeGroup = 'Group';
|
||||
const GranteeUser = 'CanonicalUser';
|
||||
|
||||
const CopyMetadata = 'COPY';
|
||||
const ReplaceMetadata = 'REPLACE';
|
||||
|
||||
const SignatureV2 = 'v2';
|
||||
const SignatureV4 = 'v4';
|
||||
const SigantureObs = 'obs';
|
||||
|
||||
const ObjectCreatedAll = 'ObjectCreated:*';
|
||||
const ObjectCreatedPut = 'ObjectCreated:Put';
|
||||
const ObjectCreatedPost = 'ObjectCreated:Post';
|
||||
const ObjectCreatedCopy = 'ObjectCreated:Copy';
|
||||
const ObjectCreatedCompleteMultipartUpload = 'ObjectCreated:CompleteMultipartUpload';
|
||||
const ObjectRemovedAll = 'ObjectRemoved:*';
|
||||
const ObjectRemovedDelete = 'ObjectRemoved:Delete';
|
||||
const ObjectRemovedDeleteMarkerCreated = 'ObjectRemoved:DeleteMarkerCreated';
|
||||
|
||||
use Internal\SendRequestTrait;
|
||||
use Internal\GetResponseTrait;
|
||||
|
||||
private $factorys;
|
||||
|
||||
public function __construct(array $config = []){
|
||||
$this ->factorys = [];
|
||||
|
||||
$this -> ak = strval($config['key']);
|
||||
$this -> sk = strval($config['secret']);
|
||||
|
||||
if(isset($config['security_token'])){
|
||||
$this -> securityToken = strval($config['security_token']);
|
||||
}
|
||||
|
||||
if(isset($config['endpoint'])){
|
||||
$this -> endpoint = trim(strval($config['endpoint']));
|
||||
}
|
||||
|
||||
if($this -> endpoint === ''){
|
||||
throw new \RuntimeException('endpoint is not set');
|
||||
}
|
||||
|
||||
while($this -> endpoint[strlen($this -> endpoint)-1] === '/'){
|
||||
$this -> endpoint = substr($this -> endpoint, 0, strlen($this -> endpoint)-1);
|
||||
}
|
||||
|
||||
if(strpos($this-> endpoint, 'http') !== 0){
|
||||
$this -> endpoint = 'https://' . $this -> endpoint;
|
||||
}
|
||||
|
||||
if(isset($config['signature'])){
|
||||
$this -> signature = strval($config['signature']);
|
||||
}
|
||||
|
||||
if(isset($config['path_style'])){
|
||||
$this -> pathStyle = $config['path_style'];
|
||||
}
|
||||
|
||||
if(isset($config['region'])){
|
||||
$this -> region = strval($config['region']);
|
||||
}
|
||||
|
||||
if(isset($config['ssl_verify'])){
|
||||
$this -> sslVerify = $config['ssl_verify'];
|
||||
}else if(isset($config['ssl.certificate_authority'])){
|
||||
$this -> sslVerify = $config['ssl.certificate_authority'];
|
||||
}
|
||||
|
||||
if(isset($config['max_retry_count'])){
|
||||
$this -> maxRetryCount = intval($config['max_retry_count']);
|
||||
}
|
||||
|
||||
if(isset($config['timeout'])){
|
||||
$this -> timeout = intval($config['timeout']);
|
||||
}
|
||||
|
||||
if(isset($config['socket_timeout'])){
|
||||
$this -> socketTimeout = intval($config['socket_timeout']);
|
||||
}
|
||||
|
||||
if(isset($config['connect_timeout'])){
|
||||
$this -> connectTimeout = intval($config['connect_timeout']);
|
||||
}
|
||||
|
||||
if(isset($config['chunk_size'])){
|
||||
$this -> chunkSize = intval($config['chunk_size']);
|
||||
}
|
||||
|
||||
if(isset($config['exception_response_mode'])){
|
||||
$this -> exceptionResponseMode = $config['exception_response_mode'];
|
||||
}
|
||||
|
||||
if (isset($config['is_cname'])) {
|
||||
$this -> isCname = $config['is_cname'];
|
||||
}
|
||||
|
||||
$host = parse_url($this -> endpoint)['host'];
|
||||
if(filter_var($host, FILTER_VALIDATE_IP) !== false) {
|
||||
$this -> pathStyle = true;
|
||||
}
|
||||
|
||||
$handler = self::choose_handler($this);
|
||||
|
||||
$this -> httpClient = new Client(
|
||||
[
|
||||
'timeout' => 0,
|
||||
'read_timeout' => $this -> socketTimeout,
|
||||
'connect_timeout' => $this -> connectTimeout,
|
||||
'allow_redirects' => false,
|
||||
'verify' => $this -> sslVerify,
|
||||
'expect' => false,
|
||||
'handler' => HandlerStack::create($handler),
|
||||
'curl' => [
|
||||
CURLOPT_BUFFERSIZE => $this -> chunkSize
|
||||
]
|
||||
]
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public function __destruct(){
|
||||
$this-> close();
|
||||
}
|
||||
|
||||
public function refresh($key, $secret, $security_token=false){
|
||||
$this -> ak = strval($key);
|
||||
$this -> sk = strval($secret);
|
||||
if($security_token){
|
||||
$this -> securityToken = strval($security_token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default User-Agent string to use with Guzzle
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function default_user_agent()
|
||||
{
|
||||
static $defaultAgent = '';
|
||||
if (!$defaultAgent) {
|
||||
$defaultAgent = 'obs-sdk-php/' . self::SDK_VERSION;
|
||||
}
|
||||
|
||||
return $defaultAgent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a new Obs client using an array of configuration options.
|
||||
*
|
||||
* @param array $config Client configuration data
|
||||
*
|
||||
* @return ObsClient
|
||||
*/
|
||||
public static function factory(array $config = [])
|
||||
{
|
||||
return new ObsClient($config);
|
||||
}
|
||||
|
||||
public function close(){
|
||||
if($this->factorys){
|
||||
foreach ($this->factorys as $factory){
|
||||
$factory->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function initLog(array $logConfig= [])
|
||||
{
|
||||
ObsLog::initLog($logConfig);
|
||||
|
||||
$msg = [];
|
||||
$msg[] = '[OBS SDK Version=' . self::SDK_VERSION;
|
||||
$msg[] = 'Endpoint=' . $this->endpoint;
|
||||
$msg[] = 'Access Mode=' . ($this->pathStyle ? 'Path' : 'Virtual Hosting').']';
|
||||
|
||||
ObsLog::commonLog(WARNING, implode("];[", $msg));
|
||||
}
|
||||
|
||||
private static function choose_handler($obsclient)
|
||||
{
|
||||
$handler = null;
|
||||
if (function_exists('curl_multi_exec') && function_exists('curl_exec')) {
|
||||
$f1 = new SdkCurlFactory(50);
|
||||
$f2 = new SdkCurlFactory(3);
|
||||
$obsclient->factorys[] = $f1;
|
||||
$obsclient->factorys[] = $f2;
|
||||
$handler = Proxy::wrapSync(new CurlMultiHandler(['handle_factory' => $f1]), new CurlHandler(['handle_factory' => $f2]));
|
||||
} elseif (function_exists('curl_exec')) {
|
||||
$f = new SdkCurlFactory(3);
|
||||
$obsclient->factorys[] = $f;
|
||||
$handler = new CurlHandler(['handle_factory' => $f]);
|
||||
} elseif (function_exists('curl_multi_exec')) {
|
||||
$f = new SdkCurlFactory(50);
|
||||
$obsclient->factorys[] = $f;
|
||||
$handler = new CurlMultiHandler(['handle_factory' => $f]);
|
||||
}
|
||||
|
||||
if (ini_get('allow_url_fopen')) {
|
||||
$handler = $handler
|
||||
? Proxy::wrapStreaming($handler, new SdkStreamHandler())
|
||||
: new SdkStreamHandler();
|
||||
} elseif (!$handler) {
|
||||
throw new \RuntimeException('GuzzleHttp requires cURL, the '
|
||||
. 'allow_url_fopen ini setting, or a custom HTTP handler.');
|
||||
}
|
||||
|
||||
return $handler;
|
||||
}
|
||||
}
|
||||
140
addons/hwobs/library/Obs/ObsException.php
Normal file
140
addons/hwobs/library/Obs/ObsException.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
* this file except in compliance with the License. You may obtain a copy of the
|
||||
* License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Obs;
|
||||
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Obs\Log\ObsLog;
|
||||
|
||||
class ObsException extends \RuntimeException
|
||||
{
|
||||
const CLIENT = 'client';
|
||||
|
||||
const SERVER = 'server';
|
||||
|
||||
private $response;
|
||||
|
||||
private $request;
|
||||
|
||||
private $requestId;
|
||||
|
||||
private $exceptionType;
|
||||
|
||||
private $exceptionCode;
|
||||
|
||||
private $exceptionMessage;
|
||||
|
||||
private $hostId;
|
||||
|
||||
public function __construct ($message = null, $code = null, $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
public function setExceptionCode($exceptionCode)
|
||||
{
|
||||
$this->exceptionCode = $exceptionCode;
|
||||
}
|
||||
|
||||
public function getExceptionCode()
|
||||
{
|
||||
return $this->exceptionCode;
|
||||
}
|
||||
|
||||
public function setExceptionMessage($exceptionMessage)
|
||||
{
|
||||
$this->exceptionMessage = $exceptionMessage;
|
||||
}
|
||||
|
||||
public function getExceptionMessage()
|
||||
{
|
||||
return $this->exceptionMessage ? $this->exceptionMessage : $this->message;
|
||||
}
|
||||
|
||||
public function setExceptionType($exceptionType)
|
||||
{
|
||||
$this->exceptionType = $exceptionType;
|
||||
}
|
||||
|
||||
public function getExceptionType()
|
||||
{
|
||||
return $this->exceptionType;
|
||||
}
|
||||
|
||||
public function setRequestId($requestId)
|
||||
{
|
||||
$this->requestId = $requestId;
|
||||
}
|
||||
|
||||
public function getRequestId()
|
||||
{
|
||||
return $this->requestId;
|
||||
}
|
||||
|
||||
public function setResponse(Response $response)
|
||||
{
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
public function setRequest(Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
public function getStatusCode()
|
||||
{
|
||||
return $this->response ? $this->response->getStatusCode() : -1;
|
||||
}
|
||||
|
||||
public function setHostId($hostId){
|
||||
$this->hostId = $hostId;
|
||||
}
|
||||
|
||||
public function getHostId(){
|
||||
return $this->hostId;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$message = get_class($this) . ': '
|
||||
. 'OBS Error Code: ' . $this->getExceptionCode() . ', '
|
||||
. 'Status Code: ' . $this->getStatusCode() . ', '
|
||||
. 'OBS Error Type: ' . $this->getExceptionType() . ', '
|
||||
. 'OBS Error Message: ' . ($this->getExceptionMessage() ? $this->getExceptionMessage():$this->getMessage());
|
||||
|
||||
// Add the User-Agent if available
|
||||
if ($this->request) {
|
||||
$message .= ', ' . 'User-Agent: ' . $this->request->getHeaderLine('User-Agent');
|
||||
}
|
||||
$message .= "\n";
|
||||
|
||||
ObsLog::commonLog(INFO, "http request:status:%d, %s",$this->getStatusCode(),"code:".$this->getExceptionCode().", message:".$this->getMessage());
|
||||
return $message;
|
||||
}
|
||||
|
||||
}
|
||||
68
addons/hwobs/library/Signer.php
Normal file
68
addons/hwobs/library/Signer.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace addons\hwobs\library;
|
||||
|
||||
use Obs\Internal\Common\Model;
|
||||
use Obs\Internal\Signature\DefaultSignature;
|
||||
|
||||
class Signer
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分片文件的签名
|
||||
* @param string $url
|
||||
* @param string $uploadId
|
||||
* @param int $fileSize
|
||||
* @param int $partSize
|
||||
* @param string $date
|
||||
* @return array
|
||||
*/
|
||||
public static function getPartsAuthorization($url, $uploadId, $fileSize, $partSize, $date)
|
||||
{
|
||||
$config = get_addon_config('hwobs');
|
||||
$i = 0;
|
||||
$size_count = $fileSize;
|
||||
$values = array();
|
||||
while ($size_count > 0) {
|
||||
$size_count -= $partSize;
|
||||
$values[] = array(
|
||||
$partSize * $i,
|
||||
($size_count > 0) ? $partSize : ($size_count + $partSize),
|
||||
);
|
||||
$i++;
|
||||
}
|
||||
|
||||
$httpMethod = "PUT";
|
||||
$headers = [
|
||||
"Host" => str_replace(['http://', 'https://'], '', $config['uploadurl']),
|
||||
"Content-Length" => 0,
|
||||
"x-amz-date" => $date,
|
||||
];
|
||||
|
||||
$result = [];
|
||||
foreach ($values as $index => $value) {
|
||||
$headers['Content-Length'] = $value[1];
|
||||
$params = ['partNumber' => $index + 1, 'uploadId' => $uploadId, 'uriParam' => $url, 'dnsParam' => $config['bucket'], 'x-amz-date' => $date];
|
||||
$model = new Model($params);
|
||||
$sign = new DefaultSignature($config['accessKey'], $config['secretKey'], false, $config['uploadurl'], $httpMethod, 'v2', false, false);
|
||||
$requestConfig = [
|
||||
'httpMethod' => $httpMethod,
|
||||
'requestParameters' => [
|
||||
'x-amz-date' => ['location' => 'header'],
|
||||
'partNumber' => ['location' => 'query'],
|
||||
'uploadId' => ['location' => 'query'],
|
||||
'uriParam' => ['location' => 'uri'],
|
||||
'dnsParam' => ['location' => 'dns'],
|
||||
]
|
||||
];
|
||||
$sig = $sign->doAuth($requestConfig, $params, $model);
|
||||
$result[] = $sig['headers']['Authorization'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user