alibabacloud-gateway-sls/main.tea (392 lines of code) (raw):

import SPI; import Credential; import Util; import OpenApiUtil; import String; import Map; import Array; import EncodeUtil; import SignatureUtil; import SLS_Util; extends SPI; type @respBodyDecompressType = map[string][string]; type @reqBodyCompressType = map[string][string]; init(){ super(); @respBodyDecompressType = { PullLogs = ['zstd', 'lz4', 'gzip'], GetLogsV2 = ['zstd', 'lz4', 'gzip'], PreviewSPL = ['lz4'] }; @reqBodyCompressType = { PutLogs = ['zstd', 'lz4', 'deflate'], PreviewSPL = ['lz4'] }; } 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 project = hostMap.project; var config = context.configuration; var credential : Credential = request.credential; var credentialModel = credential.getCredential(); var accessKeyId = credentialModel.accessKeyId; var accessKeySecret = credentialModel.accessKeySecret; var securityToken = credentialModel.securityToken; if (!Util.empty(securityToken)) { request.headers['x-acs-security-token'] = securityToken; } var signatureVersion = Util.defaultString(request.signatureVersion, 'v1'); var finalCompressType = getFinalRequestCompressType(request.action, request.headers); var contentHash = ''; // get body bytes var bodyBytes : bytes = null; if (!Util.isUnset(request.body)) { if (String.equals(request.reqBodyType, 'json') || String.equals(request.reqBodyType, 'formData')) { request.headers['content-type'] = 'application/json'; var bodyStr = Util.toJSONString(request.body); bodyBytes = Util.toBytes(bodyStr); } else if (String.equals(request.reqBodyType, 'binary')) { // content-type: application/octet-stream bodyBytes = Util.assertAsBytes(request.body); } } // get body raw size var bodyRawSize : string = '0'; var rawSizeRef = request.headers['x-log-bodyrawsize']; // for php bug, Argument #1 ($value) could not be passed by reference if (!Util.isUnset(rawSizeRef)) { bodyRawSize = rawSizeRef; } else if (!Util.isUnset(request.body)) { bodyRawSize = `${SLS_Util.bytesLength(bodyBytes)}`; } // compress if needed, and set body and hash if (!Util.isUnset(request.body)) { if (!Util.empty(finalCompressType)) { var compressed = SLS_Util.compress(bodyBytes, finalCompressType); bodyBytes = compressed; } contentHash = makeContentHash(bodyBytes, signatureVersion); request.stream = bodyBytes; } var host = getHost(config.network, project, config.endpoint); request.headers = { accept = 'application/json', host = host, user-agent = request.userAgent, x-log-apiversion = '0.6.0', ...request.headers }; request.headers['x-log-bodyrawsize'] = bodyRawSize; setDefaultAcceptEncoding(request.action, request.headers); buildRequest(context); // move param in path to query if (String.equals(signatureVersion, 'v4')) { if (Util.empty(contentHash)) { contentHash = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'; } var date = getDateISO8601(); request.headers['x-log-date'] = date; request.headers['x-log-content-sha256'] = contentHash; request.headers['authorization'] = getAuthorizationV4(context, date, contentHash, accessKeyId, accessKeySecret); return; } if (!Util.empty(accessKeyId)) { request.headers['x-log-signaturemethod'] = 'hmac-sha256'; } request.headers['date'] = Util.getDateUTCString(); request.headers['content-md5'] = contentHash; request.headers['authorization'] = getAuthorization(request.pathname, request.method, request.query, request.headers, accessKeyId, accessKeySecret); } async function getFinalRequestCompressType(action: string, headers: map[string]string): string { var compressType = headers['x-log-compresstype']; var rawSize = headers['x-log-bodyrawsize']; // for php bug, Argument #1 ($value) could not be passed by reference // 1. already compressed, has x-log-compresstype and x-log-bodyrawsize in header, we dont need compress here if (!Util.isUnset(compressType) && !Util.isUnset(rawSize)) { return ''; } // 2. not compressed, but has x-log-compresstype in header, we need compress here if (!Util.isUnset(compressType)) { return compressType; } // 3. not compressed, in pre-defined api list, try use default supported compress type in order var encodings = @reqBodyCompressType[action]; if (!Util.isUnset(encodings)) { for (var encoding : encodings) { if (SLS_Util.isCompressorAvailable(encoding)) { headers['x-log-compresstype'] = encoding; // set header x-log-compresstype return encoding; } } } // 4. otherwise we dont need compress here return ''; } async function setDefaultAcceptEncoding(action: string, headers: map[string]string): void { var acceptEncoding = headers['Accept-Encoding']; // for php warning, dont rm this line if (!Util.isUnset(acceptEncoding)) { return; } var encodings = @respBodyDecompressType[action]; if (!Util.isUnset(encodings)) { for (var c : encodings) { if (SLS_Util.isDecompressorAvailable(c)) { headers['Accept-Encoding'] = c; return; } } } } async function makeContentHash(content: bytes, signatureVersion: string) : string { if (String.equals(signatureVersion, 'v4')) { if (Util.isUnset(content) || Util.equalString(`${SLS_Util.bytesLength(content)}`, "0")) { return 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'; } return String.toLower(EncodeUtil.hexEncode(EncodeUtil.hash(content, 'SLS4-HMAC-SHA256'))); } return String.toUpper(EncodeUtil.hexEncode(SignatureUtil.MD5SignForBytes(content))); } async function modifyResponse(context: SPI.InterceptorContext, attributeMap: SPI.AttributeMap): void { var request = context.request; var response = context.response; if (Util.is4xx(response.statusCode) || Util.is5xx(response.statusCode)) { var error = Util.readAsJSON(response.body); var resMap = Util.assertAsMap(error); throw { code = resMap['errorCode'], message = resMap['errorMessage'], accessDeniedDetail = resMap['accessDeniedDetail'], data = { httpCode = response.statusCode, requestId = response.headers['x-log-requestid'], statusCode = response.statusCode, } }; } if (!Util.isUnset(response.body)) { var bodyrawSize = response.headers['x-log-bodyrawsize']; var compressType = response.headers['x-log-compresstype']; var uncompressedData : readable = response.body; if (!Util.isUnset(bodyrawSize) && !Util.isUnset(compressType)) { uncompressedData = SLS_Util.readAndUncompressBlock(response.body, compressType, bodyrawSize); } if (Util.equalString(request.bodyType, 'binary')) { response.deserializedBody = uncompressedData; } else if (Util.equalString(request.bodyType, 'byte')) { var byt = Util.readAsBytes(uncompressedData); response.deserializedBody = byt; } else if (Util.equalString(request.bodyType, 'string')) { response.deserializedBody = Util.readAsString(uncompressedData); } else if (Util.equalString(request.bodyType, 'json')) { var obj = Util.readAsJSON(uncompressedData); // var res = Util.assertAsMap(obj); response.deserializedBody = obj; } else if (Util.equalString(request.bodyType, 'array')) { response.deserializedBody = Util.readAsJSON(uncompressedData); } else { response.deserializedBody = Util.readAsString(uncompressedData); } } } 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.equals(network, 'intranet')) { return `${regionId}-intranet.log.aliyuncs.com`; } else if (String.equals(network, 'accelerate')) { return `log-global.aliyuncs.com`; } else if (String.equals(network, 'share')) { if (String.equals(regionId, 'cn-hangzhou-corp') || String.equals(regionId, 'cn-shanghai-corp')) { return `${regionId}.sls.aliyuncs.com`; } else if (String.equals(regionId, 'cn-zhangbei-corp')) { return `zhangbei-corp-share.log.aliyuncs.com`; } return `${regionId}-share.log.aliyuncs.com`; } } return `${regionId}.log.aliyuncs.com`; } async function getHost(network: string, project: string, endpoint: string): string { if (Util.isUnset(project)) { return endpoint; } return `${project}.${endpoint}`; } async function getAuthorization(pathname: string, method: string, query: map[string]string, headers: map[string]string, ak: string, secret: string): string { var sign = getSignature(pathname, method, query, headers, secret); return `LOG ${ak}:${sign}`; } async function getSignature(pathname: string, method: string, query: map[string]string, headers: map[string]string, secret: string): string { var resource : string = pathname; var stringToSign : string = ''; var canonicalizedResource = buildCanonicalizedResource(resource, query); var canonicalizedHeaders = buildCanonicalizedHeaders(headers); stringToSign = `${method}\n${canonicalizedHeaders}${canonicalizedResource}`; return EncodeUtil.base64EncodeToString(SignatureUtil.HmacSHA256Sign(stringToSign, secret)); } async function buildCanonicalizedResource(pathname: string, query: map[string]string): string { var canonicalizedResource : string = pathname; if (!Util.isUnset(query)) { var queryList : [string] = Map.keySet(query); var sortedParams = Array.ascSort(queryList); var separator : string = '?'; for(var paramName : sortedParams) { canonicalizedResource = `${canonicalizedResource}${separator}${paramName}`; var paramValue = query[paramName]; if (!Util.isUnset(paramValue)) { canonicalizedResource = `${canonicalizedResource}=${paramValue}`; } 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-log-') || String.contains(String.toLower(header), 'x-acs-')) { canonicalizedHeaders = `${canonicalizedHeaders}${header}:${headers[header]}\n`; } } return canonicalizedHeaders; } async function buildRequest(context: SPI.InterceptorContext): void { var request = context.request; var resource : string = request.pathname; if (!Util.empty(resource)) { var paths : [string] = String.split(resource, `?`, 2); resource = paths[0]; if (Util.equalNumber(Array.size(paths), 2)) { var params : [string] = String.split(paths[1], '&', null); for (var sub : params) { var item : [string] = String.split(sub, '=', null); var key : string = item[0]; var value : string = null; if (Util.equalNumber(Array.size(item), 2)) { value = item[1]; } request.query[key] = value; } } } request.pathname = resource; } async function getAuthorizationV4(context: SPI.InterceptorContext, date: string, contentHash: string, accessKeyId: string, accessKeySecret: string): string { var region = getRegion(context); var headerStr = getSignedHeaderStrV4(context.request.headers); var dateShort = String.subString(date, 0, 8); dateShort = String.replace(dateShort, 'T', '', null); // for fix php sdk bug var scope = `${dateShort}/${region}/sls/aliyun_v4_request`; var signingkey = getSigningkeyV4('SLS4-HMAC-SHA256', accessKeySecret, region, dateShort); var signature = getSignatureV4(context, 'SLS4-HMAC-SHA256', headerStr, date, scope, contentHash, signingkey); return `${'SLS4-HMAC-SHA256'} Credential=${accessKeyId}/${scope},Signature=${signature}`; } async function getSigningkeyV4(signatureAlgorithm: string, secret: string, region: string, date: string): bytes { var sc1 = `aliyun_v4${secret}`; var sc2 = SignatureUtil.HmacSHA256Sign(date, sc1); var sc3 = SignatureUtil.HmacSHA256SignByBytes(region, sc2); var sc4 = SignatureUtil.HmacSHA256SignByBytes('sls', sc3); return SignatureUtil.HmacSHA256SignByBytes('aliyun_v4_request', sc4); } async function getSignatureV4(context: SPI.InterceptorContext, signatureAlgorithm: string, signedHeaderStr: string, date: string, scope: string, contentSha256: string, signingkey: bytes): string { var request = context.request; var canonicalURI = '/'; if (!Util.empty(request.pathname)) { canonicalURI = request.pathname; } var resources = buildCanonicalizedResourceV4(request.query); var headers = buildCanonicalizedHeadersV4(request.headers, signedHeaderStr); var stringToHash = `${request.method}\n${canonicalURI}\n${resources}\n${headers}\n${signedHeaderStr}\n${contentSha256}`; var hex = EncodeUtil.hexEncode(EncodeUtil.hash(Util.toBytes(stringToHash), signatureAlgorithm)); var stringToSign = `${signatureAlgorithm}\n${date}\n${scope}\n${hex}`; var signature = SignatureUtil.HmacSHA256SignByBytes(stringToSign, signingkey); return EncodeUtil.hexEncode(signature); } async function buildCanonicalizedResourceV4(query: map[string]string): string { var canonicalizedResource : string = ''; if (!Util.isUnset(query)) { var queryArray : [string] = Map.keySet(query); var sortedQueryArray = Array.ascSort(queryArray); var separator : string = ''; for(var key : sortedQueryArray) { canonicalizedResource = `${canonicalizedResource}${separator}${key}`; if (!Util.empty(query[key])) { canonicalizedResource = `${canonicalizedResource}=${EncodeUtil.percentEncode(query[key])}`; } separator = '&'; } } return canonicalizedResource; } async function buildCanonicalizedHeadersV4(headers: map[string]string, signedHeaderStr: string): string { var canonicalizedHeaders : string = ''; var signedHeaders: [string] = String.split(signedHeaderStr, ';', null); for (var header : signedHeaders) { canonicalizedHeaders = `${canonicalizedHeaders}${header}:${String.trim(headers[header])}\n`; } return canonicalizedHeaders; } async function getSignedHeaderStrV4(headers: map[string]string): string { var headersArray : [string] = Map.keySet(headers); var sortedHeadersArray = Array.ascSort(headersArray); var tmp : string = ''; var separator : string = ''; for(var key : sortedHeadersArray) { var lowerKey = String.toLower(key); if (String.hasPrefix(lowerKey, 'x-log-') || String.hasPrefix(lowerKey, 'x-acs-') || String.equals(lowerKey, 'host') || String.equals(lowerKey, 'content-type')) { tmp = `${tmp}${separator}${lowerKey}`; separator = ';'; } } return tmp; } async function getRegion(context: SPI.InterceptorContext): string { var config = context.configuration; if (!Util.isUnset(config.regionId) && !Util.empty(config.regionId)) { return config.regionId; } // try parse region from endpoint // do not use String.subString, subString has bug in php implementation var region: string = String.replace(config.endpoint, '.log.aliyuncs.com', '', null); region = String.replace(region, '.sls.aliyuncs.com', '', null); if (String.equals(region, config.endpoint)) { throw { code = 'ClientConfigError', message = 'The regionId configuration of sls client is missing.' }; } region = String.replace(region, '-share', '', null); region = String.replace(region, '-intranet', '', null); region = String.replace(region, '-vpc', '', null); return region; } // format: YYYYMMDDTHHMMSSZ async function getDateISO8601() : string { var date = OpenApiUtil.getTimestamp(); // 2024-02-04T11:31:58Z date = String.replace(date, '-', '', null); return String.replace(date, ':', '', null); }