alibabacloud-gateway-oss/main.tea (446 lines of code) (raw):
import SPI;
import Credential;
import Util;
import OSSUtil;
import OpenApiUtil;
import XML;
import String;
import Map;
import Array;
import EncodeUtil;
import SignatureUtil;
import Time;
import OSS_Util;
extends SPI;
type @default_signed_params = [ string ]
type @except_signed_params = [ string ]
init(){
super();
@default_signed_params = [
'response-content-type', 'response-content-language',
'response-cache-control', 'logging', 'response-content-encoding',
'acl', 'uploadId', 'uploads', 'partNumber', 'group', 'link',
'delete', 'website', 'location', 'objectInfo', 'objectMeta',
'response-expires', 'response-content-disposition', 'cors', 'lifecycle',
'restore', 'qos', 'referer', 'stat', 'bucketInfo', 'append', 'position', 'security-token',
'live', 'comp', 'status', 'vod', 'startTime', 'endTime', 'x-oss-process',
'symlink', 'callback', 'callback-var', 'tagging', 'encryption', 'versions',
'versioning', 'versionId', 'policy', 'requestPayment', 'x-oss-traffic-limit', 'qosInfo', 'asyncFetch',
'x-oss-request-payer', 'sequential', 'inventory', 'inventoryId', 'continuation-token', 'callback',
'callback-var', 'worm', 'wormId', 'wormExtend', 'replication', 'replicationLocation',
'replicationProgress', 'transferAcceleration', 'cname', 'metaQuery',
'x-oss-ac-source-ip', 'x-oss-ac-subnet-mask', 'x-oss-ac-vpc-id', 'x-oss-ac-forward-allow',
'resourceGroup', 'style', 'styleName', 'x-oss-async-process', 'rtc', 'accessPoint',
'accessPointPolicy', "httpsConfig", 'regionsV2', 'publicAccessBlock', 'policyStatus',
'redundancyTransition', 'redundancyType', 'redundancyProgress', 'dataAccelerator', 'verbose',
'accessPointForObjectProcess', 'accessPointConfigForObjectProcess', 'accessPointPolicyForObjectProcess',
'bucketArchiveDirectRead', 'responseHeader', 'userDefinedLogFieldsConfig', 'reservedcapacity',
'requesterQosInfo', 'qosRequester', 'resourcePool', 'resourcePoolInfo', 'resourcePoolBuckets',
'processConfiguration', 'img', 'asyncFetch', 'virtualBucket', 'copy', 'userRegion', 'partSize',
'chunkSize', 'partUploadId', 'chunkNumber', 'userRegion', 'regionList', 'eventnotification',
'cacheConfiguration', 'dfs', 'dfsadmin', 'dfssecurity'
];
@except_signed_params = ['list-type', 'regions'];
}
async function modifyConfiguration(context: SPI.InterceptorContext, attributeMap: SPI.AttributeMap): void {
var config = context.configuration;
config.endpoint = getEndpoint(config.regionId, config.network, config.endpoint);
}
async function modifyRequest(context: SPI.InterceptorContext, attributeMap: SPI.AttributeMap): void {
var request = context.request;
var hostMap : map[string]string = {};
if (!Util.isUnset(request.hostMap)) {
hostMap = request.hostMap;
}
var bucketName = hostMap.bucket;
if (Util.isUnset(bucketName)) {
bucketName = '';
}
if (!Util.isUnset(request.headers['x-oss-meta-*'])) {
var tmp : any = Util.parseJSON(request.headers['x-oss-meta-*']);
var mapData : map[string]any = Util.assertAsMap(tmp);
var metaData : map[string]string = Util.stringifyMapValue(mapData);
var metaKeySet : [string] = Map.keySet(metaData);
request.headers['x-oss-meta-*'] = null;
for(var key : metaKeySet) {
var newKey = `x-oss-meta-${key}`;
request.headers[newKey] = metaData[key];
}
}
var config = context.configuration;
var regionId = config.regionId;
if (Util.isUnset(regionId) || Util.empty(regionId)) {
regionId = getRegionIdFromEndpoint(config.endpoint);
}
var credential : Credential = request.credential;
var accessKeyId = credential.getAccessKeyId();
var accessKeySecret = credential.getAccessKeySecret();
var securityToken = credential.getSecurityToken();
if (!Util.empty(securityToken)) {
request.headers.x-oss-security-token = securityToken;
}
if (!Util.isUnset(request.body)) {
if (String.equals(request.reqBodyType, 'xml')) {
var reqBodyMap = Util.assertAsMap(request.body);
// for python:
// xml_str = OSS_UtilClient.to_xml(req_body_map)
var xmlStr = XML.toXML(reqBodyMap);
request.stream = xmlStr;
request.headers.content-type = 'application/xml';
request.headers.content-md5 = EncodeUtil.base64EncodeToString(SignatureUtil.MD5Sign(xmlStr));
} else if (String.equals(request.reqBodyType, 'json')) {
var reqBodyStr = Util.toJSONString(request.body);
request.stream = reqBodyStr;
request.headers.content-type = 'application/json; charset=utf-8';
} else if (String.equals(request.reqBodyType, 'formData')) {
var reqBodyForm = Util.assertAsMap(request.body);
request.stream = OpenApiUtil.toForm(reqBodyForm);
request.headers.content-type = 'application/x-www-form-urlencoded';
} else if (String.equals(request.reqBodyType, 'binary')) {
attributeMap.key = {
crc = '',
md5 = '',
};
request.stream = OSSUtil.inject(request.stream, attributeMap.key);
request.headers.content-type = 'application/octet-stream';
}
}
var host = getHost(config.endpointType, bucketName, config.endpoint, context);
request.headers = {
host = host,
date = Util.getDateUTCString(),
user-agent = request.userAgent,
...request.headers
};
var originPath = request.pathname;
var originQuery = request.query;
if (!Util.empty(originPath)) {
var pathAndQueries : [string] = String.split(originPath, `?`, 2);
request.pathname = pathAndQueries[0];
if (Util.equalNumber(Array.size(pathAndQueries), 2)) {
var pathQueries : [string] = String.split(pathAndQueries[1], '&', null);
for (var sub : pathQueries) {
var item : [string] = String.split(sub, '=', null);
var queryKey : string = item[0];
var queryValue : string = '';
if (Util.equalNumber(Array.size(item), 2)) {
queryValue = item[1];
}
if (Util.empty(originQuery[queryKey])) {
request.query[queryKey] = queryValue;
}
}
}
}
var signatureVersion = Util.defaultString(request.signatureVersion, 'v4');
request.headers.authorization = getAuthorization(signatureVersion, bucketName, request.pathname, request.method, request.query, request.headers, accessKeyId, accessKeySecret, regionId);
}
async function modifyResponse(context: SPI.InterceptorContext, attributeMap: SPI.AttributeMap): void {
var request = context.request;
var response = context.response;
var bodyStr : string = null;
if (Util.is4xx(response.statusCode) || Util.is5xx(response.statusCode)) {
bodyStr = Util.readAsString(response.body);
if (!Util.empty(bodyStr)) {
var respMap : map[string]any = XML.parseXml(bodyStr, null);
var err : map[string]any = Util.assertAsMap(respMap.Error);
throw {
code = err.Code,
message = err.Message,
data = {
statusCode = response.statusCode,
requestId = err.RequestId,
ecCode = err.EC,
Recommend = err.RecommendDoc,
hostId = err.HostId,
AccessDeniedDetail = err.AccessDeniedDetail,
}
};
} else {
var headers : map[string]string = response.headers;
var requestId = headers.x-oss-request-id;
var ecCode = headers.x-oss-ec-code;
throw {
code = response.statusCode,
message = null,
data = {
statusCode = response.statusCode,
requestId = `${requestId}`,
ecCode = ecCode,
}
};
}
}
var ctx : map[string]string = attributeMap.key;
if (!Util.isUnset(ctx)) {
if (!Util.isUnset(ctx.crc)
&& !Util.isUnset(response.headers.x-oss-hash-crc64ecma)
&& !String.equals(ctx.crc, response.headers.x-oss-hash-crc64ecma)) {
throw {
code = 'CrcNotMatched',
data = {
clientCrc = ctx.crc,
serverCrc = response.headers.x-oss-hash-crc64ecma,
},
};
}
if (!Util.isUnset(ctx.md5)
&& !Util.isUnset(response.headers.content-md5)
&& !String.equals(ctx.md5, response.headers.content-md5)) {
throw {
code = 'MD5NotMatched',
data = {
clientMD5 = ctx.md5,
serverMD5 = response.headers.content-md5,
},
};
}
}
if (!Util.isUnset(response.body)) {
if (Util.equalNumber(response.statusCode, 204)) {
Util.readAsString(response.body);
} else if (String.equals(request.bodyType, 'xml')) {
bodyStr = Util.readAsString(response.body);
response.deserializedBody = bodyStr;
if (!Util.empty(bodyStr)) {
var result : any = OSS_Util.parseXml(bodyStr, request.action);
// for no util language
// var result : any = XML.parseXml(bodyStr, null);
try {
response.deserializedBody = Util.assertAsMap(result);
} catch (error) {
response.deserializedBody = result;
}
}
} else if (Util.equalString(request.bodyType, 'binary')) {
response.deserializedBody = response.body;
} else if (Util.equalString(request.bodyType, 'byte')) {
var byt = Util.readAsBytes(response.body);
response.deserializedBody = byt;
} else if (Util.equalString(request.bodyType, 'string')) {
response.deserializedBody = Util.readAsString(response.body);
} else if (Util.equalString(request.bodyType, 'json')) {
var obj = Util.readAsJSON(response.body);
var res = Util.assertAsMap(obj);
response.deserializedBody = res;
} else if (Util.equalString(request.bodyType, 'array')) {
response.deserializedBody = Util.readAsJSON(response.body);
} else {
response.deserializedBody = Util.readAsString(response.body);
}
}
}
async function getRegionIdFromEndpoint(endpoint: string) : string {
if (!Util.empty(endpoint)) {
var idx: integer = -1;
if (String.hasPrefix(endpoint, 'oss-') && String.hasSuffix(endpoint, '.aliyuncs.com')) {
idx = String.index(endpoint, '.aliyuncs.com');
return String.subString(endpoint, 4, idx);
}
if (String.hasSuffix(endpoint, '.mgw.aliyuncs.com')) {
idx = String.index(endpoint, '.mgw.aliyuncs.com');
return String.subString(endpoint, 0, idx);
}
if (String.hasSuffix(endpoint, '.mgw-internal.aliyuncs.com')) {
idx = String.index(endpoint, '.mgw-internal.aliyuncs.com');
return String.subString(endpoint, 0, idx);
}
if (String.hasSuffix(endpoint, '-internal.oss-data-acc.aliyuncs.com')) {
idx = String.index(endpoint, '-internal.oss-data-acc.aliyuncs.com');
return String.subString(endpoint, 0, idx);
}
if (String.hasSuffix(endpoint, '.oss-dls.aliyuncs.com')) {
idx = String.index(endpoint, '.oss-dls.aliyuncs.com');
return String.subString(endpoint, 0, idx);
}
}
return 'cn-hangzhou';
}
async function getEndpoint(regionId: string, network: string, endpoint: string) : string{
if (!Util.empty(endpoint)) {
return endpoint;
}
if (Util.empty(regionId)) {
regionId = 'cn-hangzhou';
}
if (!Util.empty(network)) {
if (String.contains(network, 'internal')) {
return `oss-${regionId}-internal.aliyuncs.com`;
} else if (String.contains(network, 'ipv6')) {
return `${regionId}oss.aliyuncs.com`;
} else if (String.contains(network, 'accelerate')) {
return `oss-${network}.aliyuncs.com`;
}
}
return `oss-${regionId}.aliyuncs.com`;
}
async function getHost(endpointType: string, bucketName: string, endpoint: string, context: SPI.InterceptorContext): string {
if (String.contains(endpoint,'.mgw.aliyuncs.com') && !Util.isUnset(context.request.hostMap.userid)) {
return `${context.request.hostMap.userid}.${endpoint}`;
}
if (String.contains(endpoint,'.mgw-internal.aliyuncs.com') && !Util.isUnset(context.request.hostMap.userid)) {
return `${context.request.hostMap.userid}.${endpoint}`;
}
if (Util.empty(bucketName)) {
return endpoint;
}
var host : string = `${bucketName}.${endpoint}`;
if (!Util.empty(endpointType)) {
if (String.equals(endpointType, 'ip')) {
host = `${endpoint}/${bucketName}`;
} else if (String.equals(endpointType, 'cname')) {
host = endpoint;
}
}
return host;
}
async function getAuthorization(signatureVersion: string, bucketName: string, pathname: string, method: string, query: map[string]string, headers: map[string]string, ak: string, secret: string, regionId: string): string {
var sign : string = '';
if (!Util.isUnset(signatureVersion)) {
if (String.equals(signatureVersion, 'v1')) {
sign = getSignatureV1(bucketName, pathname, method, query, headers, secret);
return `OSS ${ak}:${sign}`;
}
if (String.equals(signatureVersion, 'v2')) {
sign = getSignatureV2(bucketName, pathname, method, query, headers, secret);
return `OSS2 AccessKeyId:${ak},Signature:${sign}`;
}
}
var dateTime = OpenApiUtil.getTimestamp();
dateTime = String.replace(dateTime, '-', '', null);
dateTime = String.replace(dateTime, ':', '', null);
headers.x-oss-date = dateTime;
headers.x-oss-content-sha256 = 'UNSIGNED-PAYLOAD';
var onlyDate : string = String.subString(dateTime, 0, 8);
var cred : string = `${ak}/${onlyDate}/${regionId}/oss/aliyun_v4_request`;
sign = getSignatureV4(bucketName, pathname, method, query, headers, onlyDate, regionId, secret);
return `OSS4-HMAC-SHA256 Credential=${cred}, Signature=${sign}`;
}
async function getSignKey(secret: string, onlyDate: string, regionId: string): bytes {
var temp = `aliyun_v4${secret}`;
var res = SignatureUtil.HmacSHA256Sign(onlyDate, temp);
res = SignatureUtil.HmacSHA256SignByBytes(regionId, res);
res = SignatureUtil.HmacSHA256SignByBytes('oss', res);
res = SignatureUtil.HmacSHA256SignByBytes('aliyun_v4_request', res);
return res;
}
async function getSignatureV4(bucketName: string, pathname: string, method: string, query: map[string]string, headers: map[string]string, onlyDate: string, regionId: string, secret: string): string {
var signingkey : bytes = getSignKey(secret, onlyDate, regionId);
var canonicalizedUri : string = pathname;
if (!Util.empty(pathname)) {
if (!Util.empty(bucketName)) {
canonicalizedUri = `/${bucketName}${canonicalizedUri}`;
}
} else {
if (!Util.empty(bucketName)) {
canonicalizedUri = `/${bucketName}/`;
} else {
canonicalizedUri = "/";
}
}
// for java:
// String suffix = (!canonicalizedUri.equals("/") && canonicalizedUri.endsWith("/"))? "/" : "";
// canonicalizedUri = com.aliyun.openapiutil.Client.getEncodePath(canonicalizedUri) + suffix;
canonicalizedUri = OpenApiUtil.getEncodePath(canonicalizedUri);
var queryMap : map[string]string = {};
for (var queryKey : Map.keySet(query)) {
var queryValue : string = null;
if (!Util.empty(query[queryKey])) {
queryValue = EncodeUtil.percentEncode(query[queryKey]);
queryValue = String.replace(queryValue, '+', '%20', null);
}
queryKey = EncodeUtil.percentEncode(queryKey);
queryKey = String.replace(queryKey, '+', '%20', null);
// for go : queryMap[tea.StringValue(queryKey)] = queryValue
queryMap[queryKey] = queryValue;
}
var canonicalizedQueryString = buildCanonicalizedQueryStringV4(queryMap);
var canonicalizedHeaders = buildCanonicalizedHeadersV4(headers);
var payload = 'UNSIGNED-PAYLOAD';
var canonicalRequest = `${method}\n${canonicalizedUri}\n${canonicalizedQueryString}\n${canonicalizedHeaders}\n\n${payload}`;
var hex = EncodeUtil.hexEncode(EncodeUtil.hash(Util.toBytes(canonicalRequest), 'ACS4-HMAC-SHA256'));
var scope :string = `${onlyDate}/${regionId}/oss/aliyun_v4_request`;
var stringToSign = `OSS4-HMAC-SHA256\n${headers.x-oss-date}\n${scope}\n${hex}`;
var signature = SignatureUtil.HmacSHA256SignByBytes(stringToSign, signingkey);
return EncodeUtil.hexEncode(signature);
}
async function buildCanonicalizedQueryStringV4(queryMap: map[string]string): string {
var canonicalizedQueryString : string = '';
if (!Util.isUnset(queryMap)) {
var queryArray : [string] = Map.keySet(queryMap);
var sortedQueryArray = Array.ascSort(queryArray);
var separator : string = '';
for(var key : sortedQueryArray) {
canonicalizedQueryString = `${canonicalizedQueryString}${separator}${key}`;
if (!Util.empty(queryMap[key])) {
canonicalizedQueryString = `${canonicalizedQueryString}=${queryMap[key]}`;
}
separator = '&';
}
}
return canonicalizedQueryString;
}
async function buildCanonicalizedHeadersV4(headers: map[string]string): string {
var canonicalizedHeaders : string = '';
var headersArray : [string] = Map.keySet(headers);
var sortedHeadersArray = Array.ascSort(headersArray);
for(var key : sortedHeadersArray) {
var lowerKey = String.toLower(key);
if (String.hasPrefix(lowerKey, 'x-oss-')
|| String.equals(lowerKey, 'content-type') || String.equals(lowerKey, 'content-md5')) {
canonicalizedHeaders = `${canonicalizedHeaders}${key}:${String.trim(headers[key])}\n`;
}
}
return canonicalizedHeaders;
}
async function getSignatureV1(bucketName: string, pathname: string, method: string, query: map[string]string, headers: map[string]string, secret: string): string {
var resource : string = '';
var stringToSign : string = '';
if (!Util.empty(bucketName)) {
resource = `/${bucketName}`;
}
resource = `${resource}${pathname}`;
var canonicalizedResource = buildCanonicalizedResource(resource, query);
var canonicalizedHeaders = buildCanonicalizedHeaders(headers);
stringToSign = `${method}\n${canonicalizedHeaders}${canonicalizedResource}`;
return EncodeUtil.base64EncodeToString(SignatureUtil.HmacSHA1Sign(stringToSign, secret));
}
async function buildCanonicalizedResource(pathname: string, query: map[string]string): string {
var canonicalizedResource : string = pathname;
var queryKeys : [string] = Map.keySet(query);
var sortedParams = Array.ascSort(queryKeys);
var separator : string = '?';
for(var paramName : sortedParams) {
if (Array.contains(@default_signed_params, paramName) || String.hasPrefix(paramName, 'x-oss-')) {
canonicalizedResource = `${canonicalizedResource}${separator}${paramName}`;
if (!Util.empty(query[paramName])) {
canonicalizedResource = `${canonicalizedResource}=${query[paramName]}`;
}
separator = '&';
}
}
return canonicalizedResource;
}
async function buildCanonicalizedHeaders(headers: map[string]string): string {
var canonicalizedHeaders : string = '';
var contentType = headers.content-type;
if (Util.isUnset(contentType)) {
contentType = '';
}
var contentMd5 = headers.content-md5;
if (Util.isUnset(contentMd5)) {
contentMd5 = '';
}
canonicalizedHeaders = `${canonicalizedHeaders}${contentMd5}\n${contentType}\n${headers.date}\n`;
var keys = Map.keySet(headers);
var sortedHeaders = Array.ascSort(keys);
for(var header : sortedHeaders) {
if (String.contains(String.toLower(header), 'x-oss-') && !Util.isUnset(headers[header])) {
canonicalizedHeaders = `${canonicalizedHeaders}${header}:${headers[header]}\n`;
}
}
return canonicalizedHeaders;
}
async function getSignatureV2(bucketName: string, pathname: string, method: string, query: map[string]string, headers: map[string]string, secret: string): string {
return '';
}