| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 | <?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();			}		}	}}
 |