/**
* 验证并且执行请求,按照OSS Api协议,执行操作
*
* @param array $options
* @return ResponseCore
* @throws OssException
* @throws RequestCore_Exception
*/
private function auth($options)
{
OssUtil::validateOptions($options);
//验证bucket,list_bucket时不需要验证
$this->authPrecheckBucket($options);
//验证object
$this->authPrecheckObject($options);
//Object名称的编码必须是utf8
$this->authPrecheckObjectEncoding($options);
//验证ACL
$this->authPrecheckAcl($options);
// 获得当次请求使用的协议头,是https还是http
$scheme = $this->useSSL ? 'https://' : 'http://';
// 获得当次请求使用的hostname,如果是公共域名或者专有域名,bucket拼在前面构成三级域名
$hostname = $this->generateHostname($options[self::OSS_BUCKET]);
$string_to_sign = '';
$headers = $this->generateHeaders($options, $hostname);
$signable_query_string_params = $this->generateSignableQueryStringParam($options);
$signable_query_string = OssUtil::toQueryString($signable_query_string_params);
$resource_uri = $this->generateResourceUri($options);
//生成请求URL
$conjunction = '?';
$non_signable_resource = '';
if (isset($options[self::OSS_SUB_RESOURCE])) {
$conjunction = '&';
}
if ($signable_query_string !== '') {
$signable_query_string = $conjunction . $signable_query_string;
$conjunction = '&';
}
$query_string = $this->generateQueryString($options);
if ($query_string !== '') {
$non_signable_resource .= $conjunction . $query_string;
$conjunction = '&';
}
$this->requestUrl = $scheme . $hostname . $resource_uri . $signable_query_string . $non_signable_resource;
//创建请求
$request = new RequestCore($this->requestUrl);
$request->set_useragent($this->generateUserAgent());
// Streaming uploads
if (isset($options[self::OSS_FILE_UPLOAD])) {
if (is_resource($options[self::OSS_FILE_UPLOAD])) {
$length = null;
if (isset($options[self::OSS_CONTENT_LENGTH])) {
$length = $options[self::OSS_CONTENT_LENGTH];
} elseif (isset($options[self::OSS_SEEK_TO])) {
$stats = fstat($options[self::OSS_FILE_UPLOAD]);
if ($stats && $stats[self::OSS_SIZE] >= 0) {
$length = $stats[self::OSS_SIZE] - (int) $options[self::OSS_SEEK_TO];
}
}
$request->set_read_stream($options[self::OSS_FILE_UPLOAD], $length);
} else {
$request->set_read_file($options[self::OSS_FILE_UPLOAD]);
$length = $request->read_stream_size;
if (isset($options[self::OSS_CONTENT_LENGTH])) {
$length = $options[self::OSS_CONTENT_LENGTH];
} elseif (isset($options[self::OSS_SEEK_TO]) && isset($length)) {
$length -= (int) $options[self::OSS_SEEK_TO];
}
$request->set_read_stream_size($length);
}
}
if (isset($options[self::OSS_SEEK_TO])) {
$request->set_seek_position((int) $options[self::OSS_SEEK_TO]);
}
if (isset($options[self::OSS_FILE_DOWNLOAD])) {
if (is_resource($options[self::OSS_FILE_DOWNLOAD])) {
$request->set_write_stream($options[self::OSS_FILE_DOWNLOAD]);
} else {
$request->set_write_file($options[self::OSS_FILE_DOWNLOAD]);
}
}
if (isset($options[self::OSS_METHOD])) {
$request->set_method($options[self::OSS_METHOD]);
$string_to_sign .= $options[self::OSS_METHOD] . "\n";
}
if (isset($options[self::OSS_CONTENT])) {
$request->set_body($options[self::OSS_CONTENT]);
if ($headers[self::OSS_CONTENT_TYPE] === 'application/x-www-form-urlencoded') {
$headers[self::OSS_CONTENT_TYPE] = 'application/octet-stream';
}
$headers[self::OSS_CONTENT_LENGTH] = strlen($options[self::OSS_CONTENT]);
$headers[self::OSS_CONTENT_MD5] = base64_encode(md5($options[self::OSS_CONTENT], true));
}
if (isset($options[self::OSS_CALLBACK])) {
$headers[self::OSS_CALLBACK] = base64_encode($options[self::OSS_CALLBACK]);
}
if (isset($options[self::OSS_CALLBACK_VAR])) {
$headers[self::OSS_CALLBACK_VAR] = base64_encode($options[self::OSS_CALLBACK_VAR]);
}
if (!isset($headers[self::OSS_ACCEPT_ENCODING])) {
$headers[self::OSS_ACCEPT_ENCODING] = '';
}
uksort($headers, 'strnatcasecmp');
foreach ($headers as $header_key => $header_value) {
$header_value = str_replace(array("\r", "\n"), '', $header_value);
if ($header_value !== '' || $header_key === self::OSS_ACCEPT_ENCODING) {
$request->add_header($header_key, $header_value);
}
if (strtolower($header_key) === 'content-md5' || strtolower($header_key) === 'content-type' || strtolower($header_key) === 'date' || isset($options['self::OSS_PREAUTH']) && (int) $options['self::OSS_PREAUTH'] > 0) {
$string_to_sign .= $header_value . "\n";
} elseif (substr(strtolower($header_key), 0, 6) === self::OSS_DEFAULT_PREFIX) {
$string_to_sign .= strtolower($header_key) . ':' . $header_value . "\n";
}
}
// 生成 signable_resource
$signable_resource = $this->generateSignableResource($options);
$string_to_sign .= rawurldecode($signable_resource) . urldecode($signable_query_string);
//对?后面的要签名的string字母序排序
$string_to_sign_ordered = $this->stringToSignSorted($string_to_sign);
$signature = base64_encode(hash_hmac('sha1', $string_to_sign_ordered, $this->accessKeySecret, true));
$request->add_header('Authorization', 'OSS ' . $this->accessKeyId . ':' . $signature);
if (isset($options[self::OSS_PREAUTH]) && (int) $options[self::OSS_PREAUTH] > 0) {
$signed_url = $this->requestUrl . $conjunction . self::OSS_URL_ACCESS_KEY_ID . '=' . rawurlencode($this->accessKeyId) . '&' . self::OSS_URL_EXPIRES . '=' . $options[self::OSS_PREAUTH] . '&' . self::OSS_URL_SIGNATURE . '=' . rawurlencode($signature);
return $signed_url;
} elseif (isset($options[self::OSS_PREAUTH])) {
return $this->requestUrl;
}
if ($this->timeout !== 0) {
$request->timeout = $this->timeout;
}
if ($this->connectTimeout !== 0) {
$request->connect_timeout = $this->connectTimeout;
}
try {
$request->send_request();
} catch (RequestCore_Exception $e) {
throw new OssException('RequestCoreException: ' . $e->getMessage());
}
$response_header = $request->get_response_header();
$response_header['oss-request-url'] = $this->requestUrl;
$response_header['oss-redirects'] = $this->redirects;
$response_header['oss-stringtosign'] = $string_to_sign;
$response_header['oss-requestheaders'] = $request->request_headers;
$data = new ResponseCore($response_header, $request->get_response_body(), $request->get_response_code());
//retry if OSS Internal Error
if ((int) $request->get_response_code() === 500) {
if ($this->redirects <= $this->maxRetries) {
//设置休眠
$delay = (int) (pow(4, $this->redirects) * 100000);
usleep($delay);
$this->redirects++;
$data = $this->auth($options);
}
}
$this->redirects = 0;
return $data;
}