You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
455 lines
15 KiB
455 lines
15 KiB
9 months ago
|
<?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 isJsonResponse($response) {
|
||
|
return $response -> getHeaderLine('content-type') === 'application/json';
|
||
|
}
|
||
|
|
||
|
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 if (isset($value['type']) && $value['type'] === 'json') {
|
||
|
$jsonBody = trim($body -> getContents());
|
||
|
if ($jsonBody && ($data = json_decode($jsonBody, true))) {
|
||
|
if (is_array($data)) {
|
||
|
$model[$key] = $data;
|
||
|
} elseif (strlen($data)) {
|
||
|
ObsLog::commonLog(ERROR, "response body %s, and jsonToArray data is %s",$body, $data);
|
||
|
}
|
||
|
}
|
||
|
$closeBody = true;
|
||
|
} 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 parseJsonToException($body, $obsException) {
|
||
|
try {
|
||
|
$jsonErrorBody = trim($body -> getContents());
|
||
|
if ($jsonErrorBody && ($data = json_decode($jsonErrorBody, true))) {
|
||
|
if (is_array($data)) {
|
||
|
if ($data['request_id']) {
|
||
|
$obsException -> setRequestId(strval($data['request_id']));
|
||
|
}
|
||
|
if ($data['code']) {
|
||
|
$obsException -> setExceptionCode(strval($data['code']));
|
||
|
}
|
||
|
if ($data['message']) {
|
||
|
$obsException -> setExceptionMessage(strval($data['message']));
|
||
|
}
|
||
|
} elseif (strlen($data)) {
|
||
|
ObsLog::commonLog(ERROR, "response body %s, and jsonToArray data is %s",$body, $data);
|
||
|
$obsException-> setExceptionMessage("Invalid response data,since it is not json data");
|
||
|
}
|
||
|
}
|
||
|
} finally {
|
||
|
$body -> close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private function parseJsonToModel($body, $model) {
|
||
|
try{
|
||
|
$jsonErrorBody = trim($body -> getContents());
|
||
|
if ($jsonErrorBody && ($jsonArray = json_decode($jsonErrorBody, true))) {
|
||
|
if (is_array($jsonArray)) {
|
||
|
if ($jsonArray['request_id']) {
|
||
|
$model['RequestId'] = strval($jsonArray['request_id']);
|
||
|
}
|
||
|
if ($jsonArray['code']) {
|
||
|
$model['Code'] = strval($jsonArray['code']);
|
||
|
}
|
||
|
if ($jsonArray['message']) {
|
||
|
$model['Message'] = strval($jsonArray['message']);
|
||
|
}
|
||
|
} elseif (strlen($jsonArray)) {
|
||
|
ObsLog::commonLog(ERROR, "response body %s, and jsonToArray data is %s",$body, $jsonArray);
|
||
|
$model['Message'] = "Invalid response data,since it is not json data";
|
||
|
}
|
||
|
}
|
||
|
} 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');
|
||
|
$responseContentType = $response -> getHeaderLine('content-type');
|
||
|
|
||
|
$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');
|
||
|
if ($responseContentType === 'application/json') {
|
||
|
$this->parseJsonToException($body, $obsException);
|
||
|
} else {
|
||
|
$this->parseXmlToException($body, $obsException);
|
||
|
}
|
||
|
throw $obsException;
|
||
|
}else{
|
||
|
$this->parseCommonHeaders($model, $response);
|
||
|
if ($responseContentType === 'application/json') {
|
||
|
$this->parseJsonToModel($body, $model);
|
||
|
} else {
|
||
|
$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');
|
||
|
if ($this->isJsonResponse($response)) {
|
||
|
$this->parseJsonToException($response -> getBody(), $obsException);
|
||
|
} else {
|
||
|
$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();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|