feat(upload): 添加华为 OBS 对象存储支持
- 在 addons.php 中添加了与华为 OBS 相关的钩子 - 新增了对华为 OBS 上传功能的实现,包括分片上传和合并 - 优化了上传参数处理和错误处理 - 支持客户端和服务端两种上传模式
This commit is contained in:
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user