Commit 5217fd03 authored by Euan游根明's avatar Euan游根明

feat: 文件转换

parent 56a3c5c9
package service
import (
"encoding/json"
"errors"
"fmt"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
imm20200930 "github.com/alibabacloud-go/imm-20200930/v4/client"
openapiutil "github.com/alibabacloud-go/openapi-util/service"
util "github.com/alibabacloud-go/tea-utils/v2/service"
"github.com/alibabacloud-go/tea/tea"
"net/url"
"strings"
)
type Conversion struct {
AccessKeyId string
AccessKeySecret string
Endpoint string
Bucket string
}
func NewConversion() *Conversion {
return &Conversion{
Endpoint: "imm.cn-shenzhen.aliyuncs.com",
AccessKeyId: "LTAI5tFsFNAoCngNdX9g4CMW",
AccessKeySecret: "otz3kkD0s3SWhVitKP2cqiZdCkTCJd",
Bucket: "test-bucket-v1",
}
}
func (o *Conversion) CreateClient() (_result *openapi.Client, _err error) {
config := &openapi.Config{
AccessKeyId: tea.String(o.AccessKeyId),
AccessKeySecret: tea.String(o.AccessKeySecret),
}
config.Endpoint = tea.String(o.Endpoint) //imm.cn-shenzhen.aliyuncs.com
_result = &openapi.Client{}
_result, _err = openapi.NewClient(config)
return _result, _err
}
// CreateApiInfo API 相关
func (o *Conversion) CreateApiInfo() (_result *openapi.Params) {
params := &openapi.Params{
Action: tea.String("CreateOfficeConversionTask"),
Version: tea.String("2020-09-30"),
Protocol: tea.String("HTTPS"),
Method: tea.String("POST"),
AuthType: tea.String("AK"),
Style: tea.String("RPC"),
Pathname: tea.String("/"),
ReqBodyType: tea.String("json"),
BodyType: tea.String("json"),
}
return params
}
// GetBucket 获取bucket信息
func (o *Conversion) GetBucket(isPrivate bool) string {
if isPrivate {
return "oss://" + "test-bucket-v1"
}
return "oss://" + "test-bucket-v1-pub"
}
type ConversionRequest struct {
ProjectName string
SourceType string
TargetType string
SourceURI string
TargetURI string
IsPrivate bool
}
type GetTaskRequest struct {
ProjectName string
TaskType string
TaskId string
RequestDefinition bool
}
func (o *Conversion) checkParam(arg ConversionRequest) error {
if arg.SourceURI == "" {
return errors.New("SourceURI 不能为空")
}
//if arg.TargetURI == "" {
// return errors.New("TargetURI 不能为空")
//}
if arg.ProjectName == "" {
return errors.New("ProjectName 不能为空")
}
if arg.SourceType == "" {
return errors.New("SourceType 不能为空")
}
if arg.TargetType == "" {
return errors.New("TargetType 不能为空")
}
return nil
}
// DoConversion 文件转换
func (o *Conversion) DoConversion(arg ConversionRequest) (string, error) {
//校验参数
checkErr := o.checkParam(arg)
if checkErr != nil {
return "", checkErr
}
client := NewConversion()
params := client.CreateApiInfo()
sourceParseUrl, parseErr := url.Parse(arg.SourceURI)
if parseErr != nil {
return "", parseErr
}
targetParseUrl, parseTargetErr := url.Parse(arg.TargetURI)
if parseTargetErr != nil {
return "", parseTargetErr
}
// query params
queries := map[string]interface{}{}
queries["ProjectName"] = arg.ProjectName
queries["SourceURI"] = o.GetBucket(arg.IsPrivate) + sourceParseUrl.Path
queries["TargetURI"] = o.GetBucket(arg.IsPrivate) + targetParseUrl.Path
//queries["TargetURIPrefix"] = arg.TargetURIPrefix
queries["SourceType"] = arg.SourceType
queries["TargetType"] = arg.TargetType
// runtime options
runtime := &util.RuntimeOptions{}
request := &openapi.OpenApiRequest{
Query: openapiutil.Query(queries),
}
ossClient, _err := client.CreateClient()
if _err != nil {
return "", _err
}
fmt.Println("DoConversion request", queries)
// 返回值为 Map 类型,可从 Map 中获得三类数据:响应体 body、响应头 headers、HTTP 返回的状态码 statusCode。
res, err := ossClient.CallApi(params, request, runtime)
fmt.Println("DoConversion result", res)
if err != nil {
return "", err
}
return "", err
}
func (o *Conversion) CreateImClient() (_result *imm20200930.Client, _err error) {
config := &openapi.Config{
AccessKeyId: tea.String(o.AccessKeyId),
AccessKeySecret: tea.String(o.AccessKeySecret),
}
config.Endpoint = tea.String(o.Endpoint)
_result = &imm20200930.Client{}
_result, _err = imm20200930.NewClient(config)
return _result, _err
}
// CreateProject 创建项目
func (o *Conversion) CreateProject() (_err error) {
client, createError := o.CreateImClient()
if createError != nil {
return createError
}
createProjectRequest := &imm20200930.CreateProjectRequest{}
var ProjectName = "DOC_TO_PDF"
createProjectRequest.ProjectName = &ProjectName
runtime := &util.RuntimeOptions{}
tryErr := func() (_e error) {
defer func() {
if r := tea.Recover(recover()); r != nil {
_e = r
}
}()
createdResp, _err := client.CreateProjectWithOptions(createProjectRequest, runtime)
if _err != nil {
return _err
}
fmt.Println("创建项目返回参数", createdResp)
return nil
}()
if tryErr != nil {
var sdkErr = &tea.SDKError{}
if _t, ok := tryErr.(*tea.SDKError); ok {
sdkErr = _t
} else {
sdkErr.Message = tea.String(tryErr.Error())
}
// 诊断地址
var data interface{}
d := json.NewDecoder(strings.NewReader(tea.StringValue(sdkErr.Data)))
decodeError := d.Decode(&data)
if decodeError != nil {
return decodeError
}
if m, ok := data.(map[string]interface{}); ok {
fmt.Println("创建项目出现异常", m["Code"])
resultCode, assertCodeError := util.AssertAsString(m["Code"])
if assertCodeError != nil {
return assertCodeError
}
if *resultCode == "ResourceAlreadyExists" {
return nil
}
return errors.New(*resultCode)
}
errMsg, assertError := util.AssertAsString(sdkErr.Message)
if assertError != nil {
return assertError
}
fmt.Println("创建项目出现异常", &errMsg)
}
return nil
}
func (o *Conversion) GetTaskProject(args GetTaskRequest) (string, error) {
client, _err := o.CreateImClient()
if _err != nil {
return "", _err
}
getTaskRequest := &imm20200930.GetTaskRequest{}
getTaskRequest.TaskId = &args.TaskId
getTaskRequest.TaskType = &args.TaskType
runtime := &util.RuntimeOptions{}
tryErr := func() (_e error) {
defer func() {
if r := tea.Recover(recover()); r != nil {
_e = r
}
}()
// 复制代码运行请自行打印 API 的返回值
_, _err = client.GetTaskWithOptions(getTaskRequest, runtime)
if _err != nil {
return _err
}
return nil
}()
if tryErr != nil {
var error = &tea.SDKError{}
if _t, ok := tryErr.(*tea.SDKError); ok {
error = _t
} else {
error.Message = tea.String(tryErr.Error())
}
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
// 错误 message
fmt.Println(tea.StringValue(error.Message))
// 诊断地址
var data interface{}
d := json.NewDecoder(strings.NewReader(tea.StringValue(error.Data)))
d.Decode(&data)
if m, ok := data.(map[string]interface{}); ok {
recommend, _ := m["Recommend"]
fmt.Println(recommend)
}
_, _err = util.AssertAsString(error.Message)
if _err != nil {
return "", _err
}
}
return "", _err
}
package controllers package controllers
import ( import (
service "doc-service/Service"
"github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http" "net/http"
...@@ -8,6 +9,14 @@ import ( ...@@ -8,6 +9,14 @@ import (
type ApiController struct{} type ApiController struct{}
func res(c *gin.Context, code int64, msg string, data interface{}) {
c.JSON(http.StatusOK, gin.H{
"code": code,
"msg": msg,
"data": data,
})
}
func (con ApiController) Index(c *gin.Context) { func (con ApiController) Index(c *gin.Context) {
session := sessions.Default(c) session := sessions.Default(c)
session.Options(sessions.Options{ session.Options(sessions.Options{
...@@ -27,3 +36,55 @@ func (con ApiController) Userlist(c *gin.Context) { ...@@ -27,3 +36,55 @@ func (con ApiController) Userlist(c *gin.Context) {
func (con ApiController) Plist(c *gin.Context) { func (con ApiController) Plist(c *gin.Context) {
c.String(200, "我是一个api接口-Plist") c.String(200, "我是一个api接口-Plist")
} }
//文件转换
func (con ApiController) Conversion(c *gin.Context) {
ret := make(map[string]interface{}, 0)
ret["errcode"] = 0
ret["errmsg"] = nil
ret["base64"] = nil
req := service.ConversionRequest{}
req.ProjectName = c.PostForm("project_name")
req.SourceType = c.PostForm("source_type")
req.TargetType = c.PostForm("target_type")
req.SourceURI = c.PostForm("source_url")
req.TargetURI = c.PostForm("target_url")
req.IsPrivate = true
//创建项目
createErr := service.NewConversion().CreateProject()
if createErr != nil {
ret["errmsg"] = createErr.Error()
ret["errcode"] = 100010
res(c, http.StatusOK, "成功", ret)
return
}
//执行转换
result, errs := service.NewConversion().DoConversion(req)
if errs != nil {
ret["errmsg"] = errs.Error()
ret["errcode"] = 100011
}
ret["url"] = result
res(c, http.StatusOK, "成功", ret)
return
}
//获取转换结果
func (con ApiController) GetTaskProject(c *gin.Context) {
ret := make(map[string]interface{}, 0)
req := service.GetTaskRequest{}
req.ProjectName = c.Query("project_name")
req.TaskId = c.Query("task_id")
req.TaskType = c.Query("task_type")
result, errs := service.NewConversion().GetTaskProject(req)
if errs != nil {
ret["errmsg"] = errs.Error()
ret["errcode"] = 100011
}
ret["url"] = result
res(c, http.StatusOK, "成功", ret)
return
}
...@@ -3,8 +3,14 @@ module doc-service ...@@ -3,8 +3,14 @@ module doc-service
go 1.20 go 1.20
require ( require (
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9
github.com/alibabacloud-go/imm-20200930/v4 v4.5.4
github.com/alibabacloud-go/openapi-util v0.1.1
github.com/alibabacloud-go/tea v1.2.2
github.com/alibabacloud-go/tea-utils/v2 v2.0.6
github.com/gin-contrib/sessions v1.0.1 github.com/gin-contrib/sessions v1.0.1
github.com/gin-gonic/gin v1.10.0 github.com/gin-gonic/gin v1.10.0
github.com/pkg/errors v0.9.1
github.com/redis/go-redis/v9 v9.6.1 github.com/redis/go-redis/v9 v9.6.1
gopkg.in/ini.v1 v1.67.0 gopkg.in/ini.v1 v1.67.0
gorm.io/driver/mysql v1.5.7 gorm.io/driver/mysql v1.5.7
...@@ -12,9 +18,15 @@ require ( ...@@ -12,9 +18,15 @@ require (
) )
require ( require (
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect
github.com/alibabacloud-go/debug v1.0.0 // indirect
github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
github.com/aliyun/credentials-go v1.3.1 // indirect
github.com/bytedance/sonic v1.12.1 // indirect github.com/bytedance/sonic v1.12.1 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/clbanning/mxj/v2 v2.5.5 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect github.com/cloudwego/iasm v0.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
...@@ -37,6 +49,7 @@ require ( ...@@ -37,6 +49,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.9.0 // indirect golang.org/x/arch v0.9.0 // indirect
......
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.8/go.mod h1:CzQnh+94WDnJOnKZH5YRyouL+OOcdBnXY5VWAf0McgI=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9 h1:fxMCrZatZfXq5nLcgkmWBXmU3FLC1OR+m/SqVtMqflk=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9/go.mod h1:bb+Io8Sn2RuM3/Rpme6ll86jMyFSrD1bxeV/+v61KeU=
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY=
github.com/alibabacloud-go/debug v1.0.0 h1:3eIEQWfay1fB24PQIEzXAswlVJtdQok8f3EVN5VrBnA=
github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q=
github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
github.com/alibabacloud-go/imm-20200930/v4 v4.5.4 h1:XW1LPgiPjnaaGPPnV382D/mQmOmPAAzfEqbP/jZd7S4=
github.com/alibabacloud-go/imm-20200930/v4 v4.5.4/go.mod h1:3VIEsvZK6bjYg9p3cDaYivoCisMq4YTjPSNhVbl1mo8=
github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
github.com/alibabacloud-go/openapi-util v0.1.1 h1:ujGErJjG8ncRW6XtBBMphzHTvCxn4DjrVw4m04HsS28=
github.com/alibabacloud-go/openapi-util v0.1.1/go.mod h1:/UehBSE2cf1gYT43GV4E+RxTdLRzURImCYY0aRmlXpw=
github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg=
github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea v1.2.1/go.mod h1:qbzof29bM/IFhLMtJPrgTGK3eauV5J2wSyEUo4OEmnA=
github.com/alibabacloud-go/tea v1.2.2 h1:aTsR6Rl3ANWPfqeQugPglfurloyBJY85eFy7Gc1+8oU=
github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=
github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4=
github.com/alibabacloud-go/tea-utils/v2 v2.0.6 h1:ZkmUlhlQbaDC+Eba/GARMPy6hKdCLiSke5RsN5LcyQ0=
github.com/alibabacloud-go/tea-utils/v2 v2.0.6/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0=
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
github.com/aliyun/credentials-go v1.3.1 h1:uq/0v7kWrxmoLGpqjx7vtQ/s03f0zR//0br/xWDTE28=
github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24= github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24=
github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
github.com/gin-contrib/sessions v1.0.1 h1:3hsJyNs7v7N8OtelFmYXFrulAf6zSR7nW/putcPEHxI= github.com/gin-contrib/sessions v1.0.1 h1:3hsJyNs7v7N8OtelFmYXFrulAf6zSR7nW/putcPEHxI=
...@@ -35,21 +70,32 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o ...@@ -35,21 +70,32 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o= github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM= github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
...@@ -60,14 +106,17 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD ...@@ -60,14 +106,17 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
...@@ -75,19 +124,31 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D ...@@ -75,19 +124,31 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
...@@ -95,43 +156,138 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o ...@@ -95,43 +156,138 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k= golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
...@@ -140,5 +296,6 @@ gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkD ...@@ -140,5 +296,6 @@ gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkD
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg= gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg=
gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
...@@ -10,6 +10,8 @@ func ApiRoutersInit(r *gin.Engine) { ...@@ -10,6 +10,8 @@ func ApiRoutersInit(r *gin.Engine) {
apiRouters := r.Group("/api") //middlewares.InitMiddleware apiRouters := r.Group("/api") //middlewares.InitMiddleware
{ {
apiRouters.GET("/", controllers.ApiController{}.Index) apiRouters.GET("/", controllers.ApiController{}.Index)
apiRouters.POST("/", controllers.ApiController{}.Conversion)
apiRouters.GET("/", controllers.ApiController{}.GetTaskProject)
} }
} }
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
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.
// This file is auto-generated, don't edit it. Thanks.
package client
import (
"io"
"github.com/alibabacloud-go/tea/tea"
credential "github.com/aliyun/credentials-go/credentials"
)
type InterceptorContext struct {
Request *InterceptorContextRequest `json:"request,omitempty" xml:"request,omitempty" require:"true" type:"Struct"`
Configuration *InterceptorContextConfiguration `json:"configuration,omitempty" xml:"configuration,omitempty" require:"true" type:"Struct"`
Response *InterceptorContextResponse `json:"response,omitempty" xml:"response,omitempty" require:"true" type:"Struct"`
}
func (s InterceptorContext) String() string {
return tea.Prettify(s)
}
func (s InterceptorContext) GoString() string {
return s.String()
}
func (s *InterceptorContext) SetRequest(v *InterceptorContextRequest) *InterceptorContext {
s.Request = v
return s
}
func (s *InterceptorContext) SetConfiguration(v *InterceptorContextConfiguration) *InterceptorContext {
s.Configuration = v
return s
}
func (s *InterceptorContext) SetResponse(v *InterceptorContextResponse) *InterceptorContext {
s.Response = v
return s
}
type InterceptorContextRequest struct {
Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty"`
Query map[string]*string `json:"query,omitempty" xml:"query,omitempty"`
Body interface{} `json:"body,omitempty" xml:"body,omitempty"`
Stream io.Reader `json:"stream,omitempty" xml:"stream,omitempty"`
HostMap map[string]*string `json:"hostMap,omitempty" xml:"hostMap,omitempty"`
Pathname *string `json:"pathname,omitempty" xml:"pathname,omitempty" require:"true"`
ProductId *string `json:"productId,omitempty" xml:"productId,omitempty" require:"true"`
Action *string `json:"action,omitempty" xml:"action,omitempty" require:"true"`
Version *string `json:"version,omitempty" xml:"version,omitempty" require:"true"`
Protocol *string `json:"protocol,omitempty" xml:"protocol,omitempty" require:"true"`
Method *string `json:"method,omitempty" xml:"method,omitempty" require:"true"`
AuthType *string `json:"authType,omitempty" xml:"authType,omitempty" require:"true"`
BodyType *string `json:"bodyType,omitempty" xml:"bodyType,omitempty" require:"true"`
ReqBodyType *string `json:"reqBodyType,omitempty" xml:"reqBodyType,omitempty" require:"true"`
Style *string `json:"style,omitempty" xml:"style,omitempty"`
Credential credential.Credential `json:"credential,omitempty" xml:"credential,omitempty" require:"true"`
SignatureVersion *string `json:"signatureVersion,omitempty" xml:"signatureVersion,omitempty"`
SignatureAlgorithm *string `json:"signatureAlgorithm,omitempty" xml:"signatureAlgorithm,omitempty"`
UserAgent *string `json:"userAgent,omitempty" xml:"userAgent,omitempty" require:"true"`
}
func (s InterceptorContextRequest) String() string {
return tea.Prettify(s)
}
func (s InterceptorContextRequest) GoString() string {
return s.String()
}
func (s *InterceptorContextRequest) SetHeaders(v map[string]*string) *InterceptorContextRequest {
s.Headers = v
return s
}
func (s *InterceptorContextRequest) SetQuery(v map[string]*string) *InterceptorContextRequest {
s.Query = v
return s
}
func (s *InterceptorContextRequest) SetBody(v interface{}) *InterceptorContextRequest {
s.Body = v
return s
}
func (s *InterceptorContextRequest) SetStream(v io.Reader) *InterceptorContextRequest {
s.Stream = v
return s
}
func (s *InterceptorContextRequest) SetHostMap(v map[string]*string) *InterceptorContextRequest {
s.HostMap = v
return s
}
func (s *InterceptorContextRequest) SetPathname(v string) *InterceptorContextRequest {
s.Pathname = &v
return s
}
func (s *InterceptorContextRequest) SetProductId(v string) *InterceptorContextRequest {
s.ProductId = &v
return s
}
func (s *InterceptorContextRequest) SetAction(v string) *InterceptorContextRequest {
s.Action = &v
return s
}
func (s *InterceptorContextRequest) SetVersion(v string) *InterceptorContextRequest {
s.Version = &v
return s
}
func (s *InterceptorContextRequest) SetProtocol(v string) *InterceptorContextRequest {
s.Protocol = &v
return s
}
func (s *InterceptorContextRequest) SetMethod(v string) *InterceptorContextRequest {
s.Method = &v
return s
}
func (s *InterceptorContextRequest) SetAuthType(v string) *InterceptorContextRequest {
s.AuthType = &v
return s
}
func (s *InterceptorContextRequest) SetBodyType(v string) *InterceptorContextRequest {
s.BodyType = &v
return s
}
func (s *InterceptorContextRequest) SetReqBodyType(v string) *InterceptorContextRequest {
s.ReqBodyType = &v
return s
}
func (s *InterceptorContextRequest) SetStyle(v string) *InterceptorContextRequest {
s.Style = &v
return s
}
func (s *InterceptorContextRequest) SetCredential(v credential.Credential) *InterceptorContextRequest {
s.Credential = v
return s
}
func (s *InterceptorContextRequest) SetSignatureVersion(v string) *InterceptorContextRequest {
s.SignatureVersion = &v
return s
}
func (s *InterceptorContextRequest) SetSignatureAlgorithm(v string) *InterceptorContextRequest {
s.SignatureAlgorithm = &v
return s
}
func (s *InterceptorContextRequest) SetUserAgent(v string) *InterceptorContextRequest {
s.UserAgent = &v
return s
}
type InterceptorContextConfiguration struct {
RegionId *string `json:"regionId,omitempty" xml:"regionId,omitempty" require:"true"`
Endpoint *string `json:"endpoint,omitempty" xml:"endpoint,omitempty"`
EndpointRule *string `json:"endpointRule,omitempty" xml:"endpointRule,omitempty"`
EndpointMap map[string]*string `json:"endpointMap,omitempty" xml:"endpointMap,omitempty"`
EndpointType *string `json:"endpointType,omitempty" xml:"endpointType,omitempty"`
Network *string `json:"network,omitempty" xml:"network,omitempty"`
Suffix *string `json:"suffix,omitempty" xml:"suffix,omitempty"`
}
func (s InterceptorContextConfiguration) String() string {
return tea.Prettify(s)
}
func (s InterceptorContextConfiguration) GoString() string {
return s.String()
}
func (s *InterceptorContextConfiguration) SetRegionId(v string) *InterceptorContextConfiguration {
s.RegionId = &v
return s
}
func (s *InterceptorContextConfiguration) SetEndpoint(v string) *InterceptorContextConfiguration {
s.Endpoint = &v
return s
}
func (s *InterceptorContextConfiguration) SetEndpointRule(v string) *InterceptorContextConfiguration {
s.EndpointRule = &v
return s
}
func (s *InterceptorContextConfiguration) SetEndpointMap(v map[string]*string) *InterceptorContextConfiguration {
s.EndpointMap = v
return s
}
func (s *InterceptorContextConfiguration) SetEndpointType(v string) *InterceptorContextConfiguration {
s.EndpointType = &v
return s
}
func (s *InterceptorContextConfiguration) SetNetwork(v string) *InterceptorContextConfiguration {
s.Network = &v
return s
}
func (s *InterceptorContextConfiguration) SetSuffix(v string) *InterceptorContextConfiguration {
s.Suffix = &v
return s
}
type InterceptorContextResponse struct {
StatusCode *int `json:"statusCode,omitempty" xml:"statusCode,omitempty"`
Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty"`
Body io.Reader `json:"body,omitempty" xml:"body,omitempty"`
DeserializedBody interface{} `json:"deserializedBody,omitempty" xml:"deserializedBody,omitempty"`
}
func (s InterceptorContextResponse) String() string {
return tea.Prettify(s)
}
func (s InterceptorContextResponse) GoString() string {
return s.String()
}
func (s *InterceptorContextResponse) SetStatusCode(v int) *InterceptorContextResponse {
s.StatusCode = &v
return s
}
func (s *InterceptorContextResponse) SetHeaders(v map[string]*string) *InterceptorContextResponse {
s.Headers = v
return s
}
func (s *InterceptorContextResponse) SetBody(v io.Reader) *InterceptorContextResponse {
s.Body = v
return s
}
func (s *InterceptorContextResponse) SetDeserializedBody(v interface{}) *InterceptorContextResponse {
s.DeserializedBody = v
return s
}
type AttributeMap struct {
Attributes map[string]interface{} `json:"attributes,omitempty" xml:"attributes,omitempty" require:"true"`
Key map[string]*string `json:"key,omitempty" xml:"key,omitempty" require:"true"`
}
func (s AttributeMap) String() string {
return tea.Prettify(s)
}
func (s AttributeMap) GoString() string {
return s.String()
}
func (s *AttributeMap) SetAttributes(v map[string]interface{}) *AttributeMap {
s.Attributes = v
return s
}
func (s *AttributeMap) SetKey(v map[string]*string) *AttributeMap {
s.Key = v
return s
}
type ClientInterface interface {
ModifyConfiguration(context *InterceptorContext, attributeMap *AttributeMap) error
ModifyRequest(context *InterceptorContext, attributeMap *AttributeMap) error
ModifyResponse(context *InterceptorContext, attributeMap *AttributeMap) error
}
type Client struct {
}
func NewClient() (*Client, error) {
client := new(Client)
err := client.Init()
return client, err
}
func (client *Client) Init() (_err error) {
return nil
}
func (client *Client) ModifyConfiguration(context *InterceptorContext, attributeMap *AttributeMap) (_err error) {
panic("No Support!")
}
func (client *Client) ModifyRequest(context *InterceptorContext, attributeMap *AttributeMap) (_err error) {
panic("No Support!")
}
func (client *Client) ModifyResponse(context *InterceptorContext, attributeMap *AttributeMap) (_err error) {
panic("No Support!")
}
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
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.
// This file is auto-generated, don't edit it. Thanks.
// Description:
//
// This is for OpenApi SDK
package client
import (
"io"
spi "github.com/alibabacloud-go/alibabacloud-gateway-spi/client"
openapiutil "github.com/alibabacloud-go/openapi-util/service"
util "github.com/alibabacloud-go/tea-utils/v2/service"
xml "github.com/alibabacloud-go/tea-xml/service"
"github.com/alibabacloud-go/tea/tea"
credential "github.com/aliyun/credentials-go/credentials"
)
type GlobalParameters struct {
Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty"`
Queries map[string]*string `json:"queries,omitempty" xml:"queries,omitempty"`
}
func (s GlobalParameters) String() string {
return tea.Prettify(s)
}
func (s GlobalParameters) GoString() string {
return s.String()
}
func (s *GlobalParameters) SetHeaders(v map[string]*string) *GlobalParameters {
s.Headers = v
return s
}
func (s *GlobalParameters) SetQueries(v map[string]*string) *GlobalParameters {
s.Queries = v
return s
}
// Description:
//
// Model for initing client
type Config struct {
// accesskey id
AccessKeyId *string `json:"accessKeyId,omitempty" xml:"accessKeyId,omitempty"`
// accesskey secret
AccessKeySecret *string `json:"accessKeySecret,omitempty" xml:"accessKeySecret,omitempty"`
// security token
//
// example:
//
// a.txt
SecurityToken *string `json:"securityToken,omitempty" xml:"securityToken,omitempty"`
// bearer token
//
// example:
//
// the-bearer-token
BearerToken *string `json:"bearerToken,omitempty" xml:"bearerToken,omitempty"`
// http protocol
//
// example:
//
// http
Protocol *string `json:"protocol,omitempty" xml:"protocol,omitempty"`
// http method
//
// example:
//
// GET
Method *string `json:"method,omitempty" xml:"method,omitempty"`
// region id
//
// example:
//
// cn-hangzhou
RegionId *string `json:"regionId,omitempty" xml:"regionId,omitempty"`
// read timeout
//
// example:
//
// 10
ReadTimeout *int `json:"readTimeout,omitempty" xml:"readTimeout,omitempty"`
// connect timeout
//
// example:
//
// 10
ConnectTimeout *int `json:"connectTimeout,omitempty" xml:"connectTimeout,omitempty"`
// http proxy
//
// example:
//
// http://localhost
HttpProxy *string `json:"httpProxy,omitempty" xml:"httpProxy,omitempty"`
// https proxy
//
// example:
//
// https://localhost
HttpsProxy *string `json:"httpsProxy,omitempty" xml:"httpsProxy,omitempty"`
// credential
Credential credential.Credential `json:"credential,omitempty" xml:"credential,omitempty"`
// endpoint
//
// example:
//
// cs.aliyuncs.com
Endpoint *string `json:"endpoint,omitempty" xml:"endpoint,omitempty"`
// proxy white list
//
// example:
//
// http://localhost
NoProxy *string `json:"noProxy,omitempty" xml:"noProxy,omitempty"`
// max idle conns
//
// example:
//
// 3
MaxIdleConns *int `json:"maxIdleConns,omitempty" xml:"maxIdleConns,omitempty"`
// network for endpoint
//
// example:
//
// public
Network *string `json:"network,omitempty" xml:"network,omitempty"`
// user agent
//
// example:
//
// Alibabacloud/1
UserAgent *string `json:"userAgent,omitempty" xml:"userAgent,omitempty"`
// suffix for endpoint
//
// example:
//
// aliyun
Suffix *string `json:"suffix,omitempty" xml:"suffix,omitempty"`
// socks5 proxy
Socks5Proxy *string `json:"socks5Proxy,omitempty" xml:"socks5Proxy,omitempty"`
// socks5 network
//
// example:
//
// TCP
Socks5NetWork *string `json:"socks5NetWork,omitempty" xml:"socks5NetWork,omitempty"`
// endpoint type
//
// example:
//
// internal
EndpointType *string `json:"endpointType,omitempty" xml:"endpointType,omitempty"`
// OpenPlatform endpoint
//
// example:
//
// openplatform.aliyuncs.com
OpenPlatformEndpoint *string `json:"openPlatformEndpoint,omitempty" xml:"openPlatformEndpoint,omitempty"`
// Deprecated
//
// credential type
//
// example:
//
// access_key
Type *string `json:"type,omitempty" xml:"type,omitempty"`
// Signature Version
//
// example:
//
// v1
SignatureVersion *string `json:"signatureVersion,omitempty" xml:"signatureVersion,omitempty"`
// Signature Algorithm
//
// example:
//
// ACS3-HMAC-SHA256
SignatureAlgorithm *string `json:"signatureAlgorithm,omitempty" xml:"signatureAlgorithm,omitempty"`
// Global Parameters
GlobalParameters *GlobalParameters `json:"globalParameters,omitempty" xml:"globalParameters,omitempty"`
// privite key for client certificate
//
// example:
//
// MIIEvQ
Key *string `json:"key,omitempty" xml:"key,omitempty"`
// client certificate
//
// example:
//
// -----BEGIN CERTIFICATE-----
//
// xxx-----END CERTIFICATE-----
Cert *string `json:"cert,omitempty" xml:"cert,omitempty"`
// server certificate
//
// example:
//
// -----BEGIN CERTIFICATE-----
//
// xxx-----END CERTIFICATE-----
Ca *string `json:"ca,omitempty" xml:"ca,omitempty"`
// disable HTTP/2
//
// example:
//
// false
DisableHttp2 *bool `json:"disableHttp2,omitempty" xml:"disableHttp2,omitempty"`
}
func (s Config) String() string {
return tea.Prettify(s)
}
func (s Config) GoString() string {
return s.String()
}
func (s *Config) SetAccessKeyId(v string) *Config {
s.AccessKeyId = &v
return s
}
func (s *Config) SetAccessKeySecret(v string) *Config {
s.AccessKeySecret = &v
return s
}
func (s *Config) SetSecurityToken(v string) *Config {
s.SecurityToken = &v
return s
}
func (s *Config) SetBearerToken(v string) *Config {
s.BearerToken = &v
return s
}
func (s *Config) SetProtocol(v string) *Config {
s.Protocol = &v
return s
}
func (s *Config) SetMethod(v string) *Config {
s.Method = &v
return s
}
func (s *Config) SetRegionId(v string) *Config {
s.RegionId = &v
return s
}
func (s *Config) SetReadTimeout(v int) *Config {
s.ReadTimeout = &v
return s
}
func (s *Config) SetConnectTimeout(v int) *Config {
s.ConnectTimeout = &v
return s
}
func (s *Config) SetHttpProxy(v string) *Config {
s.HttpProxy = &v
return s
}
func (s *Config) SetHttpsProxy(v string) *Config {
s.HttpsProxy = &v
return s
}
func (s *Config) SetCredential(v credential.Credential) *Config {
s.Credential = v
return s
}
func (s *Config) SetEndpoint(v string) *Config {
s.Endpoint = &v
return s
}
func (s *Config) SetNoProxy(v string) *Config {
s.NoProxy = &v
return s
}
func (s *Config) SetMaxIdleConns(v int) *Config {
s.MaxIdleConns = &v
return s
}
func (s *Config) SetNetwork(v string) *Config {
s.Network = &v
return s
}
func (s *Config) SetUserAgent(v string) *Config {
s.UserAgent = &v
return s
}
func (s *Config) SetSuffix(v string) *Config {
s.Suffix = &v
return s
}
func (s *Config) SetSocks5Proxy(v string) *Config {
s.Socks5Proxy = &v
return s
}
func (s *Config) SetSocks5NetWork(v string) *Config {
s.Socks5NetWork = &v
return s
}
func (s *Config) SetEndpointType(v string) *Config {
s.EndpointType = &v
return s
}
func (s *Config) SetOpenPlatformEndpoint(v string) *Config {
s.OpenPlatformEndpoint = &v
return s
}
func (s *Config) SetType(v string) *Config {
s.Type = &v
return s
}
func (s *Config) SetSignatureVersion(v string) *Config {
s.SignatureVersion = &v
return s
}
func (s *Config) SetSignatureAlgorithm(v string) *Config {
s.SignatureAlgorithm = &v
return s
}
func (s *Config) SetGlobalParameters(v *GlobalParameters) *Config {
s.GlobalParameters = v
return s
}
func (s *Config) SetKey(v string) *Config {
s.Key = &v
return s
}
func (s *Config) SetCert(v string) *Config {
s.Cert = &v
return s
}
func (s *Config) SetCa(v string) *Config {
s.Ca = &v
return s
}
func (s *Config) SetDisableHttp2(v bool) *Config {
s.DisableHttp2 = &v
return s
}
type OpenApiRequest struct {
Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty"`
Query map[string]*string `json:"query,omitempty" xml:"query,omitempty"`
Body interface{} `json:"body,omitempty" xml:"body,omitempty"`
Stream io.Reader `json:"stream,omitempty" xml:"stream,omitempty"`
HostMap map[string]*string `json:"hostMap,omitempty" xml:"hostMap,omitempty"`
EndpointOverride *string `json:"endpointOverride,omitempty" xml:"endpointOverride,omitempty"`
}
func (s OpenApiRequest) String() string {
return tea.Prettify(s)
}
func (s OpenApiRequest) GoString() string {
return s.String()
}
func (s *OpenApiRequest) SetHeaders(v map[string]*string) *OpenApiRequest {
s.Headers = v
return s
}
func (s *OpenApiRequest) SetQuery(v map[string]*string) *OpenApiRequest {
s.Query = v
return s
}
func (s *OpenApiRequest) SetBody(v interface{}) *OpenApiRequest {
s.Body = v
return s
}
func (s *OpenApiRequest) SetStream(v io.Reader) *OpenApiRequest {
s.Stream = v
return s
}
func (s *OpenApiRequest) SetHostMap(v map[string]*string) *OpenApiRequest {
s.HostMap = v
return s
}
func (s *OpenApiRequest) SetEndpointOverride(v string) *OpenApiRequest {
s.EndpointOverride = &v
return s
}
type Params struct {
Action *string `json:"action,omitempty" xml:"action,omitempty" require:"true"`
Version *string `json:"version,omitempty" xml:"version,omitempty" require:"true"`
Protocol *string `json:"protocol,omitempty" xml:"protocol,omitempty" require:"true"`
Pathname *string `json:"pathname,omitempty" xml:"pathname,omitempty" require:"true"`
Method *string `json:"method,omitempty" xml:"method,omitempty" require:"true"`
AuthType *string `json:"authType,omitempty" xml:"authType,omitempty" require:"true"`
BodyType *string `json:"bodyType,omitempty" xml:"bodyType,omitempty" require:"true"`
ReqBodyType *string `json:"reqBodyType,omitempty" xml:"reqBodyType,omitempty" require:"true"`
Style *string `json:"style,omitempty" xml:"style,omitempty"`
}
func (s Params) String() string {
return tea.Prettify(s)
}
func (s Params) GoString() string {
return s.String()
}
func (s *Params) SetAction(v string) *Params {
s.Action = &v
return s
}
func (s *Params) SetVersion(v string) *Params {
s.Version = &v
return s
}
func (s *Params) SetProtocol(v string) *Params {
s.Protocol = &v
return s
}
func (s *Params) SetPathname(v string) *Params {
s.Pathname = &v
return s
}
func (s *Params) SetMethod(v string) *Params {
s.Method = &v
return s
}
func (s *Params) SetAuthType(v string) *Params {
s.AuthType = &v
return s
}
func (s *Params) SetBodyType(v string) *Params {
s.BodyType = &v
return s
}
func (s *Params) SetReqBodyType(v string) *Params {
s.ReqBodyType = &v
return s
}
func (s *Params) SetStyle(v string) *Params {
s.Style = &v
return s
}
type Client struct {
Endpoint *string
RegionId *string
Protocol *string
Method *string
UserAgent *string
EndpointRule *string
EndpointMap map[string]*string
Suffix *string
ReadTimeout *int
ConnectTimeout *int
HttpProxy *string
HttpsProxy *string
Socks5Proxy *string
Socks5NetWork *string
NoProxy *string
Network *string
ProductId *string
MaxIdleConns *int
EndpointType *string
OpenPlatformEndpoint *string
Credential credential.Credential
SignatureVersion *string
SignatureAlgorithm *string
Headers map[string]*string
Spi spi.ClientInterface
GlobalParameters *GlobalParameters
Key *string
Cert *string
Ca *string
DisableHttp2 *bool
}
// Description:
//
// # Init client with Config
//
// @param config - config contains the necessary information to create a client
func NewClient(config *Config) (*Client, error) {
client := new(Client)
err := client.Init(config)
return client, err
}
func (client *Client) Init(config *Config) (_err error) {
if tea.BoolValue(util.IsUnset(config)) {
_err = tea.NewSDKError(map[string]interface{}{
"code": "ParameterMissing",
"message": "'config' can not be unset",
})
return _err
}
if !tea.BoolValue(util.Empty(config.AccessKeyId)) && !tea.BoolValue(util.Empty(config.AccessKeySecret)) {
if !tea.BoolValue(util.Empty(config.SecurityToken)) {
config.Type = tea.String("sts")
} else {
config.Type = tea.String("access_key")
}
credentialConfig := &credential.Config{
AccessKeyId: config.AccessKeyId,
Type: config.Type,
AccessKeySecret: config.AccessKeySecret,
}
credentialConfig.SecurityToken = config.SecurityToken
client.Credential, _err = credential.NewCredential(credentialConfig)
if _err != nil {
return _err
}
} else if !tea.BoolValue(util.Empty(config.BearerToken)) {
cc := &credential.Config{
Type: tea.String("bearer"),
BearerToken: config.BearerToken,
}
client.Credential, _err = credential.NewCredential(cc)
if _err != nil {
return _err
}
} else if !tea.BoolValue(util.IsUnset(config.Credential)) {
client.Credential = config.Credential
}
client.Endpoint = config.Endpoint
client.EndpointType = config.EndpointType
client.Network = config.Network
client.Suffix = config.Suffix
client.Protocol = config.Protocol
client.Method = config.Method
client.RegionId = config.RegionId
client.UserAgent = config.UserAgent
client.ReadTimeout = config.ReadTimeout
client.ConnectTimeout = config.ConnectTimeout
client.HttpProxy = config.HttpProxy
client.HttpsProxy = config.HttpsProxy
client.NoProxy = config.NoProxy
client.Socks5Proxy = config.Socks5Proxy
client.Socks5NetWork = config.Socks5NetWork
client.MaxIdleConns = config.MaxIdleConns
client.SignatureVersion = config.SignatureVersion
client.SignatureAlgorithm = config.SignatureAlgorithm
client.GlobalParameters = config.GlobalParameters
client.Key = config.Key
client.Cert = config.Cert
client.Ca = config.Ca
client.DisableHttp2 = config.DisableHttp2
return nil
}
// Description:
//
// # Encapsulate the request and invoke the network
//
// @param action - api name
//
// @param version - product version
//
// @param protocol - http or https
//
// @param method - e.g. GET
//
// @param authType - authorization type e.g. AK
//
// @param bodyType - response body type e.g. String
//
// @param request - object of OpenApiRequest
//
// @param runtime - which controls some details of call api, such as retry times
//
// @return the response
func (client *Client) DoRPCRequest(action *string, version *string, protocol *string, method *string, authType *string, bodyType *string, request *OpenApiRequest, runtime *util.RuntimeOptions) (_result map[string]interface{}, _err error) {
_err = tea.Validate(request)
if _err != nil {
return _result, _err
}
_err = tea.Validate(runtime)
if _err != nil {
return _result, _err
}
_runtime := map[string]interface{}{
"timeouted": "retry",
"key": tea.StringValue(util.DefaultString(runtime.Key, client.Key)),
"cert": tea.StringValue(util.DefaultString(runtime.Cert, client.Cert)),
"ca": tea.StringValue(util.DefaultString(runtime.Ca, client.Ca)),
"readTimeout": tea.IntValue(util.DefaultNumber(runtime.ReadTimeout, client.ReadTimeout)),
"connectTimeout": tea.IntValue(util.DefaultNumber(runtime.ConnectTimeout, client.ConnectTimeout)),
"httpProxy": tea.StringValue(util.DefaultString(runtime.HttpProxy, client.HttpProxy)),
"httpsProxy": tea.StringValue(util.DefaultString(runtime.HttpsProxy, client.HttpsProxy)),
"noProxy": tea.StringValue(util.DefaultString(runtime.NoProxy, client.NoProxy)),
"socks5Proxy": tea.StringValue(util.DefaultString(runtime.Socks5Proxy, client.Socks5Proxy)),
"socks5NetWork": tea.StringValue(util.DefaultString(runtime.Socks5NetWork, client.Socks5NetWork)),
"maxIdleConns": tea.IntValue(util.DefaultNumber(runtime.MaxIdleConns, client.MaxIdleConns)),
"retry": map[string]interface{}{
"retryable": tea.BoolValue(runtime.Autoretry),
"maxAttempts": tea.IntValue(util.DefaultNumber(runtime.MaxAttempts, tea.Int(3))),
},
"backoff": map[string]interface{}{
"policy": tea.StringValue(util.DefaultString(runtime.BackoffPolicy, tea.String("no"))),
"period": tea.IntValue(util.DefaultNumber(runtime.BackoffPeriod, tea.Int(1))),
},
"ignoreSSL": tea.BoolValue(runtime.IgnoreSSL),
}
_resp := make(map[string]interface{})
for _retryTimes := 0; tea.BoolValue(tea.AllowRetry(_runtime["retry"], tea.Int(_retryTimes))); _retryTimes++ {
if _retryTimes > 0 {
_backoffTime := tea.GetBackoffTime(_runtime["backoff"], tea.Int(_retryTimes))
if tea.IntValue(_backoffTime) > 0 {
tea.Sleep(_backoffTime)
}
}
_resp, _err = func() (map[string]interface{}, error) {
request_ := tea.NewRequest()
request_.Protocol = util.DefaultString(client.Protocol, protocol)
request_.Method = method
request_.Pathname = tea.String("/")
globalQueries := make(map[string]*string)
globalHeaders := make(map[string]*string)
if !tea.BoolValue(util.IsUnset(client.GlobalParameters)) {
globalParams := client.GlobalParameters
if !tea.BoolValue(util.IsUnset(globalParams.Queries)) {
globalQueries = globalParams.Queries
}
if !tea.BoolValue(util.IsUnset(globalParams.Headers)) {
globalHeaders = globalParams.Headers
}
}
extendsHeaders := make(map[string]*string)
extendsQueries := make(map[string]*string)
if !tea.BoolValue(util.IsUnset(runtime.ExtendsParameters)) {
extendsParameters := runtime.ExtendsParameters
if !tea.BoolValue(util.IsUnset(extendsParameters.Headers)) {
extendsHeaders = extendsParameters.Headers
}
if !tea.BoolValue(util.IsUnset(extendsParameters.Queries)) {
extendsQueries = extendsParameters.Queries
}
}
request_.Query = tea.Merge(map[string]*string{
"Action": action,
"Format": tea.String("json"),
"Version": version,
"Timestamp": openapiutil.GetTimestamp(),
"SignatureNonce": util.GetNonce(),
}, globalQueries,
extendsQueries,
request.Query)
headers, _err := client.GetRpcHeaders()
if _err != nil {
return _result, _err
}
if tea.BoolValue(util.IsUnset(headers)) {
// endpoint is setted in product client
request_.Headers = tea.Merge(map[string]*string{
"host": client.Endpoint,
"x-acs-version": version,
"x-acs-action": action,
"user-agent": client.GetUserAgent(),
}, globalHeaders,
extendsHeaders)
} else {
request_.Headers = tea.Merge(map[string]*string{
"host": client.Endpoint,
"x-acs-version": version,
"x-acs-action": action,
"user-agent": client.GetUserAgent(),
}, globalHeaders,
extendsHeaders,
headers)
}
if !tea.BoolValue(util.IsUnset(request.Body)) {
m, _err := util.AssertAsMap(request.Body)
if _err != nil {
return _result, _err
}
tmp := util.AnyifyMapValue(openapiutil.Query(m))
request_.Body = tea.ToReader(util.ToFormString(tmp))
request_.Headers["content-type"] = tea.String("application/x-www-form-urlencoded")
}
if !tea.BoolValue(util.EqualString(authType, tea.String("Anonymous"))) {
credentialType, _err := client.GetType()
if _err != nil {
return _result, _err
}
if tea.BoolValue(util.EqualString(credentialType, tea.String("bearer"))) {
bearerToken, _err := client.GetBearerToken()
if _err != nil {
return _result, _err
}
request_.Query["BearerToken"] = bearerToken
request_.Query["SignatureType"] = tea.String("BEARERTOKEN")
} else {
accessKeyId, _err := client.GetAccessKeyId()
if _err != nil {
return _result, _err
}
accessKeySecret, _err := client.GetAccessKeySecret()
if _err != nil {
return _result, _err
}
securityToken, _err := client.GetSecurityToken()
if _err != nil {
return _result, _err
}
if !tea.BoolValue(util.Empty(securityToken)) {
request_.Query["SecurityToken"] = securityToken
}
request_.Query["SignatureMethod"] = tea.String("HMAC-SHA1")
request_.Query["SignatureVersion"] = tea.String("1.0")
request_.Query["AccessKeyId"] = accessKeyId
var t map[string]interface{}
if !tea.BoolValue(util.IsUnset(request.Body)) {
t, _err = util.AssertAsMap(request.Body)
if _err != nil {
return _result, _err
}
}
signedParam := tea.Merge(request_.Query,
openapiutil.Query(t))
request_.Query["Signature"] = openapiutil.GetRPCSignature(signedParam, request_.Method, accessKeySecret)
}
}
response_, _err := tea.DoRequest(request_, _runtime)
if _err != nil {
return _result, _err
}
if tea.BoolValue(util.Is4xx(response_.StatusCode)) || tea.BoolValue(util.Is5xx(response_.StatusCode)) {
_res, _err := util.ReadAsJSON(response_.Body)
if _err != nil {
return _result, _err
}
err, _err := util.AssertAsMap(_res)
if _err != nil {
return _result, _err
}
requestId := DefaultAny(err["RequestId"], err["requestId"])
err["statusCode"] = response_.StatusCode
_err = tea.NewSDKError(map[string]interface{}{
"code": tea.ToString(DefaultAny(err["Code"], err["code"])),
"message": "code: " + tea.ToString(tea.IntValue(response_.StatusCode)) + ", " + tea.ToString(DefaultAny(err["Message"], err["message"])) + " request id: " + tea.ToString(requestId),
"data": err,
"description": tea.ToString(DefaultAny(err["Description"], err["description"])),
"accessDeniedDetail": DefaultAny(err["AccessDeniedDetail"], err["accessDeniedDetail"]),
})
return _result, _err
}
if tea.BoolValue(util.EqualString(bodyType, tea.String("binary"))) {
resp := map[string]interface{}{
"body": response_.Body,
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}
_result = resp
return _result, _err
} else if tea.BoolValue(util.EqualString(bodyType, tea.String("byte"))) {
byt, _err := util.ReadAsBytes(response_.Body)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"body": byt,
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
} else if tea.BoolValue(util.EqualString(bodyType, tea.String("string"))) {
str, _err := util.ReadAsString(response_.Body)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"body": tea.StringValue(str),
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
} else if tea.BoolValue(util.EqualString(bodyType, tea.String("json"))) {
obj, _err := util.ReadAsJSON(response_.Body)
if _err != nil {
return _result, _err
}
res, _err := util.AssertAsMap(obj)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"body": res,
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
} else if tea.BoolValue(util.EqualString(bodyType, tea.String("array"))) {
arr, _err := util.ReadAsJSON(response_.Body)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"body": arr,
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
} else {
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
}
}()
if !tea.BoolValue(tea.Retryable(_err)) {
break
}
}
return _resp, _err
}
// Description:
//
// # Encapsulate the request and invoke the network
//
// @param action - api name
//
// @param version - product version
//
// @param protocol - http or https
//
// @param method - e.g. GET
//
// @param authType - authorization type e.g. AK
//
// @param pathname - pathname of every api
//
// @param bodyType - response body type e.g. String
//
// @param request - object of OpenApiRequest
//
// @param runtime - which controls some details of call api, such as retry times
//
// @return the response
func (client *Client) DoROARequest(action *string, version *string, protocol *string, method *string, authType *string, pathname *string, bodyType *string, request *OpenApiRequest, runtime *util.RuntimeOptions) (_result map[string]interface{}, _err error) {
_err = tea.Validate(request)
if _err != nil {
return _result, _err
}
_err = tea.Validate(runtime)
if _err != nil {
return _result, _err
}
_runtime := map[string]interface{}{
"timeouted": "retry",
"key": tea.StringValue(util.DefaultString(runtime.Key, client.Key)),
"cert": tea.StringValue(util.DefaultString(runtime.Cert, client.Cert)),
"ca": tea.StringValue(util.DefaultString(runtime.Ca, client.Ca)),
"readTimeout": tea.IntValue(util.DefaultNumber(runtime.ReadTimeout, client.ReadTimeout)),
"connectTimeout": tea.IntValue(util.DefaultNumber(runtime.ConnectTimeout, client.ConnectTimeout)),
"httpProxy": tea.StringValue(util.DefaultString(runtime.HttpProxy, client.HttpProxy)),
"httpsProxy": tea.StringValue(util.DefaultString(runtime.HttpsProxy, client.HttpsProxy)),
"noProxy": tea.StringValue(util.DefaultString(runtime.NoProxy, client.NoProxy)),
"socks5Proxy": tea.StringValue(util.DefaultString(runtime.Socks5Proxy, client.Socks5Proxy)),
"socks5NetWork": tea.StringValue(util.DefaultString(runtime.Socks5NetWork, client.Socks5NetWork)),
"maxIdleConns": tea.IntValue(util.DefaultNumber(runtime.MaxIdleConns, client.MaxIdleConns)),
"retry": map[string]interface{}{
"retryable": tea.BoolValue(runtime.Autoretry),
"maxAttempts": tea.IntValue(util.DefaultNumber(runtime.MaxAttempts, tea.Int(3))),
},
"backoff": map[string]interface{}{
"policy": tea.StringValue(util.DefaultString(runtime.BackoffPolicy, tea.String("no"))),
"period": tea.IntValue(util.DefaultNumber(runtime.BackoffPeriod, tea.Int(1))),
},
"ignoreSSL": tea.BoolValue(runtime.IgnoreSSL),
}
_resp := make(map[string]interface{})
for _retryTimes := 0; tea.BoolValue(tea.AllowRetry(_runtime["retry"], tea.Int(_retryTimes))); _retryTimes++ {
if _retryTimes > 0 {
_backoffTime := tea.GetBackoffTime(_runtime["backoff"], tea.Int(_retryTimes))
if tea.IntValue(_backoffTime) > 0 {
tea.Sleep(_backoffTime)
}
}
_resp, _err = func() (map[string]interface{}, error) {
request_ := tea.NewRequest()
request_.Protocol = util.DefaultString(client.Protocol, protocol)
request_.Method = method
request_.Pathname = pathname
globalQueries := make(map[string]*string)
globalHeaders := make(map[string]*string)
if !tea.BoolValue(util.IsUnset(client.GlobalParameters)) {
globalParams := client.GlobalParameters
if !tea.BoolValue(util.IsUnset(globalParams.Queries)) {
globalQueries = globalParams.Queries
}
if !tea.BoolValue(util.IsUnset(globalParams.Headers)) {
globalHeaders = globalParams.Headers
}
}
extendsHeaders := make(map[string]*string)
extendsQueries := make(map[string]*string)
if !tea.BoolValue(util.IsUnset(runtime.ExtendsParameters)) {
extendsParameters := runtime.ExtendsParameters
if !tea.BoolValue(util.IsUnset(extendsParameters.Headers)) {
extendsHeaders = extendsParameters.Headers
}
if !tea.BoolValue(util.IsUnset(extendsParameters.Queries)) {
extendsQueries = extendsParameters.Queries
}
}
request_.Headers = tea.Merge(map[string]*string{
"date": util.GetDateUTCString(),
"host": client.Endpoint,
"accept": tea.String("application/json"),
"x-acs-signature-nonce": util.GetNonce(),
"x-acs-signature-method": tea.String("HMAC-SHA1"),
"x-acs-signature-version": tea.String("1.0"),
"x-acs-version": version,
"x-acs-action": action,
"user-agent": util.GetUserAgent(client.UserAgent),
}, globalHeaders,
extendsHeaders,
request.Headers)
if !tea.BoolValue(util.IsUnset(request.Body)) {
request_.Body = tea.ToReader(util.ToJSONString(request.Body))
request_.Headers["content-type"] = tea.String("application/json; charset=utf-8")
}
request_.Query = tea.Merge(globalQueries,
extendsQueries)
if !tea.BoolValue(util.IsUnset(request.Query)) {
request_.Query = tea.Merge(request_.Query,
request.Query)
}
if !tea.BoolValue(util.EqualString(authType, tea.String("Anonymous"))) {
credentialType, _err := client.GetType()
if _err != nil {
return _result, _err
}
if tea.BoolValue(util.EqualString(credentialType, tea.String("bearer"))) {
bearerToken, _err := client.GetBearerToken()
if _err != nil {
return _result, _err
}
request_.Headers["x-acs-bearer-token"] = bearerToken
request_.Headers["x-acs-signature-type"] = tea.String("BEARERTOKEN")
} else {
accessKeyId, _err := client.GetAccessKeyId()
if _err != nil {
return _result, _err
}
accessKeySecret, _err := client.GetAccessKeySecret()
if _err != nil {
return _result, _err
}
securityToken, _err := client.GetSecurityToken()
if _err != nil {
return _result, _err
}
if !tea.BoolValue(util.Empty(securityToken)) {
request_.Headers["x-acs-accesskey-id"] = accessKeyId
request_.Headers["x-acs-security-token"] = securityToken
}
stringToSign := openapiutil.GetStringToSign(request_)
request_.Headers["authorization"] = tea.String("acs " + tea.StringValue(accessKeyId) + ":" + tea.StringValue(openapiutil.GetROASignature(stringToSign, accessKeySecret)))
}
}
response_, _err := tea.DoRequest(request_, _runtime)
if _err != nil {
return _result, _err
}
if tea.BoolValue(util.EqualNumber(response_.StatusCode, tea.Int(204))) {
_result = make(map[string]interface{})
_err = tea.Convert(map[string]map[string]*string{
"headers": response_.Headers,
}, &_result)
return _result, _err
}
if tea.BoolValue(util.Is4xx(response_.StatusCode)) || tea.BoolValue(util.Is5xx(response_.StatusCode)) {
_res, _err := util.ReadAsJSON(response_.Body)
if _err != nil {
return _result, _err
}
err, _err := util.AssertAsMap(_res)
if _err != nil {
return _result, _err
}
requestId := DefaultAny(err["RequestId"], err["requestId"])
requestId = DefaultAny(requestId, err["requestid"])
err["statusCode"] = response_.StatusCode
_err = tea.NewSDKError(map[string]interface{}{
"code": tea.ToString(DefaultAny(err["Code"], err["code"])),
"message": "code: " + tea.ToString(tea.IntValue(response_.StatusCode)) + ", " + tea.ToString(DefaultAny(err["Message"], err["message"])) + " request id: " + tea.ToString(requestId),
"data": err,
"description": tea.ToString(DefaultAny(err["Description"], err["description"])),
"accessDeniedDetail": DefaultAny(err["AccessDeniedDetail"], err["accessDeniedDetail"]),
})
return _result, _err
}
if tea.BoolValue(util.EqualString(bodyType, tea.String("binary"))) {
resp := map[string]interface{}{
"body": response_.Body,
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}
_result = resp
return _result, _err
} else if tea.BoolValue(util.EqualString(bodyType, tea.String("byte"))) {
byt, _err := util.ReadAsBytes(response_.Body)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"body": byt,
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
} else if tea.BoolValue(util.EqualString(bodyType, tea.String("string"))) {
str, _err := util.ReadAsString(response_.Body)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"body": tea.StringValue(str),
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
} else if tea.BoolValue(util.EqualString(bodyType, tea.String("json"))) {
obj, _err := util.ReadAsJSON(response_.Body)
if _err != nil {
return _result, _err
}
res, _err := util.AssertAsMap(obj)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"body": res,
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
} else if tea.BoolValue(util.EqualString(bodyType, tea.String("array"))) {
arr, _err := util.ReadAsJSON(response_.Body)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"body": arr,
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
} else {
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
}
}()
if !tea.BoolValue(tea.Retryable(_err)) {
break
}
}
return _resp, _err
}
// Description:
//
// # Encapsulate the request and invoke the network with form body
//
// @param action - api name
//
// @param version - product version
//
// @param protocol - http or https
//
// @param method - e.g. GET
//
// @param authType - authorization type e.g. AK
//
// @param pathname - pathname of every api
//
// @param bodyType - response body type e.g. String
//
// @param request - object of OpenApiRequest
//
// @param runtime - which controls some details of call api, such as retry times
//
// @return the response
func (client *Client) DoROARequestWithForm(action *string, version *string, protocol *string, method *string, authType *string, pathname *string, bodyType *string, request *OpenApiRequest, runtime *util.RuntimeOptions) (_result map[string]interface{}, _err error) {
_err = tea.Validate(request)
if _err != nil {
return _result, _err
}
_err = tea.Validate(runtime)
if _err != nil {
return _result, _err
}
_runtime := map[string]interface{}{
"timeouted": "retry",
"key": tea.StringValue(util.DefaultString(runtime.Key, client.Key)),
"cert": tea.StringValue(util.DefaultString(runtime.Cert, client.Cert)),
"ca": tea.StringValue(util.DefaultString(runtime.Ca, client.Ca)),
"readTimeout": tea.IntValue(util.DefaultNumber(runtime.ReadTimeout, client.ReadTimeout)),
"connectTimeout": tea.IntValue(util.DefaultNumber(runtime.ConnectTimeout, client.ConnectTimeout)),
"httpProxy": tea.StringValue(util.DefaultString(runtime.HttpProxy, client.HttpProxy)),
"httpsProxy": tea.StringValue(util.DefaultString(runtime.HttpsProxy, client.HttpsProxy)),
"noProxy": tea.StringValue(util.DefaultString(runtime.NoProxy, client.NoProxy)),
"socks5Proxy": tea.StringValue(util.DefaultString(runtime.Socks5Proxy, client.Socks5Proxy)),
"socks5NetWork": tea.StringValue(util.DefaultString(runtime.Socks5NetWork, client.Socks5NetWork)),
"maxIdleConns": tea.IntValue(util.DefaultNumber(runtime.MaxIdleConns, client.MaxIdleConns)),
"retry": map[string]interface{}{
"retryable": tea.BoolValue(runtime.Autoretry),
"maxAttempts": tea.IntValue(util.DefaultNumber(runtime.MaxAttempts, tea.Int(3))),
},
"backoff": map[string]interface{}{
"policy": tea.StringValue(util.DefaultString(runtime.BackoffPolicy, tea.String("no"))),
"period": tea.IntValue(util.DefaultNumber(runtime.BackoffPeriod, tea.Int(1))),
},
"ignoreSSL": tea.BoolValue(runtime.IgnoreSSL),
}
_resp := make(map[string]interface{})
for _retryTimes := 0; tea.BoolValue(tea.AllowRetry(_runtime["retry"], tea.Int(_retryTimes))); _retryTimes++ {
if _retryTimes > 0 {
_backoffTime := tea.GetBackoffTime(_runtime["backoff"], tea.Int(_retryTimes))
if tea.IntValue(_backoffTime) > 0 {
tea.Sleep(_backoffTime)
}
}
_resp, _err = func() (map[string]interface{}, error) {
request_ := tea.NewRequest()
request_.Protocol = util.DefaultString(client.Protocol, protocol)
request_.Method = method
request_.Pathname = pathname
globalQueries := make(map[string]*string)
globalHeaders := make(map[string]*string)
if !tea.BoolValue(util.IsUnset(client.GlobalParameters)) {
globalParams := client.GlobalParameters
if !tea.BoolValue(util.IsUnset(globalParams.Queries)) {
globalQueries = globalParams.Queries
}
if !tea.BoolValue(util.IsUnset(globalParams.Headers)) {
globalHeaders = globalParams.Headers
}
}
extendsHeaders := make(map[string]*string)
extendsQueries := make(map[string]*string)
if !tea.BoolValue(util.IsUnset(runtime.ExtendsParameters)) {
extendsParameters := runtime.ExtendsParameters
if !tea.BoolValue(util.IsUnset(extendsParameters.Headers)) {
extendsHeaders = extendsParameters.Headers
}
if !tea.BoolValue(util.IsUnset(extendsParameters.Queries)) {
extendsQueries = extendsParameters.Queries
}
}
request_.Headers = tea.Merge(map[string]*string{
"date": util.GetDateUTCString(),
"host": client.Endpoint,
"accept": tea.String("application/json"),
"x-acs-signature-nonce": util.GetNonce(),
"x-acs-signature-method": tea.String("HMAC-SHA1"),
"x-acs-signature-version": tea.String("1.0"),
"x-acs-version": version,
"x-acs-action": action,
"user-agent": util.GetUserAgent(client.UserAgent),
}, globalHeaders,
extendsHeaders,
request.Headers)
if !tea.BoolValue(util.IsUnset(request.Body)) {
m, _err := util.AssertAsMap(request.Body)
if _err != nil {
return _result, _err
}
request_.Body = tea.ToReader(openapiutil.ToForm(m))
request_.Headers["content-type"] = tea.String("application/x-www-form-urlencoded")
}
request_.Query = tea.Merge(globalQueries,
extendsQueries)
if !tea.BoolValue(util.IsUnset(request.Query)) {
request_.Query = tea.Merge(request_.Query,
request.Query)
}
if !tea.BoolValue(util.EqualString(authType, tea.String("Anonymous"))) {
credentialType, _err := client.GetType()
if _err != nil {
return _result, _err
}
if tea.BoolValue(util.EqualString(credentialType, tea.String("bearer"))) {
bearerToken, _err := client.GetBearerToken()
if _err != nil {
return _result, _err
}
request_.Headers["x-acs-bearer-token"] = bearerToken
request_.Headers["x-acs-signature-type"] = tea.String("BEARERTOKEN")
} else {
accessKeyId, _err := client.GetAccessKeyId()
if _err != nil {
return _result, _err
}
accessKeySecret, _err := client.GetAccessKeySecret()
if _err != nil {
return _result, _err
}
securityToken, _err := client.GetSecurityToken()
if _err != nil {
return _result, _err
}
if !tea.BoolValue(util.Empty(securityToken)) {
request_.Headers["x-acs-accesskey-id"] = accessKeyId
request_.Headers["x-acs-security-token"] = securityToken
}
stringToSign := openapiutil.GetStringToSign(request_)
request_.Headers["authorization"] = tea.String("acs " + tea.StringValue(accessKeyId) + ":" + tea.StringValue(openapiutil.GetROASignature(stringToSign, accessKeySecret)))
}
}
response_, _err := tea.DoRequest(request_, _runtime)
if _err != nil {
return _result, _err
}
if tea.BoolValue(util.EqualNumber(response_.StatusCode, tea.Int(204))) {
_result = make(map[string]interface{})
_err = tea.Convert(map[string]map[string]*string{
"headers": response_.Headers,
}, &_result)
return _result, _err
}
if tea.BoolValue(util.Is4xx(response_.StatusCode)) || tea.BoolValue(util.Is5xx(response_.StatusCode)) {
_res, _err := util.ReadAsJSON(response_.Body)
if _err != nil {
return _result, _err
}
err, _err := util.AssertAsMap(_res)
if _err != nil {
return _result, _err
}
err["statusCode"] = response_.StatusCode
_err = tea.NewSDKError(map[string]interface{}{
"code": tea.ToString(DefaultAny(err["Code"], err["code"])),
"message": "code: " + tea.ToString(tea.IntValue(response_.StatusCode)) + ", " + tea.ToString(DefaultAny(err["Message"], err["message"])) + " request id: " + tea.ToString(DefaultAny(err["RequestId"], err["requestId"])),
"data": err,
"description": tea.ToString(DefaultAny(err["Description"], err["description"])),
"accessDeniedDetail": DefaultAny(err["AccessDeniedDetail"], err["accessDeniedDetail"]),
})
return _result, _err
}
if tea.BoolValue(util.EqualString(bodyType, tea.String("binary"))) {
resp := map[string]interface{}{
"body": response_.Body,
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}
_result = resp
return _result, _err
} else if tea.BoolValue(util.EqualString(bodyType, tea.String("byte"))) {
byt, _err := util.ReadAsBytes(response_.Body)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"body": byt,
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
} else if tea.BoolValue(util.EqualString(bodyType, tea.String("string"))) {
str, _err := util.ReadAsString(response_.Body)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"body": tea.StringValue(str),
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
} else if tea.BoolValue(util.EqualString(bodyType, tea.String("json"))) {
obj, _err := util.ReadAsJSON(response_.Body)
if _err != nil {
return _result, _err
}
res, _err := util.AssertAsMap(obj)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"body": res,
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
} else if tea.BoolValue(util.EqualString(bodyType, tea.String("array"))) {
arr, _err := util.ReadAsJSON(response_.Body)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"body": arr,
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
} else {
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
}
}()
if !tea.BoolValue(tea.Retryable(_err)) {
break
}
}
return _resp, _err
}
// Description:
//
// # Encapsulate the request and invoke the network
//
// @param action - api name
//
// @param version - product version
//
// @param protocol - http or https
//
// @param method - e.g. GET
//
// @param authType - authorization type e.g. AK
//
// @param bodyType - response body type e.g. String
//
// @param request - object of OpenApiRequest
//
// @param runtime - which controls some details of call api, such as retry times
//
// @return the response
func (client *Client) DoRequest(params *Params, request *OpenApiRequest, runtime *util.RuntimeOptions) (_result map[string]interface{}, _err error) {
_err = tea.Validate(params)
if _err != nil {
return _result, _err
}
_err = tea.Validate(request)
if _err != nil {
return _result, _err
}
_err = tea.Validate(runtime)
if _err != nil {
return _result, _err
}
_runtime := map[string]interface{}{
"timeouted": "retry",
"key": tea.StringValue(util.DefaultString(runtime.Key, client.Key)),
"cert": tea.StringValue(util.DefaultString(runtime.Cert, client.Cert)),
"ca": tea.StringValue(util.DefaultString(runtime.Ca, client.Ca)),
"readTimeout": tea.IntValue(util.DefaultNumber(runtime.ReadTimeout, client.ReadTimeout)),
"connectTimeout": tea.IntValue(util.DefaultNumber(runtime.ConnectTimeout, client.ConnectTimeout)),
"httpProxy": tea.StringValue(util.DefaultString(runtime.HttpProxy, client.HttpProxy)),
"httpsProxy": tea.StringValue(util.DefaultString(runtime.HttpsProxy, client.HttpsProxy)),
"noProxy": tea.StringValue(util.DefaultString(runtime.NoProxy, client.NoProxy)),
"socks5Proxy": tea.StringValue(util.DefaultString(runtime.Socks5Proxy, client.Socks5Proxy)),
"socks5NetWork": tea.StringValue(util.DefaultString(runtime.Socks5NetWork, client.Socks5NetWork)),
"maxIdleConns": tea.IntValue(util.DefaultNumber(runtime.MaxIdleConns, client.MaxIdleConns)),
"retry": map[string]interface{}{
"retryable": tea.BoolValue(runtime.Autoretry),
"maxAttempts": tea.IntValue(util.DefaultNumber(runtime.MaxAttempts, tea.Int(3))),
},
"backoff": map[string]interface{}{
"policy": tea.StringValue(util.DefaultString(runtime.BackoffPolicy, tea.String("no"))),
"period": tea.IntValue(util.DefaultNumber(runtime.BackoffPeriod, tea.Int(1))),
},
"ignoreSSL": tea.BoolValue(runtime.IgnoreSSL),
}
_resp := make(map[string]interface{})
for _retryTimes := 0; tea.BoolValue(tea.AllowRetry(_runtime["retry"], tea.Int(_retryTimes))); _retryTimes++ {
if _retryTimes > 0 {
_backoffTime := tea.GetBackoffTime(_runtime["backoff"], tea.Int(_retryTimes))
if tea.IntValue(_backoffTime) > 0 {
tea.Sleep(_backoffTime)
}
}
_resp, _err = func() (map[string]interface{}, error) {
request_ := tea.NewRequest()
request_.Protocol = util.DefaultString(client.Protocol, params.Protocol)
request_.Method = params.Method
request_.Pathname = params.Pathname
globalQueries := make(map[string]*string)
globalHeaders := make(map[string]*string)
if !tea.BoolValue(util.IsUnset(client.GlobalParameters)) {
globalParams := client.GlobalParameters
if !tea.BoolValue(util.IsUnset(globalParams.Queries)) {
globalQueries = globalParams.Queries
}
if !tea.BoolValue(util.IsUnset(globalParams.Headers)) {
globalHeaders = globalParams.Headers
}
}
extendsHeaders := make(map[string]*string)
extendsQueries := make(map[string]*string)
if !tea.BoolValue(util.IsUnset(runtime.ExtendsParameters)) {
extendsParameters := runtime.ExtendsParameters
if !tea.BoolValue(util.IsUnset(extendsParameters.Headers)) {
extendsHeaders = extendsParameters.Headers
}
if !tea.BoolValue(util.IsUnset(extendsParameters.Queries)) {
extendsQueries = extendsParameters.Queries
}
}
request_.Query = tea.Merge(globalQueries,
extendsQueries,
request.Query)
// endpoint is setted in product client
request_.Headers = tea.Merge(map[string]*string{
"host": client.Endpoint,
"x-acs-version": params.Version,
"x-acs-action": params.Action,
"user-agent": client.GetUserAgent(),
"x-acs-date": openapiutil.GetTimestamp(),
"x-acs-signature-nonce": util.GetNonce(),
"accept": tea.String("application/json"),
}, globalHeaders,
extendsHeaders,
request.Headers)
if tea.BoolValue(util.EqualString(params.Style, tea.String("RPC"))) {
headers, _err := client.GetRpcHeaders()
if _err != nil {
return _result, _err
}
if !tea.BoolValue(util.IsUnset(headers)) {
request_.Headers = tea.Merge(request_.Headers,
headers)
}
}
signatureAlgorithm := util.DefaultString(client.SignatureAlgorithm, tea.String("ACS3-HMAC-SHA256"))
hashedRequestPayload := openapiutil.HexEncode(openapiutil.Hash(util.ToBytes(tea.String("")), signatureAlgorithm))
if !tea.BoolValue(util.IsUnset(request.Stream)) {
tmp, _err := util.ReadAsBytes(request.Stream)
if _err != nil {
return _result, _err
}
hashedRequestPayload = openapiutil.HexEncode(openapiutil.Hash(tmp, signatureAlgorithm))
request_.Body = tea.ToReader(tmp)
request_.Headers["content-type"] = tea.String("application/octet-stream")
} else {
if !tea.BoolValue(util.IsUnset(request.Body)) {
if tea.BoolValue(util.EqualString(params.ReqBodyType, tea.String("byte"))) {
byteObj, _err := util.AssertAsBytes(request.Body)
if _err != nil {
return _result, _err
}
hashedRequestPayload = openapiutil.HexEncode(openapiutil.Hash(byteObj, signatureAlgorithm))
request_.Body = tea.ToReader(byteObj)
} else if tea.BoolValue(util.EqualString(params.ReqBodyType, tea.String("json"))) {
jsonObj := util.ToJSONString(request.Body)
hashedRequestPayload = openapiutil.HexEncode(openapiutil.Hash(util.ToBytes(jsonObj), signatureAlgorithm))
request_.Body = tea.ToReader(jsonObj)
request_.Headers["content-type"] = tea.String("application/json; charset=utf-8")
} else {
m, _err := util.AssertAsMap(request.Body)
if _err != nil {
return _result, _err
}
formObj := openapiutil.ToForm(m)
hashedRequestPayload = openapiutil.HexEncode(openapiutil.Hash(util.ToBytes(formObj), signatureAlgorithm))
request_.Body = tea.ToReader(formObj)
request_.Headers["content-type"] = tea.String("application/x-www-form-urlencoded")
}
}
}
request_.Headers["x-acs-content-sha256"] = hashedRequestPayload
if !tea.BoolValue(util.EqualString(params.AuthType, tea.String("Anonymous"))) {
credentialModel, _err := client.Credential.GetCredential()
if _err != nil {
return _result, _err
}
authType := credentialModel.Type
if tea.BoolValue(util.EqualString(authType, tea.String("bearer"))) {
bearerToken := credentialModel.BearerToken
request_.Headers["x-acs-bearer-token"] = bearerToken
if tea.BoolValue(util.EqualString(params.Style, tea.String("RPC"))) {
request_.Query["SignatureType"] = tea.String("BEARERTOKEN")
} else {
request_.Headers["x-acs-signature-type"] = tea.String("BEARERTOKEN")
}
} else {
accessKeyId := credentialModel.AccessKeyId
accessKeySecret := credentialModel.AccessKeySecret
securityToken := credentialModel.SecurityToken
if !tea.BoolValue(util.Empty(securityToken)) {
request_.Headers["x-acs-accesskey-id"] = accessKeyId
request_.Headers["x-acs-security-token"] = securityToken
}
request_.Headers["Authorization"] = openapiutil.GetAuthorization(request_, signatureAlgorithm, hashedRequestPayload, accessKeyId, accessKeySecret)
}
}
response_, _err := tea.DoRequest(request_, _runtime)
if _err != nil {
return _result, _err
}
if tea.BoolValue(util.Is4xx(response_.StatusCode)) || tea.BoolValue(util.Is5xx(response_.StatusCode)) {
err := map[string]interface{}{}
if !tea.BoolValue(util.IsUnset(response_.Headers["content-type"])) && tea.BoolValue(util.EqualString(response_.Headers["content-type"], tea.String("text/xml;charset=utf-8"))) {
_str, _err := util.ReadAsString(response_.Body)
if _err != nil {
return _result, _err
}
respMap := xml.ParseXml(_str, nil)
err, _err = util.AssertAsMap(respMap["Error"])
if _err != nil {
return _result, _err
}
} else {
_res, _err := util.ReadAsJSON(response_.Body)
if _err != nil {
return _result, _err
}
err, _err = util.AssertAsMap(_res)
if _err != nil {
return _result, _err
}
}
err["statusCode"] = response_.StatusCode
_err = tea.NewSDKError(map[string]interface{}{
"code": tea.ToString(DefaultAny(err["Code"], err["code"])),
"message": "code: " + tea.ToString(tea.IntValue(response_.StatusCode)) + ", " + tea.ToString(DefaultAny(err["Message"], err["message"])) + " request id: " + tea.ToString(DefaultAny(err["RequestId"], err["requestId"])),
"data": err,
"description": tea.ToString(DefaultAny(err["Description"], err["description"])),
"accessDeniedDetail": DefaultAny(err["AccessDeniedDetail"], err["accessDeniedDetail"]),
})
return _result, _err
}
if tea.BoolValue(util.EqualString(params.BodyType, tea.String("binary"))) {
resp := map[string]interface{}{
"body": response_.Body,
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}
_result = resp
return _result, _err
} else if tea.BoolValue(util.EqualString(params.BodyType, tea.String("byte"))) {
byt, _err := util.ReadAsBytes(response_.Body)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"body": byt,
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
} else if tea.BoolValue(util.EqualString(params.BodyType, tea.String("string"))) {
str, _err := util.ReadAsString(response_.Body)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"body": tea.StringValue(str),
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
} else if tea.BoolValue(util.EqualString(params.BodyType, tea.String("json"))) {
obj, _err := util.ReadAsJSON(response_.Body)
if _err != nil {
return _result, _err
}
res, _err := util.AssertAsMap(obj)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"body": res,
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
} else if tea.BoolValue(util.EqualString(params.BodyType, tea.String("array"))) {
arr, _err := util.ReadAsJSON(response_.Body)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"body": arr,
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
} else {
anything, _err := util.ReadAsString(response_.Body)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"body": tea.StringValue(anything),
"headers": response_.Headers,
"statusCode": tea.IntValue(response_.StatusCode),
}, &_result)
return _result, _err
}
}()
if !tea.BoolValue(tea.Retryable(_err)) {
break
}
}
return _resp, _err
}
// Description:
//
// # Encapsulate the request and invoke the network
//
// @param action - api name
//
// @param version - product version
//
// @param protocol - http or https
//
// @param method - e.g. GET
//
// @param authType - authorization type e.g. AK
//
// @param bodyType - response body type e.g. String
//
// @param request - object of OpenApiRequest
//
// @param runtime - which controls some details of call api, such as retry times
//
// @return the response
func (client *Client) Execute(params *Params, request *OpenApiRequest, runtime *util.RuntimeOptions) (_result map[string]interface{}, _err error) {
_err = tea.Validate(params)
if _err != nil {
return _result, _err
}
_err = tea.Validate(request)
if _err != nil {
return _result, _err
}
_err = tea.Validate(runtime)
if _err != nil {
return _result, _err
}
_runtime := map[string]interface{}{
"timeouted": "retry",
"key": tea.StringValue(util.DefaultString(runtime.Key, client.Key)),
"cert": tea.StringValue(util.DefaultString(runtime.Cert, client.Cert)),
"ca": tea.StringValue(util.DefaultString(runtime.Ca, client.Ca)),
"readTimeout": tea.IntValue(util.DefaultNumber(runtime.ReadTimeout, client.ReadTimeout)),
"connectTimeout": tea.IntValue(util.DefaultNumber(runtime.ConnectTimeout, client.ConnectTimeout)),
"httpProxy": tea.StringValue(util.DefaultString(runtime.HttpProxy, client.HttpProxy)),
"httpsProxy": tea.StringValue(util.DefaultString(runtime.HttpsProxy, client.HttpsProxy)),
"noProxy": tea.StringValue(util.DefaultString(runtime.NoProxy, client.NoProxy)),
"socks5Proxy": tea.StringValue(util.DefaultString(runtime.Socks5Proxy, client.Socks5Proxy)),
"socks5NetWork": tea.StringValue(util.DefaultString(runtime.Socks5NetWork, client.Socks5NetWork)),
"maxIdleConns": tea.IntValue(util.DefaultNumber(runtime.MaxIdleConns, client.MaxIdleConns)),
"retry": map[string]interface{}{
"retryable": tea.BoolValue(runtime.Autoretry),
"maxAttempts": tea.IntValue(util.DefaultNumber(runtime.MaxAttempts, tea.Int(3))),
},
"backoff": map[string]interface{}{
"policy": tea.StringValue(util.DefaultString(runtime.BackoffPolicy, tea.String("no"))),
"period": tea.IntValue(util.DefaultNumber(runtime.BackoffPeriod, tea.Int(1))),
},
"ignoreSSL": tea.BoolValue(runtime.IgnoreSSL),
"disableHttp2": DefaultAny(client.DisableHttp2, tea.Bool(false)),
}
_resp := make(map[string]interface{})
for _retryTimes := 0; tea.BoolValue(tea.AllowRetry(_runtime["retry"], tea.Int(_retryTimes))); _retryTimes++ {
if _retryTimes > 0 {
_backoffTime := tea.GetBackoffTime(_runtime["backoff"], tea.Int(_retryTimes))
if tea.IntValue(_backoffTime) > 0 {
tea.Sleep(_backoffTime)
}
}
_resp, _err = func() (map[string]interface{}, error) {
request_ := tea.NewRequest()
// spi = new Gateway();//Gateway implements SPI,这一步在产品 SDK 中实例化
headers, _err := client.GetRpcHeaders()
if _err != nil {
return _result, _err
}
globalQueries := make(map[string]*string)
globalHeaders := make(map[string]*string)
if !tea.BoolValue(util.IsUnset(client.GlobalParameters)) {
globalParams := client.GlobalParameters
if !tea.BoolValue(util.IsUnset(globalParams.Queries)) {
globalQueries = globalParams.Queries
}
if !tea.BoolValue(util.IsUnset(globalParams.Headers)) {
globalHeaders = globalParams.Headers
}
}
extendsHeaders := make(map[string]*string)
extendsQueries := make(map[string]*string)
if !tea.BoolValue(util.IsUnset(runtime.ExtendsParameters)) {
extendsParameters := runtime.ExtendsParameters
if !tea.BoolValue(util.IsUnset(extendsParameters.Headers)) {
extendsHeaders = extendsParameters.Headers
}
if !tea.BoolValue(util.IsUnset(extendsParameters.Queries)) {
extendsQueries = extendsParameters.Queries
}
}
requestContext := &spi.InterceptorContextRequest{
Headers: tea.Merge(globalHeaders,
extendsHeaders,
request.Headers,
headers),
Query: tea.Merge(globalQueries,
extendsQueries,
request.Query),
Body: request.Body,
Stream: request.Stream,
HostMap: request.HostMap,
Pathname: params.Pathname,
ProductId: client.ProductId,
Action: params.Action,
Version: params.Version,
Protocol: util.DefaultString(client.Protocol, params.Protocol),
Method: util.DefaultString(client.Method, params.Method),
AuthType: params.AuthType,
BodyType: params.BodyType,
ReqBodyType: params.ReqBodyType,
Style: params.Style,
Credential: client.Credential,
SignatureVersion: client.SignatureVersion,
SignatureAlgorithm: client.SignatureAlgorithm,
UserAgent: client.GetUserAgent(),
}
configurationContext := &spi.InterceptorContextConfiguration{
RegionId: client.RegionId,
Endpoint: util.DefaultString(request.EndpointOverride, client.Endpoint),
EndpointRule: client.EndpointRule,
EndpointMap: client.EndpointMap,
EndpointType: client.EndpointType,
Network: client.Network,
Suffix: client.Suffix,
}
interceptorContext := &spi.InterceptorContext{
Request: requestContext,
Configuration: configurationContext,
}
attributeMap := &spi.AttributeMap{}
// 1. spi.modifyConfiguration(context: SPI.InterceptorContext, attributeMap: SPI.AttributeMap);
_err = client.Spi.ModifyConfiguration(interceptorContext, attributeMap)
if _err != nil {
return _result, _err
}
// 2. spi.modifyRequest(context: SPI.InterceptorContext, attributeMap: SPI.AttributeMap);
_err = client.Spi.ModifyRequest(interceptorContext, attributeMap)
if _err != nil {
return _result, _err
}
request_.Protocol = interceptorContext.Request.Protocol
request_.Method = interceptorContext.Request.Method
request_.Pathname = interceptorContext.Request.Pathname
request_.Query = interceptorContext.Request.Query
request_.Body = interceptorContext.Request.Stream
request_.Headers = interceptorContext.Request.Headers
response_, _err := tea.DoRequest(request_, _runtime)
if _err != nil {
return _result, _err
}
responseContext := &spi.InterceptorContextResponse{
StatusCode: response_.StatusCode,
Headers: response_.Headers,
Body: response_.Body,
}
interceptorContext.Response = responseContext
// 3. spi.modifyResponse(context: SPI.InterceptorContext, attributeMap: SPI.AttributeMap);
_err = client.Spi.ModifyResponse(interceptorContext, attributeMap)
if _err != nil {
return _result, _err
}
_result = make(map[string]interface{})
_err = tea.Convert(map[string]interface{}{
"headers": interceptorContext.Response.Headers,
"statusCode": tea.IntValue(interceptorContext.Response.StatusCode),
"body": interceptorContext.Response.DeserializedBody,
}, &_result)
return _result, _err
}()
if !tea.BoolValue(tea.Retryable(_err)) {
break
}
}
return _resp, _err
}
func (client *Client) CallApi(params *Params, request *OpenApiRequest, runtime *util.RuntimeOptions) (_result map[string]interface{}, _err error) {
if tea.BoolValue(util.IsUnset(params)) {
_err = tea.NewSDKError(map[string]interface{}{
"code": "ParameterMissing",
"message": "'params' can not be unset",
})
return _result, _err
}
if tea.BoolValue(util.IsUnset(client.SignatureAlgorithm)) || !tea.BoolValue(util.EqualString(client.SignatureAlgorithm, tea.String("v2"))) {
_result = make(map[string]interface{})
_body, _err := client.DoRequest(params, request, runtime)
if _err != nil {
return _result, _err
}
_result = _body
return _result, _err
} else if tea.BoolValue(util.EqualString(params.Style, tea.String("ROA"))) && tea.BoolValue(util.EqualString(params.ReqBodyType, tea.String("json"))) {
_result = make(map[string]interface{})
_body, _err := client.DoROARequest(params.Action, params.Version, params.Protocol, params.Method, params.AuthType, params.Pathname, params.BodyType, request, runtime)
if _err != nil {
return _result, _err
}
_result = _body
return _result, _err
} else if tea.BoolValue(util.EqualString(params.Style, tea.String("ROA"))) {
_result = make(map[string]interface{})
_body, _err := client.DoROARequestWithForm(params.Action, params.Version, params.Protocol, params.Method, params.AuthType, params.Pathname, params.BodyType, request, runtime)
if _err != nil {
return _result, _err
}
_result = _body
return _result, _err
} else {
_result = make(map[string]interface{})
_body, _err := client.DoRPCRequest(params.Action, params.Version, params.Protocol, params.Method, params.AuthType, params.BodyType, request, runtime)
if _err != nil {
return _result, _err
}
_result = _body
return _result, _err
}
}
// Description:
//
// # Get user agent
//
// @return user agent
func (client *Client) GetUserAgent() (_result *string) {
userAgent := util.GetUserAgent(client.UserAgent)
_result = userAgent
return _result
}
// Description:
//
// # Get accesskey id by using credential
//
// @return accesskey id
func (client *Client) GetAccessKeyId() (_result *string, _err error) {
if tea.BoolValue(util.IsUnset(client.Credential)) {
_result = tea.String("")
return _result, _err
}
accessKeyId, _err := client.Credential.GetAccessKeyId()
if _err != nil {
return _result, _err
}
_result = accessKeyId
return _result, _err
}
// Description:
//
// # Get accesskey secret by using credential
//
// @return accesskey secret
func (client *Client) GetAccessKeySecret() (_result *string, _err error) {
if tea.BoolValue(util.IsUnset(client.Credential)) {
_result = tea.String("")
return _result, _err
}
secret, _err := client.Credential.GetAccessKeySecret()
if _err != nil {
return _result, _err
}
_result = secret
return _result, _err
}
// Description:
//
// # Get security token by using credential
//
// @return security token
func (client *Client) GetSecurityToken() (_result *string, _err error) {
if tea.BoolValue(util.IsUnset(client.Credential)) {
_result = tea.String("")
return _result, _err
}
token, _err := client.Credential.GetSecurityToken()
if _err != nil {
return _result, _err
}
_result = token
return _result, _err
}
// Description:
//
// # Get bearer token by credential
//
// @return bearer token
func (client *Client) GetBearerToken() (_result *string, _err error) {
if tea.BoolValue(util.IsUnset(client.Credential)) {
_result = tea.String("")
return _result, _err
}
token := client.Credential.GetBearerToken()
_result = token
return _result, _err
}
// Description:
//
// # Get credential type by credential
//
// @return credential type e.g. access_key
func (client *Client) GetType() (_result *string, _err error) {
if tea.BoolValue(util.IsUnset(client.Credential)) {
_result = tea.String("")
return _result, _err
}
authType := client.Credential.GetType()
_result = authType
return _result, _err
}
// Description:
//
// # If inputValue is not null, return it or return defaultValue
//
// @param inputValue - users input value
//
// @param defaultValue - default value
//
// @return the final result
func DefaultAny(inputValue interface{}, defaultValue interface{}) (_result interface{}) {
if tea.BoolValue(util.IsUnset(inputValue)) {
_result = defaultValue
return _result
}
_result = inputValue
return _result
}
// Description:
//
// # If the endpointRule and config.endpoint are empty, throw error
//
// @param config - config contains the necessary information to create a client
func (client *Client) CheckConfig(config *Config) (_err error) {
if tea.BoolValue(util.Empty(client.EndpointRule)) && tea.BoolValue(util.Empty(config.Endpoint)) {
_err = tea.NewSDKError(map[string]interface{}{
"code": "ParameterMissing",
"message": "'config.endpoint' can not be empty",
})
return _err
}
return _err
}
// Description:
//
// set gateway client
//
// @param spi - .
func (client *Client) SetGatewayClient(spi spi.ClientInterface) (_err error) {
client.Spi = spi
return _err
}
// Description:
//
// set RPC header for debug
//
// @param headers - headers for debug, this header can be used only once.
func (client *Client) SetRpcHeaders(headers map[string]*string) (_err error) {
client.Headers = headers
return _err
}
// Description:
//
// get RPC header for debug
func (client *Client) GetRpcHeaders() (_result map[string]*string, _err error) {
headers := client.Headers
client.Headers = nil
_result = headers
return _result, _err
}
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
package debug
import (
"reflect"
"testing"
)
func assertEqual(t *testing.T, a, b interface{}) {
if !reflect.DeepEqual(a, b) {
t.Errorf("%v != %v", a, b)
}
}
package debug
import (
"fmt"
"os"
"strings"
)
type Debug func(format string, v ...interface{})
var hookGetEnv = func() string {
return os.Getenv("DEBUG")
}
var hookPrint = func(input string) {
fmt.Println(input)
}
func Init(flag string) Debug {
enable := false
env := hookGetEnv()
parts := strings.Split(env, ",")
for _, part := range parts {
if part == flag {
enable = true
break
}
}
return func(format string, v ...interface{}) {
if enable {
hookPrint(fmt.Sprintf(format, v...))
}
}
}
// This file is auto-generated, don't edit it. Thanks.
/**
* Get endpoint
* @return string
*/
package service
import (
"fmt"
"strings"
"github.com/alibabacloud-go/tea/tea"
)
func GetEndpointRules(product, regionId, endpointType, network, suffix *string) (_result *string, _err error) {
if tea.StringValue(endpointType) == "regional" {
if tea.StringValue(regionId) == "" {
_err = fmt.Errorf("RegionId is empty, please set a valid RegionId")
return tea.String(""), _err
}
_result = tea.String(strings.Replace("<product><suffix><network>.<region_id>.aliyuncs.com",
"<region_id>", tea.StringValue(regionId), 1))
} else {
_result = tea.String("<product><suffix><network>.aliyuncs.com")
}
_result = tea.String(strings.Replace(tea.StringValue(_result),
"<product>", strings.ToLower(tea.StringValue(product)), 1))
if tea.StringValue(network) == "" || tea.StringValue(network) == "public" {
_result = tea.String(strings.Replace(tea.StringValue(_result), "<network>", "", 1))
} else {
_result = tea.String(strings.Replace(tea.StringValue(_result),
"<network>", "-"+tea.StringValue(network), 1))
}
if tea.StringValue(suffix) == "" {
_result = tea.String(strings.Replace(tea.StringValue(_result), "<suffix>", "", 1))
} else {
_result = tea.String(strings.Replace(tea.StringValue(_result),
"<suffix>", "-"+tea.StringValue(suffix), 1))
}
return _result, nil
}
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
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.
This source diff could not be displayed because it is too large. You can view the blob instead.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
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.
// This file is auto-generated, don't edit it. Thanks.
/**
* This is for OpenApi Util
*/
package service
import (
"bytes"
"crypto"
"crypto/hmac"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"hash"
"io"
"net/http"
"net/textproto"
"net/url"
"reflect"
"sort"
"strconv"
"strings"
"time"
util "github.com/alibabacloud-go/tea-utils/v2/service"
"github.com/alibabacloud-go/tea/tea"
"github.com/tjfoc/gmsm/sm3"
)
const (
PEM_BEGIN = "-----BEGIN RSA PRIVATE KEY-----\n"
PEM_END = "\n-----END RSA PRIVATE KEY-----"
)
type Sorter struct {
Keys []string
Vals []string
}
func newSorter(m map[string]string) *Sorter {
hs := &Sorter{
Keys: make([]string, 0, len(m)),
Vals: make([]string, 0, len(m)),
}
for k, v := range m {
hs.Keys = append(hs.Keys, k)
hs.Vals = append(hs.Vals, v)
}
return hs
}
// Sort is an additional function for function SignHeader.
func (hs *Sorter) Sort() {
sort.Sort(hs)
}
// Len is an additional function for function SignHeader.
func (hs *Sorter) Len() int {
return len(hs.Vals)
}
// Less is an additional function for function SignHeader.
func (hs *Sorter) Less(i, j int) bool {
return bytes.Compare([]byte(hs.Keys[i]), []byte(hs.Keys[j])) < 0
}
// Swap is an additional function for function SignHeader.
func (hs *Sorter) Swap(i, j int) {
hs.Vals[i], hs.Vals[j] = hs.Vals[j], hs.Vals[i]
hs.Keys[i], hs.Keys[j] = hs.Keys[j], hs.Keys[i]
}
/**
* Convert all params of body other than type of readable into content
* @param body source Model
* @param content target Model
* @return void
*/
func Convert(body interface{}, content interface{}) {
res := make(map[string]interface{})
val := reflect.ValueOf(body).Elem()
dataType := val.Type()
for i := 0; i < dataType.NumField(); i++ {
field := dataType.Field(i)
name, _ := field.Tag.Lookup("json")
name = strings.Split(name, ",omitempty")[0]
_, ok := val.Field(i).Interface().(io.Reader)
if !ok {
res[name] = val.Field(i).Interface()
}
}
byt, _ := json.Marshal(res)
json.Unmarshal(byt, content)
}
/**
* Get the string to be signed according to request
* @param request which contains signed messages
* @return the signed string
*/
func GetStringToSign(request *tea.Request) (_result *string) {
return tea.String(getStringToSign(request))
}
func getStringToSign(request *tea.Request) string {
resource := tea.StringValue(request.Pathname)
queryParams := request.Query
// sort QueryParams by key
var queryKeys []string
for key := range queryParams {
queryKeys = append(queryKeys, key)
}
sort.Strings(queryKeys)
tmp := ""
for i := 0; i < len(queryKeys); i++ {
queryKey := queryKeys[i]
v := tea.StringValue(queryParams[queryKey])
if v != "" {
tmp = tmp + "&" + queryKey + "=" + v
} else {
tmp = tmp + "&" + queryKey
}
}
if tmp != "" {
tmp = strings.TrimLeft(tmp, "&")
resource = resource + "?" + tmp
}
return getSignedStr(request, resource)
}
func getSignedStr(req *tea.Request, canonicalizedResource string) string {
temp := make(map[string]string)
for k, v := range req.Headers {
if strings.HasPrefix(strings.ToLower(k), "x-acs-") {
temp[strings.ToLower(k)] = tea.StringValue(v)
}
}
hs := newSorter(temp)
// Sort the temp by the ascending order
hs.Sort()
// Get the canonicalizedOSSHeaders
canonicalizedOSSHeaders := ""
for i := range hs.Keys {
canonicalizedOSSHeaders += hs.Keys[i] + ":" + hs.Vals[i] + "\n"
}
// Give other parameters values
// when sign URL, date is expires
date := tea.StringValue(req.Headers["date"])
accept := tea.StringValue(req.Headers["accept"])
contentType := tea.StringValue(req.Headers["content-type"])
contentMd5 := tea.StringValue(req.Headers["content-md5"])
signStr := tea.StringValue(req.Method) + "\n" + accept + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + canonicalizedResource
return signStr
}
/**
* Get signature according to stringToSign, secret
* @param stringToSign the signed string
* @param secret accesskey secret
* @return the signature
*/
func GetROASignature(stringToSign *string, secret *string) (_result *string) {
h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(tea.StringValue(secret)))
io.WriteString(h, tea.StringValue(stringToSign))
signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))
return tea.String(signedStr)
}
func GetEndpoint(endpoint *string, server *bool, endpointType *string) *string {
if tea.StringValue(endpointType) == "internal" {
strs := strings.Split(tea.StringValue(endpoint), ".")
strs[0] += "-internal"
endpoint = tea.String(strings.Join(strs, "."))
}
if tea.BoolValue(server) && tea.StringValue(endpointType) == "accelerate" {
return tea.String("oss-accelerate.aliyuncs.com")
}
return endpoint
}
func HexEncode(raw []byte) *string {
return tea.String(hex.EncodeToString(raw))
}
func Hash(raw []byte, signatureAlgorithm *string) []byte {
signType := tea.StringValue(signatureAlgorithm)
if signType == "ACS3-HMAC-SHA256" || signType == "ACS3-RSA-SHA256" {
h := sha256.New()
h.Write(raw)
return h.Sum(nil)
} else if signType == "ACS3-HMAC-SM3" {
h := sm3.New()
h.Write(raw)
return h.Sum(nil)
}
return nil
}
func GetEncodePath(path *string) *string {
uri := tea.StringValue(path)
strs := strings.Split(uri, "/")
for i, v := range strs {
strs[i] = url.QueryEscape(v)
}
uri = strings.Join(strs, "/")
uri = strings.Replace(uri, "+", "%20", -1)
uri = strings.Replace(uri, "*", "%2A", -1)
uri = strings.Replace(uri, "%7E", "~", -1)
return tea.String(uri)
}
func GetEncodeParam(param *string) *string {
uri := tea.StringValue(param)
uri = url.QueryEscape(uri)
uri = strings.Replace(uri, "+", "%20", -1)
uri = strings.Replace(uri, "*", "%2A", -1)
uri = strings.Replace(uri, "%7E", "~", -1)
return tea.String(uri)
}
func GetAuthorization(request *tea.Request, signatureAlgorithm, payload, acesskey, secret *string) *string {
canonicalURI := tea.StringValue(request.Pathname)
if canonicalURI == "" {
canonicalURI = "/"
}
canonicalURI = strings.Replace(canonicalURI, "+", "%20", -1)
canonicalURI = strings.Replace(canonicalURI, "*", "%2A", -1)
canonicalURI = strings.Replace(canonicalURI, "%7E", "~", -1)
method := tea.StringValue(request.Method)
canonicalQueryString := getCanonicalQueryString(request.Query)
canonicalheaders, signedHeaders := getCanonicalHeaders(request.Headers)
canonicalRequest := method + "\n" + canonicalURI + "\n" + canonicalQueryString + "\n" + canonicalheaders + "\n" +
strings.Join(signedHeaders, ";") + "\n" + tea.StringValue(payload)
signType := tea.StringValue(signatureAlgorithm)
StringToSign := signType + "\n" + tea.StringValue(HexEncode(Hash([]byte(canonicalRequest), signatureAlgorithm)))
signature := tea.StringValue(HexEncode(SignatureMethod(tea.StringValue(secret), StringToSign, signType)))
auth := signType + " Credential=" + tea.StringValue(acesskey) + ",SignedHeaders=" +
strings.Join(signedHeaders, ";") + ",Signature=" + signature
return tea.String(auth)
}
func SignatureMethod(secret, source, signatureAlgorithm string) []byte {
if signatureAlgorithm == "ACS3-HMAC-SHA256" {
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(source))
return h.Sum(nil)
} else if signatureAlgorithm == "ACS3-HMAC-SM3" {
h := hmac.New(sm3.New, []byte(secret))
h.Write([]byte(source))
return h.Sum(nil)
} else if signatureAlgorithm == "ACS3-RSA-SHA256" {
return rsaSign(source, secret)
}
return nil
}
func rsaSign(content, secret string) []byte {
h := crypto.SHA256.New()
h.Write([]byte(content))
hashed := h.Sum(nil)
priv, err := parsePrivateKey(secret)
if err != nil {
return nil
}
sign, err := rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA256, hashed)
if err != nil {
return nil
}
return sign
}
func parsePrivateKey(privateKey string) (*rsa.PrivateKey, error) {
privateKey = formatPrivateKey(privateKey)
block, _ := pem.Decode([]byte(privateKey))
if block == nil {
return nil, errors.New("PrivateKey is invalid")
}
priKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
switch priKey.(type) {
case *rsa.PrivateKey:
return priKey.(*rsa.PrivateKey), nil
default:
return nil, nil
}
}
func formatPrivateKey(privateKey string) string {
if !strings.HasPrefix(privateKey, PEM_BEGIN) {
privateKey = PEM_BEGIN + privateKey
}
if !strings.HasSuffix(privateKey, PEM_END) {
privateKey += PEM_END
}
return privateKey
}
func getCanonicalHeaders(headers map[string]*string) (string, []string) {
tmp := make(map[string]string)
tmpHeader := http.Header{}
for k, v := range headers {
if strings.HasPrefix(strings.ToLower(k), "x-acs-") || strings.ToLower(k) == "host" ||
strings.ToLower(k) == "content-type" {
tmp[strings.ToLower(k)] = strings.TrimSpace(tea.StringValue(v))
tmpHeader.Add(strings.ToLower(k), strings.TrimSpace(tea.StringValue(v)))
}
}
hs := newSorter(tmp)
// Sort the temp by the ascending order
hs.Sort()
canonicalheaders := ""
for _, key := range hs.Keys {
vals := tmpHeader[textproto.CanonicalMIMEHeaderKey(key)]
sort.Strings(vals)
canonicalheaders += key + ":" + strings.Join(vals, ",") + "\n"
}
return canonicalheaders, hs.Keys
}
func getCanonicalQueryString(query map[string]*string) string {
canonicalQueryString := ""
if tea.BoolValue(util.IsUnset(query)) {
return canonicalQueryString
}
tmp := make(map[string]string)
for k, v := range query {
tmp[k] = tea.StringValue(v)
}
hs := newSorter(tmp)
// Sort the temp by the ascending order
hs.Sort()
for i := range hs.Keys {
if hs.Vals[i] != "" {
canonicalQueryString += "&" + hs.Keys[i] + "=" + url.QueryEscape(hs.Vals[i])
} else {
canonicalQueryString += "&" + hs.Keys[i] + "="
}
}
canonicalQueryString = strings.Replace(canonicalQueryString, "+", "%20", -1)
canonicalQueryString = strings.Replace(canonicalQueryString, "*", "%2A", -1)
canonicalQueryString = strings.Replace(canonicalQueryString, "%7E", "~", -1)
if canonicalQueryString != "" {
canonicalQueryString = strings.TrimLeft(canonicalQueryString, "&")
}
return canonicalQueryString
}
/**
* Parse filter into a form string
* @param filter object
* @return the string
*/
func ToForm(filter map[string]interface{}) (_result *string) {
tmp := make(map[string]interface{})
byt, _ := json.Marshal(filter)
d := json.NewDecoder(bytes.NewReader(byt))
d.UseNumber()
_ = d.Decode(&tmp)
result := make(map[string]*string)
for key, value := range tmp {
filterValue := reflect.ValueOf(value)
flatRepeatedList(filterValue, result, key)
}
m := util.AnyifyMapValue(result)
return util.ToFormString(m)
}
func flatRepeatedList(dataValue reflect.Value, result map[string]*string, prefix string) {
if !dataValue.IsValid() {
return
}
dataType := dataValue.Type()
if dataType.Kind().String() == "slice" {
handleRepeatedParams(dataValue, result, prefix)
} else if dataType.Kind().String() == "map" {
handleMap(dataValue, result, prefix)
} else {
result[prefix] = tea.String(fmt.Sprintf("%v", dataValue.Interface()))
}
}
func handleRepeatedParams(repeatedFieldValue reflect.Value, result map[string]*string, prefix string) {
if repeatedFieldValue.IsValid() && !repeatedFieldValue.IsNil() {
for m := 0; m < repeatedFieldValue.Len(); m++ {
elementValue := repeatedFieldValue.Index(m)
key := prefix + "." + strconv.Itoa(m+1)
fieldValue := reflect.ValueOf(elementValue.Interface())
if fieldValue.Kind().String() == "map" {
handleMap(fieldValue, result, key)
} else {
result[key] = tea.String(fmt.Sprintf("%v", fieldValue.Interface()))
}
}
}
}
func handleMap(valueField reflect.Value, result map[string]*string, prefix string) {
if valueField.IsValid() && valueField.String() != "" {
valueFieldType := valueField.Type()
if valueFieldType.Kind().String() == "map" {
var byt []byte
byt, _ = json.Marshal(valueField.Interface())
cache := make(map[string]interface{})
d := json.NewDecoder(bytes.NewReader(byt))
d.UseNumber()
_ = d.Decode(&cache)
for key, value := range cache {
pre := ""
if prefix != "" {
pre = prefix + "." + key
} else {
pre = key
}
fieldValue := reflect.ValueOf(value)
flatRepeatedList(fieldValue, result, pre)
}
}
}
}
/**
* Get timestamp
* @return the timestamp string
*/
func GetTimestamp() (_result *string) {
gmt := time.FixedZone("GMT", 0)
return tea.String(time.Now().In(gmt).Format("2006-01-02T15:04:05Z"))
}
/**
* Parse filter into a object which's type is map[string]string
* @param filter query param
* @return the object
*/
func Query(filter interface{}) (_result map[string]*string) {
tmp := make(map[string]interface{})
byt, _ := json.Marshal(filter)
d := json.NewDecoder(bytes.NewReader(byt))
d.UseNumber()
_ = d.Decode(&tmp)
result := make(map[string]*string)
for key, value := range tmp {
filterValue := reflect.ValueOf(value)
flatRepeatedList(filterValue, result, key)
}
return result
}
/**
* Get signature according to signedParams, method and secret
* @param signedParams params which need to be signed
* @param method http method e.g. GET
* @param secret AccessKeySecret
* @return the signature
*/
func GetRPCSignature(signedParams map[string]*string, method *string, secret *string) (_result *string) {
stringToSign := buildRpcStringToSign(signedParams, tea.StringValue(method))
signature := sign(stringToSign, tea.StringValue(secret), "&")
return tea.String(signature)
}
/**
* Parse array into a string with specified style
* @param array the array
* @param prefix the prefix string
* @style specified style e.g. repeatList
* @return the string
*/
func ArrayToStringWithSpecifiedStyle(array interface{}, prefix *string, style *string) (_result *string) {
if tea.BoolValue(util.IsUnset(array)) {
return tea.String("")
}
sty := tea.StringValue(style)
if sty == "repeatList" {
tmp := map[string]interface{}{
tea.StringValue(prefix): array,
}
return flatRepeatList(tmp)
} else if sty == "simple" || sty == "spaceDelimited" || sty == "pipeDelimited" {
return flatArray(array, sty)
} else if sty == "json" {
return util.ToJSONString(array)
}
return tea.String("")
}
func ParseToMap(in interface{}) map[string]interface{} {
if tea.BoolValue(util.IsUnset(in)) {
return nil
}
tmp := make(map[string]interface{})
byt, _ := json.Marshal(in)
d := json.NewDecoder(bytes.NewReader(byt))
d.UseNumber()
err := d.Decode(&tmp)
if err != nil {
return nil
}
return tmp
}
func flatRepeatList(filter map[string]interface{}) (_result *string) {
tmp := make(map[string]interface{})
byt, _ := json.Marshal(filter)
d := json.NewDecoder(bytes.NewReader(byt))
d.UseNumber()
_ = d.Decode(&tmp)
result := make(map[string]*string)
for key, value := range tmp {
filterValue := reflect.ValueOf(value)
flatRepeatedList(filterValue, result, key)
}
res := make(map[string]string)
for k, v := range result {
res[k] = tea.StringValue(v)
}
hs := newSorter(res)
hs.Sort()
// Get the canonicalizedOSSHeaders
t := ""
for i := range hs.Keys {
if i == len(hs.Keys)-1 {
t += hs.Keys[i] + "=" + hs.Vals[i]
} else {
t += hs.Keys[i] + "=" + hs.Vals[i] + "&&"
}
}
return tea.String(t)
}
func flatArray(array interface{}, sty string) *string {
t := reflect.ValueOf(array)
strs := make([]string, 0)
for i := 0; i < t.Len(); i++ {
tmp := t.Index(i)
if tmp.Kind() == reflect.Ptr || tmp.Kind() == reflect.Interface {
tmp = tmp.Elem()
}
if tmp.Kind() == reflect.Ptr {
tmp = tmp.Elem()
}
if tmp.Kind() == reflect.String {
strs = append(strs, tmp.String())
} else {
inter := tmp.Interface()
byt, _ := json.Marshal(inter)
strs = append(strs, string(byt))
}
}
str := ""
if sty == "simple" {
str = strings.Join(strs, ",")
} else if sty == "spaceDelimited" {
str = strings.Join(strs, " ")
} else if sty == "pipeDelimited" {
str = strings.Join(strs, "|")
}
return tea.String(str)
}
func buildRpcStringToSign(signedParam map[string]*string, method string) (stringToSign string) {
signParams := make(map[string]string)
for key, value := range signedParam {
signParams[key] = tea.StringValue(value)
}
stringToSign = getUrlFormedMap(signParams)
stringToSign = strings.Replace(stringToSign, "+", "%20", -1)
stringToSign = strings.Replace(stringToSign, "*", "%2A", -1)
stringToSign = strings.Replace(stringToSign, "%7E", "~", -1)
stringToSign = url.QueryEscape(stringToSign)
stringToSign = method + "&%2F&" + stringToSign
return
}
func getUrlFormedMap(source map[string]string) (urlEncoded string) {
urlEncoder := url.Values{}
for key, value := range source {
urlEncoder.Add(key, value)
}
urlEncoded = urlEncoder.Encode()
return
}
func sign(stringToSign, accessKeySecret, secretSuffix string) string {
secret := accessKeySecret + secretSuffix
signedBytes := shaHmac1(stringToSign, secret)
signedString := base64.StdEncoding.EncodeToString(signedBytes)
return signedString
}
func shaHmac1(source, secret string) []byte {
key := []byte(secret)
hmac := hmac.New(sha1.New, key)
hmac.Write([]byte(source))
return hmac.Sum(nil)
}
func getTimeLeft(rateLimit *string) (_result *int64) {
if rateLimit != nil {
pairs := strings.Split(tea.StringValue(rateLimit), ",")
for _, pair := range pairs {
kv := strings.Split(pair, ":")
if len(kv) == 2 {
key, value := kv[0], kv[1]
if key == "TimeLeft" {
timeLeftValue, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return nil
}
return tea.Int64(timeLeftValue)
}
}
}
}
return nil
}
/**
* Get throttling param
* @param the response headers
* @return time left
*/
func GetThrottlingTimeLeft(headers map[string]*string) (_result *int64) {
rateLimitForUserApi := headers["x-ratelimit-user-api"]
rateLimitForUser := headers["x-ratelimit-user"]
timeLeftForUserApi := getTimeLeft(rateLimitForUserApi)
timeLeftForUser := getTimeLeft(rateLimitForUser)
if tea.Int64Value(timeLeftForUserApi) > tea.Int64Value(timeLeftForUser) {
return timeLeftForUserApi
} else {
return timeLeftForUser
}
}
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
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.
package service
import (
"bytes"
"crypto/md5"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"reflect"
"runtime"
"strconv"
"strings"
"sync/atomic"
"time"
"github.com/alibabacloud-go/tea/tea"
)
var defaultUserAgent = fmt.Sprintf("AlibabaCloud (%s; %s) Golang/%s Core/%s TeaDSL/1", runtime.GOOS, runtime.GOARCH, strings.Trim(runtime.Version(), "go"), "0.01")
type ExtendsParameters struct {
Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty"`
Queries map[string]*string `json:"queries,omitempty" xml:"queries,omitempty"`
}
func (s ExtendsParameters) String() string {
return tea.Prettify(s)
}
func (s ExtendsParameters) GoString() string {
return s.String()
}
func (s *ExtendsParameters) SetHeaders(v map[string]*string) *ExtendsParameters {
s.Headers = v
return s
}
func (s *ExtendsParameters) SetQueries(v map[string]*string) *ExtendsParameters {
s.Queries = v
return s
}
type RuntimeOptions struct {
Autoretry *bool `json:"autoretry" xml:"autoretry"`
IgnoreSSL *bool `json:"ignoreSSL" xml:"ignoreSSL"`
Key *string `json:"key,omitempty" xml:"key,omitempty"`
Cert *string `json:"cert,omitempty" xml:"cert,omitempty"`
Ca *string `json:"ca,omitempty" xml:"ca,omitempty"`
MaxAttempts *int `json:"maxAttempts" xml:"maxAttempts"`
BackoffPolicy *string `json:"backoffPolicy" xml:"backoffPolicy"`
BackoffPeriod *int `json:"backoffPeriod" xml:"backoffPeriod"`
ReadTimeout *int `json:"readTimeout" xml:"readTimeout"`
ConnectTimeout *int `json:"connectTimeout" xml:"connectTimeout"`
LocalAddr *string `json:"localAddr" xml:"localAddr"`
HttpProxy *string `json:"httpProxy" xml:"httpProxy"`
HttpsProxy *string `json:"httpsProxy" xml:"httpsProxy"`
NoProxy *string `json:"noProxy" xml:"noProxy"`
MaxIdleConns *int `json:"maxIdleConns" xml:"maxIdleConns"`
Socks5Proxy *string `json:"socks5Proxy" xml:"socks5Proxy"`
Socks5NetWork *string `json:"socks5NetWork" xml:"socks5NetWork"`
KeepAlive *bool `json:"keepAlive" xml:"keepAlive"`
ExtendsParameters *ExtendsParameters `json:"extendsParameters,omitempty" xml:"extendsParameters,omitempty"`
}
var processStartTime int64 = time.Now().UnixNano() / 1e6
var seqId int64 = 0
func getGID() uint64 {
// https://blog.sgmansfield.com/2015/12/goroutine-ids/
b := make([]byte, 64)
b = b[:runtime.Stack(b, false)]
b = bytes.TrimPrefix(b, []byte("goroutine "))
b = b[:bytes.IndexByte(b, ' ')]
n, _ := strconv.ParseUint(string(b), 10, 64)
return n
}
func (s RuntimeOptions) String() string {
return tea.Prettify(s)
}
func (s RuntimeOptions) GoString() string {
return s.String()
}
func (s *RuntimeOptions) SetAutoretry(v bool) *RuntimeOptions {
s.Autoretry = &v
return s
}
func (s *RuntimeOptions) SetIgnoreSSL(v bool) *RuntimeOptions {
s.IgnoreSSL = &v
return s
}
func (s *RuntimeOptions) SetKey(v string) *RuntimeOptions {
s.Key = &v
return s
}
func (s *RuntimeOptions) SetCert(v string) *RuntimeOptions {
s.Cert = &v
return s
}
func (s *RuntimeOptions) SetCa(v string) *RuntimeOptions {
s.Ca = &v
return s
}
func (s *RuntimeOptions) SetMaxAttempts(v int) *RuntimeOptions {
s.MaxAttempts = &v
return s
}
func (s *RuntimeOptions) SetBackoffPolicy(v string) *RuntimeOptions {
s.BackoffPolicy = &v
return s
}
func (s *RuntimeOptions) SetBackoffPeriod(v int) *RuntimeOptions {
s.BackoffPeriod = &v
return s
}
func (s *RuntimeOptions) SetReadTimeout(v int) *RuntimeOptions {
s.ReadTimeout = &v
return s
}
func (s *RuntimeOptions) SetConnectTimeout(v int) *RuntimeOptions {
s.ConnectTimeout = &v
return s
}
func (s *RuntimeOptions) SetHttpProxy(v string) *RuntimeOptions {
s.HttpProxy = &v
return s
}
func (s *RuntimeOptions) SetHttpsProxy(v string) *RuntimeOptions {
s.HttpsProxy = &v
return s
}
func (s *RuntimeOptions) SetNoProxy(v string) *RuntimeOptions {
s.NoProxy = &v
return s
}
func (s *RuntimeOptions) SetMaxIdleConns(v int) *RuntimeOptions {
s.MaxIdleConns = &v
return s
}
func (s *RuntimeOptions) SetLocalAddr(v string) *RuntimeOptions {
s.LocalAddr = &v
return s
}
func (s *RuntimeOptions) SetSocks5Proxy(v string) *RuntimeOptions {
s.Socks5Proxy = &v
return s
}
func (s *RuntimeOptions) SetSocks5NetWork(v string) *RuntimeOptions {
s.Socks5NetWork = &v
return s
}
func (s *RuntimeOptions) SetKeepAlive(v bool) *RuntimeOptions {
s.KeepAlive = &v
return s
}
func (s *RuntimeOptions) SetExtendsParameters(v *ExtendsParameters) *RuntimeOptions {
s.ExtendsParameters = v
return s
}
func ReadAsString(body io.Reader) (*string, error) {
byt, err := ioutil.ReadAll(body)
if err != nil {
return tea.String(""), err
}
r, ok := body.(io.ReadCloser)
if ok {
r.Close()
}
return tea.String(string(byt)), nil
}
func StringifyMapValue(a map[string]interface{}) map[string]*string {
res := make(map[string]*string)
for key, value := range a {
if value != nil {
res[key] = ToJSONString(value)
}
}
return res
}
func AnyifyMapValue(a map[string]*string) map[string]interface{} {
res := make(map[string]interface{})
for key, value := range a {
res[key] = tea.StringValue(value)
}
return res
}
func ReadAsBytes(body io.Reader) ([]byte, error) {
byt, err := ioutil.ReadAll(body)
if err != nil {
return nil, err
}
r, ok := body.(io.ReadCloser)
if ok {
r.Close()
}
return byt, nil
}
func DefaultString(reaStr, defaultStr *string) *string {
if reaStr == nil {
return defaultStr
}
return reaStr
}
func ToJSONString(a interface{}) *string {
switch v := a.(type) {
case *string:
return v
case string:
return tea.String(v)
case []byte:
return tea.String(string(v))
case io.Reader:
byt, err := ioutil.ReadAll(v)
if err != nil {
return nil
}
return tea.String(string(byt))
}
byt := bytes.NewBuffer([]byte{})
jsonEncoder := json.NewEncoder(byt)
jsonEncoder.SetEscapeHTML(false)
if err := jsonEncoder.Encode(a); err != nil {
return nil
}
return tea.String(string(bytes.TrimSpace(byt.Bytes())))
}
func DefaultNumber(reaNum, defaultNum *int) *int {
if reaNum == nil {
return defaultNum
}
return reaNum
}
func ReadAsJSON(body io.Reader) (result interface{}, err error) {
byt, err := ioutil.ReadAll(body)
if err != nil {
return
}
if string(byt) == "" {
return
}
r, ok := body.(io.ReadCloser)
if ok {
r.Close()
}
d := json.NewDecoder(bytes.NewReader(byt))
d.UseNumber()
err = d.Decode(&result)
return
}
func GetNonce() *string {
routineId := getGID()
currentTime := time.Now().UnixNano() / 1e6
seq := atomic.AddInt64(&seqId, 1)
randNum := rand.Int63()
msg := fmt.Sprintf("%d-%d-%d-%d-%d", processStartTime, routineId, currentTime, seq, randNum)
h := md5.New()
h.Write([]byte(msg))
ret := hex.EncodeToString(h.Sum(nil))
return &ret
}
func Empty(val *string) *bool {
return tea.Bool(val == nil || tea.StringValue(val) == "")
}
func ValidateModel(a interface{}) error {
if a == nil {
return nil
}
err := tea.Validate(a)
return err
}
func EqualString(val1, val2 *string) *bool {
return tea.Bool(tea.StringValue(val1) == tea.StringValue(val2))
}
func EqualNumber(val1, val2 *int) *bool {
return tea.Bool(tea.IntValue(val1) == tea.IntValue(val2))
}
func IsUnset(val interface{}) *bool {
if val == nil {
return tea.Bool(true)
}
v := reflect.ValueOf(val)
if v.Kind() == reflect.Ptr || v.Kind() == reflect.Slice || v.Kind() == reflect.Map {
return tea.Bool(v.IsNil())
}
valType := reflect.TypeOf(val)
valZero := reflect.Zero(valType)
return tea.Bool(valZero == v)
}
func ToBytes(a *string) []byte {
return []byte(tea.StringValue(a))
}
func AssertAsMap(a interface{}) (_result map[string]interface{}, _err error) {
r := reflect.ValueOf(a)
if r.Kind().String() != "map" {
return nil, errors.New(fmt.Sprintf("%v is not a map[string]interface{}", a))
}
res := make(map[string]interface{})
tmp := r.MapKeys()
for _, key := range tmp {
res[key.String()] = r.MapIndex(key).Interface()
}
return res, nil
}
func AssertAsNumber(a interface{}) (_result *int, _err error) {
res := 0
switch a.(type) {
case int:
tmp := a.(int)
res = tmp
case *int:
tmp := a.(*int)
res = tea.IntValue(tmp)
default:
return nil, errors.New(fmt.Sprintf("%v is not a int", a))
}
return tea.Int(res), nil
}
/**
* Assert a value, if it is a integer, return it, otherwise throws
* @return the integer value
*/
func AssertAsInteger(value interface{}) (_result *int, _err error) {
res := 0
switch value.(type) {
case int:
tmp := value.(int)
res = tmp
case *int:
tmp := value.(*int)
res = tea.IntValue(tmp)
default:
return nil, errors.New(fmt.Sprintf("%v is not a int", value))
}
return tea.Int(res), nil
}
func AssertAsBoolean(a interface{}) (_result *bool, _err error) {
res := false
switch a.(type) {
case bool:
tmp := a.(bool)
res = tmp
case *bool:
tmp := a.(*bool)
res = tea.BoolValue(tmp)
default:
return nil, errors.New(fmt.Sprintf("%v is not a bool", a))
}
return tea.Bool(res), nil
}
func AssertAsString(a interface{}) (_result *string, _err error) {
res := ""
switch a.(type) {
case string:
tmp := a.(string)
res = tmp
case *string:
tmp := a.(*string)
res = tea.StringValue(tmp)
default:
return nil, errors.New(fmt.Sprintf("%v is not a string", a))
}
return tea.String(res), nil
}
func AssertAsBytes(a interface{}) (_result []byte, _err error) {
res, ok := a.([]byte)
if !ok {
return nil, errors.New(fmt.Sprintf("%v is not a []byte", a))
}
return res, nil
}
func AssertAsReadable(a interface{}) (_result io.Reader, _err error) {
res, ok := a.(io.Reader)
if !ok {
return nil, errors.New(fmt.Sprintf("%v is not a reader", a))
}
return res, nil
}
func AssertAsArray(a interface{}) (_result []interface{}, _err error) {
r := reflect.ValueOf(a)
if r.Kind().String() != "array" && r.Kind().String() != "slice" {
return nil, errors.New(fmt.Sprintf("%v is not a []interface{}", a))
}
aLen := r.Len()
res := make([]interface{}, 0)
for i := 0; i < aLen; i++ {
res = append(res, r.Index(i).Interface())
}
return res, nil
}
func ParseJSON(a *string) interface{} {
mapTmp := make(map[string]interface{})
d := json.NewDecoder(bytes.NewReader([]byte(tea.StringValue(a))))
d.UseNumber()
err := d.Decode(&mapTmp)
if err == nil {
return mapTmp
}
sliceTmp := make([]interface{}, 0)
d = json.NewDecoder(bytes.NewReader([]byte(tea.StringValue(a))))
d.UseNumber()
err = d.Decode(&sliceTmp)
if err == nil {
return sliceTmp
}
if num, err := strconv.Atoi(tea.StringValue(a)); err == nil {
return num
}
if ok, err := strconv.ParseBool(tea.StringValue(a)); err == nil {
return ok
}
if floa64tVal, err := strconv.ParseFloat(tea.StringValue(a), 64); err == nil {
return floa64tVal
}
return nil
}
func ToString(a []byte) *string {
return tea.String(string(a))
}
func ToMap(in interface{}) map[string]interface{} {
if in == nil {
return nil
}
res := tea.ToMap(in)
return res
}
func ToFormString(a map[string]interface{}) *string {
if a == nil {
return tea.String("")
}
res := ""
urlEncoder := url.Values{}
for key, value := range a {
v := fmt.Sprintf("%v", value)
urlEncoder.Add(key, v)
}
res = urlEncoder.Encode()
return tea.String(res)
}
func GetDateUTCString() *string {
return tea.String(time.Now().UTC().Format(http.TimeFormat))
}
func GetUserAgent(userAgent *string) *string {
if userAgent != nil && tea.StringValue(userAgent) != "" {
return tea.String(defaultUserAgent + " " + tea.StringValue(userAgent))
}
return tea.String(defaultUserAgent)
}
func Is2xx(code *int) *bool {
tmp := tea.IntValue(code)
return tea.Bool(tmp >= 200 && tmp < 300)
}
func Is3xx(code *int) *bool {
tmp := tea.IntValue(code)
return tea.Bool(tmp >= 300 && tmp < 400)
}
func Is4xx(code *int) *bool {
tmp := tea.IntValue(code)
return tea.Bool(tmp >= 400 && tmp < 500)
}
func Is5xx(code *int) *bool {
tmp := tea.IntValue(code)
return tea.Bool(tmp >= 500 && tmp < 600)
}
func Sleep(millisecond *int) error {
ms := tea.IntValue(millisecond)
time.Sleep(time.Duration(ms) * time.Millisecond)
return nil
}
func ToArray(in interface{}) []map[string]interface{} {
if tea.BoolValue(IsUnset(in)) {
return nil
}
tmp := make([]map[string]interface{}, 0)
byt, _ := json.Marshal(in)
d := json.NewDecoder(bytes.NewReader(byt))
d.UseNumber()
err := d.Decode(&tmp)
if err != nil {
return nil
}
return tmp
}
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
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.
package service
import (
"bytes"
"encoding/xml"
"fmt"
"reflect"
"strings"
"github.com/alibabacloud-go/tea/tea"
v2 "github.com/clbanning/mxj/v2"
)
func ToXML(obj map[string]interface{}) *string {
return tea.String(mapToXML(obj))
}
func ParseXml(val *string, result interface{}) map[string]interface{} {
resp := make(map[string]interface{})
start := getStartElement([]byte(tea.StringValue(val)))
if result == nil {
vm, err := v2.NewMapXml([]byte(tea.StringValue(val)))
if err != nil {
return nil
}
return vm
}
out, err := xmlUnmarshal([]byte(tea.StringValue(val)), result)
if err != nil {
return resp
}
resp[start] = out
return resp
}
func mapToXML(val map[string]interface{}) string {
res := ""
for key, value := range val {
switch value.(type) {
case []interface{}:
for _, v := range value.([]interface{}) {
switch v.(type) {
case map[string]interface{}:
res += `<` + key + `>`
res += mapToXML(v.(map[string]interface{}))
res += `</` + key + `>`
default:
if fmt.Sprintf("%v", v) != `<nil>` {
res += `<` + key + `>`
res += fmt.Sprintf("%v", v)
res += `</` + key + `>`
}
}
}
case map[string]interface{}:
res += `<` + key + `>`
res += mapToXML(value.(map[string]interface{}))
res += `</` + key + `>`
default:
if fmt.Sprintf("%v", value) != `<nil>` {
res += `<` + key + `>`
res += fmt.Sprintf("%v", value)
res += `</` + key + `>`
}
}
}
return res
}
func getStartElement(body []byte) string {
d := xml.NewDecoder(bytes.NewReader(body))
for {
tok, err := d.Token()
if err != nil {
return ""
}
if t, ok := tok.(xml.StartElement); ok {
return t.Name.Local
}
}
}
func xmlUnmarshal(body []byte, result interface{}) (interface{}, error) {
start := getStartElement(body)
dataValue := reflect.ValueOf(result).Elem()
dataType := dataValue.Type()
for i := 0; i < dataType.NumField(); i++ {
field := dataType.Field(i)
name, containsNameTag := field.Tag.Lookup("xml")
name = strings.Replace(name, ",omitempty", "", -1)
if containsNameTag {
if name == start {
realType := dataValue.Field(i).Type()
realValue := reflect.New(realType).Interface()
err := xml.Unmarshal(body, realValue)
if err != nil {
return nil, err
}
return realValue, nil
}
}
}
return nil, nil
}
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
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.
package tea
import (
"encoding/json"
"io"
"math"
"reflect"
"strconv"
"strings"
"unsafe"
jsoniter "github.com/json-iterator/go"
"github.com/modern-go/reflect2"
)
const maxUint = ^uint(0)
const maxInt = int(maxUint >> 1)
const minInt = -maxInt - 1
var jsonParser jsoniter.API
func init() {
jsonParser = jsoniter.Config{
EscapeHTML: true,
SortMapKeys: true,
ValidateJsonRawMessage: true,
CaseSensitive: true,
}.Froze()
jsonParser.RegisterExtension(newBetterFuzzyExtension())
}
func newBetterFuzzyExtension() jsoniter.DecoderExtension {
return jsoniter.DecoderExtension{
reflect2.DefaultTypeOfKind(reflect.String): &nullableFuzzyStringDecoder{},
reflect2.DefaultTypeOfKind(reflect.Bool): &fuzzyBoolDecoder{},
reflect2.DefaultTypeOfKind(reflect.Float32): &nullableFuzzyFloat32Decoder{},
reflect2.DefaultTypeOfKind(reflect.Float64): &nullableFuzzyFloat64Decoder{},
reflect2.DefaultTypeOfKind(reflect.Int): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(maxInt) || val < float64(minInt) {
iter.ReportError("fuzzy decode int", "exceed range")
return
}
*((*int)(ptr)) = int(val)
} else {
*((*int)(ptr)) = iter.ReadInt()
}
}},
reflect2.DefaultTypeOfKind(reflect.Uint): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(maxUint) || val < 0 {
iter.ReportError("fuzzy decode uint", "exceed range")
return
}
*((*uint)(ptr)) = uint(val)
} else {
*((*uint)(ptr)) = iter.ReadUint()
}
}},
reflect2.DefaultTypeOfKind(reflect.Int8): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(math.MaxInt8) || val < float64(math.MinInt8) {
iter.ReportError("fuzzy decode int8", "exceed range")
return
}
*((*int8)(ptr)) = int8(val)
} else {
*((*int8)(ptr)) = iter.ReadInt8()
}
}},
reflect2.DefaultTypeOfKind(reflect.Uint8): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(math.MaxUint8) || val < 0 {
iter.ReportError("fuzzy decode uint8", "exceed range")
return
}
*((*uint8)(ptr)) = uint8(val)
} else {
*((*uint8)(ptr)) = iter.ReadUint8()
}
}},
reflect2.DefaultTypeOfKind(reflect.Int16): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(math.MaxInt16) || val < float64(math.MinInt16) {
iter.ReportError("fuzzy decode int16", "exceed range")
return
}
*((*int16)(ptr)) = int16(val)
} else {
*((*int16)(ptr)) = iter.ReadInt16()
}
}},
reflect2.DefaultTypeOfKind(reflect.Uint16): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(math.MaxUint16) || val < 0 {
iter.ReportError("fuzzy decode uint16", "exceed range")
return
}
*((*uint16)(ptr)) = uint16(val)
} else {
*((*uint16)(ptr)) = iter.ReadUint16()
}
}},
reflect2.DefaultTypeOfKind(reflect.Int32): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(math.MaxInt32) || val < float64(math.MinInt32) {
iter.ReportError("fuzzy decode int32", "exceed range")
return
}
*((*int32)(ptr)) = int32(val)
} else {
*((*int32)(ptr)) = iter.ReadInt32()
}
}},
reflect2.DefaultTypeOfKind(reflect.Uint32): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(math.MaxUint32) || val < 0 {
iter.ReportError("fuzzy decode uint32", "exceed range")
return
}
*((*uint32)(ptr)) = uint32(val)
} else {
*((*uint32)(ptr)) = iter.ReadUint32()
}
}},
reflect2.DefaultTypeOfKind(reflect.Int64): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(math.MaxInt64) || val < float64(math.MinInt64) {
iter.ReportError("fuzzy decode int64", "exceed range")
return
}
*((*int64)(ptr)) = int64(val)
} else {
*((*int64)(ptr)) = iter.ReadInt64()
}
}},
reflect2.DefaultTypeOfKind(reflect.Uint64): &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(math.MaxUint64) || val < 0 {
iter.ReportError("fuzzy decode uint64", "exceed range")
return
}
*((*uint64)(ptr)) = uint64(val)
} else {
*((*uint64)(ptr)) = iter.ReadUint64()
}
}},
}
}
type nullableFuzzyStringDecoder struct {
}
func (decoder *nullableFuzzyStringDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
valueType := iter.WhatIsNext()
switch valueType {
case jsoniter.NumberValue:
var number json.Number
iter.ReadVal(&number)
*((*string)(ptr)) = string(number)
case jsoniter.StringValue:
*((*string)(ptr)) = iter.ReadString()
case jsoniter.BoolValue:
*((*string)(ptr)) = strconv.FormatBool(iter.ReadBool())
case jsoniter.NilValue:
iter.ReadNil()
*((*string)(ptr)) = ""
default:
iter.ReportError("fuzzyStringDecoder", "not number or string or bool")
}
}
type fuzzyBoolDecoder struct {
}
func (decoder *fuzzyBoolDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
valueType := iter.WhatIsNext()
switch valueType {
case jsoniter.BoolValue:
*((*bool)(ptr)) = iter.ReadBool()
case jsoniter.NumberValue:
var number json.Number
iter.ReadVal(&number)
num, err := number.Int64()
if err != nil {
iter.ReportError("fuzzyBoolDecoder", "get value from json.number failed")
}
if num == 0 {
*((*bool)(ptr)) = false
} else {
*((*bool)(ptr)) = true
}
case jsoniter.StringValue:
strValue := strings.ToLower(iter.ReadString())
if strValue == "true" {
*((*bool)(ptr)) = true
} else if strValue == "false" || strValue == "" {
*((*bool)(ptr)) = false
} else {
iter.ReportError("fuzzyBoolDecoder", "unsupported bool value: "+strValue)
}
case jsoniter.NilValue:
iter.ReadNil()
*((*bool)(ptr)) = false
default:
iter.ReportError("fuzzyBoolDecoder", "not number or string or nil")
}
}
type nullableFuzzyIntegerDecoder struct {
fun func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator)
}
func (decoder *nullableFuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
valueType := iter.WhatIsNext()
var str string
switch valueType {
case jsoniter.NumberValue:
var number json.Number
iter.ReadVal(&number)
str = string(number)
case jsoniter.StringValue:
str = iter.ReadString()
// support empty string
if str == "" {
str = "0"
}
case jsoniter.BoolValue:
if iter.ReadBool() {
str = "1"
} else {
str = "0"
}
case jsoniter.NilValue:
iter.ReadNil()
str = "0"
default:
iter.ReportError("fuzzyIntegerDecoder", "not number or string")
}
newIter := iter.Pool().BorrowIterator([]byte(str))
defer iter.Pool().ReturnIterator(newIter)
isFloat := strings.IndexByte(str, '.') != -1
decoder.fun(isFloat, ptr, newIter)
if newIter.Error != nil && newIter.Error != io.EOF {
iter.Error = newIter.Error
}
}
type nullableFuzzyFloat32Decoder struct {
}
func (decoder *nullableFuzzyFloat32Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
valueType := iter.WhatIsNext()
var str string
switch valueType {
case jsoniter.NumberValue:
*((*float32)(ptr)) = iter.ReadFloat32()
case jsoniter.StringValue:
str = iter.ReadString()
// support empty string
if str == "" {
*((*float32)(ptr)) = 0
return
}
newIter := iter.Pool().BorrowIterator([]byte(str))
defer iter.Pool().ReturnIterator(newIter)
*((*float32)(ptr)) = newIter.ReadFloat32()
if newIter.Error != nil && newIter.Error != io.EOF {
iter.Error = newIter.Error
}
case jsoniter.BoolValue:
// support bool to float32
if iter.ReadBool() {
*((*float32)(ptr)) = 1
} else {
*((*float32)(ptr)) = 0
}
case jsoniter.NilValue:
iter.ReadNil()
*((*float32)(ptr)) = 0
default:
iter.ReportError("nullableFuzzyFloat32Decoder", "not number or string")
}
}
type nullableFuzzyFloat64Decoder struct {
}
func (decoder *nullableFuzzyFloat64Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
valueType := iter.WhatIsNext()
var str string
switch valueType {
case jsoniter.NumberValue:
*((*float64)(ptr)) = iter.ReadFloat64()
case jsoniter.StringValue:
str = iter.ReadString()
// support empty string
if str == "" {
*((*float64)(ptr)) = 0
return
}
newIter := iter.Pool().BorrowIterator([]byte(str))
defer iter.Pool().ReturnIterator(newIter)
*((*float64)(ptr)) = newIter.ReadFloat64()
if newIter.Error != nil && newIter.Error != io.EOF {
iter.Error = newIter.Error
}
case jsoniter.BoolValue:
// support bool to float64
if iter.ReadBool() {
*((*float64)(ptr)) = 1
} else {
*((*float64)(ptr)) = 0
}
case jsoniter.NilValue:
// support empty string
iter.ReadNil()
*((*float64)(ptr)) = 0
default:
iter.ReportError("nullableFuzzyFloat64Decoder", "not number or string")
}
}
package tea
import (
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"math"
"math/rand"
"net"
"net/http"
"net/url"
"os"
"reflect"
"regexp"
"strconv"
"strings"
"sync"
"time"
"github.com/alibabacloud-go/debug/debug"
"github.com/alibabacloud-go/tea/utils"
"golang.org/x/net/proxy"
)
var debugLog = debug.Init("tea")
var hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) {
return fn
}
var basicTypes = []string{
"int", "int16", "int64", "int32", "float32", "float64", "string", "bool", "uint64", "uint32", "uint16",
}
// Verify whether the parameters meet the requirements
var validateParams = []string{"require", "pattern", "maxLength", "minLength", "maximum", "minimum", "maxItems", "minItems"}
// CastError is used for cast type fails
type CastError struct {
Message *string
}
// Request is used wrap http request
type Request struct {
Protocol *string
Port *int
Method *string
Pathname *string
Domain *string
Headers map[string]*string
Query map[string]*string
Body io.Reader
}
// Response is use d wrap http response
type Response struct {
Body io.ReadCloser
StatusCode *int
StatusMessage *string
Headers map[string]*string
}
// SDKError struct is used save error code and message
type SDKError struct {
Code *string
StatusCode *int
Message *string
Data *string
Stack *string
errMsg *string
Description *string
AccessDeniedDetail map[string]interface{}
}
// RuntimeObject is used for converting http configuration
type RuntimeObject struct {
IgnoreSSL *bool `json:"ignoreSSL" xml:"ignoreSSL"`
ReadTimeout *int `json:"readTimeout" xml:"readTimeout"`
ConnectTimeout *int `json:"connectTimeout" xml:"connectTimeout"`
LocalAddr *string `json:"localAddr" xml:"localAddr"`
HttpProxy *string `json:"httpProxy" xml:"httpProxy"`
HttpsProxy *string `json:"httpsProxy" xml:"httpsProxy"`
NoProxy *string `json:"noProxy" xml:"noProxy"`
MaxIdleConns *int `json:"maxIdleConns" xml:"maxIdleConns"`
Key *string `json:"key" xml:"key"`
Cert *string `json:"cert" xml:"cert"`
CA *string `json:"ca" xml:"ca"`
Socks5Proxy *string `json:"socks5Proxy" xml:"socks5Proxy"`
Socks5NetWork *string `json:"socks5NetWork" xml:"socks5NetWork"`
Listener utils.ProgressListener `json:"listener" xml:"listener"`
Tracker *utils.ReaderTracker `json:"tracker" xml:"tracker"`
Logger *utils.Logger `json:"logger" xml:"logger"`
}
type teaClient struct {
sync.Mutex
httpClient *http.Client
ifInit bool
}
var clientPool = &sync.Map{}
func (r *RuntimeObject) getClientTag(domain string) string {
return strconv.FormatBool(BoolValue(r.IgnoreSSL)) + strconv.Itoa(IntValue(r.ReadTimeout)) +
strconv.Itoa(IntValue(r.ConnectTimeout)) + StringValue(r.LocalAddr) + StringValue(r.HttpProxy) +
StringValue(r.HttpsProxy) + StringValue(r.NoProxy) + StringValue(r.Socks5Proxy) + StringValue(r.Socks5NetWork) + domain
}
// NewRuntimeObject is used for shortly create runtime object
func NewRuntimeObject(runtime map[string]interface{}) *RuntimeObject {
if runtime == nil {
return &RuntimeObject{}
}
runtimeObject := &RuntimeObject{
IgnoreSSL: TransInterfaceToBool(runtime["ignoreSSL"]),
ReadTimeout: TransInterfaceToInt(runtime["readTimeout"]),
ConnectTimeout: TransInterfaceToInt(runtime["connectTimeout"]),
LocalAddr: TransInterfaceToString(runtime["localAddr"]),
HttpProxy: TransInterfaceToString(runtime["httpProxy"]),
HttpsProxy: TransInterfaceToString(runtime["httpsProxy"]),
NoProxy: TransInterfaceToString(runtime["noProxy"]),
MaxIdleConns: TransInterfaceToInt(runtime["maxIdleConns"]),
Socks5Proxy: TransInterfaceToString(runtime["socks5Proxy"]),
Socks5NetWork: TransInterfaceToString(runtime["socks5NetWork"]),
Key: TransInterfaceToString(runtime["key"]),
Cert: TransInterfaceToString(runtime["cert"]),
CA: TransInterfaceToString(runtime["ca"]),
}
if runtime["listener"] != nil {
runtimeObject.Listener = runtime["listener"].(utils.ProgressListener)
}
if runtime["tracker"] != nil {
runtimeObject.Tracker = runtime["tracker"].(*utils.ReaderTracker)
}
if runtime["logger"] != nil {
runtimeObject.Logger = runtime["logger"].(*utils.Logger)
}
return runtimeObject
}
// NewCastError is used for cast type fails
func NewCastError(message *string) (err error) {
return &CastError{
Message: message,
}
}
// NewRequest is used shortly create Request
func NewRequest() (req *Request) {
return &Request{
Headers: map[string]*string{},
Query: map[string]*string{},
}
}
// NewResponse is create response with http response
func NewResponse(httpResponse *http.Response) (res *Response) {
res = &Response{}
res.Body = httpResponse.Body
res.Headers = make(map[string]*string)
res.StatusCode = Int(httpResponse.StatusCode)
res.StatusMessage = String(httpResponse.Status)
return
}
// NewSDKError is used for shortly create SDKError object
func NewSDKError(obj map[string]interface{}) *SDKError {
err := &SDKError{}
if val, ok := obj["code"].(int); ok {
err.Code = String(strconv.Itoa(val))
} else if val, ok := obj["code"].(string); ok {
err.Code = String(val)
}
if obj["message"] != nil {
err.Message = String(obj["message"].(string))
}
if obj["description"] != nil {
err.Description = String(obj["description"].(string))
}
if detail := obj["accessDeniedDetail"]; detail != nil {
r := reflect.ValueOf(detail)
if r.Kind().String() == "map" {
res := make(map[string]interface{})
tmp := r.MapKeys()
for _, key := range tmp {
res[key.String()] = r.MapIndex(key).Interface()
}
err.AccessDeniedDetail = res
}
}
if data := obj["data"]; data != nil {
r := reflect.ValueOf(data)
if r.Kind().String() == "map" {
res := make(map[string]interface{})
tmp := r.MapKeys()
for _, key := range tmp {
res[key.String()] = r.MapIndex(key).Interface()
}
if statusCode := res["statusCode"]; statusCode != nil {
if code, ok := statusCode.(int); ok {
err.StatusCode = Int(code)
} else if tmp, ok := statusCode.(string); ok {
code, err_ := strconv.Atoi(tmp)
if err_ == nil {
err.StatusCode = Int(code)
}
} else if code, ok := statusCode.(*int); ok {
err.StatusCode = code
}
}
}
byt := bytes.NewBuffer([]byte{})
jsonEncoder := json.NewEncoder(byt)
jsonEncoder.SetEscapeHTML(false)
jsonEncoder.Encode(data)
err.Data = String(string(bytes.TrimSpace(byt.Bytes())))
}
if statusCode, ok := obj["statusCode"].(int); ok {
err.StatusCode = Int(statusCode)
} else if status, ok := obj["statusCode"].(string); ok {
statusCode, err_ := strconv.Atoi(status)
if err_ == nil {
err.StatusCode = Int(statusCode)
}
}
return err
}
// Set ErrMsg by msg
func (err *SDKError) SetErrMsg(msg string) {
err.errMsg = String(msg)
}
func (err *SDKError) Error() string {
if err.errMsg == nil {
str := fmt.Sprintf("SDKError:\n StatusCode: %d\n Code: %s\n Message: %s\n Data: %s\n",
IntValue(err.StatusCode), StringValue(err.Code), StringValue(err.Message), StringValue(err.Data))
err.SetErrMsg(str)
}
return StringValue(err.errMsg)
}
// Return message of CastError
func (err *CastError) Error() string {
return StringValue(err.Message)
}
// Convert is use convert map[string]interface object to struct
func Convert(in interface{}, out interface{}) error {
byt, _ := json.Marshal(in)
decoder := jsonParser.NewDecoder(bytes.NewReader(byt))
decoder.UseNumber()
err := decoder.Decode(&out)
return err
}
// Recover is used to format error
func Recover(in interface{}) error {
if in == nil {
return nil
}
return errors.New(fmt.Sprint(in))
}
// ReadBody is used read response body
func (response *Response) ReadBody() (body []byte, err error) {
defer response.Body.Close()
var buffer [512]byte
result := bytes.NewBuffer(nil)
for {
n, err := response.Body.Read(buffer[0:])
result.Write(buffer[0:n])
if err != nil && err == io.EOF {
break
} else if err != nil {
return nil, err
}
}
return result.Bytes(), nil
}
func getTeaClient(tag string) *teaClient {
client, ok := clientPool.Load(tag)
if client == nil && !ok {
client = &teaClient{
httpClient: &http.Client{},
ifInit: false,
}
clientPool.Store(tag, client)
}
return client.(*teaClient)
}
// DoRequest is used send request to server
func DoRequest(request *Request, requestRuntime map[string]interface{}) (response *Response, err error) {
runtimeObject := NewRuntimeObject(requestRuntime)
fieldMap := make(map[string]string)
utils.InitLogMsg(fieldMap)
defer func() {
if runtimeObject.Logger != nil {
runtimeObject.Logger.PrintLog(fieldMap, err)
}
}()
if request.Method == nil {
request.Method = String("GET")
}
if request.Protocol == nil {
request.Protocol = String("http")
} else {
request.Protocol = String(strings.ToLower(StringValue(request.Protocol)))
}
requestURL := ""
request.Domain = request.Headers["host"]
if request.Port != nil {
request.Domain = String(fmt.Sprintf("%s:%d", StringValue(request.Domain), IntValue(request.Port)))
}
requestURL = fmt.Sprintf("%s://%s%s", StringValue(request.Protocol), StringValue(request.Domain), StringValue(request.Pathname))
queryParams := request.Query
// sort QueryParams by key
q := url.Values{}
for key, value := range queryParams {
q.Add(key, StringValue(value))
}
querystring := q.Encode()
if len(querystring) > 0 {
if strings.Contains(requestURL, "?") {
requestURL = fmt.Sprintf("%s&%s", requestURL, querystring)
} else {
requestURL = fmt.Sprintf("%s?%s", requestURL, querystring)
}
}
debugLog("> %s %s", StringValue(request.Method), requestURL)
httpRequest, err := http.NewRequest(StringValue(request.Method), requestURL, request.Body)
if err != nil {
return
}
httpRequest.Host = StringValue(request.Domain)
client := getTeaClient(runtimeObject.getClientTag(StringValue(request.Domain)))
client.Lock()
if !client.ifInit {
trans, err := getHttpTransport(request, runtimeObject)
if err != nil {
return nil, err
}
client.httpClient.Timeout = time.Duration(IntValue(runtimeObject.ReadTimeout)) * time.Millisecond
client.httpClient.Transport = trans
client.ifInit = true
}
client.Unlock()
for key, value := range request.Headers {
if value == nil || key == "content-length" {
continue
} else if key == "host" {
httpRequest.Header["Host"] = []string{*value}
delete(httpRequest.Header, "host")
} else if key == "user-agent" {
httpRequest.Header["User-Agent"] = []string{*value}
delete(httpRequest.Header, "user-agent")
} else {
httpRequest.Header[key] = []string{*value}
}
debugLog("> %s: %s", key, StringValue(value))
}
contentlength, _ := strconv.Atoi(StringValue(request.Headers["content-length"]))
event := utils.NewProgressEvent(utils.TransferStartedEvent, 0, int64(contentlength), 0)
utils.PublishProgress(runtimeObject.Listener, event)
putMsgToMap(fieldMap, httpRequest)
startTime := time.Now()
fieldMap["{start_time}"] = startTime.Format("2006-01-02 15:04:05")
res, err := hookDo(client.httpClient.Do)(httpRequest)
fieldMap["{cost}"] = time.Since(startTime).String()
completedBytes := int64(0)
if runtimeObject.Tracker != nil {
completedBytes = runtimeObject.Tracker.CompletedBytes
}
if err != nil {
event = utils.NewProgressEvent(utils.TransferFailedEvent, completedBytes, int64(contentlength), 0)
utils.PublishProgress(runtimeObject.Listener, event)
return
}
event = utils.NewProgressEvent(utils.TransferCompletedEvent, completedBytes, int64(contentlength), 0)
utils.PublishProgress(runtimeObject.Listener, event)
response = NewResponse(res)
fieldMap["{code}"] = strconv.Itoa(res.StatusCode)
fieldMap["{res_headers}"] = transToString(res.Header)
debugLog("< HTTP/1.1 %s", res.Status)
for key, value := range res.Header {
debugLog("< %s: %s", key, strings.Join(value, ""))
if len(value) != 0 {
response.Headers[strings.ToLower(key)] = String(value[0])
}
}
return
}
func getHttpTransport(req *Request, runtime *RuntimeObject) (*http.Transport, error) {
trans := new(http.Transport)
httpProxy, err := getHttpProxy(StringValue(req.Protocol), StringValue(req.Domain), runtime)
if err != nil {
return nil, err
}
if strings.ToLower(*req.Protocol) == "https" {
if BoolValue(runtime.IgnoreSSL) != true {
trans.TLSClientConfig = &tls.Config{
InsecureSkipVerify: false,
}
if runtime.Key != nil && runtime.Cert != nil && StringValue(runtime.Key) != "" && StringValue(runtime.Cert) != "" {
cert, err := tls.X509KeyPair([]byte(StringValue(runtime.Cert)), []byte(StringValue(runtime.Key)))
if err != nil {
return nil, err
}
trans.TLSClientConfig.Certificates = []tls.Certificate{cert}
}
if runtime.CA != nil && StringValue(runtime.CA) != "" {
clientCertPool := x509.NewCertPool()
ok := clientCertPool.AppendCertsFromPEM([]byte(StringValue(runtime.CA)))
if !ok {
return nil, errors.New("Failed to parse root certificate")
}
trans.TLSClientConfig.RootCAs = clientCertPool
}
} else {
trans.TLSClientConfig = &tls.Config{
InsecureSkipVerify: true,
}
}
}
if httpProxy != nil {
trans.Proxy = http.ProxyURL(httpProxy)
if httpProxy.User != nil {
password, _ := httpProxy.User.Password()
auth := httpProxy.User.Username() + ":" + password
basic := "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
req.Headers["Proxy-Authorization"] = String(basic)
}
}
if runtime.Socks5Proxy != nil && StringValue(runtime.Socks5Proxy) != "" {
socks5Proxy, err := getSocks5Proxy(runtime)
if err != nil {
return nil, err
}
if socks5Proxy != nil {
var auth *proxy.Auth
if socks5Proxy.User != nil {
password, _ := socks5Proxy.User.Password()
auth = &proxy.Auth{
User: socks5Proxy.User.Username(),
Password: password,
}
}
dialer, err := proxy.SOCKS5(strings.ToLower(StringValue(runtime.Socks5NetWork)), socks5Proxy.String(), auth,
&net.Dialer{
Timeout: time.Duration(IntValue(runtime.ConnectTimeout)) * time.Millisecond,
DualStack: true,
LocalAddr: getLocalAddr(StringValue(runtime.LocalAddr)),
})
if err != nil {
return nil, err
}
trans.Dial = dialer.Dial
}
} else {
trans.DialContext = setDialContext(runtime)
}
if runtime.MaxIdleConns != nil && *runtime.MaxIdleConns > 0 {
trans.MaxIdleConns = IntValue(runtime.MaxIdleConns)
trans.MaxIdleConnsPerHost = IntValue(runtime.MaxIdleConns)
}
return trans, nil
}
func transToString(object interface{}) string {
byt, _ := json.Marshal(object)
return string(byt)
}
func putMsgToMap(fieldMap map[string]string, request *http.Request) {
fieldMap["{host}"] = request.Host
fieldMap["{method}"] = request.Method
fieldMap["{uri}"] = request.URL.RequestURI()
fieldMap["{pid}"] = strconv.Itoa(os.Getpid())
fieldMap["{version}"] = strings.Split(request.Proto, "/")[1]
hostname, _ := os.Hostname()
fieldMap["{hostname}"] = hostname
fieldMap["{req_headers}"] = transToString(request.Header)
fieldMap["{target}"] = request.URL.Path + request.URL.RawQuery
}
func getNoProxy(protocol string, runtime *RuntimeObject) []string {
var urls []string
if runtime.NoProxy != nil && StringValue(runtime.NoProxy) != "" {
urls = strings.Split(StringValue(runtime.NoProxy), ",")
} else if rawurl := os.Getenv("NO_PROXY"); rawurl != "" {
urls = strings.Split(rawurl, ",")
} else if rawurl := os.Getenv("no_proxy"); rawurl != "" {
urls = strings.Split(rawurl, ",")
}
return urls
}
func ToReader(obj interface{}) io.Reader {
switch obj.(type) {
case *string:
tmp := obj.(*string)
return strings.NewReader(StringValue(tmp))
case []byte:
return strings.NewReader(string(obj.([]byte)))
case io.Reader:
return obj.(io.Reader)
default:
panic("Invalid Body. Please set a valid Body.")
}
}
func ToString(val interface{}) string {
return fmt.Sprintf("%v", val)
}
func getHttpProxy(protocol, host string, runtime *RuntimeObject) (proxy *url.URL, err error) {
urls := getNoProxy(protocol, runtime)
for _, url := range urls {
if url == host {
return nil, nil
}
}
if protocol == "https" {
if runtime.HttpsProxy != nil && StringValue(runtime.HttpsProxy) != "" {
proxy, err = url.Parse(StringValue(runtime.HttpsProxy))
} else if rawurl := os.Getenv("HTTPS_PROXY"); rawurl != "" {
proxy, err = url.Parse(rawurl)
} else if rawurl := os.Getenv("https_proxy"); rawurl != "" {
proxy, err = url.Parse(rawurl)
}
} else {
if runtime.HttpProxy != nil && StringValue(runtime.HttpProxy) != "" {
proxy, err = url.Parse(StringValue(runtime.HttpProxy))
} else if rawurl := os.Getenv("HTTP_PROXY"); rawurl != "" {
proxy, err = url.Parse(rawurl)
} else if rawurl := os.Getenv("http_proxy"); rawurl != "" {
proxy, err = url.Parse(rawurl)
}
}
return proxy, err
}
func getSocks5Proxy(runtime *RuntimeObject) (proxy *url.URL, err error) {
if runtime.Socks5Proxy != nil && StringValue(runtime.Socks5Proxy) != "" {
proxy, err = url.Parse(StringValue(runtime.Socks5Proxy))
}
return proxy, err
}
func getLocalAddr(localAddr string) (addr *net.TCPAddr) {
if localAddr != "" {
addr = &net.TCPAddr{
IP: []byte(localAddr),
}
}
return addr
}
func setDialContext(runtime *RuntimeObject) func(cxt context.Context, net, addr string) (c net.Conn, err error) {
return func(ctx context.Context, network, address string) (net.Conn, error) {
if runtime.LocalAddr != nil && StringValue(runtime.LocalAddr) != "" {
netAddr := &net.TCPAddr{
IP: []byte(StringValue(runtime.LocalAddr)),
}
return (&net.Dialer{
Timeout: time.Duration(IntValue(runtime.ConnectTimeout)) * time.Second,
DualStack: true,
LocalAddr: netAddr,
}).DialContext(ctx, network, address)
}
return (&net.Dialer{
Timeout: time.Duration(IntValue(runtime.ConnectTimeout)) * time.Second,
DualStack: true,
}).DialContext(ctx, network, address)
}
}
func ToObject(obj interface{}) map[string]interface{} {
result := make(map[string]interface{})
byt, _ := json.Marshal(obj)
err := json.Unmarshal(byt, &result)
if err != nil {
return nil
}
return result
}
func AllowRetry(retry interface{}, retryTimes *int) *bool {
if IntValue(retryTimes) == 0 {
return Bool(true)
}
retryMap, ok := retry.(map[string]interface{})
if !ok {
return Bool(false)
}
retryable, ok := retryMap["retryable"].(bool)
if !ok || !retryable {
return Bool(false)
}
maxAttempts, ok := retryMap["maxAttempts"].(int)
if !ok || maxAttempts < IntValue(retryTimes) {
return Bool(false)
}
return Bool(true)
}
func Merge(args ...interface{}) map[string]*string {
finalArg := make(map[string]*string)
for _, obj := range args {
switch obj.(type) {
case map[string]*string:
arg := obj.(map[string]*string)
for key, value := range arg {
if value != nil {
finalArg[key] = value
}
}
default:
byt, _ := json.Marshal(obj)
arg := make(map[string]string)
err := json.Unmarshal(byt, &arg)
if err != nil {
return finalArg
}
for key, value := range arg {
if value != "" {
finalArg[key] = String(value)
}
}
}
}
return finalArg
}
func isNil(a interface{}) bool {
defer func() {
recover()
}()
vi := reflect.ValueOf(a)
return vi.IsNil()
}
func ToMap(args ...interface{}) map[string]interface{} {
isNotNil := false
finalArg := make(map[string]interface{})
for _, obj := range args {
if obj == nil {
continue
}
if isNil(obj) {
continue
}
isNotNil = true
switch obj.(type) {
case map[string]*string:
arg := obj.(map[string]*string)
for key, value := range arg {
if value != nil {
finalArg[key] = StringValue(value)
}
}
case map[string]interface{}:
arg := obj.(map[string]interface{})
for key, value := range arg {
if value != nil {
finalArg[key] = value
}
}
case *string:
str := obj.(*string)
arg := make(map[string]interface{})
err := json.Unmarshal([]byte(StringValue(str)), &arg)
if err == nil {
for key, value := range arg {
if value != nil {
finalArg[key] = value
}
}
}
tmp := make(map[string]string)
err = json.Unmarshal([]byte(StringValue(str)), &tmp)
if err == nil {
for key, value := range arg {
if value != "" {
finalArg[key] = value
}
}
}
case []byte:
byt := obj.([]byte)
arg := make(map[string]interface{})
err := json.Unmarshal(byt, &arg)
if err == nil {
for key, value := range arg {
if value != nil {
finalArg[key] = value
}
}
break
}
default:
val := reflect.ValueOf(obj)
res := structToMap(val)
for key, value := range res {
if value != nil {
finalArg[key] = value
}
}
}
}
if !isNotNil {
return nil
}
return finalArg
}
func structToMap(dataValue reflect.Value) map[string]interface{} {
out := make(map[string]interface{})
if !dataValue.IsValid() {
return out
}
if dataValue.Kind().String() == "ptr" {
if dataValue.IsNil() {
return out
}
dataValue = dataValue.Elem()
}
if !dataValue.IsValid() {
return out
}
dataType := dataValue.Type()
if dataType.Kind().String() != "struct" {
return out
}
for i := 0; i < dataType.NumField(); i++ {
field := dataType.Field(i)
name, containsNameTag := field.Tag.Lookup("json")
if !containsNameTag {
name = field.Name
} else {
strs := strings.Split(name, ",")
name = strs[0]
}
fieldValue := dataValue.FieldByName(field.Name)
if !fieldValue.IsValid() || fieldValue.IsNil() {
continue
}
if field.Type.String() == "io.Reader" || field.Type.String() == "io.Writer" {
continue
} else if field.Type.Kind().String() == "struct" {
out[name] = structToMap(fieldValue)
} else if field.Type.Kind().String() == "ptr" &&
field.Type.Elem().Kind().String() == "struct" {
if fieldValue.Elem().IsValid() {
out[name] = structToMap(fieldValue)
}
} else if field.Type.Kind().String() == "ptr" {
if fieldValue.IsValid() && !fieldValue.IsNil() {
out[name] = fieldValue.Elem().Interface()
}
} else if field.Type.Kind().String() == "slice" {
tmp := make([]interface{}, 0)
num := fieldValue.Len()
for i := 0; i < num; i++ {
value := fieldValue.Index(i)
if !value.IsValid() {
continue
}
if value.Type().Kind().String() == "ptr" &&
value.Type().Elem().Kind().String() == "struct" {
if value.IsValid() && !value.IsNil() {
tmp = append(tmp, structToMap(value))
}
} else if value.Type().Kind().String() == "struct" {
tmp = append(tmp, structToMap(value))
} else if value.Type().Kind().String() == "ptr" {
if value.IsValid() && !value.IsNil() {
tmp = append(tmp, value.Elem().Interface())
}
} else {
tmp = append(tmp, value.Interface())
}
}
if len(tmp) > 0 {
out[name] = tmp
}
} else {
out[name] = fieldValue.Interface()
}
}
return out
}
func Retryable(err error) *bool {
if err == nil {
return Bool(false)
}
if realErr, ok := err.(*SDKError); ok {
if realErr.StatusCode == nil {
return Bool(false)
}
code := IntValue(realErr.StatusCode)
return Bool(code >= http.StatusInternalServerError)
}
return Bool(true)
}
func GetBackoffTime(backoff interface{}, retrytimes *int) *int {
backoffMap, ok := backoff.(map[string]interface{})
if !ok {
return Int(0)
}
policy, ok := backoffMap["policy"].(string)
if !ok || policy == "no" {
return Int(0)
}
period, ok := backoffMap["period"].(int)
if !ok || period == 0 {
return Int(0)
}
maxTime := math.Pow(2.0, float64(IntValue(retrytimes)))
return Int(rand.Intn(int(maxTime-1)) * period)
}
func Sleep(backoffTime *int) {
sleeptime := time.Duration(IntValue(backoffTime)) * time.Second
time.Sleep(sleeptime)
}
func Validate(params interface{}) error {
if params == nil {
return nil
}
requestValue := reflect.ValueOf(params)
if requestValue.IsNil() {
return nil
}
err := validate(requestValue.Elem())
return err
}
// Verify whether the parameters meet the requirements
func validate(dataValue reflect.Value) error {
if strings.HasPrefix(dataValue.Type().String(), "*") { // Determines whether the input is a structure object or a pointer object
if dataValue.IsNil() {
return nil
}
dataValue = dataValue.Elem()
}
dataType := dataValue.Type()
for i := 0; i < dataType.NumField(); i++ {
field := dataType.Field(i)
valueField := dataValue.Field(i)
for _, value := range validateParams {
err := validateParam(field, valueField, value)
if err != nil {
return err
}
}
}
return nil
}
func validateParam(field reflect.StructField, valueField reflect.Value, tagName string) error {
tag, containsTag := field.Tag.Lookup(tagName) // Take out the checked regular expression
if containsTag && tagName == "require" {
err := checkRequire(field, valueField)
if err != nil {
return err
}
}
if strings.HasPrefix(field.Type.String(), "[]") { // Verify the parameters of the array type
err := validateSlice(field, valueField, containsTag, tag, tagName)
if err != nil {
return err
}
} else if valueField.Kind() == reflect.Ptr { // Determines whether it is a pointer object
err := validatePtr(field, valueField, containsTag, tag, tagName)
if err != nil {
return err
}
}
return nil
}
func validateSlice(field reflect.StructField, valueField reflect.Value, containsregexpTag bool, tag, tagName string) error {
if valueField.IsValid() && !valueField.IsNil() { // Determines whether the parameter has a value
if containsregexpTag {
if tagName == "maxItems" {
err := checkMaxItems(field, valueField, tag)
if err != nil {
return err
}
}
if tagName == "minItems" {
err := checkMinItems(field, valueField, tag)
if err != nil {
return err
}
}
}
for m := 0; m < valueField.Len(); m++ {
elementValue := valueField.Index(m)
if elementValue.Type().Kind() == reflect.Ptr { // Determines whether the child elements of an array are of a basic type
err := validatePtr(field, elementValue, containsregexpTag, tag, tagName)
if err != nil {
return err
}
}
}
}
return nil
}
func validatePtr(field reflect.StructField, elementValue reflect.Value, containsregexpTag bool, tag, tagName string) error {
if elementValue.IsNil() {
return nil
}
if isFilterType(elementValue.Elem().Type().String(), basicTypes) {
if containsregexpTag {
if tagName == "pattern" {
err := checkPattern(field, elementValue.Elem(), tag)
if err != nil {
return err
}
}
if tagName == "maxLength" {
err := checkMaxLength(field, elementValue.Elem(), tag)
if err != nil {
return err
}
}
if tagName == "minLength" {
err := checkMinLength(field, elementValue.Elem(), tag)
if err != nil {
return err
}
}
if tagName == "maximum" {
err := checkMaximum(field, elementValue.Elem(), tag)
if err != nil {
return err
}
}
if tagName == "minimum" {
err := checkMinimum(field, elementValue.Elem(), tag)
if err != nil {
return err
}
}
}
} else {
err := validate(elementValue)
if err != nil {
return err
}
}
return nil
}
func checkRequire(field reflect.StructField, valueField reflect.Value) error {
name, _ := field.Tag.Lookup("json")
strs := strings.Split(name, ",")
name = strs[0]
if !valueField.IsNil() && valueField.IsValid() {
return nil
}
return errors.New(name + " should be setted")
}
func checkPattern(field reflect.StructField, valueField reflect.Value, tag string) error {
if valueField.IsValid() && valueField.String() != "" {
value := valueField.String()
r, _ := regexp.Compile("^" + tag + "$")
if match := r.MatchString(value); !match { // Determines whether the parameter value satisfies the regular expression or not, and throws an error
return errors.New(value + " is not matched " + tag)
}
}
return nil
}
func checkMaxItems(field reflect.StructField, valueField reflect.Value, tag string) error {
if valueField.IsValid() && valueField.String() != "" {
maxItems, err := strconv.Atoi(tag)
if err != nil {
return err
}
length := valueField.Len()
if maxItems < length {
errMsg := fmt.Sprintf("The length of %s is %d which is more than %d", field.Name, length, maxItems)
return errors.New(errMsg)
}
}
return nil
}
func checkMinItems(field reflect.StructField, valueField reflect.Value, tag string) error {
if valueField.IsValid() {
minItems, err := strconv.Atoi(tag)
if err != nil {
return err
}
length := valueField.Len()
if minItems > length {
errMsg := fmt.Sprintf("The length of %s is %d which is less than %d", field.Name, length, minItems)
return errors.New(errMsg)
}
}
return nil
}
func checkMaxLength(field reflect.StructField, valueField reflect.Value, tag string) error {
if valueField.IsValid() && valueField.String() != "" {
maxLength, err := strconv.Atoi(tag)
if err != nil {
return err
}
length := valueField.Len()
if valueField.Kind().String() == "string" {
length = strings.Count(valueField.String(), "") - 1
}
if maxLength < length {
errMsg := fmt.Sprintf("The length of %s is %d which is more than %d", field.Name, length, maxLength)
return errors.New(errMsg)
}
}
return nil
}
func checkMinLength(field reflect.StructField, valueField reflect.Value, tag string) error {
if valueField.IsValid() {
minLength, err := strconv.Atoi(tag)
if err != nil {
return err
}
length := valueField.Len()
if valueField.Kind().String() == "string" {
length = strings.Count(valueField.String(), "") - 1
}
if minLength > length {
errMsg := fmt.Sprintf("The length of %s is %d which is less than %d", field.Name, length, minLength)
return errors.New(errMsg)
}
}
return nil
}
func checkMaximum(field reflect.StructField, valueField reflect.Value, tag string) error {
if valueField.IsValid() && valueField.String() != "" {
maximum, err := strconv.ParseFloat(tag, 64)
if err != nil {
return err
}
byt, _ := json.Marshal(valueField.Interface())
num, err := strconv.ParseFloat(string(byt), 64)
if err != nil {
return err
}
if maximum < num {
errMsg := fmt.Sprintf("The size of %s is %f which is greater than %f", field.Name, num, maximum)
return errors.New(errMsg)
}
}
return nil
}
func checkMinimum(field reflect.StructField, valueField reflect.Value, tag string) error {
if valueField.IsValid() && valueField.String() != "" {
minimum, err := strconv.ParseFloat(tag, 64)
if err != nil {
return err
}
byt, _ := json.Marshal(valueField.Interface())
num, err := strconv.ParseFloat(string(byt), 64)
if err != nil {
return err
}
if minimum > num {
errMsg := fmt.Sprintf("The size of %s is %f which is less than %f", field.Name, num, minimum)
return errors.New(errMsg)
}
}
return nil
}
// Determines whether realType is in filterTypes
func isFilterType(realType string, filterTypes []string) bool {
for _, value := range filterTypes {
if value == realType {
return true
}
}
return false
}
func TransInterfaceToBool(val interface{}) *bool {
if val == nil {
return nil
}
return Bool(val.(bool))
}
func TransInterfaceToInt(val interface{}) *int {
if val == nil {
return nil
}
return Int(val.(int))
}
func TransInterfaceToString(val interface{}) *string {
if val == nil {
return nil
}
return String(val.(string))
}
func Prettify(i interface{}) string {
resp, _ := json.MarshalIndent(i, "", " ")
return string(resp)
}
func ToInt(a *int32) *int {
return Int(int(Int32Value(a)))
}
func ToInt32(a *int) *int32 {
return Int32(int32(IntValue(a)))
}
package tea
func String(a string) *string {
return &a
}
func StringValue(a *string) string {
if a == nil {
return ""
}
return *a
}
func Int(a int) *int {
return &a
}
func IntValue(a *int) int {
if a == nil {
return 0
}
return *a
}
func Int8(a int8) *int8 {
return &a
}
func Int8Value(a *int8) int8 {
if a == nil {
return 0
}
return *a
}
func Int16(a int16) *int16 {
return &a
}
func Int16Value(a *int16) int16 {
if a == nil {
return 0
}
return *a
}
func Int32(a int32) *int32 {
return &a
}
func Int32Value(a *int32) int32 {
if a == nil {
return 0
}
return *a
}
func Int64(a int64) *int64 {
return &a
}
func Int64Value(a *int64) int64 {
if a == nil {
return 0
}
return *a
}
func Bool(a bool) *bool {
return &a
}
func BoolValue(a *bool) bool {
if a == nil {
return false
}
return *a
}
func Uint(a uint) *uint {
return &a
}
func UintValue(a *uint) uint {
if a == nil {
return 0
}
return *a
}
func Uint8(a uint8) *uint8 {
return &a
}
func Uint8Value(a *uint8) uint8 {
if a == nil {
return 0
}
return *a
}
func Uint16(a uint16) *uint16 {
return &a
}
func Uint16Value(a *uint16) uint16 {
if a == nil {
return 0
}
return *a
}
func Uint32(a uint32) *uint32 {
return &a
}
func Uint32Value(a *uint32) uint32 {
if a == nil {
return 0
}
return *a
}
func Uint64(a uint64) *uint64 {
return &a
}
func Uint64Value(a *uint64) uint64 {
if a == nil {
return 0
}
return *a
}
func Float32(a float32) *float32 {
return &a
}
func Float32Value(a *float32) float32 {
if a == nil {
return 0
}
return *a
}
func Float64(a float64) *float64 {
return &a
}
func Float64Value(a *float64) float64 {
if a == nil {
return 0
}
return *a
}
func IntSlice(a []int) []*int {
if a == nil {
return nil
}
res := make([]*int, len(a))
for i := 0; i < len(a); i++ {
res[i] = &a[i]
}
return res
}
func IntValueSlice(a []*int) []int {
if a == nil {
return nil
}
res := make([]int, len(a))
for i := 0; i < len(a); i++ {
if a[i] != nil {
res[i] = *a[i]
}
}
return res
}
func Int8Slice(a []int8) []*int8 {
if a == nil {
return nil
}
res := make([]*int8, len(a))
for i := 0; i < len(a); i++ {
res[i] = &a[i]
}
return res
}
func Int8ValueSlice(a []*int8) []int8 {
if a == nil {
return nil
}
res := make([]int8, len(a))
for i := 0; i < len(a); i++ {
if a[i] != nil {
res[i] = *a[i]
}
}
return res
}
func Int16Slice(a []int16) []*int16 {
if a == nil {
return nil
}
res := make([]*int16, len(a))
for i := 0; i < len(a); i++ {
res[i] = &a[i]
}
return res
}
func Int16ValueSlice(a []*int16) []int16 {
if a == nil {
return nil
}
res := make([]int16, len(a))
for i := 0; i < len(a); i++ {
if a[i] != nil {
res[i] = *a[i]
}
}
return res
}
func Int32Slice(a []int32) []*int32 {
if a == nil {
return nil
}
res := make([]*int32, len(a))
for i := 0; i < len(a); i++ {
res[i] = &a[i]
}
return res
}
func Int32ValueSlice(a []*int32) []int32 {
if a == nil {
return nil
}
res := make([]int32, len(a))
for i := 0; i < len(a); i++ {
if a[i] != nil {
res[i] = *a[i]
}
}
return res
}
func Int64Slice(a []int64) []*int64 {
if a == nil {
return nil
}
res := make([]*int64, len(a))
for i := 0; i < len(a); i++ {
res[i] = &a[i]
}
return res
}
func Int64ValueSlice(a []*int64) []int64 {
if a == nil {
return nil
}
res := make([]int64, len(a))
for i := 0; i < len(a); i++ {
if a[i] != nil {
res[i] = *a[i]
}
}
return res
}
func UintSlice(a []uint) []*uint {
if a == nil {
return nil
}
res := make([]*uint, len(a))
for i := 0; i < len(a); i++ {
res[i] = &a[i]
}
return res
}
func UintValueSlice(a []*uint) []uint {
if a == nil {
return nil
}
res := make([]uint, len(a))
for i := 0; i < len(a); i++ {
if a[i] != nil {
res[i] = *a[i]
}
}
return res
}
func Uint8Slice(a []uint8) []*uint8 {
if a == nil {
return nil
}
res := make([]*uint8, len(a))
for i := 0; i < len(a); i++ {
res[i] = &a[i]
}
return res
}
func Uint8ValueSlice(a []*uint8) []uint8 {
if a == nil {
return nil
}
res := make([]uint8, len(a))
for i := 0; i < len(a); i++ {
if a[i] != nil {
res[i] = *a[i]
}
}
return res
}
func Uint16Slice(a []uint16) []*uint16 {
if a == nil {
return nil
}
res := make([]*uint16, len(a))
for i := 0; i < len(a); i++ {
res[i] = &a[i]
}
return res
}
func Uint16ValueSlice(a []*uint16) []uint16 {
if a == nil {
return nil
}
res := make([]uint16, len(a))
for i := 0; i < len(a); i++ {
if a[i] != nil {
res[i] = *a[i]
}
}
return res
}
func Uint32Slice(a []uint32) []*uint32 {
if a == nil {
return nil
}
res := make([]*uint32, len(a))
for i := 0; i < len(a); i++ {
res[i] = &a[i]
}
return res
}
func Uint32ValueSlice(a []*uint32) []uint32 {
if a == nil {
return nil
}
res := make([]uint32, len(a))
for i := 0; i < len(a); i++ {
if a[i] != nil {
res[i] = *a[i]
}
}
return res
}
func Uint64Slice(a []uint64) []*uint64 {
if a == nil {
return nil
}
res := make([]*uint64, len(a))
for i := 0; i < len(a); i++ {
res[i] = &a[i]
}
return res
}
func Uint64ValueSlice(a []*uint64) []uint64 {
if a == nil {
return nil
}
res := make([]uint64, len(a))
for i := 0; i < len(a); i++ {
if a[i] != nil {
res[i] = *a[i]
}
}
return res
}
func Float32Slice(a []float32) []*float32 {
if a == nil {
return nil
}
res := make([]*float32, len(a))
for i := 0; i < len(a); i++ {
res[i] = &a[i]
}
return res
}
func Float32ValueSlice(a []*float32) []float32 {
if a == nil {
return nil
}
res := make([]float32, len(a))
for i := 0; i < len(a); i++ {
if a[i] != nil {
res[i] = *a[i]
}
}
return res
}
func Float64Slice(a []float64) []*float64 {
if a == nil {
return nil
}
res := make([]*float64, len(a))
for i := 0; i < len(a); i++ {
res[i] = &a[i]
}
return res
}
func Float64ValueSlice(a []*float64) []float64 {
if a == nil {
return nil
}
res := make([]float64, len(a))
for i := 0; i < len(a); i++ {
if a[i] != nil {
res[i] = *a[i]
}
}
return res
}
func StringSlice(a []string) []*string {
if a == nil {
return nil
}
res := make([]*string, len(a))
for i := 0; i < len(a); i++ {
res[i] = &a[i]
}
return res
}
func StringSliceValue(a []*string) []string {
if a == nil {
return nil
}
res := make([]string, len(a))
for i := 0; i < len(a); i++ {
if a[i] != nil {
res[i] = *a[i]
}
}
return res
}
func BoolSlice(a []bool) []*bool {
if a == nil {
return nil
}
res := make([]*bool, len(a))
for i := 0; i < len(a); i++ {
res[i] = &a[i]
}
return res
}
func BoolSliceValue(a []*bool) []bool {
if a == nil {
return nil
}
res := make([]bool, len(a))
for i := 0; i < len(a); i++ {
if a[i] != nil {
res[i] = *a[i]
}
}
return res
}
package utils
import (
"reflect"
"strings"
"testing"
)
func isNil(object interface{}) bool {
if object == nil {
return true
}
value := reflect.ValueOf(object)
kind := value.Kind()
isNilableKind := containsKind(
[]reflect.Kind{
reflect.Chan, reflect.Func,
reflect.Interface, reflect.Map,
reflect.Ptr, reflect.Slice},
kind)
if isNilableKind && value.IsNil() {
return true
}
return false
}
func containsKind(kinds []reflect.Kind, kind reflect.Kind) bool {
for i := 0; i < len(kinds); i++ {
if kind == kinds[i] {
return true
}
}
return false
}
func AssertEqual(t *testing.T, a, b interface{}) {
if !reflect.DeepEqual(a, b) {
t.Errorf("%v != %v", a, b)
}
}
func AssertNil(t *testing.T, object interface{}) {
if !isNil(object) {
t.Errorf("%v is not nil", object)
}
}
func AssertNotNil(t *testing.T, object interface{}) {
if isNil(object) {
t.Errorf("%v is nil", object)
}
}
func AssertContains(t *testing.T, contains string, msgAndArgs ...string) {
for _, value := range msgAndArgs {
if ok := strings.Contains(contains, value); !ok {
t.Errorf("%s does not contain %s", contains, value)
}
}
}
package utils
import (
"io"
"log"
"strings"
"time"
)
type Logger struct {
*log.Logger
formatTemplate string
isOpen bool
lastLogMsg string
}
var defaultLoggerTemplate = `{time} {channel}: "{method} {uri} HTTP/{version}" {code} {cost} {hostname}`
var loggerParam = []string{"{time}", "{start_time}", "{ts}", "{channel}", "{pid}", "{host}", "{method}", "{uri}", "{version}", "{target}", "{hostname}", "{code}", "{error}", "{req_headers}", "{res_body}", "{res_headers}", "{cost}"}
var logChannel string
func InitLogMsg(fieldMap map[string]string) {
for _, value := range loggerParam {
fieldMap[value] = ""
}
}
func (logger *Logger) SetFormatTemplate(template string) {
logger.formatTemplate = template
}
func (logger *Logger) GetFormatTemplate() string {
return logger.formatTemplate
}
func NewLogger(level string, channel string, out io.Writer, template string) *Logger {
if level == "" {
level = "info"
}
logChannel = "AlibabaCloud"
if channel != "" {
logChannel = channel
}
log := log.New(out, "["+strings.ToUpper(level)+"]", log.Lshortfile)
if template == "" {
template = defaultLoggerTemplate
}
return &Logger{
Logger: log,
formatTemplate: template,
isOpen: true,
}
}
func (logger *Logger) OpenLogger() {
logger.isOpen = true
}
func (logger *Logger) CloseLogger() {
logger.isOpen = false
}
func (logger *Logger) SetIsopen(isopen bool) {
logger.isOpen = isopen
}
func (logger *Logger) GetIsopen() bool {
return logger.isOpen
}
func (logger *Logger) SetLastLogMsg(lastLogMsg string) {
logger.lastLogMsg = lastLogMsg
}
func (logger *Logger) GetLastLogMsg() string {
return logger.lastLogMsg
}
func SetLogChannel(channel string) {
logChannel = channel
}
func (logger *Logger) PrintLog(fieldMap map[string]string, err error) {
if err != nil {
fieldMap["{error}"] = err.Error()
}
fieldMap["{time}"] = time.Now().Format("2006-01-02 15:04:05")
fieldMap["{ts}"] = getTimeInFormatISO8601()
fieldMap["{channel}"] = logChannel
if logger != nil {
logMsg := logger.formatTemplate
for key, value := range fieldMap {
logMsg = strings.Replace(logMsg, key, value, -1)
}
logger.lastLogMsg = logMsg
if logger.isOpen == true {
logger.Output(2, logMsg)
}
}
}
func getTimeInFormatISO8601() (timeStr string) {
gmt := time.FixedZone("GMT", 0)
return time.Now().In(gmt).Format("2006-01-02T15:04:05Z")
}
package utils
// ProgressEventType defines transfer progress event type
type ProgressEventType int
const (
// TransferStartedEvent transfer started, set TotalBytes
TransferStartedEvent ProgressEventType = 1 + iota
// TransferDataEvent transfer data, set ConsumedBytes anmd TotalBytes
TransferDataEvent
// TransferCompletedEvent transfer completed
TransferCompletedEvent
// TransferFailedEvent transfer encounters an error
TransferFailedEvent
)
// ProgressEvent defines progress event
type ProgressEvent struct {
ConsumedBytes int64
TotalBytes int64
RwBytes int64
EventType ProgressEventType
}
// ProgressListener listens progress change
type ProgressListener interface {
ProgressChanged(event *ProgressEvent)
}
// -------------------- Private --------------------
func NewProgressEvent(eventType ProgressEventType, consumed, total int64, rwBytes int64) *ProgressEvent {
return &ProgressEvent{
ConsumedBytes: consumed,
TotalBytes: total,
RwBytes: rwBytes,
EventType: eventType}
}
// publishProgress
func PublishProgress(listener ProgressListener, event *ProgressEvent) {
if listener != nil && event != nil {
listener.ProgressChanged(event)
}
}
func GetProgressListener(obj interface{}) ProgressListener {
if obj == nil {
return nil
}
listener, ok := obj.(ProgressListener)
if !ok {
return nil
}
return listener
}
type ReaderTracker struct {
CompletedBytes int64
}
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
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.
package credentials
import "github.com/alibabacloud-go/tea/tea"
// AccessKeyCredential is a kind of credential
type AccessKeyCredential struct {
AccessKeyId string
AccessKeySecret string
}
func newAccessKeyCredential(accessKeyId, accessKeySecret string) *AccessKeyCredential {
return &AccessKeyCredential{
AccessKeyId: accessKeyId,
AccessKeySecret: accessKeySecret,
}
}
func (s *AccessKeyCredential) GetCredential() (*CredentialModel, error) {
credential := &CredentialModel{
AccessKeyId: tea.String(s.AccessKeyId),
AccessKeySecret: tea.String(s.AccessKeySecret),
Type: tea.String("access_key"),
}
return credential, nil
}
// GetAccessKeyId reutrns AccessKeyCreential's AccessKeyId
func (a *AccessKeyCredential) GetAccessKeyId() (*string, error) {
return tea.String(a.AccessKeyId), nil
}
// GetAccessSecret reutrns AccessKeyCreential's AccessKeySecret
func (a *AccessKeyCredential) GetAccessKeySecret() (*string, error) {
return tea.String(a.AccessKeySecret), nil
}
// GetSecurityToken is useless for AccessKeyCreential
func (a *AccessKeyCredential) GetSecurityToken() (*string, error) {
return tea.String(""), nil
}
// GetBearerToken is useless for AccessKeyCreential
func (a *AccessKeyCredential) GetBearerToken() *string {
return tea.String("")
}
// GetType reutrns AccessKeyCreential's type
func (a *AccessKeyCredential) GetType() *string {
return tea.String("access_key")
}
package credentials
import "github.com/alibabacloud-go/tea/tea"
// BearerTokenCredential is a kind of credential
type BearerTokenCredential struct {
BearerToken string
}
// newBearerTokenCredential return a BearerTokenCredential object
func newBearerTokenCredential(token string) *BearerTokenCredential {
return &BearerTokenCredential{
BearerToken: token,
}
}
func (s *BearerTokenCredential) GetCredential() (*CredentialModel, error) {
credential := &CredentialModel{
BearerToken: tea.String(s.BearerToken),
Type: tea.String("bearer"),
}
return credential, nil
}
// GetAccessKeyId is useless for BearerTokenCredential
func (b *BearerTokenCredential) GetAccessKeyId() (*string, error) {
return tea.String(""), nil
}
// GetAccessSecret is useless for BearerTokenCredential
func (b *BearerTokenCredential) GetAccessKeySecret() (*string, error) {
return tea.String(("")), nil
}
// GetSecurityToken is useless for BearerTokenCredential
func (b *BearerTokenCredential) GetSecurityToken() (*string, error) {
return tea.String(""), nil
}
// GetBearerToken reutrns BearerTokenCredential's BearerToken
func (b *BearerTokenCredential) GetBearerToken() *string {
return tea.String(b.BearerToken)
}
// GetType reutrns BearerTokenCredential's type
func (b *BearerTokenCredential) GetType() *string {
return tea.String("bearer")
}
package credentials
import (
"bufio"
"errors"
"fmt"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/alibabacloud-go/debug/debug"
"github.com/alibabacloud-go/tea/tea"
"github.com/aliyun/credentials-go/credentials/request"
"github.com/aliyun/credentials-go/credentials/response"
"github.com/aliyun/credentials-go/credentials/utils"
)
var debuglog = debug.Init("credential")
var hookParse = func(err error) error {
return err
}
// Credential is an interface for getting actual credential
type Credential interface {
GetAccessKeyId() (*string, error)
GetAccessKeySecret() (*string, error)
GetSecurityToken() (*string, error)
GetBearerToken() *string
GetType() *string
GetCredential() (*CredentialModel, error)
}
// Config is important when call NewCredential
type Config struct {
Type *string `json:"type"`
AccessKeyId *string `json:"access_key_id"`
AccessKeySecret *string `json:"access_key_secret"`
OIDCProviderArn *string `json:"oidc_provider_arn"`
OIDCTokenFilePath *string `json:"oidc_token"`
RoleArn *string `json:"role_arn"`
RoleSessionName *string `json:"role_session_name"`
PublicKeyId *string `json:"public_key_id"`
RoleName *string `json:"role_name"`
SessionExpiration *int `json:"session_expiration"`
PrivateKeyFile *string `json:"private_key_file"`
BearerToken *string `json:"bearer_token"`
SecurityToken *string `json:"security_token"`
RoleSessionExpiration *int `json:"role_session_expiratioon"`
Policy *string `json:"policy"`
Host *string `json:"host"`
Timeout *int `json:"timeout"`
ConnectTimeout *int `json:"connect_timeout"`
Proxy *string `json:"proxy"`
InAdvanceScale *float64 `json:"inAdvanceScale"`
Url *string `json:"url"`
STSEndpoint *string `json:"sts_endpoint"`
ExternalId *string `json:"external_id"`
}
func (s Config) String() string {
return tea.Prettify(s)
}
func (s Config) GoString() string {
return s.String()
}
func (s *Config) SetAccessKeyId(v string) *Config {
s.AccessKeyId = &v
return s
}
func (s *Config) SetAccessKeySecret(v string) *Config {
s.AccessKeySecret = &v
return s
}
func (s *Config) SetSecurityToken(v string) *Config {
s.SecurityToken = &v
return s
}
func (s *Config) SetRoleArn(v string) *Config {
s.RoleArn = &v
return s
}
func (s *Config) SetRoleSessionName(v string) *Config {
s.RoleSessionName = &v
return s
}
func (s *Config) SetPublicKeyId(v string) *Config {
s.PublicKeyId = &v
return s
}
func (s *Config) SetRoleName(v string) *Config {
s.RoleName = &v
return s
}
func (s *Config) SetSessionExpiration(v int) *Config {
s.SessionExpiration = &v
return s
}
func (s *Config) SetPrivateKeyFile(v string) *Config {
s.PrivateKeyFile = &v
return s
}
func (s *Config) SetBearerToken(v string) *Config {
s.BearerToken = &v
return s
}
func (s *Config) SetRoleSessionExpiration(v int) *Config {
s.RoleSessionExpiration = &v
return s
}
func (s *Config) SetPolicy(v string) *Config {
s.Policy = &v
return s
}
func (s *Config) SetHost(v string) *Config {
s.Host = &v
return s
}
func (s *Config) SetTimeout(v int) *Config {
s.Timeout = &v
return s
}
func (s *Config) SetConnectTimeout(v int) *Config {
s.ConnectTimeout = &v
return s
}
func (s *Config) SetProxy(v string) *Config {
s.Proxy = &v
return s
}
func (s *Config) SetType(v string) *Config {
s.Type = &v
return s
}
func (s *Config) SetOIDCTokenFilePath(v string) *Config {
s.OIDCTokenFilePath = &v
return s
}
func (s *Config) SetOIDCProviderArn(v string) *Config {
s.OIDCProviderArn = &v
return s
}
func (s *Config) SetURLCredential(v string) *Config {
if v == "" {
v = os.Getenv("ALIBABA_CLOUD_CREDENTIALS_URI")
}
s.Url = &v
return s
}
func (s *Config) SetSTSEndpoint(v string) *Config {
s.STSEndpoint = &v
return s
}
// NewCredential return a credential according to the type in config.
// if config is nil, the function will use default provider chain to get credential.
// please see README.md for detail.
func NewCredential(config *Config) (credential Credential, err error) {
if config == nil {
config, err = defaultChain.resolve()
if err != nil {
return
}
return NewCredential(config)
}
switch tea.StringValue(config.Type) {
case "credentials_uri":
credential = newURLCredential(tea.StringValue(config.Url))
case "oidc_role_arn":
err = checkoutAssumeRamoidc(config)
if err != nil {
return
}
runtime := &utils.Runtime{
Host: tea.StringValue(config.Host),
Proxy: tea.StringValue(config.Proxy),
ReadTimeout: tea.IntValue(config.Timeout),
ConnectTimeout: tea.IntValue(config.ConnectTimeout),
STSEndpoint: tea.StringValue(config.STSEndpoint),
}
credential = newOIDCRoleArnCredential(tea.StringValue(config.AccessKeyId), tea.StringValue(config.AccessKeySecret), tea.StringValue(config.RoleArn), tea.StringValue(config.OIDCProviderArn), tea.StringValue(config.OIDCTokenFilePath), tea.StringValue(config.RoleSessionName), tea.StringValue(config.Policy), tea.IntValue(config.RoleSessionExpiration), runtime)
case "access_key":
err = checkAccessKey(config)
if err != nil {
return
}
credential = newAccessKeyCredential(tea.StringValue(config.AccessKeyId), tea.StringValue(config.AccessKeySecret))
case "sts":
err = checkSTS(config)
if err != nil {
return
}
credential = newStsTokenCredential(tea.StringValue(config.AccessKeyId), tea.StringValue(config.AccessKeySecret), tea.StringValue(config.SecurityToken))
case "ecs_ram_role":
checkEcsRAMRole(config)
runtime := &utils.Runtime{
Host: tea.StringValue(config.Host),
Proxy: tea.StringValue(config.Proxy),
ReadTimeout: tea.IntValue(config.Timeout),
ConnectTimeout: tea.IntValue(config.ConnectTimeout),
}
credential = newEcsRAMRoleCredential(tea.StringValue(config.RoleName), tea.Float64Value(config.InAdvanceScale), runtime)
case "ram_role_arn":
err = checkRAMRoleArn(config)
if err != nil {
return
}
runtime := &utils.Runtime{
Host: tea.StringValue(config.Host),
Proxy: tea.StringValue(config.Proxy),
ReadTimeout: tea.IntValue(config.Timeout),
ConnectTimeout: tea.IntValue(config.ConnectTimeout),
STSEndpoint: tea.StringValue(config.STSEndpoint),
}
credential = newRAMRoleArnWithExternalIdCredential(
tea.StringValue(config.AccessKeyId),
tea.StringValue(config.AccessKeySecret),
tea.StringValue(config.RoleArn),
tea.StringValue(config.RoleSessionName),
tea.StringValue(config.Policy),
tea.IntValue(config.RoleSessionExpiration),
tea.StringValue(config.ExternalId),
runtime)
case "rsa_key_pair":
err = checkRSAKeyPair(config)
if err != nil {
return
}
file, err1 := os.Open(tea.StringValue(config.PrivateKeyFile))
if err1 != nil {
err = fmt.Errorf("InvalidPath: Can not open PrivateKeyFile, err is %s", err1.Error())
return
}
defer file.Close()
var privateKey string
scan := bufio.NewScanner(file)
for scan.Scan() {
if strings.HasPrefix(scan.Text(), "----") {
continue
}
privateKey += scan.Text() + "\n"
}
runtime := &utils.Runtime{
Host: tea.StringValue(config.Host),
Proxy: tea.StringValue(config.Proxy),
ReadTimeout: tea.IntValue(config.Timeout),
ConnectTimeout: tea.IntValue(config.ConnectTimeout),
STSEndpoint: tea.StringValue(config.STSEndpoint),
}
credential = newRsaKeyPairCredential(privateKey, tea.StringValue(config.PublicKeyId), tea.IntValue(config.SessionExpiration), runtime)
case "bearer":
if tea.StringValue(config.BearerToken) == "" {
err = errors.New("BearerToken cannot be empty")
return
}
credential = newBearerTokenCredential(tea.StringValue(config.BearerToken))
default:
err = errors.New("Invalid type option, support: access_key, sts, ecs_ram_role, ram_role_arn, rsa_key_pair")
return
}
return credential, nil
}
func checkRSAKeyPair(config *Config) (err error) {
if tea.StringValue(config.PrivateKeyFile) == "" {
err = errors.New("PrivateKeyFile cannot be empty")
return
}
if tea.StringValue(config.PublicKeyId) == "" {
err = errors.New("PublicKeyId cannot be empty")
return
}
return
}
func checkoutAssumeRamoidc(config *Config) (err error) {
if tea.StringValue(config.RoleArn) == "" {
err = errors.New("RoleArn cannot be empty")
return
}
if tea.StringValue(config.OIDCProviderArn) == "" {
err = errors.New("OIDCProviderArn cannot be empty")
return
}
return
}
func checkRAMRoleArn(config *Config) (err error) {
if tea.StringValue(config.AccessKeySecret) == "" {
err = errors.New("AccessKeySecret cannot be empty")
return
}
if tea.StringValue(config.RoleArn) == "" {
err = errors.New("RoleArn cannot be empty")
return
}
if tea.StringValue(config.RoleSessionName) == "" {
err = errors.New("RoleSessionName cannot be empty")
return
}
if tea.StringValue(config.AccessKeyId) == "" {
err = errors.New("AccessKeyId cannot be empty")
return
}
return
}
func checkEcsRAMRole(config *Config) (err error) {
return
}
func checkSTS(config *Config) (err error) {
if tea.StringValue(config.AccessKeyId) == "" {
err = errors.New("AccessKeyId cannot be empty")
return
}
if tea.StringValue(config.AccessKeySecret) == "" {
err = errors.New("AccessKeySecret cannot be empty")
return
}
if tea.StringValue(config.SecurityToken) == "" {
err = errors.New("SecurityToken cannot be empty")
return
}
return
}
func checkAccessKey(config *Config) (err error) {
if tea.StringValue(config.AccessKeyId) == "" {
err = errors.New("AccessKeyId cannot be empty")
return
}
if tea.StringValue(config.AccessKeySecret) == "" {
err = errors.New("AccessKeySecret cannot be empty")
return
}
return
}
func doAction(request *request.CommonRequest, runtime *utils.Runtime) (content []byte, err error) {
var urlEncoded string
if request.BodyParams != nil {
urlEncoded = utils.GetURLFormedMap(request.BodyParams)
}
httpRequest, err := http.NewRequest(request.Method, request.URL, strings.NewReader(urlEncoded))
if err != nil {
return
}
httpRequest.Proto = "HTTP/1.1"
httpRequest.Host = request.Domain
debuglog("> %s %s %s", httpRequest.Method, httpRequest.URL.RequestURI(), httpRequest.Proto)
debuglog("> Host: %s", httpRequest.Host)
for key, value := range request.Headers {
if value != "" {
debuglog("> %s: %s", key, value)
httpRequest.Header[key] = []string{value}
}
}
debuglog(">")
httpClient := &http.Client{}
httpClient.Timeout = time.Duration(runtime.ReadTimeout) * time.Second
proxy := &url.URL{}
if runtime.Proxy != "" {
proxy, err = url.Parse(runtime.Proxy)
if err != nil {
return
}
}
trans := &http.Transport{}
if proxy != nil && runtime.Proxy != "" {
trans.Proxy = http.ProxyURL(proxy)
}
trans.DialContext = utils.Timeout(time.Duration(runtime.ConnectTimeout) * time.Second)
httpClient.Transport = trans
httpResponse, err := hookDo(httpClient.Do)(httpRequest)
if err != nil {
return
}
debuglog("< %s %s", httpResponse.Proto, httpResponse.Status)
for key, value := range httpResponse.Header {
debuglog("< %s: %v", key, strings.Join(value, ""))
}
debuglog("<")
resp := &response.CommonResponse{}
err = hookParse(resp.ParseFromHTTPResponse(httpResponse))
if err != nil {
return
}
debuglog("%s", resp.GetHTTPContentString())
if resp.GetHTTPStatus() != http.StatusOK {
err = fmt.Errorf("httpStatus: %d, message = %s", resp.GetHTTPStatus(), resp.GetHTTPContentString())
return
}
return resp.GetHTTPContentBytes(), nil
}
package credentials
import "github.com/alibabacloud-go/tea/tea"
// CredentialModel is a model
type CredentialModel struct {
// accesskey id
AccessKeyId *string `json:"accessKeyId,omitempty" xml:"accessKeyId,omitempty"`
// accesskey secret
AccessKeySecret *string `json:"accessKeySecret,omitempty" xml:"accessKeySecret,omitempty"`
// security token
SecurityToken *string `json:"securityToken,omitempty" xml:"securityToken,omitempty"`
// bearer token
BearerToken *string `json:"bearerToken,omitempty" xml:"bearerToken,omitempty"`
// type
Type *string `json:"type,omitempty" xml:"type,omitempty"`
}
func (s CredentialModel) String() string {
return tea.Prettify(s)
}
func (s CredentialModel) GoString() string {
return s.String()
}
func (s *CredentialModel) SetAccessKeyId(v string) *CredentialModel {
s.AccessKeyId = &v
return s
}
func (s *CredentialModel) SetAccessKeySecret(v string) *CredentialModel {
s.AccessKeySecret = &v
return s
}
func (s *CredentialModel) SetSecurityToken(v string) *CredentialModel {
s.SecurityToken = &v
return s
}
func (s *CredentialModel) SetBearerToken(v string) *CredentialModel {
s.BearerToken = &v
return s
}
func (s *CredentialModel) SetType(v string) *CredentialModel {
s.Type = &v
return s
}
package credentials
import (
"net/http"
"time"
)
const defaultInAdvanceScale = 0.95
var hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) {
return fn
}
type credentialUpdater struct {
credentialExpiration int
lastUpdateTimestamp int64
inAdvanceScale float64
}
func (updater *credentialUpdater) needUpdateCredential() (result bool) {
if updater.inAdvanceScale == 0 {
updater.inAdvanceScale = defaultInAdvanceScale
}
return time.Now().Unix()-updater.lastUpdateTimestamp >= int64(float64(updater.credentialExpiration)*updater.inAdvanceScale)
}
package credentials
import (
"encoding/json"
"fmt"
"time"
"github.com/alibabacloud-go/tea/tea"
"github.com/aliyun/credentials-go/credentials/request"
"github.com/aliyun/credentials-go/credentials/utils"
)
var securityCredURL = "http://100.100.100.200/latest/meta-data/ram/security-credentials/"
// EcsRAMRoleCredential is a kind of credential
type EcsRAMRoleCredential struct {
*credentialUpdater
RoleName string
sessionCredential *sessionCredential
runtime *utils.Runtime
}
type ecsRAMRoleResponse struct {
Code string `json:"Code" xml:"Code"`
AccessKeyId string `json:"AccessKeyId" xml:"AccessKeyId"`
AccessKeySecret string `json:"AccessKeySecret" xml:"AccessKeySecret"`
SecurityToken string `json:"SecurityToken" xml:"SecurityToken"`
Expiration string `json:"Expiration" xml:"Expiration"`
}
func newEcsRAMRoleCredential(roleName string, inAdvanceScale float64, runtime *utils.Runtime) *EcsRAMRoleCredential {
credentialUpdater := new(credentialUpdater)
if inAdvanceScale < 1 && inAdvanceScale > 0 {
credentialUpdater.inAdvanceScale = inAdvanceScale
}
return &EcsRAMRoleCredential{
RoleName: roleName,
credentialUpdater: credentialUpdater,
runtime: runtime,
}
}
func (e *EcsRAMRoleCredential) GetCredential() (*CredentialModel, error) {
if e.sessionCredential == nil || e.needUpdateCredential() {
err := e.updateCredential()
if err != nil {
return nil, err
}
}
credential := &CredentialModel{
AccessKeyId: tea.String(e.sessionCredential.AccessKeyId),
AccessKeySecret: tea.String(e.sessionCredential.AccessKeySecret),
SecurityToken: tea.String(e.sessionCredential.SecurityToken),
Type: tea.String("ecs_ram_role"),
}
return credential, nil
}
// GetAccessKeyId reutrns EcsRAMRoleCredential's AccessKeyId
// if AccessKeyId is not exist or out of date, the function will update it.
func (e *EcsRAMRoleCredential) GetAccessKeyId() (*string, error) {
if e.sessionCredential == nil || e.needUpdateCredential() {
err := e.updateCredential()
if err != nil {
if e.credentialExpiration > (int(time.Now().Unix()) - int(e.lastUpdateTimestamp)) {
return &e.sessionCredential.AccessKeyId, nil
}
return tea.String(""), err
}
}
return tea.String(e.sessionCredential.AccessKeyId), nil
}
// GetAccessSecret reutrns EcsRAMRoleCredential's AccessKeySecret
// if AccessKeySecret is not exist or out of date, the function will update it.
func (e *EcsRAMRoleCredential) GetAccessKeySecret() (*string, error) {
if e.sessionCredential == nil || e.needUpdateCredential() {
err := e.updateCredential()
if err != nil {
if e.credentialExpiration > (int(time.Now().Unix()) - int(e.lastUpdateTimestamp)) {
return &e.sessionCredential.AccessKeySecret, nil
}
return tea.String(""), err
}
}
return tea.String(e.sessionCredential.AccessKeySecret), nil
}
// GetSecurityToken reutrns EcsRAMRoleCredential's SecurityToken
// if SecurityToken is not exist or out of date, the function will update it.
func (e *EcsRAMRoleCredential) GetSecurityToken() (*string, error) {
if e.sessionCredential == nil || e.needUpdateCredential() {
err := e.updateCredential()
if err != nil {
if e.credentialExpiration > (int(time.Now().Unix()) - int(e.lastUpdateTimestamp)) {
return &e.sessionCredential.SecurityToken, nil
}
return tea.String(""), err
}
}
return tea.String(e.sessionCredential.SecurityToken), nil
}
// GetBearerToken is useless for EcsRAMRoleCredential
func (e *EcsRAMRoleCredential) GetBearerToken() *string {
return tea.String("")
}
// GetType reutrns EcsRAMRoleCredential's type
func (e *EcsRAMRoleCredential) GetType() *string {
return tea.String("ecs_ram_role")
}
func getRoleName() (string, error) {
runtime := utils.NewRuntime(1, 1, "", "")
request := request.NewCommonRequest()
request.URL = securityCredURL
request.Method = "GET"
content, err := doAction(request, runtime)
if err != nil {
return "", err
}
return string(content), nil
}
func (e *EcsRAMRoleCredential) updateCredential() (err error) {
if e.runtime == nil {
e.runtime = new(utils.Runtime)
}
request := request.NewCommonRequest()
if e.RoleName == "" {
e.RoleName, err = getRoleName()
if err != nil {
return fmt.Errorf("refresh Ecs sts token err: %s", err.Error())
}
}
request.URL = securityCredURL + e.RoleName
request.Method = "GET"
content, err := doAction(request, e.runtime)
if err != nil {
return fmt.Errorf("refresh Ecs sts token err: %s", err.Error())
}
var resp *ecsRAMRoleResponse
err = json.Unmarshal(content, &resp)
if err != nil {
return fmt.Errorf("refresh Ecs sts token err: Json Unmarshal fail: %s", err.Error())
}
if resp.Code != "Success" {
return fmt.Errorf("refresh Ecs sts token err: Code is not Success")
}
if resp.AccessKeyId == "" || resp.AccessKeySecret == "" || resp.SecurityToken == "" || resp.Expiration == "" {
return fmt.Errorf("refresh Ecs sts token err: AccessKeyId: %s, AccessKeySecret: %s, SecurityToken: %s, Expiration: %s", resp.AccessKeyId, resp.AccessKeySecret, resp.SecurityToken, resp.Expiration)
}
expirationTime, err := time.Parse("2006-01-02T15:04:05Z", resp.Expiration)
e.lastUpdateTimestamp = time.Now().Unix()
e.credentialExpiration = int(expirationTime.Unix() - time.Now().Unix())
e.sessionCredential = &sessionCredential{
AccessKeyId: resp.AccessKeyId,
AccessKeySecret: resp.AccessKeySecret,
SecurityToken: resp.SecurityToken,
}
return
}
package credentials
import (
"errors"
"os"
"github.com/alibabacloud-go/tea/tea"
)
type envProvider struct{}
var providerEnv = new(envProvider)
const (
// EnvVarAccessKeyId is a name of ALIBABA_CLOUD_ACCESS_KEY_Id
EnvVarAccessKeyId = "ALIBABA_CLOUD_ACCESS_KEY_Id"
EnvVarAccessKeyIdNew = "ALIBABA_CLOUD_ACCESS_KEY_ID"
// EnvVarAccessKeySecret is a name of ALIBABA_CLOUD_ACCESS_KEY_SECRET
EnvVarAccessKeySecret = "ALIBABA_CLOUD_ACCESS_KEY_SECRET"
)
func newEnvProvider() Provider {
return &envProvider{}
}
func (p *envProvider) resolve() (*Config, error) {
accessKeyId, ok1 := os.LookupEnv(EnvVarAccessKeyIdNew)
if !ok1 || accessKeyId == "" {
accessKeyId, ok1 = os.LookupEnv(EnvVarAccessKeyId)
}
accessKeySecret, ok2 := os.LookupEnv(EnvVarAccessKeySecret)
if !ok1 || !ok2 {
return nil, nil
}
if accessKeyId == "" {
return nil, errors.New(EnvVarAccessKeyIdNew + " or " + EnvVarAccessKeyId + " cannot be empty")
}
if accessKeySecret == "" {
return nil, errors.New(EnvVarAccessKeySecret + " cannot be empty")
}
config := &Config{
Type: tea.String("access_key"),
AccessKeyId: tea.String(accessKeyId),
AccessKeySecret: tea.String(accessKeySecret),
}
return config, nil
}
package credentials
import (
"os"
"github.com/alibabacloud-go/tea/tea"
)
type instanceCredentialsProvider struct{}
var providerInstance = new(instanceCredentialsProvider)
func newInstanceCredentialsProvider() Provider {
return &instanceCredentialsProvider{}
}
func (p *instanceCredentialsProvider) resolve() (*Config, error) {
roleName, ok := os.LookupEnv(ENVEcsMetadata)
if !ok {
return nil, nil
}
config := &Config{
Type: tea.String("ecs_ram_role"),
RoleName: tea.String(roleName),
}
return config, nil
}
package credentials
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"time"
"github.com/alibabacloud-go/tea/tea"
"github.com/aliyun/credentials-go/credentials/request"
"github.com/aliyun/credentials-go/credentials/utils"
)
const defaultOIDCDurationSeconds = 3600
// OIDCCredential is a kind of credentials
type OIDCCredential struct {
*credentialUpdater
AccessKeyId string
AccessKeySecret string
RoleArn string
OIDCProviderArn string
OIDCTokenFilePath string
Policy string
RoleSessionName string
RoleSessionExpiration int
sessionCredential *sessionCredential
runtime *utils.Runtime
}
type OIDCResponse struct {
Credentials *credentialsInResponse `json:"Credentials" xml:"Credentials"`
}
type OIDCcredentialsInResponse struct {
AccessKeyId string `json:"AccessKeyId" xml:"AccessKeyId"`
AccessKeySecret string `json:"AccessKeySecret" xml:"AccessKeySecret"`
SecurityToken string `json:"SecurityToken" xml:"SecurityToken"`
Expiration string `json:"Expiration" xml:"Expiration"`
}
func newOIDCRoleArnCredential(accessKeyId, accessKeySecret, roleArn, OIDCProviderArn, OIDCTokenFilePath, RoleSessionName, policy string, RoleSessionExpiration int, runtime *utils.Runtime) *OIDCCredential {
return &OIDCCredential{
AccessKeyId: accessKeyId,
AccessKeySecret: accessKeySecret,
RoleArn: roleArn,
OIDCProviderArn: OIDCProviderArn,
OIDCTokenFilePath: OIDCTokenFilePath,
RoleSessionName: RoleSessionName,
Policy: policy,
RoleSessionExpiration: RoleSessionExpiration,
credentialUpdater: new(credentialUpdater),
runtime: runtime,
}
}
func (e *OIDCCredential) GetCredential() (*CredentialModel, error) {
if e.sessionCredential == nil || e.needUpdateCredential() {
err := e.updateCredential()
if err != nil {
return nil, err
}
}
credential := &CredentialModel{
AccessKeyId: tea.String(e.sessionCredential.AccessKeyId),
AccessKeySecret: tea.String(e.sessionCredential.AccessKeySecret),
SecurityToken: tea.String(e.sessionCredential.SecurityToken),
Type: tea.String("oidc_role_arn"),
}
return credential, nil
}
// GetAccessKeyId reutrns OIDCCredential's AccessKeyId
// if AccessKeyId is not exist or out of date, the function will update it.
func (r *OIDCCredential) GetAccessKeyId() (*string, error) {
if r.sessionCredential == nil || r.needUpdateCredential() {
err := r.updateCredential()
if err != nil {
return tea.String(""), err
}
}
return tea.String(r.sessionCredential.AccessKeyId), nil
}
// GetAccessSecret reutrns OIDCCredential's AccessKeySecret
// if AccessKeySecret is not exist or out of date, the function will update it.
func (r *OIDCCredential) GetAccessKeySecret() (*string, error) {
if r.sessionCredential == nil || r.needUpdateCredential() {
err := r.updateCredential()
if err != nil {
return tea.String(""), err
}
}
return tea.String(r.sessionCredential.AccessKeySecret), nil
}
// GetSecurityToken reutrns OIDCCredential's SecurityToken
// if SecurityToken is not exist or out of date, the function will update it.
func (r *OIDCCredential) GetSecurityToken() (*string, error) {
if r.sessionCredential == nil || r.needUpdateCredential() {
err := r.updateCredential()
if err != nil {
return tea.String(""), err
}
}
return tea.String(r.sessionCredential.SecurityToken), nil
}
// GetBearerToken is useless OIDCCredential
func (r *OIDCCredential) GetBearerToken() *string {
return tea.String("")
}
// GetType reutrns OIDCCredential's type
func (r *OIDCCredential) GetType() *string {
return tea.String("oidc_role_arn")
}
func (r *OIDCCredential) GetOIDCToken(OIDCTokenFilePath string) *string {
tokenPath := OIDCTokenFilePath
_, err := os.Stat(tokenPath)
if os.IsNotExist(err) {
tokenPath = os.Getenv("ALIBABA_CLOUD_OIDC_TOKEN_FILE")
if tokenPath == "" {
return nil
}
}
byt, err := ioutil.ReadFile(tokenPath)
if err != nil {
return nil
}
return tea.String(string(byt))
}
func (r *OIDCCredential) updateCredential() (err error) {
if r.runtime == nil {
r.runtime = new(utils.Runtime)
}
request := request.NewCommonRequest()
request.Domain = "sts.aliyuncs.com"
if r.runtime.STSEndpoint != "" {
request.Domain = r.runtime.STSEndpoint
}
request.Scheme = "HTTPS"
request.Method = "POST"
request.QueryParams["Timestamp"] = utils.GetTimeInFormatISO8601()
request.QueryParams["Action"] = "AssumeRoleWithOIDC"
request.QueryParams["Format"] = "JSON"
request.BodyParams["RoleArn"] = r.RoleArn
request.BodyParams["OIDCProviderArn"] = r.OIDCProviderArn
token := r.GetOIDCToken(r.OIDCTokenFilePath)
request.BodyParams["OIDCToken"] = tea.StringValue(token)
if r.Policy != "" {
request.QueryParams["Policy"] = r.Policy
}
request.QueryParams["RoleSessionName"] = r.RoleSessionName
request.QueryParams["Version"] = "2015-04-01"
request.QueryParams["SignatureNonce"] = utils.GetUUID()
request.Headers["Host"] = request.Domain
request.Headers["Accept-Encoding"] = "identity"
request.Headers["content-type"] = "application/x-www-form-urlencoded"
request.URL = request.BuildURL()
content, err := doAction(request, r.runtime)
if err != nil {
return fmt.Errorf("refresh RoleArn sts token err: %s", err.Error())
}
var resp *OIDCResponse
err = json.Unmarshal(content, &resp)
if err != nil {
return fmt.Errorf("refresh RoleArn sts token err: Json.Unmarshal fail: %s", err.Error())
}
if resp == nil || resp.Credentials == nil {
return fmt.Errorf("refresh RoleArn sts token err: Credentials is empty")
}
respCredentials := resp.Credentials
if respCredentials.AccessKeyId == "" || respCredentials.AccessKeySecret == "" || respCredentials.SecurityToken == "" || respCredentials.Expiration == "" {
return fmt.Errorf("refresh RoleArn sts token err: AccessKeyId: %s, AccessKeySecret: %s, SecurityToken: %s, Expiration: %s", respCredentials.AccessKeyId, respCredentials.AccessKeySecret, respCredentials.SecurityToken, respCredentials.Expiration)
}
expirationTime, err := time.Parse("2006-01-02T15:04:05Z", respCredentials.Expiration)
r.lastUpdateTimestamp = time.Now().Unix()
r.credentialExpiration = int(expirationTime.Unix() - time.Now().Unix())
r.sessionCredential = &sessionCredential{
AccessKeyId: respCredentials.AccessKeyId,
AccessKeySecret: respCredentials.AccessKeySecret,
SecurityToken: respCredentials.SecurityToken,
}
return
}
package credentials
import (
"os"
"github.com/alibabacloud-go/tea/tea"
)
type oidcCredentialsProvider struct{}
var providerOIDC = new(oidcCredentialsProvider)
func newOidcCredentialsProvider() Provider {
return &oidcCredentialsProvider{}
}
func (p *oidcCredentialsProvider) resolve() (*Config, error) {
roleArn, ok1 := os.LookupEnv(ENVRoleArn)
oidcProviderArn, ok2 := os.LookupEnv(ENVOIDCProviderArn)
oidcTokenFilePath, ok3 := os.LookupEnv(ENVOIDCTokenFile)
if !ok1 || !ok2 || !ok3 {
return nil, nil
}
config := &Config{
Type: tea.String("oidc_role_arn"),
RoleArn: tea.String(roleArn),
OIDCProviderArn: tea.String(oidcProviderArn),
OIDCTokenFilePath: tea.String(oidcTokenFilePath),
RoleSessionName: tea.String("defaultSessionName"),
}
roleSessionName, ok := os.LookupEnv(ENVRoleSessionName)
if ok {
config.RoleSessionName = tea.String(roleSessionName)
}
return config, nil
}
test_long_oidc_token_eyJhbGciOiJSUzI1NiIsImtpZCI6ImFQaXlpNEVGSU8wWnlGcFh1V0psQUNWbklZVlJsUkNmM2tlSzNMUlhWT1UifQ.eyJhdWQiOlsic3RzLmFsaXl1bmNzLmNvbSJdLCJleHAiOjE2NDUxMTk3ODAsImlhdCI6MTY0NTA4Mzc4MCwiaXNzIjoiaHR0cHM6Ly9vaWRjLWFjay1jbi1oYW5nemhvdS5vc3MtY24taGFuZ3pob3UtaW50ZXJuYWwuYWxpeXVuY3MuY29tL2NmMWQ4ZGIwMjM0ZDk0YzEyOGFiZDM3MTc4NWJjOWQxNSIsImt1YmVybmV0ZXMuaW8iOnsibmFtZXNwYWNlIjoidGVzdC1ycnNhIiwicG9kIjp7Im5hbWUiOiJydW4tYXMtcm9vdCIsInVpZCI6ImIzMGI0MGY2LWNiZTAtNGY0Yy1hZGYyLWM1OGQ4ZmExZTAxMCJ9LCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoidXNlcjEiLCJ1aWQiOiJiZTEyMzdjYS01MTY4LTQyMzYtYWUyMC00NDM1YjhmMGI4YzAifX0sIm5iZiI6MTY0NTA4Mzc4MCwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OnRlc3QtcnJzYTp1c2VyMSJ9.XGP-wgLj-iMiAHjLe0lZLh7y48Qsj9HzsEbNh706WwerBoxnssdsyGFb9lzd2FyM8CssbAOCstr7OuAMWNdJmDZgpiOGGSbQ-KXXmbfnIS4ix-V3pQF6LVBFr7xJlj20J6YY89um3rv_04t0iCGxKWs2ZMUyU1FbZpIPRep24LVKbUz1saiiVGgDBTIZdHA13Z-jUvYAnsxK_Kj5tc1K-IuQQU0IwSKJh5OShMcdPugMV5LwTL3ogCikfB7yljq5vclBhCeF2lXLIibvwF711TOhuJ5lMlh-a2KkIgwBHhANg_U9k4Mt_VadctfUGc4hxlSbBD0w9o9mDGKwgGmW5Q
\ No newline at end of file
package credentials
import (
"errors"
"fmt"
"os"
"runtime"
"strings"
"github.com/alibabacloud-go/tea/tea"
ini "gopkg.in/ini.v1"
)
type profileProvider struct {
Profile string
}
var providerProfile = newProfileProvider()
var hookOS = func(goos string) string {
return goos
}
var hookState = func(info os.FileInfo, err error) (os.FileInfo, error) {
return info, err
}
// NewProfileProvider receive zero or more parameters,
// when length of name is 0, the value of field Profile will be "default",
// and when there are multiple inputs, the function will take the
// first one and discard the other values.
func newProfileProvider(name ...string) Provider {
p := new(profileProvider)
if len(name) == 0 {
p.Profile = "default"
} else {
p.Profile = name[0]
}
return p
}
// resolve implements the Provider interface
// when credential type is rsa_key_pair, the content of private_key file
// must be able to be parsed directly into the required string
// that NewRsaKeyPairCredential function needed
func (p *profileProvider) resolve() (*Config, error) {
path, ok := os.LookupEnv(ENVCredentialFile)
if !ok {
defaultPath, err := checkDefaultPath()
if err != nil {
return nil, err
}
path = defaultPath
if path == "" {
return nil, nil
}
} else if path == "" {
return nil, errors.New(ENVCredentialFile + " cannot be empty")
}
value, section, err := getType(path, p.Profile)
if err != nil {
return nil, err
}
switch value.String() {
case "access_key":
config, err := getAccessKey(section)
if err != nil {
return nil, err
}
return config, nil
case "sts":
config, err := getSTS(section)
if err != nil {
return nil, err
}
return config, nil
case "bearer":
config, err := getBearerToken(section)
if err != nil {
return nil, err
}
return config, nil
case "ecs_ram_role":
config, err := getEcsRAMRole(section)
if err != nil {
return nil, err
}
return config, nil
case "ram_role_arn":
config, err := getRAMRoleArn(section)
if err != nil {
return nil, err
}
return config, nil
case "rsa_key_pair":
config, err := getRSAKeyPair(section)
if err != nil {
return nil, err
}
return config, nil
default:
return nil, errors.New("Invalid type option, support: access_key, sts, ecs_ram_role, ram_role_arn, rsa_key_pair")
}
}
func getRSAKeyPair(section *ini.Section) (*Config, error) {
publicKeyId, err := section.GetKey("public_key_id")
if err != nil {
return nil, errors.New("Missing required public_key_id option in profile for rsa_key_pair")
}
if publicKeyId.String() == "" {
return nil, errors.New("public_key_id cannot be empty")
}
privateKeyFile, err := section.GetKey("private_key_file")
if err != nil {
return nil, errors.New("Missing required private_key_file option in profile for rsa_key_pair")
}
if privateKeyFile.String() == "" {
return nil, errors.New("private_key_file cannot be empty")
}
sessionExpiration, _ := section.GetKey("session_expiration")
expiration := 0
if sessionExpiration != nil {
expiration, err = sessionExpiration.Int()
if err != nil {
return nil, errors.New("session_expiration must be an int")
}
}
config := &Config{
Type: tea.String("rsa_key_pair"),
PublicKeyId: tea.String(publicKeyId.String()),
PrivateKeyFile: tea.String(privateKeyFile.String()),
SessionExpiration: tea.Int(expiration),
}
err = setRuntimeToConfig(config, section)
if err != nil {
return nil, err
}
return config, nil
}
func getRAMRoleArn(section *ini.Section) (*Config, error) {
accessKeyId, err := section.GetKey("access_key_id")
if err != nil {
return nil, errors.New("Missing required access_key_id option in profile for ram_role_arn")
}
if accessKeyId.String() == "" {
return nil, errors.New("access_key_id cannot be empty")
}
accessKeySecret, err := section.GetKey("access_key_secret")
if err != nil {
return nil, errors.New("Missing required access_key_secret option in profile for ram_role_arn")
}
if accessKeySecret.String() == "" {
return nil, errors.New("access_key_secret cannot be empty")
}
roleArn, err := section.GetKey("role_arn")
if err != nil {
return nil, errors.New("Missing required role_arn option in profile for ram_role_arn")
}
if roleArn.String() == "" {
return nil, errors.New("role_arn cannot be empty")
}
roleSessionName, err := section.GetKey("role_session_name")
if err != nil {
return nil, errors.New("Missing required role_session_name option in profile for ram_role_arn")
}
if roleSessionName.String() == "" {
return nil, errors.New("role_session_name cannot be empty")
}
roleSessionExpiration, _ := section.GetKey("role_session_expiration")
expiration := 0
if roleSessionExpiration != nil {
expiration, err = roleSessionExpiration.Int()
if err != nil {
return nil, errors.New("role_session_expiration must be an int")
}
}
config := &Config{
Type: tea.String("ram_role_arn"),
AccessKeyId: tea.String(accessKeyId.String()),
AccessKeySecret: tea.String(accessKeySecret.String()),
RoleArn: tea.String(roleArn.String()),
RoleSessionName: tea.String(roleSessionName.String()),
RoleSessionExpiration: tea.Int(expiration),
}
err = setRuntimeToConfig(config, section)
if err != nil {
return nil, err
}
return config, nil
}
func getEcsRAMRole(section *ini.Section) (*Config, error) {
roleName, _ := section.GetKey("role_name")
config := &Config{
Type: tea.String("ecs_ram_role"),
}
if roleName != nil {
config.RoleName = tea.String(roleName.String())
}
err := setRuntimeToConfig(config, section)
if err != nil {
return nil, err
}
return config, nil
}
func getBearerToken(section *ini.Section) (*Config, error) {
bearerToken, err := section.GetKey("bearer_token")
if err != nil {
return nil, errors.New("Missing required bearer_token option in profile for bearer")
}
if bearerToken.String() == "" {
return nil, errors.New("bearer_token cannot be empty")
}
config := &Config{
Type: tea.String("bearer"),
BearerToken: tea.String(bearerToken.String()),
}
return config, nil
}
func getSTS(section *ini.Section) (*Config, error) {
accesskeyid, err := section.GetKey("access_key_id")
if err != nil {
return nil, errors.New("Missing required access_key_id option in profile for sts")
}
if accesskeyid.String() == "" {
return nil, errors.New("access_key_id cannot be empty")
}
accessKeySecret, err := section.GetKey("access_key_secret")
if err != nil {
return nil, errors.New("Missing required access_key_secret option in profile for sts")
}
if accessKeySecret.String() == "" {
return nil, errors.New("access_key_secret cannot be empty")
}
securityToken, err := section.GetKey("security_token")
if err != nil {
return nil, errors.New("Missing required security_token option in profile for sts")
}
if securityToken.String() == "" {
return nil, errors.New("security_token cannot be empty")
}
config := &Config{
Type: tea.String("sts"),
AccessKeyId: tea.String(accesskeyid.String()),
AccessKeySecret: tea.String(accessKeySecret.String()),
SecurityToken: tea.String(securityToken.String()),
}
return config, nil
}
func getAccessKey(section *ini.Section) (*Config, error) {
accesskeyid, err := section.GetKey("access_key_id")
if err != nil {
return nil, errors.New("Missing required access_key_id option in profile for access_key")
}
if accesskeyid.String() == "" {
return nil, errors.New("access_key_id cannot be empty")
}
accessKeySecret, err := section.GetKey("access_key_secret")
if err != nil {
return nil, errors.New("Missing required access_key_secret option in profile for access_key")
}
if accessKeySecret.String() == "" {
return nil, errors.New("access_key_secret cannot be empty")
}
config := &Config{
Type: tea.String("access_key"),
AccessKeyId: tea.String(accesskeyid.String()),
AccessKeySecret: tea.String(accessKeySecret.String()),
}
return config, nil
}
func getType(path, profile string) (*ini.Key, *ini.Section, error) {
ini, err := ini.Load(path)
if err != nil {
return nil, nil, errors.New("ERROR: Can not open file " + err.Error())
}
section, err := ini.GetSection(profile)
if err != nil {
return nil, nil, errors.New("ERROR: Can not load section " + err.Error())
}
value, err := section.GetKey("type")
if err != nil {
return nil, nil, errors.New("Missing required type option " + err.Error())
}
return value, section, nil
}
func getHomePath() string {
if hookOS(runtime.GOOS) == "windows" {
path, ok := os.LookupEnv("USERPROFILE")
if !ok {
return ""
}
return path
}
path, ok := os.LookupEnv("HOME")
if !ok {
return ""
}
return path
}
func checkDefaultPath() (path string, err error) {
path = getHomePath()
if path == "" {
return "", errors.New("The default credential file path is invalid")
}
path = strings.Replace("~/.alibabacloud/credentials", "~", path, 1)
_, err = hookState(os.Stat(path))
if err != nil {
return "", nil
}
return path, nil
}
func setRuntimeToConfig(config *Config, section *ini.Section) error {
rawTimeout, _ := section.GetKey("timeout")
rawConnectTimeout, _ := section.GetKey("connect_timeout")
rawProxy, _ := section.GetKey("proxy")
rawHost, _ := section.GetKey("host")
if rawProxy != nil {
config.Proxy = tea.String(rawProxy.String())
}
if rawConnectTimeout != nil {
connectTimeout, err := rawConnectTimeout.Int()
if err != nil {
return fmt.Errorf("Please set connect_timeout with an int value")
}
config.ConnectTimeout = tea.Int(connectTimeout)
}
if rawTimeout != nil {
timeout, err := rawTimeout.Int()
if err != nil {
return fmt.Errorf("Please set timeout with an int value")
}
config.Timeout = tea.Int(timeout)
}
if rawHost != nil {
config.Host = tea.String(rawHost.String())
}
return nil
}
package credentials
//Environmental virables that may be used by the provider
const (
ENVCredentialFile = "ALIBABA_CLOUD_CREDENTIALS_FILE"
ENVEcsMetadata = "ALIBABA_CLOUD_ECS_METADATA"
PATHCredentialFile = "~/.alibabacloud/credentials"
ENVRoleArn = "ALIBABA_CLOUD_ROLE_ARN"
ENVOIDCProviderArn = "ALIBABA_CLOUD_OIDC_PROVIDER_ARN"
ENVOIDCTokenFile = "ALIBABA_CLOUD_OIDC_TOKEN_FILE"
ENVRoleSessionName = "ALIBABA_CLOUD_ROLE_SESSION_NAME"
)
// Provider will be implemented When you want to customize the provider.
type Provider interface {
resolve() (*Config, error)
}
package credentials
import (
"errors"
)
type providerChain struct {
Providers []Provider
}
var defaultproviders = []Provider{providerEnv, providerOIDC, providerProfile, providerInstance}
var defaultChain = newProviderChain(defaultproviders)
func newProviderChain(providers []Provider) Provider {
return &providerChain{
Providers: providers,
}
}
func (p *providerChain) resolve() (*Config, error) {
for _, provider := range p.Providers {
config, err := provider.resolve()
if err != nil {
return nil, err
} else if config == nil {
continue
}
return config, err
}
return nil, errors.New("No credential found")
}
package request
import (
"fmt"
"net/url"
"strings"
"time"
"github.com/aliyun/credentials-go/credentials/utils"
)
// CommonRequest is for requesting credential
type CommonRequest struct {
Scheme string
Method string
Domain string
RegionId string
URL string
ReadTimeout time.Duration
ConnectTimeout time.Duration
isInsecure *bool
BodyParams map[string]string
userAgent map[string]string
QueryParams map[string]string
Headers map[string]string
queries string
}
// NewCommonRequest returns a CommonRequest
func NewCommonRequest() *CommonRequest {
return &CommonRequest{
BodyParams: make(map[string]string),
QueryParams: make(map[string]string),
Headers: make(map[string]string),
}
}
// BuildURL returns a url
func (request *CommonRequest) BuildURL() string {
url := fmt.Sprintf("%s://%s", strings.ToLower(request.Scheme), request.Domain)
request.queries = "/?" + utils.GetURLFormedMap(request.QueryParams)
return url + request.queries
}
// BuildStringToSign returns BuildStringToSign
func (request *CommonRequest) BuildStringToSign() (stringToSign string) {
signParams := make(map[string]string)
for key, value := range request.QueryParams {
signParams[key] = value
}
for key, value := range request.BodyParams {
signParams[key] = value
}
stringToSign = utils.GetURLFormedMap(signParams)
stringToSign = strings.Replace(stringToSign, "+", "%20", -1)
stringToSign = strings.Replace(stringToSign, "*", "%2A", -1)
stringToSign = strings.Replace(stringToSign, "%7E", "~", -1)
stringToSign = url.QueryEscape(stringToSign)
stringToSign = request.Method + "&%2F&" + stringToSign
return
}
package response
import (
"io"
"io/ioutil"
"net/http"
)
var hookReadAll = func(fn func(r io.Reader) (b []byte, err error)) func(r io.Reader) (b []byte, err error) {
return fn
}
// CommonResponse is for storing message of httpResponse
type CommonResponse struct {
httpStatus int
httpHeaders map[string][]string
httpContentString string
httpContentBytes []byte
}
// ParseFromHTTPResponse assigns for CommonResponse, returns err when body is too large.
func (resp *CommonResponse) ParseFromHTTPResponse(httpResponse *http.Response) (err error) {
defer httpResponse.Body.Close()
body, err := hookReadAll(ioutil.ReadAll)(httpResponse.Body)
if err != nil {
return
}
resp.httpStatus = httpResponse.StatusCode
resp.httpHeaders = httpResponse.Header
resp.httpContentBytes = body
resp.httpContentString = string(body)
return
}
// GetHTTPStatus returns httpStatus
func (resp *CommonResponse) GetHTTPStatus() int {
return resp.httpStatus
}
// GetHTTPHeaders returns httpresponse's headers
func (resp *CommonResponse) GetHTTPHeaders() map[string][]string {
return resp.httpHeaders
}
// GetHTTPContentString return body content as string
func (resp *CommonResponse) GetHTTPContentString() string {
return resp.httpContentString
}
// GetHTTPContentBytes return body content as []byte
func (resp *CommonResponse) GetHTTPContentBytes() []byte {
return resp.httpContentBytes
}
package credentials
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"time"
"github.com/alibabacloud-go/tea/tea"
"github.com/aliyun/credentials-go/credentials/request"
"github.com/aliyun/credentials-go/credentials/utils"
)
// RsaKeyPairCredential is a kind of credentials
type RsaKeyPairCredential struct {
*credentialUpdater
PrivateKey string
PublicKeyId string
SessionExpiration int
sessionCredential *sessionCredential
runtime *utils.Runtime
}
type rsaKeyPairResponse struct {
SessionAccessKey *sessionAccessKey `json:"SessionAccessKey" xml:"SessionAccessKey"`
}
type sessionAccessKey struct {
SessionAccessKeyId string `json:"SessionAccessKeyId" xml:"SessionAccessKeyId"`
SessionAccessKeySecret string `json:"SessionAccessKeySecret" xml:"SessionAccessKeySecret"`
Expiration string `json:"Expiration" xml:"Expiration"`
}
func newRsaKeyPairCredential(privateKey, publicKeyId string, sessionExpiration int, runtime *utils.Runtime) *RsaKeyPairCredential {
return &RsaKeyPairCredential{
PrivateKey: privateKey,
PublicKeyId: publicKeyId,
SessionExpiration: sessionExpiration,
credentialUpdater: new(credentialUpdater),
runtime: runtime,
}
}
func (e *RsaKeyPairCredential) GetCredential() (*CredentialModel, error) {
if e.sessionCredential == nil || e.needUpdateCredential() {
err := e.updateCredential()
if err != nil {
return nil, err
}
}
credential := &CredentialModel{
AccessKeyId: tea.String(e.sessionCredential.AccessKeyId),
AccessKeySecret: tea.String(e.sessionCredential.AccessKeySecret),
SecurityToken: tea.String(e.sessionCredential.SecurityToken),
Type: tea.String("rsa_key_pair"),
}
return credential, nil
}
// GetAccessKeyId reutrns RsaKeyPairCredential's AccessKeyId
// if AccessKeyId is not exist or out of date, the function will update it.
func (r *RsaKeyPairCredential) GetAccessKeyId() (*string, error) {
if r.sessionCredential == nil || r.needUpdateCredential() {
err := r.updateCredential()
if err != nil {
return tea.String(""), err
}
}
return tea.String(r.sessionCredential.AccessKeyId), nil
}
// GetAccessSecret reutrns RsaKeyPairCredential's AccessKeySecret
// if AccessKeySecret is not exist or out of date, the function will update it.
func (r *RsaKeyPairCredential) GetAccessKeySecret() (*string, error) {
if r.sessionCredential == nil || r.needUpdateCredential() {
err := r.updateCredential()
if err != nil {
return tea.String(""), err
}
}
return tea.String(r.sessionCredential.AccessKeySecret), nil
}
// GetSecurityToken is useless RsaKeyPairCredential
func (r *RsaKeyPairCredential) GetSecurityToken() (*string, error) {
return tea.String(""), nil
}
// GetBearerToken is useless for RsaKeyPairCredential
func (r *RsaKeyPairCredential) GetBearerToken() *string {
return tea.String("")
}
// GetType reutrns RsaKeyPairCredential's type
func (r *RsaKeyPairCredential) GetType() *string {
return tea.String("rsa_key_pair")
}
func (r *RsaKeyPairCredential) updateCredential() (err error) {
if r.runtime == nil {
r.runtime = new(utils.Runtime)
}
request := request.NewCommonRequest()
request.Domain = "sts.aliyuncs.com"
if r.runtime.Host != "" {
request.Domain = r.runtime.Host
} else if r.runtime.STSEndpoint != "" {
request.Domain = r.runtime.STSEndpoint
}
request.Scheme = "HTTPS"
request.Method = "GET"
request.QueryParams["AccessKeyId"] = r.PublicKeyId
request.QueryParams["Action"] = "GenerateSessionAccessKey"
request.QueryParams["Format"] = "JSON"
if r.SessionExpiration > 0 {
if r.SessionExpiration >= 900 && r.SessionExpiration <= 3600 {
request.QueryParams["DurationSeconds"] = strconv.Itoa(r.SessionExpiration)
} else {
err = errors.New("[InvalidParam]:Key Pair session duration should be in the range of 15min - 1Hr")
return
}
} else {
request.QueryParams["DurationSeconds"] = strconv.Itoa(defaultDurationSeconds)
}
request.QueryParams["SignatureMethod"] = "SHA256withRSA"
request.QueryParams["SignatureType"] = "PRIVATEKEY"
request.QueryParams["SignatureVersion"] = "1.0"
request.QueryParams["Version"] = "2015-04-01"
request.QueryParams["Timestamp"] = utils.GetTimeInFormatISO8601()
request.QueryParams["SignatureNonce"] = utils.GetUUID()
signature := utils.Sha256WithRsa(request.BuildStringToSign(), r.PrivateKey)
request.QueryParams["Signature"] = signature
request.Headers["Host"] = request.Domain
request.Headers["Accept-Encoding"] = "identity"
request.URL = request.BuildURL()
content, err := doAction(request, r.runtime)
if err != nil {
return fmt.Errorf("refresh KeyPair err: %s", err.Error())
}
var resp *rsaKeyPairResponse
err = json.Unmarshal(content, &resp)
if err != nil {
return fmt.Errorf("refresh KeyPair err: Json Unmarshal fail: %s", err.Error())
}
if resp == nil || resp.SessionAccessKey == nil {
return fmt.Errorf("refresh KeyPair err: SessionAccessKey is empty")
}
sessionAccessKey := resp.SessionAccessKey
if sessionAccessKey.SessionAccessKeyId == "" || sessionAccessKey.SessionAccessKeySecret == "" || sessionAccessKey.Expiration == "" {
return fmt.Errorf("refresh KeyPair err: SessionAccessKeyId: %v, SessionAccessKeySecret: %v, Expiration: %v", sessionAccessKey.SessionAccessKeyId, sessionAccessKey.SessionAccessKeySecret, sessionAccessKey.Expiration)
}
expirationTime, err := time.Parse("2006-01-02T15:04:05Z", sessionAccessKey.Expiration)
r.lastUpdateTimestamp = time.Now().Unix()
r.credentialExpiration = int(expirationTime.Unix() - time.Now().Unix())
r.sessionCredential = &sessionCredential{
AccessKeyId: sessionAccessKey.SessionAccessKeyId,
AccessKeySecret: sessionAccessKey.SessionAccessKeySecret,
}
return
}
package credentials
type sessionCredential struct {
AccessKeyId string
AccessKeySecret string
SecurityToken string
}
package credentials
import "github.com/alibabacloud-go/tea/tea"
// StsTokenCredential is a kind of credentials
type StsTokenCredential struct {
AccessKeyId string
AccessKeySecret string
SecurityToken string
}
func newStsTokenCredential(accessKeyId, accessKeySecret, securityToken string) *StsTokenCredential {
return &StsTokenCredential{
AccessKeyId: accessKeyId,
AccessKeySecret: accessKeySecret,
SecurityToken: securityToken,
}
}
func (s *StsTokenCredential) GetCredential() (*CredentialModel, error) {
credential := &CredentialModel{
AccessKeyId: tea.String(s.AccessKeyId),
AccessKeySecret: tea.String(s.AccessKeySecret),
SecurityToken: tea.String(s.SecurityToken),
Type: tea.String("sts"),
}
return credential, nil
}
// GetAccessKeyId reutrns StsTokenCredential's AccessKeyId
func (s *StsTokenCredential) GetAccessKeyId() (*string, error) {
return tea.String(s.AccessKeyId), nil
}
// GetAccessSecret reutrns StsTokenCredential's AccessKeySecret
func (s *StsTokenCredential) GetAccessKeySecret() (*string, error) {
return tea.String(s.AccessKeySecret), nil
}
// GetSecurityToken reutrns StsTokenCredential's SecurityToken
func (s *StsTokenCredential) GetSecurityToken() (*string, error) {
return tea.String(s.SecurityToken), nil
}
// GetBearerToken is useless StsTokenCredential
func (s *StsTokenCredential) GetBearerToken() *string {
return tea.String("")
}
// GetType reutrns StsTokenCredential's type
func (s *StsTokenCredential) GetType() *string {
return tea.String("sts")
}
package credentials
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"time"
"github.com/alibabacloud-go/tea/tea"
"github.com/aliyun/credentials-go/credentials/request"
"github.com/aliyun/credentials-go/credentials/utils"
)
const defaultDurationSeconds = 3600
// RAMRoleArnCredential is a kind of credentials
type RAMRoleArnCredential struct {
*credentialUpdater
AccessKeyId string
AccessKeySecret string
RoleArn string
RoleSessionName string
RoleSessionExpiration int
Policy string
ExternalId string
sessionCredential *sessionCredential
runtime *utils.Runtime
}
type ramRoleArnResponse struct {
Credentials *credentialsInResponse `json:"Credentials" xml:"Credentials"`
}
type credentialsInResponse struct {
AccessKeyId string `json:"AccessKeyId" xml:"AccessKeyId"`
AccessKeySecret string `json:"AccessKeySecret" xml:"AccessKeySecret"`
SecurityToken string `json:"SecurityToken" xml:"SecurityToken"`
Expiration string `json:"Expiration" xml:"Expiration"`
}
func newRAMRoleArnCredential(accessKeyId, accessKeySecret, roleArn, roleSessionName, policy string, roleSessionExpiration int, runtime *utils.Runtime) *RAMRoleArnCredential {
return &RAMRoleArnCredential{
AccessKeyId: accessKeyId,
AccessKeySecret: accessKeySecret,
RoleArn: roleArn,
RoleSessionName: roleSessionName,
RoleSessionExpiration: roleSessionExpiration,
Policy: policy,
credentialUpdater: new(credentialUpdater),
runtime: runtime,
}
}
func newRAMRoleArnWithExternalIdCredential(accessKeyId, accessKeySecret, roleArn, roleSessionName, policy string, roleSessionExpiration int, externalId string, runtime *utils.Runtime) *RAMRoleArnCredential {
return &RAMRoleArnCredential{
AccessKeyId: accessKeyId,
AccessKeySecret: accessKeySecret,
RoleArn: roleArn,
RoleSessionName: roleSessionName,
RoleSessionExpiration: roleSessionExpiration,
Policy: policy,
ExternalId: externalId,
credentialUpdater: new(credentialUpdater),
runtime: runtime,
}
}
func (e *RAMRoleArnCredential) GetCredential() (*CredentialModel, error) {
if e.sessionCredential == nil || e.needUpdateCredential() {
err := e.updateCredential()
if err != nil {
return nil, err
}
}
credential := &CredentialModel{
AccessKeyId: tea.String(e.sessionCredential.AccessKeyId),
AccessKeySecret: tea.String(e.sessionCredential.AccessKeySecret),
SecurityToken: tea.String(e.sessionCredential.SecurityToken),
Type: tea.String("ram_role_arn"),
}
return credential, nil
}
// GetAccessKeyId reutrns RamRoleArnCredential's AccessKeyId
// if AccessKeyId is not exist or out of date, the function will update it.
func (r *RAMRoleArnCredential) GetAccessKeyId() (*string, error) {
if r.sessionCredential == nil || r.needUpdateCredential() {
err := r.updateCredential()
if err != nil {
return tea.String(""), err
}
}
return tea.String(r.sessionCredential.AccessKeyId), nil
}
// GetAccessSecret reutrns RamRoleArnCredential's AccessKeySecret
// if AccessKeySecret is not exist or out of date, the function will update it.
func (r *RAMRoleArnCredential) GetAccessKeySecret() (*string, error) {
if r.sessionCredential == nil || r.needUpdateCredential() {
err := r.updateCredential()
if err != nil {
return tea.String(""), err
}
}
return tea.String(r.sessionCredential.AccessKeySecret), nil
}
// GetSecurityToken reutrns RamRoleArnCredential's SecurityToken
// if SecurityToken is not exist or out of date, the function will update it.
func (r *RAMRoleArnCredential) GetSecurityToken() (*string, error) {
if r.sessionCredential == nil || r.needUpdateCredential() {
err := r.updateCredential()
if err != nil {
return tea.String(""), err
}
}
return tea.String(r.sessionCredential.SecurityToken), nil
}
// GetBearerToken is useless RamRoleArnCredential
func (r *RAMRoleArnCredential) GetBearerToken() *string {
return tea.String("")
}
// GetType reutrns RamRoleArnCredential's type
func (r *RAMRoleArnCredential) GetType() *string {
return tea.String("ram_role_arn")
}
func (r *RAMRoleArnCredential) updateCredential() (err error) {
if r.runtime == nil {
r.runtime = new(utils.Runtime)
}
request := request.NewCommonRequest()
request.Domain = "sts.aliyuncs.com"
if r.runtime.STSEndpoint != "" {
request.Domain = r.runtime.STSEndpoint
}
request.Scheme = "HTTPS"
request.Method = "GET"
request.QueryParams["AccessKeyId"] = r.AccessKeyId
request.QueryParams["Action"] = "AssumeRole"
request.QueryParams["Format"] = "JSON"
if r.RoleSessionExpiration > 0 {
if r.RoleSessionExpiration >= 900 && r.RoleSessionExpiration <= 3600 {
request.QueryParams["DurationSeconds"] = strconv.Itoa(r.RoleSessionExpiration)
} else {
err = errors.New("[InvalidParam]:Assume Role session duration should be in the range of 15min - 1Hr")
return
}
} else {
request.QueryParams["DurationSeconds"] = strconv.Itoa(defaultDurationSeconds)
}
request.QueryParams["RoleArn"] = r.RoleArn
if r.Policy != "" {
request.QueryParams["Policy"] = r.Policy
}
if r.ExternalId != "" {
request.QueryParams["ExternalId"] = r.ExternalId
}
request.QueryParams["RoleSessionName"] = r.RoleSessionName
request.QueryParams["SignatureMethod"] = "HMAC-SHA1"
request.QueryParams["SignatureVersion"] = "1.0"
request.QueryParams["Version"] = "2015-04-01"
request.QueryParams["Timestamp"] = utils.GetTimeInFormatISO8601()
request.QueryParams["SignatureNonce"] = utils.GetUUID()
signature := utils.ShaHmac1(request.BuildStringToSign(), r.AccessKeySecret+"&")
request.QueryParams["Signature"] = signature
request.Headers["Host"] = request.Domain
request.Headers["Accept-Encoding"] = "identity"
request.URL = request.BuildURL()
content, err := doAction(request, r.runtime)
if err != nil {
return fmt.Errorf("refresh RoleArn sts token err: %s", err.Error())
}
var resp *ramRoleArnResponse
err = json.Unmarshal(content, &resp)
if err != nil {
return fmt.Errorf("refresh RoleArn sts token err: Json.Unmarshal fail: %s", err.Error())
}
if resp == nil || resp.Credentials == nil {
return fmt.Errorf("refresh RoleArn sts token err: Credentials is empty")
}
respCredentials := resp.Credentials
if respCredentials.AccessKeyId == "" || respCredentials.AccessKeySecret == "" || respCredentials.SecurityToken == "" || respCredentials.Expiration == "" {
return fmt.Errorf("refresh RoleArn sts token err: AccessKeyId: %s, AccessKeySecret: %s, SecurityToken: %s, Expiration: %s", respCredentials.AccessKeyId, respCredentials.AccessKeySecret, respCredentials.SecurityToken, respCredentials.Expiration)
}
expirationTime, err := time.Parse("2006-01-02T15:04:05Z", respCredentials.Expiration)
r.lastUpdateTimestamp = time.Now().Unix()
r.credentialExpiration = int(expirationTime.Unix() - time.Now().Unix())
r.sessionCredential = &sessionCredential{
AccessKeyId: respCredentials.AccessKeyId,
AccessKeySecret: respCredentials.AccessKeySecret,
SecurityToken: respCredentials.SecurityToken,
}
return
}
package credentials
import (
"encoding/json"
"fmt"
"os"
"time"
"github.com/alibabacloud-go/tea/tea"
"github.com/aliyun/credentials-go/credentials/request"
"github.com/aliyun/credentials-go/credentials/utils"
)
// URLCredential is a kind of credential
type URLCredential struct {
URL string
*credentialUpdater
*sessionCredential
runtime *utils.Runtime
}
type URLResponse struct {
AccessKeyId string `json:"AccessKeyId" xml:"AccessKeyId"`
AccessKeySecret string `json:"AccessKeySecret" xml:"AccessKeySecret"`
SecurityToken string `json:"SecurityToken" xml:"SecurityToken"`
Expiration string `json:"Expiration" xml:"Expiration"`
}
func newURLCredential(URL string) *URLCredential {
credentialUpdater := new(credentialUpdater)
if URL == "" {
URL = os.Getenv("ALIBABA_CLOUD_CREDENTIALS_URI")
}
return &URLCredential{
URL: URL,
credentialUpdater: credentialUpdater,
}
}
func (e *URLCredential) GetCredential() (*CredentialModel, error) {
if e.sessionCredential == nil || e.needUpdateCredential() {
err := e.updateCredential()
if err != nil {
return nil, err
}
}
credential := &CredentialModel{
AccessKeyId: tea.String(e.sessionCredential.AccessKeyId),
AccessKeySecret: tea.String(e.sessionCredential.AccessKeySecret),
SecurityToken: tea.String(e.sessionCredential.SecurityToken),
Type: tea.String("credential_uri"),
}
return credential, nil
}
// GetAccessKeyId reutrns URLCredential's AccessKeyId
// if AccessKeyId is not exist or out of date, the function will update it.
func (e *URLCredential) GetAccessKeyId() (*string, error) {
if e.sessionCredential == nil || e.needUpdateCredential() {
err := e.updateCredential()
if err != nil {
if e.credentialExpiration > (int(time.Now().Unix()) - int(e.lastUpdateTimestamp)) {
return &e.sessionCredential.AccessKeyId, nil
}
return tea.String(""), err
}
}
return tea.String(e.sessionCredential.AccessKeyId), nil
}
// GetAccessSecret reutrns URLCredential's AccessKeySecret
// if AccessKeySecret is not exist or out of date, the function will update it.
func (e *URLCredential) GetAccessKeySecret() (*string, error) {
if e.sessionCredential == nil || e.needUpdateCredential() {
err := e.updateCredential()
if err != nil {
if e.credentialExpiration > (int(time.Now().Unix()) - int(e.lastUpdateTimestamp)) {
return &e.sessionCredential.AccessKeySecret, nil
}
return tea.String(""), err
}
}
return tea.String(e.sessionCredential.AccessKeySecret), nil
}
// GetSecurityToken reutrns URLCredential's SecurityToken
// if SecurityToken is not exist or out of date, the function will update it.
func (e *URLCredential) GetSecurityToken() (*string, error) {
if e.sessionCredential == nil || e.needUpdateCredential() {
err := e.updateCredential()
if err != nil {
if e.credentialExpiration > (int(time.Now().Unix()) - int(e.lastUpdateTimestamp)) {
return &e.sessionCredential.SecurityToken, nil
}
return tea.String(""), err
}
}
return tea.String(e.sessionCredential.SecurityToken), nil
}
// GetBearerToken is useless for URLCredential
func (e *URLCredential) GetBearerToken() *string {
return tea.String("")
}
// GetType reutrns URLCredential's type
func (e *URLCredential) GetType() *string {
return tea.String("credential_uri")
}
func (e *URLCredential) updateCredential() (err error) {
if e.runtime == nil {
e.runtime = new(utils.Runtime)
}
request := request.NewCommonRequest()
request.URL = e.URL
request.Method = "GET"
content, err := doAction(request, e.runtime)
if err != nil {
return fmt.Errorf("refresh Ecs sts token err: %s", err.Error())
}
var resp *URLResponse
err = json.Unmarshal(content, &resp)
if err != nil {
return fmt.Errorf("refresh Ecs sts token err: Json Unmarshal fail: %s", err.Error())
}
if resp.AccessKeyId == "" || resp.AccessKeySecret == "" || resp.SecurityToken == "" || resp.Expiration == "" {
return fmt.Errorf("refresh Ecs sts token err: AccessKeyId: %s, AccessKeySecret: %s, SecurityToken: %s, Expiration: %s", resp.AccessKeyId, resp.AccessKeySecret, resp.SecurityToken, resp.Expiration)
}
expirationTime, err := time.Parse("2006-01-02T15:04:05Z", resp.Expiration)
e.lastUpdateTimestamp = time.Now().Unix()
e.credentialExpiration = int(expirationTime.Unix() - time.Now().Unix())
e.sessionCredential = &sessionCredential{
AccessKeyId: resp.AccessKeyId,
AccessKeySecret: resp.AccessKeySecret,
SecurityToken: resp.SecurityToken,
}
return
}
package utils
import (
"context"
"net"
"time"
)
// Runtime is for setting timeout, proxy and host
type Runtime struct {
ReadTimeout int
ConnectTimeout int
Proxy string
Host string
STSEndpoint string
}
// NewRuntime returns a Runtime
func NewRuntime(readTimeout, connectTimeout int, proxy string, host string) *Runtime {
return &Runtime{
ReadTimeout: readTimeout,
ConnectTimeout: connectTimeout,
Proxy: proxy,
Host: host,
}
}
// Timeout is for connect Timeout
func Timeout(connectTimeout time.Duration) func(cxt context.Context, net, addr string) (c net.Conn, err error) {
return func(ctx context.Context, network, address string) (net.Conn, error) {
return (&net.Dialer{
Timeout: connectTimeout,
DualStack: true,
}).DialContext(ctx, network, address)
}
}
package utils
import (
"crypto"
"crypto/hmac"
"crypto/md5"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"hash"
"io"
rand2 "math/rand"
"net/url"
"time"
)
type uuid [16]byte
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
var hookRead = func(fn func(p []byte) (n int, err error)) func(p []byte) (n int, err error) {
return fn
}
var hookRSA = func(fn func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error)) func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
return fn
}
// GetUUID returns a uuid
func GetUUID() (uuidHex string) {
uuid := newUUID()
uuidHex = hex.EncodeToString(uuid[:])
return
}
// RandStringBytes returns a rand string
func RandStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand2.Intn(len(letterBytes))]
}
return string(b)
}
// ShaHmac1 return a string which has been hashed
func ShaHmac1(source, secret string) string {
key := []byte(secret)
hmac := hmac.New(sha1.New, key)
hmac.Write([]byte(source))
signedBytes := hmac.Sum(nil)
signedString := base64.StdEncoding.EncodeToString(signedBytes)
return signedString
}
// Sha256WithRsa return a string which has been hashed with Rsa
func Sha256WithRsa(source, secret string) string {
decodeString, err := base64.StdEncoding.DecodeString(secret)
if err != nil {
panic(err)
}
private, err := x509.ParsePKCS8PrivateKey(decodeString)
if err != nil {
panic(err)
}
h := crypto.Hash.New(crypto.SHA256)
h.Write([]byte(source))
hashed := h.Sum(nil)
signature, err := hookRSA(rsa.SignPKCS1v15)(rand.Reader, private.(*rsa.PrivateKey),
crypto.SHA256, hashed)
if err != nil {
panic(err)
}
return base64.StdEncoding.EncodeToString(signature)
}
// GetMD5Base64 returns a string which has been base64
func GetMD5Base64(bytes []byte) (base64Value string) {
md5Ctx := md5.New()
md5Ctx.Write(bytes)
md5Value := md5Ctx.Sum(nil)
base64Value = base64.StdEncoding.EncodeToString(md5Value)
return
}
// GetTimeInFormatISO8601 returns a time string
func GetTimeInFormatISO8601() (timeStr string) {
gmt := time.FixedZone("GMT", 0)
return time.Now().In(gmt).Format("2006-01-02T15:04:05Z")
}
// GetURLFormedMap returns a url encoded string
func GetURLFormedMap(source map[string]string) (urlEncoded string) {
urlEncoder := url.Values{}
for key, value := range source {
urlEncoder.Add(key, value)
}
urlEncoded = urlEncoder.Encode()
return
}
func newUUID() uuid {
ns := uuid{}
safeRandom(ns[:])
u := newFromHash(md5.New(), ns, RandStringBytes(16))
u[6] = (u[6] & 0x0f) | (byte(2) << 4)
u[8] = (u[8]&(0xff>>2) | (0x02 << 6))
return u
}
func newFromHash(h hash.Hash, ns uuid, name string) uuid {
u := uuid{}
h.Write(ns[:])
h.Write([]byte(name))
copy(u[:], h.Sum(nil))
return u
}
func safeRandom(dest []byte) {
if _, err := hookRead(rand.Read)(dest); err != nil {
panic(err)
}
}
func (u uuid) String() string {
buf := make([]byte, 36)
hex.Encode(buf[0:8], u[0:4])
buf[8] = '-'
hex.Encode(buf[9:13], u[4:6])
buf[13] = '-'
hex.Encode(buf[14:18], u[6:8])
buf[18] = '-'
hex.Encode(buf[19:23], u[8:10])
buf[23] = '-'
hex.Encode(buf[24:], u[10:])
return string(buf)
}
language: go
go:
- 1.x
\ No newline at end of file
Copyright (c) 2012-2021 Charles Banning <clbanning@gmail.com>. All rights reserved.
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
package mxj
import (
"bytes"
"encoding/xml"
"reflect"
)
const (
DefaultElementTag = "element"
)
// Encode arbitrary value as XML.
//
// Note: unmarshaling the resultant
// XML may not return the original value, since tag labels may have been injected
// to create the XML representation of the value.
/*
Encode an arbitrary JSON object.
package main
import (
"encoding/json"
"fmt"
"github.com/clbanning/mxj"
)
func main() {
jsondata := []byte(`[
{ "somekey":"somevalue" },
"string",
3.14159265,
true
]`)
var i interface{}
err := json.Unmarshal(jsondata, &i)
if err != nil {
// do something
}
x, err := mxj.AnyXmlIndent(i, "", " ", "mydoc")
if err != nil {
// do something else
}
fmt.Println(string(x))
}
output:
<mydoc>
<somekey>somevalue</somekey>
<element>string</element>
<element>3.14159265</element>
<element>true</element>
</mydoc>
An extreme example is available in examples/goofy_map.go.
*/
// Alternative values for DefaultRootTag and DefaultElementTag can be set as:
// AnyXml( v, myRootTag, myElementTag).
func AnyXml(v interface{}, tags ...string) ([]byte, error) {
var rt, et string
if len(tags) == 1 || len(tags) == 2 {
rt = tags[0]
} else {
rt = DefaultRootTag
}
if len(tags) == 2 {
et = tags[1]
} else {
et = DefaultElementTag
}
if v == nil {
if useGoXmlEmptyElemSyntax {
return []byte("<" + rt + "></" + rt + ">"), nil
}
return []byte("<" + rt + "/>"), nil
}
if reflect.TypeOf(v).Kind() == reflect.Struct {
return xml.Marshal(v)
}
var err error
s := new(bytes.Buffer)
p := new(pretty)
var b []byte
switch v.(type) {
case []interface{}:
if _, err = s.WriteString("<" + rt + ">"); err != nil {
return nil, err
}
for _, vv := range v.([]interface{}) {
switch vv.(type) {
case map[string]interface{}:
m := vv.(map[string]interface{})
if len(m) == 1 {
for tag, val := range m {
err = marshalMapToXmlIndent(false, s, tag, val, p)
}
} else {
err = marshalMapToXmlIndent(false, s, et, vv, p)
}
default:
err = marshalMapToXmlIndent(false, s, et, vv, p)
}
if err != nil {
break
}
}
if _, err = s.WriteString("</" + rt + ">"); err != nil {
return nil, err
}
b = s.Bytes()
case map[string]interface{}:
m := Map(v.(map[string]interface{}))
b, err = m.Xml(rt)
default:
err = marshalMapToXmlIndent(false, s, rt, v, p)
b = s.Bytes()
}
return b, err
}
// Encode an arbitrary value as a pretty XML string.
// Alternative values for DefaultRootTag and DefaultElementTag can be set as:
// AnyXmlIndent( v, "", " ", myRootTag, myElementTag).
func AnyXmlIndent(v interface{}, prefix, indent string, tags ...string) ([]byte, error) {
var rt, et string
if len(tags) == 1 || len(tags) == 2 {
rt = tags[0]
} else {
rt = DefaultRootTag
}
if len(tags) == 2 {
et = tags[1]
} else {
et = DefaultElementTag
}
if v == nil {
if useGoXmlEmptyElemSyntax {
return []byte(prefix + "<" + rt + "></" + rt + ">"), nil
}
return []byte(prefix + "<" + rt + "/>"), nil
}
if reflect.TypeOf(v).Kind() == reflect.Struct {
return xml.MarshalIndent(v, prefix, indent)
}
var err error
s := new(bytes.Buffer)
p := new(pretty)
p.indent = indent
p.padding = prefix
var b []byte
switch v.(type) {
case []interface{}:
if _, err = s.WriteString("<" + rt + ">\n"); err != nil {
return nil, err
}
p.Indent()
for _, vv := range v.([]interface{}) {
switch vv.(type) {
case map[string]interface{}:
m := vv.(map[string]interface{})
if len(m) == 1 {
for tag, val := range m {
err = marshalMapToXmlIndent(true, s, tag, val, p)
}
} else {
p.start = 1 // we 1 tag in
err = marshalMapToXmlIndent(true, s, et, vv, p)
// *s += "\n"
if _, err = s.WriteString("\n"); err != nil {
return nil, err
}
}
default:
p.start = 0 // in case trailing p.start = 1
err = marshalMapToXmlIndent(true, s, et, vv, p)
}
if err != nil {
break
}
}
if _, err = s.WriteString(`</` + rt + `>`); err != nil {
return nil, err
}
b = s.Bytes()
case map[string]interface{}:
m := Map(v.(map[string]interface{}))
b, err = m.XmlIndent(prefix, indent, rt)
default:
err = marshalMapToXmlIndent(true, s, rt, v, p)
b = s.Bytes()
}
return b, err
}
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us" updated="2009-10-04T01:35:58+00:00"><title>Code Review - My issues</title><link href="http://codereview.appspot.com/" rel="alternate"></link><link href="http://codereview.appspot.com/rss/mine/rsc" rel="self"></link><id>http://codereview.appspot.com/</id><author><name>rietveld&lt;&gt;</name></author><entry><title>rietveld: an attempt at pubsubhubbub
</title><link href="http://codereview.appspot.com/126085" rel="alternate"></link><updated>2009-10-04T01:35:58+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:134d9179c41f806be79b3a5f7877d19a</id><summary type="html">
An attempt at adding pubsubhubbub support to Rietveld.
http://code.google.com/p/pubsubhubbub
http://code.google.com/p/rietveld/issues/detail?id=155
The server side of the protocol is trivial:
1. add a &amp;lt;link rel=&amp;quot;hub&amp;quot; href=&amp;quot;hub-server&amp;quot;&amp;gt; tag to all
feeds that will be pubsubhubbubbed.
2. every time one of those feeds changes, tell the hub
with a simple POST request.
I have tested this by adding debug prints to a local hub
server and checking that the server got the right publish
requests.
I can&amp;#39;t quite get the server to work, but I think the bug
is not in my code. I think that the server expects to be
able to grab the feed and see the feed&amp;#39;s actual URL in
the link rel=&amp;quot;self&amp;quot;, but the default value for that drops
the :port from the URL, and I cannot for the life of me
figure out how to get the Atom generator deep inside
django not to do that, or even where it is doing that,
or even what code is running to generate the Atom feed.
(I thought I knew but I added some assert False statements
and it kept running!)
Ignoring that particular problem, I would appreciate
feedback on the right way to get the two values at
the top of feeds.py marked NOTE(rsc).
</summary></entry><entry><title>rietveld: correct tab handling
</title><link href="http://codereview.appspot.com/124106" rel="alternate"></link><updated>2009-10-03T23:02:17+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:0a2a4f19bb815101f0ba2904aed7c35a</id><summary type="html">
This fixes the buggy tab rendering that can be seen at
http://codereview.appspot.com/116075/diff/1/2
The fundamental problem was that the tab code was
not being told what column the text began in, so it
didn&amp;#39;t know where to put the tab stops. Another problem
was that some of the code assumed that string byte
offsets were the same as column offsets, which is only
true if there are no tabs.
In the process of fixing this, I cleaned up the arguments
to Fold and ExpandTabs and renamed them Break and
_ExpandTabs so that I could be sure that I found all the
call sites. I also wanted to verify that ExpandTabs was
not being used from outside intra_region_diff.py.
</summary></entry></feed> `
// mxj - A collection of map[string]interface{} and associated XML and JSON utilities.
// Copyright 2012-2019, Charles Banning. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file
/*
Marshal/Unmarshal XML to/from map[string]interface{} values (and JSON); extract/modify values from maps by key or key-path, including wildcards.
mxj supplants the legacy x2j and j2x packages. The subpackage x2j-wrapper is provided to facilitate migrating from the x2j package. The x2j and j2x subpackages provide similar functionality of the old packages but are not function-name compatible with them.
Note: this library was designed for processing ad hoc anonymous messages. Bulk processing large data sets may be much more efficiently performed using the encoding/xml or encoding/json packages from Go's standard library directly.
Related Packages:
checkxml: github.com/clbanning/checkxml provides functions for validating XML data.
Notes:
2020.05.01: v2.2 - optimize map to XML encoding for large XML docs.
2019.07.04: v2.0 - remove unnecessary methods - mv.XmlWriterRaw, mv.XmlIndentWriterRaw - for Map and MapSeq.
2019.07.04: Add MapSeq type and move associated functions and methods from Map to MapSeq.
2019.01.21: DecodeSimpleValuesAsMap - decode to map[<tag>:map["#text":<value>]] rather than map[<tag>:<value>].
2018.04.18: mv.Xml/mv.XmlIndent encodes non-map[string]interface{} map values - map[string]string, map[int]uint, etc.
2018.03.29: mv.Gob/NewMapGob support gob encoding/decoding of Maps.
2018.03.26: Added mxj/x2j-wrapper sub-package for migrating from legacy x2j package.
2017.02.22: LeafNode paths can use ".N" syntax rather than "[N]" for list member indexing.
2017.02.21: github.com/clbanning/checkxml provides functions for validating XML data.
2017.02.10: SetFieldSeparator changes field separator for args in UpdateValuesForPath, ValuesFor... methods.
2017.02.06: Support XMPP stream processing - HandleXMPPStreamTag().
2016.11.07: Preserve name space prefix syntax in XmlSeq parser - NewMapXmlSeq(), etc.
2016.06.25: Support overriding default XML attribute prefix, "-", in Map keys - SetAttrPrefix().
2016.05.26: Support customization of xml.Decoder by exposing CustomDecoder variable.
2016.03.19: Escape invalid chars when encoding XML attribute and element values - XMLEscapeChars().
2016.03.02: By default decoding XML with float64 and bool value casting will not cast "NaN", "Inf", and "-Inf".
To cast them to float64, first set flag with CastNanInf(true).
2016.02.22: New mv.Root(), mv.Elements(), mv.Attributes methods let you examine XML document structure.
2016.02.16: Add CoerceKeysToLower() option to handle tags with mixed capitalization.
2016.02.12: Seek for first xml.StartElement token; only return error if io.EOF is reached first (handles BOM).
2015-12-02: NewMapXmlSeq() with mv.XmlSeq() & co. will try to preserve structure of XML doc when re-encoding.
2014-08-02: AnyXml() and AnyXmlIndent() will try to marshal arbitrary values to XML.
SUMMARY
type Map map[string]interface{}
Create a Map value, 'mv', from any map[string]interface{} value, 'v':
mv := Map(v)
Unmarshal / marshal XML as a Map value, 'mv':
mv, err := NewMapXml(xmlValue) // unmarshal
xmlValue, err := mv.Xml() // marshal
Unmarshal XML from an io.Reader as a Map value, 'mv':
mv, err := NewMapXmlReader(xmlReader) // repeated calls, as with an os.File Reader, will process stream
mv, raw, err := NewMapXmlReaderRaw(xmlReader) // 'raw' is the raw XML that was decoded
Marshal Map value, 'mv', to an XML Writer (io.Writer):
err := mv.XmlWriter(xmlWriter)
raw, err := mv.XmlWriterRaw(xmlWriter) // 'raw' is the raw XML that was written on xmlWriter
Also, for prettified output:
xmlValue, err := mv.XmlIndent(prefix, indent, ...)
err := mv.XmlIndentWriter(xmlWriter, prefix, indent, ...)
raw, err := mv.XmlIndentWriterRaw(xmlWriter, prefix, indent, ...)
Bulk process XML with error handling (note: handlers must return a boolean value):
err := HandleXmlReader(xmlReader, mapHandler(Map), errHandler(error))
err := HandleXmlReaderRaw(xmlReader, mapHandler(Map, []byte), errHandler(error, []byte))
Converting XML to JSON: see Examples for NewMapXml and HandleXmlReader.
There are comparable functions and methods for JSON processing.
Arbitrary structure values can be decoded to / encoded from Map values:
mv, err := NewMapStruct(structVal)
err := mv.Struct(structPointer)
To work with XML tag values, JSON or Map key values or structure field values, decode the XML, JSON
or structure to a Map value, 'mv', or cast a map[string]interface{} value to a Map value, 'mv', then:
paths := mv.PathsForKey(key)
path := mv.PathForKeyShortest(key)
values, err := mv.ValuesForKey(key, subkeys)
values, err := mv.ValuesForPath(path, subkeys) // 'path' can be dot-notation with wildcards and indexed arrays.
count, err := mv.UpdateValuesForPath(newVal, path, subkeys)
Get everything at once, irrespective of path depth:
leafnodes := mv.LeafNodes()
leafvalues := mv.LeafValues()
A new Map with whatever keys are desired can be created from the current Map and then encoded in XML
or JSON. (Note: keys can use dot-notation. 'oldKey' can also use wildcards and indexed arrays.)
newMap, err := mv.NewMap("oldKey_1:newKey_1", "oldKey_2:newKey_2", ..., "oldKey_N:newKey_N")
newMap, err := mv.NewMap("oldKey1", "oldKey3", "oldKey5") // a subset of 'mv'; see "examples/partial.go"
newXml, err := newMap.Xml() // for example
newJson, err := newMap.Json() // ditto
XML PARSING CONVENTIONS
Using NewMapXml()
- Attributes are parsed to `map[string]interface{}` values by prefixing a hyphen, `-`,
to the attribute label. (Unless overridden by `PrependAttrWithHyphen(false)` or
`SetAttrPrefix()`.)
- If the element is a simple element and has attributes, the element value
is given the key `#text` for its `map[string]interface{}` representation. (See
the 'atomFeedString.xml' test data, below.)
- XML comments, directives, and process instructions are ignored.
- If CoerceKeysToLower() has been called, then the resultant keys will be lower case.
Using NewMapXmlSeq()
- Attributes are parsed to `map["#attr"]map[<attr_label>]map[string]interface{}`values
where the `<attr_label>` value has "#text" and "#seq" keys - the "#text" key holds the
value for `<attr_label>`.
- All elements, except for the root, have a "#seq" key.
- Comments, directives, and process instructions are unmarshalled into the Map using the
keys "#comment", "#directive", and "#procinst", respectively. (See documentation for more
specifics.)
- Name space syntax is preserved:
- <ns:key>something</ns.key> parses to map["ns:key"]interface{}{"something"}
- xmlns:ns="http://myns.com/ns" parses to map["xmlns:ns"]interface{}{"http://myns.com/ns"}
Both
- By default, "Nan", "Inf", and "-Inf" values are not cast to float64. If you want them
to be cast, set a flag to cast them using CastNanInf(true).
XML ENCODING CONVENTIONS
- 'nil' Map values, which may represent 'null' JSON values, are encoded as "<tag/>".
NOTE: the operation is not symmetric as "<tag/>" elements are decoded as 'tag:""' Map values,
which, then, encode in JSON as '"tag":""' values..
- ALSO: there is no guarantee that the encoded XML doc will be the same as the decoded one. (Go
randomizes the walk through map[string]interface{} values.) If you plan to re-encode the
Map value to XML and want the same sequencing of elements look at NewMapXmlSeq() and
mv.XmlSeq() - these try to preserve the element sequencing but with added complexity when
working with the Map representation.
*/
package mxj
// Copyright 2016 Charles Banning. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
package mxj
import (
"bytes"
)
var xmlEscapeChars bool
// XMLEscapeChars(true) forces escaping invalid characters in attribute and element values.
// NOTE: this is brute force with NO interrogation of '&' being escaped already; if it is
// then '&amp;' will be re-escaped as '&amp;amp;'.
//
/*
The values are:
" &quot;
' &apos;
< &lt;
> &gt;
& &amp;
*/
//
// Note: if XMLEscapeCharsDecoder(true) has been called - or the default, 'false,' value
// has been toggled to 'true' - then XMLEscapeChars(true) is ignored. If XMLEscapeChars(true)
// has already been called before XMLEscapeCharsDecoder(true), XMLEscapeChars(false) is called
// to turn escape encoding on mv.Xml, etc., to prevent double escaping ampersands, '&'.
func XMLEscapeChars(b ...bool) {
var bb bool
if len(b) == 0 {
bb = !xmlEscapeChars
} else {
bb = b[0]
}
if bb == true && xmlEscapeCharsDecoder == false {
xmlEscapeChars = true
} else {
xmlEscapeChars = false
}
}
// Scan for '&' first, since 's' may contain "&amp;" that is parsed to "&amp;amp;"
// - or "&lt;" that is parsed to "&amp;lt;".
var escapechars = [][2][]byte{
{[]byte(`&`), []byte(`&amp;`)},
{[]byte(`<`), []byte(`&lt;`)},
{[]byte(`>`), []byte(`&gt;`)},
{[]byte(`"`), []byte(`&quot;`)},
{[]byte(`'`), []byte(`&apos;`)},
}
func escapeChars(s string) string {
if len(s) == 0 {
return s
}
b := []byte(s)
for _, v := range escapechars {
n := bytes.Count(b, v[0])
if n == 0 {
continue
}
b = bytes.Replace(b, v[0], v[1], n)
}
return string(b)
}
// per issue #84, escape CharData values from xml.Decoder
var xmlEscapeCharsDecoder bool
// XMLEscapeCharsDecoder(b ...bool) escapes XML characters in xml.CharData values
// returned by Decoder.Token. Thus, the internal Map values will contain escaped
// values, and you do not need to set XMLEscapeChars for proper encoding.
//
// By default, the Map values have the non-escaped values returned by Decoder.Token.
// XMLEscapeCharsDecoder(true) - or, XMLEscapeCharsDecoder() - will toggle escape
// encoding 'on.'
//
// Note: if XMLEscapeCharDecoder(true) is call then XMLEscapeChars(false) is
// called to prevent re-escaping the values on encoding using mv.Xml, etc.
func XMLEscapeCharsDecoder(b ...bool) {
if len(b) == 0 {
xmlEscapeCharsDecoder = !xmlEscapeCharsDecoder
} else {
xmlEscapeCharsDecoder = b[0]
}
if xmlEscapeCharsDecoder == true && xmlEscapeChars == true {
xmlEscapeChars = false
}
}
package mxj
// Checks whether the path exists. If err != nil then 'false' is returned
// along with the error encountered parsing either the "path" or "subkeys"
// argument.
func (mv Map) Exists(path string, subkeys ...string) (bool, error) {
v, err := mv.ValuesForPath(path, subkeys...)
return (err == nil && len(v) > 0), err
}
package mxj
import (
"fmt"
"io"
"os"
)
type Maps []Map
func NewMaps() Maps {
return make(Maps, 0)
}
type MapRaw struct {
M Map
R []byte
}
// NewMapsFromXmlFile - creates an array from a file of JSON values.
func NewMapsFromJsonFile(name string) (Maps, error) {
fi, err := os.Stat(name)
if err != nil {
return nil, err
}
if !fi.Mode().IsRegular() {
return nil, fmt.Errorf("file %s is not a regular file", name)
}
fh, err := os.Open(name)
if err != nil {
return nil, err
}
defer fh.Close()
am := make([]Map, 0)
for {
m, raw, err := NewMapJsonReaderRaw(fh)
if err != nil && err != io.EOF {
return am, fmt.Errorf("error: %s - reading: %s", err.Error(), string(raw))
}
if len(m) > 0 {
am = append(am, m)
}
if err == io.EOF {
break
}
}
return am, nil
}
// ReadMapsFromJsonFileRaw - creates an array of MapRaw from a file of JSON values.
func NewMapsFromJsonFileRaw(name string) ([]MapRaw, error) {
fi, err := os.Stat(name)
if err != nil {
return nil, err
}
if !fi.Mode().IsRegular() {
return nil, fmt.Errorf("file %s is not a regular file", name)
}
fh, err := os.Open(name)
if err != nil {
return nil, err
}
defer fh.Close()
am := make([]MapRaw, 0)
for {
mr := new(MapRaw)
mr.M, mr.R, err = NewMapJsonReaderRaw(fh)
if err != nil && err != io.EOF {
return am, fmt.Errorf("error: %s - reading: %s", err.Error(), string(mr.R))
}
if len(mr.M) > 0 {
am = append(am, *mr)
}
if err == io.EOF {
break
}
}
return am, nil
}
// NewMapsFromXmlFile - creates an array from a file of XML values.
func NewMapsFromXmlFile(name string) (Maps, error) {
fi, err := os.Stat(name)
if err != nil {
return nil, err
}
if !fi.Mode().IsRegular() {
return nil, fmt.Errorf("file %s is not a regular file", name)
}
fh, err := os.Open(name)
if err != nil {
return nil, err
}
defer fh.Close()
am := make([]Map, 0)
for {
m, raw, err := NewMapXmlReaderRaw(fh)
if err != nil && err != io.EOF {
return am, fmt.Errorf("error: %s - reading: %s", err.Error(), string(raw))
}
if len(m) > 0 {
am = append(am, m)
}
if err == io.EOF {
break
}
}
return am, nil
}
// NewMapsFromXmlFileRaw - creates an array of MapRaw from a file of XML values.
// NOTE: the slice with the raw XML is clean with no extra capacity - unlike NewMapXmlReaderRaw().
// It is slow at parsing a file from disk and is intended for relatively small utility files.
func NewMapsFromXmlFileRaw(name string) ([]MapRaw, error) {
fi, err := os.Stat(name)
if err != nil {
return nil, err
}
if !fi.Mode().IsRegular() {
return nil, fmt.Errorf("file %s is not a regular file", name)
}
fh, err := os.Open(name)
if err != nil {
return nil, err
}
defer fh.Close()
am := make([]MapRaw, 0)
for {
mr := new(MapRaw)
mr.M, mr.R, err = NewMapXmlReaderRaw(fh)
if err != nil && err != io.EOF {
return am, fmt.Errorf("error: %s - reading: %s", err.Error(), string(mr.R))
}
if len(mr.M) > 0 {
am = append(am, *mr)
}
if err == io.EOF {
break
}
}
return am, nil
}
// ------------------------ Maps writing -------------------------
// These are handy-dandy methods for dumping configuration data, etc.
// JsonString - analogous to mv.Json()
func (mvs Maps) JsonString(safeEncoding ...bool) (string, error) {
var s string
for _, v := range mvs {
j, err := v.Json()
if err != nil {
return s, err
}
s += string(j)
}
return s, nil
}
// JsonStringIndent - analogous to mv.JsonIndent()
func (mvs Maps) JsonStringIndent(prefix, indent string, safeEncoding ...bool) (string, error) {
var s string
var haveFirst bool
for _, v := range mvs {
j, err := v.JsonIndent(prefix, indent)
if err != nil {
return s, err
}
if haveFirst {
s += "\n"
} else {
haveFirst = true
}
s += string(j)
}
return s, nil
}
// XmlString - analogous to mv.Xml()
func (mvs Maps) XmlString() (string, error) {
var s string
for _, v := range mvs {
x, err := v.Xml()
if err != nil {
return s, err
}
s += string(x)
}
return s, nil
}
// XmlStringIndent - analogous to mv.XmlIndent()
func (mvs Maps) XmlStringIndent(prefix, indent string) (string, error) {
var s string
for _, v := range mvs {
x, err := v.XmlIndent(prefix, indent)
if err != nil {
return s, err
}
s += string(x)
}
return s, nil
}
// JsonFile - write Maps to named file as JSON
// Note: the file will be created, if necessary; if it exists it will be truncated.
// If you need to append to a file, open it and use JsonWriter method.
func (mvs Maps) JsonFile(file string, safeEncoding ...bool) error {
var encoding bool
if len(safeEncoding) == 1 {
encoding = safeEncoding[0]
}
s, err := mvs.JsonString(encoding)
if err != nil {
return err
}
fh, err := os.Create(file)
if err != nil {
return err
}
defer fh.Close()
fh.WriteString(s)
return nil
}
// JsonFileIndent - write Maps to named file as pretty JSON
// Note: the file will be created, if necessary; if it exists it will be truncated.
// If you need to append to a file, open it and use JsonIndentWriter method.
func (mvs Maps) JsonFileIndent(file, prefix, indent string, safeEncoding ...bool) error {
var encoding bool
if len(safeEncoding) == 1 {
encoding = safeEncoding[0]
}
s, err := mvs.JsonStringIndent(prefix, indent, encoding)
if err != nil {
return err
}
fh, err := os.Create(file)
if err != nil {
return err
}
defer fh.Close()
fh.WriteString(s)
return nil
}
// XmlFile - write Maps to named file as XML
// Note: the file will be created, if necessary; if it exists it will be truncated.
// If you need to append to a file, open it and use XmlWriter method.
func (mvs Maps) XmlFile(file string) error {
s, err := mvs.XmlString()
if err != nil {
return err
}
fh, err := os.Create(file)
if err != nil {
return err
}
defer fh.Close()
fh.WriteString(s)
return nil
}
// XmlFileIndent - write Maps to named file as pretty XML
// Note: the file will be created,if necessary; if it exists it will be truncated.
// If you need to append to a file, open it and use XmlIndentWriter method.
func (mvs Maps) XmlFileIndent(file, prefix, indent string) error {
s, err := mvs.XmlStringIndent(prefix, indent)
if err != nil {
return err
}
fh, err := os.Create(file)
if err != nil {
return err
}
defer fh.Close()
fh.WriteString(s)
return nil
}
{ "this":"is", "a":"test", "file":"for", "files_test.go":"case" }
{ "with":"some", "bad":JSON, "in":"it" }
<doc>
<some>test</some>
<data>for files.go</data>
</doc>
<msg>
<just>some</just>
<another>doc</other>
<for>test case</for>
</msg>
{ "this":"is", "a":"test", "file":"for", "files_test.go":"case" }
{ "with":"just", "two":2, "JSON":"values", "true":true }
<doc>
<some>test</some>
<data>for files.go</data>
</doc>
<msg>
<just>some</just>
<another>doc</another>
<for>test case</for>
</msg>
{"a":"test","file":"for","files_test.go":"case","this":"is"}{"JSON":"values","true":true,"two":2,"with":"just"}
\ No newline at end of file
<doc><data>for files.go</data><some>test</some></doc><msg><another>doc</another><for>test case</for><just>some</just></msg>
\ No newline at end of file
{
"a": "test",
"file": "for",
"files_test.go": "case",
"this": "is"
}
{
"JSON": "values",
"true": true,
"two": 2,
"with": "just"
}
\ No newline at end of file
<doc>
<data>for files.go</data>
<some>test</some>
</doc><msg>
<another>doc</another>
<for>test case</for>
<just>some</just>
</msg>
\ No newline at end of file
// gob.go - Encode/Decode a Map into a gob object.
package mxj
import (
"bytes"
"encoding/gob"
)
// NewMapGob returns a Map value for a gob object that has been
// encoded from a map[string]interface{} (or compatible type) value.
// It is intended to provide symmetric handling of Maps that have
// been encoded using mv.Gob.
func NewMapGob(gobj []byte) (Map, error) {
m := make(map[string]interface{}, 0)
if len(gobj) == 0 {
return m, nil
}
r := bytes.NewReader(gobj)
dec := gob.NewDecoder(r)
if err := dec.Decode(&m); err != nil {
return m, err
}
return m, nil
}
// Gob returns a gob-encoded value for the Map 'mv'.
func (mv Map) Gob() ([]byte, error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
if err := enc.Encode(map[string]interface{}(mv)); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// Copyright 2012-2014 Charles Banning. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
package mxj
import (
"bytes"
"encoding/json"
"fmt"
"io"
"time"
)
// ------------------------------ write JSON -----------------------
// Just a wrapper on json.Marshal.
// If option safeEncoding is'true' then safe encoding of '<', '>' and '&'
// is preserved. (see encoding/json#Marshal, encoding/json#Encode)
func (mv Map) Json(safeEncoding ...bool) ([]byte, error) {
var s bool
if len(safeEncoding) == 1 {
s = safeEncoding[0]
}
b, err := json.Marshal(mv)
if !s {
b = bytes.Replace(b, []byte("\\u003c"), []byte("<"), -1)
b = bytes.Replace(b, []byte("\\u003e"), []byte(">"), -1)
b = bytes.Replace(b, []byte("\\u0026"), []byte("&"), -1)
}
return b, err
}
// Just a wrapper on json.MarshalIndent.
// If option safeEncoding is'true' then safe encoding of '<' , '>' and '&'
// is preserved. (see encoding/json#Marshal, encoding/json#Encode)
func (mv Map) JsonIndent(prefix, indent string, safeEncoding ...bool) ([]byte, error) {
var s bool
if len(safeEncoding) == 1 {
s = safeEncoding[0]
}
b, err := json.MarshalIndent(mv, prefix, indent)
if !s {
b = bytes.Replace(b, []byte("\\u003c"), []byte("<"), -1)
b = bytes.Replace(b, []byte("\\u003e"), []byte(">"), -1)
b = bytes.Replace(b, []byte("\\u0026"), []byte("&"), -1)
}
return b, err
}
// The following implementation is provided for symmetry with NewMapJsonReader[Raw]
// The names will also provide a key for the number of return arguments.
// Writes the Map as JSON on the Writer.
// If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.
func (mv Map) JsonWriter(jsonWriter io.Writer, safeEncoding ...bool) error {
b, err := mv.Json(safeEncoding...)
if err != nil {
return err
}
_, err = jsonWriter.Write(b)
return err
}
// Writes the Map as JSON on the Writer. []byte is the raw JSON that was written.
// If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.
func (mv Map) JsonWriterRaw(jsonWriter io.Writer, safeEncoding ...bool) ([]byte, error) {
b, err := mv.Json(safeEncoding...)
if err != nil {
return b, err
}
_, err = jsonWriter.Write(b)
return b, err
}
// Writes the Map as pretty JSON on the Writer.
// If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.
func (mv Map) JsonIndentWriter(jsonWriter io.Writer, prefix, indent string, safeEncoding ...bool) error {
b, err := mv.JsonIndent(prefix, indent, safeEncoding...)
if err != nil {
return err
}
_, err = jsonWriter.Write(b)
return err
}
// Writes the Map as pretty JSON on the Writer. []byte is the raw JSON that was written.
// If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.
func (mv Map) JsonIndentWriterRaw(jsonWriter io.Writer, prefix, indent string, safeEncoding ...bool) ([]byte, error) {
b, err := mv.JsonIndent(prefix, indent, safeEncoding...)
if err != nil {
return b, err
}
_, err = jsonWriter.Write(b)
return b, err
}
// --------------------------- read JSON -----------------------------
// Decode numericvalues as json.Number type Map values - see encoding/json#Number.
// NOTE: this is for decoding JSON into a Map with NewMapJson(), NewMapJsonReader(),
// etc.; it does not affect NewMapXml(), etc. The XML encoders mv.Xml() and mv.XmlIndent()
// do recognize json.Number types; a JSON object can be decoded to a Map with json.Number
// value types and the resulting Map can be correctly encoded into a XML object.
var JsonUseNumber bool
// Just a wrapper on json.Unmarshal
// Converting JSON to XML is a simple as:
// ...
// mapVal, merr := mxj.NewMapJson(jsonVal)
// if merr != nil {
// // handle error
// }
// xmlVal, xerr := mapVal.Xml()
// if xerr != nil {
// // handle error
// }
// NOTE: as a special case, passing a list, e.g., [{"some-null-value":"", "a-non-null-value":"bar"}],
// will be interpreted as having the root key 'object' prepended - {"object":[ ... ]} - to unmarshal to a Map.
// See mxj/j2x/j2x_test.go.
func NewMapJson(jsonVal []byte) (Map, error) {
// empty or nil begets empty
if len(jsonVal) == 0 {
m := make(map[string]interface{}, 0)
return m, nil
}
// handle a goofy case ...
if jsonVal[0] == '[' {
jsonVal = []byte(`{"object":` + string(jsonVal) + `}`)
}
m := make(map[string]interface{})
// err := json.Unmarshal(jsonVal, &m)
buf := bytes.NewReader(jsonVal)
dec := json.NewDecoder(buf)
if JsonUseNumber {
dec.UseNumber()
}
err := dec.Decode(&m)
return m, err
}
// Retrieve a Map value from an io.Reader.
// NOTE: The raw JSON off the reader is buffered to []byte using a ByteReader. If the io.Reader is an
// os.File, there may be significant performance impact. If the io.Reader is wrapping a []byte
// value in-memory, however, such as http.Request.Body you CAN use it to efficiently unmarshal
// a JSON object.
func NewMapJsonReader(jsonReader io.Reader) (Map, error) {
jb, err := getJson(jsonReader)
if err != nil || len(*jb) == 0 {
return nil, err
}
// Unmarshal the 'presumed' JSON string
return NewMapJson(*jb)
}
// Retrieve a Map value and raw JSON - []byte - from an io.Reader.
// NOTE: The raw JSON off the reader is buffered to []byte using a ByteReader. If the io.Reader is an
// os.File, there may be significant performance impact. If the io.Reader is wrapping a []byte
// value in-memory, however, such as http.Request.Body you CAN use it to efficiently unmarshal
// a JSON object and retrieve the raw JSON in a single call.
func NewMapJsonReaderRaw(jsonReader io.Reader) (Map, []byte, error) {
jb, err := getJson(jsonReader)
if err != nil || len(*jb) == 0 {
return nil, *jb, err
}
// Unmarshal the 'presumed' JSON string
m, merr := NewMapJson(*jb)
return m, *jb, merr
}
// Pull the next JSON string off the stream: just read from first '{' to its closing '}'.
// Returning a pointer to the slice saves 16 bytes - maybe unnecessary, but internal to package.
func getJson(rdr io.Reader) (*[]byte, error) {
bval := make([]byte, 1)
jb := make([]byte, 0)
var inQuote, inJson bool
var parenCnt int
var previous byte
// scan the input for a matched set of {...}
// json.Unmarshal will handle syntax checking.
for {
_, err := rdr.Read(bval)
if err != nil {
if err == io.EOF && inJson && parenCnt > 0 {
return &jb, fmt.Errorf("no closing } for JSON string: %s", string(jb))
}
return &jb, err
}
switch bval[0] {
case '{':
if !inQuote {
parenCnt++
inJson = true
}
case '}':
if !inQuote {
parenCnt--
}
if parenCnt < 0 {
return nil, fmt.Errorf("closing } without opening {: %s", string(jb))
}
case '"':
if inQuote {
if previous == '\\' {
break
}
inQuote = false
} else {
inQuote = true
}
case '\n', '\r', '\t', ' ':
if !inQuote {
continue
}
}
if inJson {
jb = append(jb, bval[0])
if parenCnt == 0 {
break
}
}
previous = bval[0]
}
return &jb, nil
}
// ------------------------------- JSON Reader handler via Map values -----------------------
// Default poll delay to keep Handler from spinning on an open stream
// like sitting on os.Stdin waiting for imput.
var jhandlerPollInterval = time.Duration(1e6)
// While unnecessary, we make HandleJsonReader() have the same signature as HandleXmlReader().
// This avoids treating one or other as a special case and discussing the underlying stdlib logic.
// Bulk process JSON using handlers that process a Map value.
// 'rdr' is an io.Reader for the JSON (stream).
// 'mapHandler' is the Map processing handler. Return of 'false' stops io.Reader processing.
// 'errHandler' is the error processor. Return of 'false' stops io.Reader processing and returns the error.
// Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
// This means that you can stop reading the file on error or after processing a particular message.
// To have reading and handling run concurrently, pass argument to a go routine in handler and return 'true'.
func HandleJsonReader(jsonReader io.Reader, mapHandler func(Map) bool, errHandler func(error) bool) error {
var n int
for {
m, merr := NewMapJsonReader(jsonReader)
n++
// handle error condition with errhandler
if merr != nil && merr != io.EOF {
merr = fmt.Errorf("[jsonReader: %d] %s", n, merr.Error())
if ok := errHandler(merr); !ok {
// caused reader termination
return merr
}
continue
}
// pass to maphandler
if len(m) != 0 {
if ok := mapHandler(m); !ok {
break
}
} else if merr != io.EOF {
<-time.After(jhandlerPollInterval)
}
if merr == io.EOF {
break
}
}
return nil
}
// Bulk process JSON using handlers that process a Map value and the raw JSON.
// 'rdr' is an io.Reader for the JSON (stream).
// 'mapHandler' is the Map and raw JSON - []byte - processor. Return of 'false' stops io.Reader processing.
// 'errHandler' is the error and raw JSON processor. Return of 'false' stops io.Reader processing and returns the error.
// Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
// This means that you can stop reading the file on error or after processing a particular message.
// To have reading and handling run concurrently, pass argument(s) to a go routine in handler and return 'true'.
func HandleJsonReaderRaw(jsonReader io.Reader, mapHandler func(Map, []byte) bool, errHandler func(error, []byte) bool) error {
var n int
for {
m, raw, merr := NewMapJsonReaderRaw(jsonReader)
n++
// handle error condition with errhandler
if merr != nil && merr != io.EOF {
merr = fmt.Errorf("[jsonReader: %d] %s", n, merr.Error())
if ok := errHandler(merr, raw); !ok {
// caused reader termination
return merr
}
continue
}
// pass to maphandler
if len(m) != 0 {
if ok := mapHandler(m, raw); !ok {
break
}
} else if merr != io.EOF {
<-time.After(jhandlerPollInterval)
}
if merr == io.EOF {
break
}
}
return nil
}
// Copyright 2012-2014 Charles Banning. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
// keyvalues.go: Extract values from an arbitrary XML doc. Tag path can include wildcard characters.
package mxj
import (
"errors"
"fmt"
"strconv"
"strings"
)
// ----------------------------- get everything FOR a single key -------------------------
const (
minArraySize = 32
)
var defaultArraySize int = minArraySize
// SetArraySize adjust the buffers for expected number of values to return from ValuesForKey() and ValuesForPath().
// This can have the effect of significantly reducing memory allocation-copy functions for large data sets.
// Returns the initial buffer size.
func SetArraySize(size int) int {
if size > minArraySize {
defaultArraySize = size
} else {
defaultArraySize = minArraySize
}
return defaultArraySize
}
// ValuesForKey return all values in Map, 'mv', associated with a 'key'. If len(returned_values) == 0, then no match.
// On error, the returned slice is 'nil'. NOTE: 'key' can be wildcard, "*".
// 'subkeys' (optional) are "key:val[:type]" strings representing attributes or elements in a list.
// - By default 'val' is of type string. "key:val:bool" and "key:val:float" to coerce them.
// - For attributes prefix the label with the attribute prefix character, by default a
// hyphen, '-', e.g., "-seq:3". (See SetAttrPrefix function.)
// - If the 'key' refers to a list, then "key:value" could select a list member of the list.
// - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
// - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
// exclusion critera - e.g., "!author:William T. Gaddis".
// - If val contains ":" symbol, use SetFieldSeparator to a unused symbol, perhaps "|".
func (mv Map) ValuesForKey(key string, subkeys ...string) ([]interface{}, error) {
m := map[string]interface{}(mv)
var subKeyMap map[string]interface{}
if len(subkeys) > 0 {
var err error
subKeyMap, err = getSubKeyMap(subkeys...)
if err != nil {
return nil, err
}
}
ret := make([]interface{}, 0, defaultArraySize)
var cnt int
hasKey(m, key, &ret, &cnt, subKeyMap)
return ret[:cnt], nil
}
var KeyNotExistError = errors.New("Key does not exist")
// ValueForKey is a wrapper on ValuesForKey. It returns the first member of []interface{}, if any.
// If there is no value, "nil, nil" is returned.
func (mv Map) ValueForKey(key string, subkeys ...string) (interface{}, error) {
vals, err := mv.ValuesForKey(key, subkeys...)
if err != nil {
return nil, err
}
if len(vals) == 0 {
return nil, KeyNotExistError
}
return vals[0], nil
}
// hasKey - if the map 'key' exists append it to array
// if it doesn't do nothing except scan array and map values
func hasKey(iv interface{}, key string, ret *[]interface{}, cnt *int, subkeys map[string]interface{}) {
// func hasKey(iv interface{}, key string, ret *[]interface{}, subkeys map[string]interface{}) {
switch iv.(type) {
case map[string]interface{}:
vv := iv.(map[string]interface{})
// see if the current value is of interest
if v, ok := vv[key]; ok {
switch v.(type) {
case map[string]interface{}:
if hasSubKeys(v, subkeys) {
*ret = append(*ret, v)
*cnt++
}
case []interface{}:
for _, av := range v.([]interface{}) {
if hasSubKeys(av, subkeys) {
*ret = append(*ret, av)
*cnt++
}
}
default:
if len(subkeys) == 0 {
*ret = append(*ret, v)
*cnt++
}
}
}
// wildcard case
if key == "*" {
for _, v := range vv {
switch v.(type) {
case map[string]interface{}:
if hasSubKeys(v, subkeys) {
*ret = append(*ret, v)
*cnt++
}
case []interface{}:
for _, av := range v.([]interface{}) {
if hasSubKeys(av, subkeys) {
*ret = append(*ret, av)
*cnt++
}
}
default:
if len(subkeys) == 0 {
*ret = append(*ret, v)
*cnt++
}
}
}
}
// scan the rest
for _, v := range vv {
hasKey(v, key, ret, cnt, subkeys)
}
case []interface{}:
for _, v := range iv.([]interface{}) {
hasKey(v, key, ret, cnt, subkeys)
}
}
}
// ----------------------- get everything for a node in the Map ---------------------------
// Allow indexed arrays in "path" specification. (Request from Abhijit Kadam - abhijitk100@gmail.com.)
// 2014.04.28 - implementation note.
// Implemented as a wrapper of (old)ValuesForPath() because we need look-ahead logic to handle expansion
// of wildcards and unindexed arrays. Embedding such logic into valuesForKeyPath() would have made the
// code much more complicated; this wrapper is straightforward, easy to debug, and doesn't add significant overhead.
// ValuesForPatb retrieves all values for a path from the Map. If len(returned_values) == 0, then no match.
// On error, the returned array is 'nil'.
// 'path' is a dot-separated path of key values.
// - If a node in the path is '*', then everything beyond is walked.
// - 'path' can contain indexed array references, such as, "*.data[1]" and "msgs[2].data[0].field" -
// even "*[2].*[0].field".
// 'subkeys' (optional) are "key:val[:type]" strings representing attributes or elements in a list.
// - By default 'val' is of type string. "key:val:bool" and "key:val:float" to coerce them.
// - For attributes prefix the label with the attribute prefix character, by default a
// hyphen, '-', e.g., "-seq:3". (See SetAttrPrefix function.)
// - If the 'path' refers to a list, then "tag:value" would return member of the list.
// - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
// - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
// exclusion critera - e.g., "!author:William T. Gaddis".
// - If val contains ":" symbol, use SetFieldSeparator to a unused symbol, perhaps "|".
func (mv Map) ValuesForPath(path string, subkeys ...string) ([]interface{}, error) {
// If there are no array indexes in path, use legacy ValuesForPath() logic.
if strings.Index(path, "[") < 0 {
return mv.oldValuesForPath(path, subkeys...)
}
var subKeyMap map[string]interface{}
if len(subkeys) > 0 {
var err error
subKeyMap, err = getSubKeyMap(subkeys...)
if err != nil {
return nil, err
}
}
keys, kerr := parsePath(path)
if kerr != nil {
return nil, kerr
}
vals, verr := valuesForArray(keys, mv)
if verr != nil {
return nil, verr // Vals may be nil, but return empty array.
}
// Need to handle subkeys ... only return members of vals that satisfy conditions.
retvals := make([]interface{}, 0)
for _, v := range vals {
if hasSubKeys(v, subKeyMap) {
retvals = append(retvals, v)
}
}
return retvals, nil
}
func valuesForArray(keys []*key, m Map) ([]interface{}, error) {
var tmppath string
var haveFirst bool
var vals []interface{}
var verr error
lastkey := len(keys) - 1
for i := 0; i <= lastkey; i++ {
if !haveFirst {
tmppath = keys[i].name
haveFirst = true
} else {
tmppath += "." + keys[i].name
}
// Look-ahead: explode wildcards and unindexed arrays.
// Need to handle un-indexed list recursively:
// e.g., path is "stuff.data[0]" rather than "stuff[0].data[0]".
// Need to treat it as "stuff[0].data[0]", "stuff[1].data[0]", ...
if !keys[i].isArray && i < lastkey && keys[i+1].isArray {
// Can't pass subkeys because we may not be at literal end of path.
vv, vverr := m.oldValuesForPath(tmppath)
if vverr != nil {
return nil, vverr
}
for _, v := range vv {
// See if we can walk the value.
am, ok := v.(map[string]interface{})
if !ok {
continue
}
// Work the backend.
nvals, nvalserr := valuesForArray(keys[i+1:], Map(am))
if nvalserr != nil {
return nil, nvalserr
}
vals = append(vals, nvals...)
}
break // have recursed the whole path - return
}
if keys[i].isArray || i == lastkey {
// Don't pass subkeys because may not be at literal end of path.
vals, verr = m.oldValuesForPath(tmppath)
} else {
continue
}
if verr != nil {
return nil, verr
}
if i == lastkey && !keys[i].isArray {
break
}
// Now we're looking at an array - supposedly.
// Is index in range of vals?
if len(vals) <= keys[i].position {
vals = nil
break
}
// Return the array member of interest, if at end of path.
if i == lastkey {
vals = vals[keys[i].position:(keys[i].position + 1)]
break
}
// Extract the array member of interest.
am := vals[keys[i].position:(keys[i].position + 1)]
// must be a map[string]interface{} value so we can keep walking the path
amm, ok := am[0].(map[string]interface{})
if !ok {
vals = nil
break
}
m = Map(amm)
haveFirst = false
}
return vals, nil
}
type key struct {
name string
isArray bool
position int
}
func parsePath(s string) ([]*key, error) {
keys := strings.Split(s, ".")
ret := make([]*key, 0)
for i := 0; i < len(keys); i++ {
if keys[i] == "" {
continue
}
newkey := new(key)
if strings.Index(keys[i], "[") < 0 {
newkey.name = keys[i]
ret = append(ret, newkey)
continue
}
p := strings.Split(keys[i], "[")
newkey.name = p[0]
p = strings.Split(p[1], "]")
if p[0] == "" { // no right bracket
return nil, fmt.Errorf("no right bracket on key index: %s", keys[i])
}
// convert p[0] to a int value
pos, nerr := strconv.ParseInt(p[0], 10, 32)
if nerr != nil {
return nil, fmt.Errorf("cannot convert index to int value: %s", p[0])
}
newkey.position = int(pos)
newkey.isArray = true
ret = append(ret, newkey)
}
return ret, nil
}
// legacy ValuesForPath() - now wrapped to handle special case of indexed arrays in 'path'.
func (mv Map) oldValuesForPath(path string, subkeys ...string) ([]interface{}, error) {
m := map[string]interface{}(mv)
var subKeyMap map[string]interface{}
if len(subkeys) > 0 {
var err error
subKeyMap, err = getSubKeyMap(subkeys...)
if err != nil {
return nil, err
}
}
keys := strings.Split(path, ".")
if keys[len(keys)-1] == "" {
keys = keys[:len(keys)-1]
}
ivals := make([]interface{}, 0, defaultArraySize)
var cnt int
valuesForKeyPath(&ivals, &cnt, m, keys, subKeyMap)
return ivals[:cnt], nil
}
func valuesForKeyPath(ret *[]interface{}, cnt *int, m interface{}, keys []string, subkeys map[string]interface{}) {
lenKeys := len(keys)
// load 'm' values into 'ret'
// expand any lists
if lenKeys == 0 {
switch m.(type) {
case map[string]interface{}:
if subkeys != nil {
if ok := hasSubKeys(m, subkeys); !ok {
return
}
}
*ret = append(*ret, m)
*cnt++
case []interface{}:
for i, v := range m.([]interface{}) {
if subkeys != nil {
if ok := hasSubKeys(v, subkeys); !ok {
continue // only load list members with subkeys
}
}
*ret = append(*ret, (m.([]interface{}))[i])
*cnt++
}
default:
if subkeys != nil {
return // must be map[string]interface{} if there are subkeys
}
*ret = append(*ret, m)
*cnt++
}
return
}
// key of interest
key := keys[0]
switch key {
case "*": // wildcard - scan all values
switch m.(type) {
case map[string]interface{}:
for _, v := range m.(map[string]interface{}) {
// valuesForKeyPath(ret, v, keys[1:], subkeys)
valuesForKeyPath(ret, cnt, v, keys[1:], subkeys)
}
case []interface{}:
for _, v := range m.([]interface{}) {
switch v.(type) {
// flatten out a list of maps - keys are processed
case map[string]interface{}:
for _, vv := range v.(map[string]interface{}) {
// valuesForKeyPath(ret, vv, keys[1:], subkeys)
valuesForKeyPath(ret, cnt, vv, keys[1:], subkeys)
}
default:
// valuesForKeyPath(ret, v, keys[1:], subkeys)
valuesForKeyPath(ret, cnt, v, keys[1:], subkeys)
}
}
}
default: // key - must be map[string]interface{}
switch m.(type) {
case map[string]interface{}:
if v, ok := m.(map[string]interface{})[key]; ok {
// valuesForKeyPath(ret, v, keys[1:], subkeys)
valuesForKeyPath(ret, cnt, v, keys[1:], subkeys)
}
case []interface{}: // may be buried in list
for _, v := range m.([]interface{}) {
switch v.(type) {
case map[string]interface{}:
if vv, ok := v.(map[string]interface{})[key]; ok {
// valuesForKeyPath(ret, vv, keys[1:], subkeys)
valuesForKeyPath(ret, cnt, vv, keys[1:], subkeys)
}
}
}
}
}
}
// hasSubKeys() - interface{} equality works for string, float64, bool
// 'v' must be a map[string]interface{} value to have subkeys
// 'a' can have k:v pairs with v.(string) == "*", which is treated like a wildcard.
func hasSubKeys(v interface{}, subkeys map[string]interface{}) bool {
if len(subkeys) == 0 {
return true
}
switch v.(type) {
case map[string]interface{}:
// do all subKey name:value pairs match?
mv := v.(map[string]interface{})
for skey, sval := range subkeys {
isNotKey := false
if skey[:1] == "!" { // a NOT-key
skey = skey[1:]
isNotKey = true
}
vv, ok := mv[skey]
if !ok { // key doesn't exist
if isNotKey { // key not there, but that's what we want
if kv, ok := sval.(string); ok && kv == "*" {
continue
}
}
return false
}
// wildcard check
if kv, ok := sval.(string); ok && kv == "*" {
if isNotKey { // key is there, and we don't want it
return false
}
continue
}
switch sval.(type) {
case string:
if s, ok := vv.(string); ok && s == sval.(string) {
if isNotKey {
return false
}
continue
}
case bool:
if b, ok := vv.(bool); ok && b == sval.(bool) {
if isNotKey {
return false
}
continue
}
case float64:
if f, ok := vv.(float64); ok && f == sval.(float64) {
if isNotKey {
return false
}
continue
}
}
// key there but didn't match subkey value
if isNotKey { // that's what we want
continue
}
return false
}
// all subkeys matched
return true
}
// not a map[string]interface{} value, can't have subkeys
return false
}
// Generate map of key:value entries as map[string]string.
// 'kv' arguments are "name:value" pairs: attribute keys are designated with prepended hyphen, '-'.
// If len(kv) == 0, the return is (nil, nil).
func getSubKeyMap(kv ...string) (map[string]interface{}, error) {
if len(kv) == 0 {
return nil, nil
}
m := make(map[string]interface{}, 0)
for _, v := range kv {
vv := strings.Split(v, fieldSep)
switch len(vv) {
case 2:
m[vv[0]] = interface{}(vv[1])
case 3:
switch vv[2] {
case "string", "char", "text":
m[vv[0]] = interface{}(vv[1])
case "bool", "boolean":
// ParseBool treats "1"==true & "0"==false
b, err := strconv.ParseBool(vv[1])
if err != nil {
return nil, fmt.Errorf("can't convert subkey value to bool: %s", vv[1])
}
m[vv[0]] = interface{}(b)
case "float", "float64", "num", "number", "numeric":
f, err := strconv.ParseFloat(vv[1], 64)
if err != nil {
return nil, fmt.Errorf("can't convert subkey value to float: %s", vv[1])
}
m[vv[0]] = interface{}(f)
default:
return nil, fmt.Errorf("unknown subkey conversion spec: %s", v)
}
default:
return nil, fmt.Errorf("unknown subkey spec: %s", v)
}
}
return m, nil
}
// ------------------------------- END of valuesFor ... ----------------------------
// ----------------------- locate where a key value is in the tree -------------------
//----------------------------- find all paths to a key --------------------------------
// PathsForKey returns all paths through Map, 'mv', (in dot-notation) that terminate with the specified key.
// Results can be used with ValuesForPath.
func (mv Map) PathsForKey(key string) []string {
m := map[string]interface{}(mv)
breadbasket := make(map[string]bool, 0)
breadcrumbs := ""
hasKeyPath(breadcrumbs, m, key, breadbasket)
if len(breadbasket) == 0 {
return nil
}
// unpack map keys to return
res := make([]string, len(breadbasket))
var i int
for k := range breadbasket {
res[i] = k
i++
}
return res
}
// PathForKeyShortest extracts the shortest path from all possible paths - from PathsForKey() - in Map, 'mv'..
// Paths are strings using dot-notation.
func (mv Map) PathForKeyShortest(key string) string {
paths := mv.PathsForKey(key)
lp := len(paths)
if lp == 0 {
return ""
}
if lp == 1 {
return paths[0]
}
shortest := paths[0]
shortestLen := len(strings.Split(shortest, "."))
for i := 1; i < len(paths); i++ {
vlen := len(strings.Split(paths[i], "."))
if vlen < shortestLen {
shortest = paths[i]
shortestLen = vlen
}
}
return shortest
}
// hasKeyPath - if the map 'key' exists append it to KeyPath.path and increment KeyPath.depth
// This is really just a breadcrumber that saves all trails that hit the prescribed 'key'.
func hasKeyPath(crumbs string, iv interface{}, key string, basket map[string]bool) {
switch iv.(type) {
case map[string]interface{}:
vv := iv.(map[string]interface{})
if _, ok := vv[key]; ok {
// create a new breadcrumb, intialized with the one we have
var nbc string
if crumbs == "" {
nbc = key
} else {
nbc = crumbs + "." + key
}
basket[nbc] = true
}
// walk on down the path, key could occur again at deeper node
for k, v := range vv {
// create a new breadcrumb, intialized with the one we have
var nbc string
if crumbs == "" {
nbc = k
} else {
nbc = crumbs + "." + k
}
hasKeyPath(nbc, v, key, basket)
}
case []interface{}:
// crumb-trail doesn't change, pass it on
for _, v := range iv.([]interface{}) {
hasKeyPath(crumbs, v, key, basket)
}
}
}
var PathNotExistError = errors.New("Path does not exist")
// ValueForPath wraps ValuesFor Path and returns the first value returned.
// If no value is found it returns 'nil' and PathNotExistError.
func (mv Map) ValueForPath(path string) (interface{}, error) {
vals, err := mv.ValuesForPath(path)
if err != nil {
return nil, err
}
if len(vals) == 0 {
return nil, PathNotExistError
}
return vals[0], nil
}
// ValuesForPathString returns the first found value for the path as a string.
func (mv Map) ValueForPathString(path string) (string, error) {
vals, err := mv.ValuesForPath(path)
if err != nil {
return "", err
}
if len(vals) == 0 {
return "", errors.New("ValueForPath: path not found")
}
val := vals[0]
return fmt.Sprintf("%v", val), nil
}
// ValueOrEmptyForPathString returns the first found value for the path as a string.
// If the path is not found then it returns an empty string.
func (mv Map) ValueOrEmptyForPathString(path string) string {
str, _ := mv.ValueForPathString(path)
return str
}
package mxj
// leafnode.go - return leaf nodes with paths and values for the Map
// inspired by: https://groups.google.com/forum/#!topic/golang-nuts/3JhuVKRuBbw
import (
"strconv"
"strings"
)
const (
NoAttributes = true // suppress LeafNode values that are attributes
)
// LeafNode - a terminal path value in a Map.
// For XML Map values it represents an attribute or simple element value - of type
// string unless Map was created using Cast flag. For JSON Map values it represents
// a string, numeric, boolean, or null value.
type LeafNode struct {
Path string // a dot-notation representation of the path with array subscripting
Value interface{} // the value at the path termination
}
// LeafNodes - returns an array of all LeafNode values for the Map.
// The option no_attr argument suppresses attribute values (keys with prepended hyphen, '-')
// as well as the "#text" key for the associated simple element value.
//
// PrependAttrWithHypen(false) will result in attributes having .attr-name as
// terminal node in 'path' while the path for the element value, itself, will be
// the base path w/o "#text".
//
// LeafUseDotNotation(true) causes list members to be identified using ".N" syntax
// rather than "[N]" syntax.
func (mv Map) LeafNodes(no_attr ...bool) []LeafNode {
var a bool
if len(no_attr) == 1 {
a = no_attr[0]
}
l := make([]LeafNode, 0)
getLeafNodes("", "", map[string]interface{}(mv), &l, a)
return l
}
func getLeafNodes(path, node string, mv interface{}, l *[]LeafNode, noattr bool) {
// if stripping attributes, then also strip "#text" key
if !noattr || node != "#text" {
if path != "" && node[:1] != "[" {
path += "."
}
path += node
}
switch mv.(type) {
case map[string]interface{}:
for k, v := range mv.(map[string]interface{}) {
// if noattr && k[:1] == "-" {
if noattr && len(attrPrefix) > 0 && strings.Index(k, attrPrefix) == 0 {
continue
}
getLeafNodes(path, k, v, l, noattr)
}
case []interface{}:
for i, v := range mv.([]interface{}) {
if useDotNotation {
getLeafNodes(path, strconv.Itoa(i), v, l, noattr)
} else {
getLeafNodes(path, "["+strconv.Itoa(i)+"]", v, l, noattr)
}
}
default:
// can't walk any further, so create leaf
n := LeafNode{path, mv}
*l = append(*l, n)
}
}
// LeafPaths - all paths that terminate in LeafNode values.
func (mv Map) LeafPaths(no_attr ...bool) []string {
ln := mv.LeafNodes()
ss := make([]string, len(ln))
for i := 0; i < len(ln); i++ {
ss[i] = ln[i].Path
}
return ss
}
// LeafValues - all terminal values in the Map.
func (mv Map) LeafValues(no_attr ...bool) []interface{} {
ln := mv.LeafNodes()
vv := make([]interface{}, len(ln))
for i := 0; i < len(ln); i++ {
vv[i] = ln[i].Value
}
return vv
}
// ====================== utilities ======================
// https://groups.google.com/forum/#!topic/golang-nuts/pj0C5IrZk4I
var useDotNotation bool
// LeafUseDotNotation sets a flag that list members in LeafNode paths
// should be identified using ".N" syntax rather than the default "[N]"
// syntax. Calling LeafUseDotNotation with no arguments toggles the
// flag on/off; otherwise, the argument sets the flag value 'true'/'false'.
func LeafUseDotNotation(b ...bool) {
if len(b) == 0 {
useDotNotation = !useDotNotation
return
}
useDotNotation = b[0]
}
// Copyright 2016 Charles Banning. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
// misc.go - mimic functions (+others) called out in:
// https://groups.google.com/forum/#!topic/golang-nuts/jm_aGsJNbdQ
// Primarily these methods let you retrive XML structure information.
package mxj
import (
"fmt"
"sort"
"strings"
)
// Return the root element of the Map. If there is not a single key in Map,
// then an error is returned.
func (mv Map) Root() (string, error) {
mm := map[string]interface{}(mv)
if len(mm) != 1 {
return "", fmt.Errorf("Map does not have singleton root. Len: %d.", len(mm))
}
for k, _ := range mm {
return k, nil
}
return "", nil
}
// If the path is an element with sub-elements, return a list of the sub-element
// keys. (The list is alphabeticly sorted.) NOTE: Map keys that are prefixed with
// '-', a hyphen, are considered attributes; see m.Attributes(path).
func (mv Map) Elements(path string) ([]string, error) {
e, err := mv.ValueForPath(path)
if err != nil {
return nil, err
}
switch e.(type) {
case map[string]interface{}:
ee := e.(map[string]interface{})
elems := make([]string, len(ee))
var i int
for k, _ := range ee {
if len(attrPrefix) > 0 && strings.Index(k, attrPrefix) == 0 {
continue // skip attributes
}
elems[i] = k
i++
}
elems = elems[:i]
// alphabetic sort keeps things tidy
sort.Strings(elems)
return elems, nil
}
return nil, fmt.Errorf("no elements for path: %s", path)
}
// If the path is an element with attributes, return a list of the attribute
// keys. (The list is alphabeticly sorted.) NOTE: Map keys that are not prefixed with
// '-', a hyphen, are not treated as attributes; see m.Elements(path). Also, if the
// attribute prefix is "" - SetAttrPrefix("") or PrependAttrWithHyphen(false) - then
// there are no identifiable attributes.
func (mv Map) Attributes(path string) ([]string, error) {
a, err := mv.ValueForPath(path)
if err != nil {
return nil, err
}
switch a.(type) {
case map[string]interface{}:
aa := a.(map[string]interface{})
attrs := make([]string, len(aa))
var i int
for k, _ := range aa {
if len(attrPrefix) == 0 || strings.Index(k, attrPrefix) != 0 {
continue // skip non-attributes
}
attrs[i] = k[len(attrPrefix):]
i++
}
attrs = attrs[:i]
// alphabetic sort keeps things tidy
sort.Strings(attrs)
return attrs, nil
}
return nil, fmt.Errorf("no attributes for path: %s", path)
}
// mxj - A collection of map[string]interface{} and associated XML and JSON utilities.
// Copyright 2012-2014 Charles Banning. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
package mxj
import (
"fmt"
"sort"
)
const (
Cast = true // for clarity - e.g., mxj.NewMapXml(doc, mxj.Cast)
SafeEncoding = true // ditto - e.g., mv.Json(mxj.SafeEncoding)
)
type Map map[string]interface{}
// Allocate a Map.
func New() Map {
m := make(map[string]interface{}, 0)
return m
}
// Cast a Map to map[string]interface{}
func (mv Map) Old() map[string]interface{} {
return mv
}
// Return a copy of mv as a newly allocated Map. If the Map only contains string,
// numeric, map[string]interface{}, and []interface{} values, then it can be thought
// of as a "deep copy." Copying a structure (or structure reference) value is subject
// to the noted restrictions.
// NOTE: If 'mv' includes structure values with, possibly, JSON encoding tags
// then only public fields of the structure are in the new Map - and with
// keys that conform to any encoding tag instructions. The structure itself will
// be represented as a map[string]interface{} value.
func (mv Map) Copy() (Map, error) {
// this is the poor-man's deep copy
// not efficient, but it works
j, jerr := mv.Json()
// must handle, we don't know how mv got built
if jerr != nil {
return nil, jerr
}
return NewMapJson(j)
}
// --------------- StringIndent ... from x2j.WriteMap -------------
// Pretty print a Map.
func (mv Map) StringIndent(offset ...int) string {
return writeMap(map[string]interface{}(mv), true, true, offset...)
}
// Pretty print a Map without the value type information - just key:value entries.
func (mv Map) StringIndentNoTypeInfo(offset ...int) string {
return writeMap(map[string]interface{}(mv), false, true, offset...)
}
// writeMap - dumps the map[string]interface{} for examination.
// 'typeInfo' causes value type to be printed.
// 'offset' is initial indentation count; typically: Write(m).
func writeMap(m interface{}, typeInfo, root bool, offset ...int) string {
var indent int
if len(offset) == 1 {
indent = offset[0]
}
var s string
switch m.(type) {
case []interface{}:
if typeInfo {
s += "[[]interface{}]"
}
for _, v := range m.([]interface{}) {
s += "\n"
for i := 0; i < indent; i++ {
s += " "
}
s += writeMap(v, typeInfo, false, indent+1)
}
case map[string]interface{}:
list := make([][2]string, len(m.(map[string]interface{})))
var n int
for k, v := range m.(map[string]interface{}) {
list[n][0] = k
list[n][1] = writeMap(v, typeInfo, false, indent+1)
n++
}
sort.Sort(mapList(list))
for _, v := range list {
if root {
root = false
} else {
s += "\n"
}
for i := 0; i < indent; i++ {
s += " "
}
s += v[0] + " : " + v[1]
}
default:
if typeInfo {
s += fmt.Sprintf("[%T] %+v", m, m)
} else {
s += fmt.Sprintf("%+v", m)
}
}
return s
}
// ======================== utility ===============
type mapList [][2]string
func (ml mapList) Len() int {
return len(ml)
}
func (ml mapList) Swap(i, j int) {
ml[i], ml[j] = ml[j], ml[i]
}
func (ml mapList) Less(i, j int) bool {
return ml[i][0] <= ml[j][0]
}
// mxj - A collection of map[string]interface{} and associated XML and JSON utilities.
// Copyright 2012-2014, 2018 Charles Banning. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
// remap.go - build a new Map from the current Map based on keyOld:keyNew mapppings
// keys can use dot-notation, keyOld can use wildcard, '*'
//
// Computational strategy -
// Using the key path - []string - traverse a new map[string]interface{} and
// insert the oldVal as the newVal when we arrive at the end of the path.
// If the type at the end is nil, then that is newVal
// If the type at the end is a singleton (string, float64, bool) an array is created.
// If the type at the end is an array, newVal is just appended.
// If the type at the end is a map, it is inserted if possible or the map value
// is converted into an array if necessary.
package mxj
import (
"errors"
"strings"
)
// (Map)NewMap - create a new Map from data in the current Map.
// 'keypairs' are key mappings "oldKey:newKey" and specify that the current value of 'oldKey'
// should be the value for 'newKey' in the returned Map.
// - 'oldKey' supports dot-notation as described for (Map)ValuesForPath()
// - 'newKey' supports dot-notation but with no wildcards, '*', or indexed arrays
// - "oldKey" is shorthand for the keypair value "oldKey:oldKey"
// - "oldKey:" and ":newKey" are invalid keypair values
// - if 'oldKey' does not exist in the current Map, it is not written to the new Map.
// "null" is not supported unless it is the current Map.
// - see newmap_test.go for several syntax examples
// - mv.NewMap() == mxj.New()
//
// NOTE: "examples/partial.go" shows how to create arbitrary sub-docs of an XML doc.
func (mv Map) NewMap(keypairs ...string) (Map, error) {
n := make(map[string]interface{}, 0)
if len(keypairs) == 0 {
return n, nil
}
// loop through the pairs
var oldKey, newKey string
var path []string
for _, v := range keypairs {
if len(v) == 0 {
continue // just skip over empty keypair arguments
}
// initialize oldKey, newKey and check
vv := strings.Split(v, ":")
if len(vv) > 2 {
return n, errors.New("oldKey:newKey keypair value not valid - " + v)
}
if len(vv) == 1 {
oldKey, newKey = vv[0], vv[0]
} else {
oldKey, newKey = vv[0], vv[1]
}
strings.TrimSpace(oldKey)
strings.TrimSpace(newKey)
if i := strings.Index(newKey, "*"); i > -1 {
return n, errors.New("newKey value cannot contain wildcard character - " + v)
}
if i := strings.Index(newKey, "["); i > -1 {
return n, errors.New("newKey value cannot contain indexed arrays - " + v)
}
if oldKey == "" || newKey == "" {
return n, errors.New("oldKey or newKey is not specified - " + v)
}
// get oldKey value
oldVal, err := mv.ValuesForPath(oldKey)
if err != nil {
return n, err
}
if len(oldVal) == 0 {
continue // oldKey has no value, may not exist in mv
}
// break down path
path = strings.Split(newKey, ".")
if path[len(path)-1] == "" { // ignore a trailing dot in newKey spec
path = path[:len(path)-1]
}
addNewVal(&n, path, oldVal)
}
return n, nil
}
// navigate 'n' to end of path and add val
func addNewVal(n *map[string]interface{}, path []string, val []interface{}) {
// newVal - either singleton or array
var newVal interface{}
if len(val) == 1 {
newVal = val[0] // is type interface{}
} else {
newVal = interface{}(val)
}
// walk to the position of interest, create it if necessary
m := (*n) // initialize map walker
var k string // key for m
lp := len(path) - 1 // when to stop looking
for i := 0; i < len(path); i++ {
k = path[i]
if i == lp {
break
}
var nm map[string]interface{} // holds position of next-map
switch m[k].(type) {
case nil: // need a map for next node in path, so go there
nm = make(map[string]interface{}, 0)
m[k] = interface{}(nm)
m = m[k].(map[string]interface{})
case map[string]interface{}:
// OK - got somewhere to walk to, go there
m = m[k].(map[string]interface{})
case []interface{}:
// add a map and nm points to new map unless there's already
// a map in the array, then nm points there
// The placement of the next value in the array is dependent
// on the sequence of members - could land on a map or a nil
// value first. TODO: how to test this.
a := make([]interface{}, 0)
var foundmap bool
for _, vv := range m[k].([]interface{}) {
switch vv.(type) {
case nil: // doesn't appear that this occurs, need a test case
if foundmap { // use the first one in array
a = append(a, vv)
continue
}
nm = make(map[string]interface{}, 0)
a = append(a, interface{}(nm))
foundmap = true
case map[string]interface{}:
if foundmap { // use the first one in array
a = append(a, vv)
continue
}
nm = vv.(map[string]interface{})
a = append(a, vv)
foundmap = true
default:
a = append(a, vv)
}
}
// no map found in array
if !foundmap {
nm = make(map[string]interface{}, 0)
a = append(a, interface{}(nm))
}
m[k] = interface{}(a) // must insert in map
m = nm
default: // it's a string, float, bool, etc.
aa := make([]interface{}, 0)
nm = make(map[string]interface{}, 0)
aa = append(aa, m[k], nm)
m[k] = interface{}(aa)
m = nm
}
}
// value is nil, array or a singleton of some kind
// initially m.(type) == map[string]interface{}
v := m[k]
switch v.(type) {
case nil: // initialized
m[k] = newVal
case []interface{}:
a := m[k].([]interface{})
a = append(a, newVal)
m[k] = interface{}(a)
default: // v exists:string, float64, bool, map[string]interface, etc.
a := make([]interface{}, 0)
a = append(a, v, newVal)
m[k] = interface{}(a)
}
}
<h2>mxj - to/from maps, XML and JSON</h2>
Decode/encode XML to/from map[string]interface{} (or JSON) values, and extract/modify values from maps by key or key-path, including wildcards.
mxj supplants the legacy x2j and j2x packages. If you want the old syntax, use mxj/x2j and mxj/j2x packages.
<h4>Installation</h4>
Using go.mod:
<pre>
go get github.com/clbanning/mxj/v2@v2.3.2
</pre>
<pre>
import "github.com/clbanning/mxj/v2"
</pre>
... or just vendor the package.
<h4>Related Packages</h4>
https://github.com/clbanning/checkxml provides functions for validating XML data.
<h4>Refactor Encoder - 2020.05.01</h4>
Issue #70 highlighted that encoding large maps does not scale well, since the original logic used string appends operations. Using bytes.Buffer results in linear scaling for very large XML docs. (Metrics based on MacBook Pro i7 w/ 16 GB.)
Nodes m.XML() time
54809 12.53708ms
109780 32.403183ms
164678 59.826412ms
482598 109.358007ms
<h4>Refactor Decoder - 2015.11.15</h4>
For over a year I've wanted to refactor the XML-to-map[string]interface{} decoder to make it more performant. I recently took the time to do that, since we were using github.com/clbanning/mxj in a production system that could be deployed on a Raspberry Pi. Now the decoder is comparable to the stdlib JSON-to-map[string]interface{} decoder in terms of its additional processing overhead relative to decoding to a structure value. As shown by:
BenchmarkNewMapXml-4 100000 18043 ns/op
BenchmarkNewStructXml-4 100000 14892 ns/op
BenchmarkNewMapJson-4 300000 4633 ns/op
BenchmarkNewStructJson-4 300000 3427 ns/op
BenchmarkNewMapXmlBooks-4 20000 82850 ns/op
BenchmarkNewStructXmlBooks-4 20000 67822 ns/op
BenchmarkNewMapJsonBooks-4 100000 17222 ns/op
BenchmarkNewStructJsonBooks-4 100000 15309 ns/op
<h4>Notices</h4>
2021.02.02: v2.5 - add XmlCheckIsValid toggle to force checking that the encoded XML is valid
2020.12.14: v2.4 - add XMLEscapeCharsDecoder to preserve XML escaped characters in Map values
2020.10.28: v2.3 - add TrimWhiteSpace option
2020.05.01: v2.2 - optimize map to XML encoding for large XML docs.
2019.07.04: v2.0 - remove unnecessary methods - mv.XmlWriterRaw, mv.XmlIndentWriterRaw - for Map and MapSeq.
2019.07.04: Add MapSeq type and move associated functions and methods from Map to MapSeq.
2019.01.21: DecodeSimpleValuesAsMap - decode to map[<tag>:map["#text":<value>]] rather than map[<tag>:<value>]
2018.04.18: mv.Xml/mv.XmlIndent encodes non-map[string]interface{} map values - map[string]string, map[int]uint, etc.
2018.03.29: mv.Gob/NewMapGob support gob encoding/decoding of Maps.
2018.03.26: Added mxj/x2j-wrapper sub-package for migrating from legacy x2j package.
2017.02.22: LeafNode paths can use ".N" syntax rather than "[N]" for list member indexing.
2017.02.10: SetFieldSeparator changes field separator for args in UpdateValuesForPath, ValuesFor... methods.
2017.02.06: Support XMPP stream processing - HandleXMPPStreamTag().
2016.11.07: Preserve name space prefix syntax in XmlSeq parser - NewMapXmlSeq(), etc.
2016.06.25: Support overriding default XML attribute prefix, "-", in Map keys - SetAttrPrefix().
2016.05.26: Support customization of xml.Decoder by exposing CustomDecoder variable.
2016.03.19: Escape invalid chars when encoding XML attribute and element values - XMLEscapeChars().
2016.03.02: By default decoding XML with float64 and bool value casting will not cast "NaN", "Inf", and "-Inf".
To cast them to float64, first set flag with CastNanInf(true).
2016.02.22: New mv.Root(), mv.Elements(), mv.Attributes methods let you examine XML document structure.
2016.02.16: Add CoerceKeysToLower() option to handle tags with mixed capitalization.
2016.02.12: Seek for first xml.StartElement token; only return error if io.EOF is reached first (handles BOM).
2015.12.02: XML decoding/encoding that preserves original structure of document. See NewMapXmlSeq()
and mv.XmlSeq() / mv.XmlSeqIndent().
2015-05-20: New: mv.StringIndentNoTypeInfo().
Also, alphabetically sort map[string]interface{} values by key to prettify output for mv.Xml(),
mv.XmlIndent(), mv.StringIndent(), mv.StringIndentNoTypeInfo().
2014-11-09: IncludeTagSeqNum() adds "_seq" key with XML doc positional information.
(NOTE: PreserveXmlList() is similar and will be here soon.)
2014-09-18: inspired by NYTimes fork, added PrependAttrWithHyphen() to allow stripping hyphen from attribute tag.
2014-08-02: AnyXml() and AnyXmlIndent() will try to marshal arbitrary values to XML.
2014-04-28: ValuesForPath() and NewMap() now accept path with indexed array references.
<h4>Basic Unmarshal XML to map[string]interface{}</h4>
<pre>type Map map[string]interface{}</pre>
Create a `Map` value, 'mv', from any `map[string]interface{}` value, 'v':
<pre>mv := Map(v)</pre>
Unmarshal / marshal XML as a `Map` value, 'mv':
<pre>mv, err := NewMapXml(xmlValue) // unmarshal
xmlValue, err := mv.Xml() // marshal</pre>
Unmarshal XML from an `io.Reader` as a `Map` value, 'mv':
<pre>mv, err := NewMapXmlReader(xmlReader) // repeated calls, as with an os.File Reader, will process stream
mv, raw, err := NewMapXmlReaderRaw(xmlReader) // 'raw' is the raw XML that was decoded</pre>
Marshal `Map` value, 'mv', to an XML Writer (`io.Writer`):
<pre>err := mv.XmlWriter(xmlWriter)
raw, err := mv.XmlWriterRaw(xmlWriter) // 'raw' is the raw XML that was written on xmlWriter</pre>
Also, for prettified output:
<pre>xmlValue, err := mv.XmlIndent(prefix, indent, ...)
err := mv.XmlIndentWriter(xmlWriter, prefix, indent, ...)
raw, err := mv.XmlIndentWriterRaw(xmlWriter, prefix, indent, ...)</pre>
Bulk process XML with error handling (note: handlers must return a boolean value):
<pre>err := HandleXmlReader(xmlReader, mapHandler(Map), errHandler(error))
err := HandleXmlReaderRaw(xmlReader, mapHandler(Map, []byte), errHandler(error, []byte))</pre>
Converting XML to JSON: see Examples for `NewMapXml` and `HandleXmlReader`.
There are comparable functions and methods for JSON processing.
Arbitrary structure values can be decoded to / encoded from `Map` values:
<pre>mv, err := NewMapStruct(structVal)
err := mv.Struct(structPointer)</pre>
<h4>Extract / modify Map values</h4>
To work with XML tag values, JSON or Map key values or structure field values, decode the XML, JSON
or structure to a `Map` value, 'mv', or cast a `map[string]interface{}` value to a `Map` value, 'mv', then:
<pre>paths := mv.PathsForKey(key)
path := mv.PathForKeyShortest(key)
values, err := mv.ValuesForKey(key, subkeys)
values, err := mv.ValuesForPath(path, subkeys)
count, err := mv.UpdateValuesForPath(newVal, path, subkeys)</pre>
Get everything at once, irrespective of path depth:
<pre>leafnodes := mv.LeafNodes()
leafvalues := mv.LeafValues()</pre>
A new `Map` with whatever keys are desired can be created from the current `Map` and then encoded in XML
or JSON. (Note: keys can use dot-notation.)
<pre>newMap, err := mv.NewMap("oldKey_1:newKey_1", "oldKey_2:newKey_2", ..., "oldKey_N:newKey_N")
newMap, err := mv.NewMap("oldKey1", "oldKey3", "oldKey5") // a subset of 'mv'; see "examples/partial.go"
newXml, err := newMap.Xml() // for example
newJson, err := newMap.Json() // ditto</pre>
<h4>Usage</h4>
The package is fairly well [self-documented with examples](http://godoc.org/github.com/clbanning/mxj).
Also, the subdirectory "examples" contains a wide range of examples, several taken from golang-nuts discussions.
<h4>XML parsing conventions</h4>
Using NewMapXml()
- Attributes are parsed to `map[string]interface{}` values by prefixing a hyphen, `-`,
to the attribute label. (Unless overridden by `PrependAttrWithHyphen(false)` or
`SetAttrPrefix()`.)
- If the element is a simple element and has attributes, the element value
is given the key `#text` for its `map[string]interface{}` representation. (See
the 'atomFeedString.xml' test data, below.)
- XML comments, directives, and process instructions are ignored.
- If CoerceKeysToLower() has been called, then the resultant keys will be lower case.
Using NewMapXmlSeq()
- Attributes are parsed to `map["#attr"]map[<attr_label>]map[string]interface{}`values
where the `<attr_label>` value has "#text" and "#seq" keys - the "#text" key holds the
value for `<attr_label>`.
- All elements, except for the root, have a "#seq" key.
- Comments, directives, and process instructions are unmarshalled into the Map using the
keys "#comment", "#directive", and "#procinst", respectively. (See documentation for more
specifics.)
- Name space syntax is preserved:
- `<ns:key>something</ns.key>` parses to `map["ns:key"]interface{}{"something"}`
- `xmlns:ns="http://myns.com/ns"` parses to `map["xmlns:ns"]interface{}{"http://myns.com/ns"}`
Both
- By default, "Nan", "Inf", and "-Inf" values are not cast to float64. If you want them
to be cast, set a flag to cast them using CastNanInf(true).
<h4>XML encoding conventions</h4>
- 'nil' `Map` values, which may represent 'null' JSON values, are encoded as `<tag/>`.
NOTE: the operation is not symmetric as `<tag/>` elements are decoded as `tag:""` `Map` values,
which, then, encode in JSON as `"tag":""` values.
- ALSO: there is no guarantee that the encoded XML doc will be the same as the decoded one. (Go
randomizes the walk through map[string]interface{} values.) If you plan to re-encode the
Map value to XML and want the same sequencing of elements look at NewMapXmlSeq() and
mv.XmlSeq() - these try to preserve the element sequencing but with added complexity when
working with the Map representation.
<h4>Running "go test"</h4>
Because there are no guarantees on the sequence map elements are retrieved, the tests have been
written for visual verification in most cases. One advantage is that you can easily use the
output from running "go test" as examples of calling the various functions and methods.
<h4>Motivation</h4>
I make extensive use of JSON for messaging and typically unmarshal the messages into
`map[string]interface{}` values. This is easily done using `json.Unmarshal` from the
standard Go libraries. Unfortunately, many legacy solutions use structured
XML messages; in those environments the applications would have to be refactored to
interoperate with my components.
The better solution is to just provide an alternative HTTP handler that receives
XML messages and parses it into a `map[string]interface{}` value and then reuse
all the JSON-based code. The Go `xml.Unmarshal()` function does not provide the same
option of unmarshaling XML messages into `map[string]interface{}` values. So I wrote
a couple of small functions to fill this gap and released them as the x2j package.
Over the next year and a half additional features were added, and the companion j2x
package was released to address XML encoding of arbitrary JSON and `map[string]interface{}`
values. As part of a refactoring of our production system and looking at how we had been
using the x2j and j2x packages we found that we rarely performed direct XML-to-JSON or
JSON-to_XML conversion and that working with the XML or JSON as `map[string]interface{}`
values was the primary value. Thus, everything was refactored into the mxj package.
package mxj
import "strings"
// Removes the path.
func (mv Map) Remove(path string) error {
m := map[string]interface{}(mv)
return remove(m, path)
}
func remove(m interface{}, path string) error {
val, err := prevValueByPath(m, path)
if err != nil {
return err
}
lastKey := lastKey(path)
delete(val, lastKey)
return nil
}
// returns the last key of the path.
// lastKey("a.b.c") would had returned "c"
func lastKey(path string) string {
keys := strings.Split(path, ".")
key := keys[len(keys)-1]
return key
}
// returns the path without the last key
// parentPath("a.b.c") whould had returned "a.b"
func parentPath(path string) string {
keys := strings.Split(path, ".")
parentPath := strings.Join(keys[0:len(keys)-1], ".")
return parentPath
}
package mxj
import (
"errors"
"strings"
)
// RenameKey renames a key in a Map.
// It works only for nested maps.
// It doesn't work for cases when the key is in a list.
func (mv Map) RenameKey(path string, newName string) error {
var v bool
var err error
if v, err = mv.Exists(path); err == nil && !v {
return errors.New("RenameKey: path not found: " + path)
} else if err != nil {
return err
}
if v, err = mv.Exists(parentPath(path) + "." + newName); err == nil && v {
return errors.New("RenameKey: key already exists: " + newName)
} else if err != nil {
return err
}
m := map[string]interface{}(mv)
return renameKey(m, path, newName)
}
func renameKey(m interface{}, path string, newName string) error {
val, err := prevValueByPath(m, path)
if err != nil {
return err
}
oldName := lastKey(path)
val[newName] = val[oldName]
delete(val, oldName)
return nil
}
// returns a value which contains a last key in the path
// For example: prevValueByPath("a.b.c", {a{b{c: 3}}}) returns {c: 3}
func prevValueByPath(m interface{}, path string) (map[string]interface{}, error) {
keys := strings.Split(path, ".")
switch mValue := m.(type) {
case map[string]interface{}:
for key, value := range mValue {
if key == keys[0] {
if len(keys) == 1 {
return mValue, nil
} else {
// keep looking for the full path to the key
return prevValueByPath(value, strings.Join(keys[1:], "."))
}
}
}
}
return nil, errors.New("prevValueByPath: didn't find path – " + path)
}
package mxj
import (
"strings"
)
// Sets the value for the path
func (mv Map) SetValueForPath(value interface{}, path string) error {
pathAry := strings.Split(path, ".")
parentPathAry := pathAry[0 : len(pathAry)-1]
parentPath := strings.Join(parentPathAry, ".")
val, err := mv.ValueForPath(parentPath)
if err != nil {
return err
}
if val == nil {
return nil // we just ignore the request if there's no val
}
key := pathAry[len(pathAry)-1]
cVal := val.(map[string]interface{})
cVal[key] = value
return nil
}
package mxj
// Per: https://github.com/clbanning/mxj/issues/37#issuecomment-278651862
var fieldSep string = ":"
// SetFieldSeparator changes the default field separator, ":", for the
// newVal argument in mv.UpdateValuesForPath and the optional 'subkey' arguments
// in mv.ValuesForKey and mv.ValuesForPath.
//
// E.g., if the newVal value is "http://blah/blah", setting the field separator
// to "|" will allow the newVal specification, "<key>|http://blah/blah" to parse
// properly. If called with no argument or an empty string value, the field
// separator is set to the default, ":".
func SetFieldSeparator(s ...string) {
if len(s) == 0 || s[0] == "" {
fieldSep = ":" // the default
return
}
fieldSep = s[0]
}
<msg mtype="alert" mpriority="1">
<text>help me!</text>
<song title="A Long Time" author="Mayer Hawthorne">
<verses>
<verse name="verse 1" no="1">
<line no="1">Henry was a renegade</line>
<line no="2">Didn't like to play it safe</line>
<line no="3">One component at a time</line>
<line no="4">There's got to be a better way</line>
<line no="5">Oh, people came from miles around</line>
<line no="6">Searching for a steady job</line>
<line no="7">Welcome to the Motor Town</line>
<line no="8">Booming like an atom bomb</line>
</verse>
<verse name="verse 2" no="2">
<line no="1">Oh, Henry was the end of the story</line>
<line no="2">Then everything went wrong</line>
<line no="3">And we'll return it to its former glory</line>
<line no="4">But it just takes so long</line>
</verse>
</verses>
<chorus>
<line no="1">It's going to take a long time</line>
<line no="2">It's going to take it, but we'll make it one day</line>
<line no="3">It's going to take a long time</line>
<line no="4">It's going to take it, but we'll make it one day</line>
</chorus>
</song>
</msg>
// Copyright 2016 Charles Banning. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
// strict.go actually addresses setting xml.Decoder attribute
// values. This'll let you parse non-standard XML.
package mxj
import (
"encoding/xml"
)
// CustomDecoder can be used to specify xml.Decoder attribute
// values, e.g., Strict:false, to be used. By default CustomDecoder
// is nil. If CustomeDecoder != nil, then mxj.XmlCharsetReader variable is
// ignored and must be set as part of the CustomDecoder value, if needed.
// Usage:
// mxj.CustomDecoder = &xml.Decoder{Strict:false}
var CustomDecoder *xml.Decoder
// useCustomDecoder copy over public attributes from customDecoder
func useCustomDecoder(d *xml.Decoder) {
d.Strict = CustomDecoder.Strict
d.AutoClose = CustomDecoder.AutoClose
d.Entity = CustomDecoder.Entity
d.CharsetReader = CustomDecoder.CharsetReader
d.DefaultSpace = CustomDecoder.DefaultSpace
}
// Copyright 2012-2017 Charles Banning. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
package mxj
import (
"encoding/json"
"errors"
"reflect"
// "github.com/fatih/structs"
)
// Create a new Map value from a structure. Error returned if argument is not a structure.
// Only public structure fields are decoded in the Map value. See github.com/fatih/structs#Map
// for handling of "structs" tags.
// DEPRECATED - import github.com/fatih/structs and cast result of structs.Map to mxj.Map.
// import "github.com/fatih/structs"
// ...
// sm, err := structs.Map(<some struct>)
// if err != nil {
// // handle error
// }
// m := mxj.Map(sm)
// Alernatively uncomment the old source and import in struct.go.
func NewMapStruct(structVal interface{}) (Map, error) {
return nil, errors.New("deprecated - see package documentation")
/*
if !structs.IsStruct(structVal) {
return nil, errors.New("NewMapStruct() error: argument is not type Struct")
}
return structs.Map(structVal), nil
*/
}
// Marshal a map[string]interface{} into a structure referenced by 'structPtr'. Error returned
// if argument is not a pointer or if json.Unmarshal returns an error.
// json.Unmarshal structure encoding rules are followed to encode public structure fields.
func (mv Map) Struct(structPtr interface{}) error {
// should check that we're getting a pointer.
if reflect.ValueOf(structPtr).Kind() != reflect.Ptr {
return errors.New("mv.Struct() error: argument is not type Ptr")
}
m := map[string]interface{}(mv)
j, err := json.Marshal(m)
if err != nil {
return err
}
return json.Unmarshal(j, structPtr)
}
// Copyright 2012-2014, 2017 Charles Banning. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
// updatevalues.go - modify a value based on path and possibly sub-keys
// TODO(clb): handle simple elements with attributes and NewMapXmlSeq Map values.
package mxj
import (
"fmt"
"strconv"
"strings"
)
// Update value based on path and possible sub-key values.
// A count of the number of values changed and any error are returned.
// If the count == 0, then no path (and subkeys) matched.
// 'newVal' can be a Map or map[string]interface{} value with a single 'key' that is the key to be modified
// or a string value "key:value[:type]" where type is "bool" or "num" to cast the value.
// 'path' is dot-notation list of keys to traverse; last key in path can be newVal key
// NOTE: 'path' spec does not currently support indexed array references.
// 'subkeys' are "key:value[:type]" entries that must match for path node
// - For attributes prefix the label with the attribute prefix character, by default a
// hyphen, '-', e.g., "-seq:3". (See SetAttrPrefix function.)
// - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
// - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
// exclusion critera - e.g., "!author:William T. Gaddis".
//
// NOTES:
// 1. Simple elements with attributes need a path terminated as ".#text" to modify the actual value.
// 2. Values in Maps created using NewMapXmlSeq are map[string]interface{} values with a "#text" key.
// 3. If values in 'newVal' or 'subkeys' args contain ":", use SetFieldSeparator to an unused symbol,
// perhaps "|".
func (mv Map) UpdateValuesForPath(newVal interface{}, path string, subkeys ...string) (int, error) {
m := map[string]interface{}(mv)
// extract the subkeys
var subKeyMap map[string]interface{}
if len(subkeys) > 0 {
var err error
subKeyMap, err = getSubKeyMap(subkeys...)
if err != nil {
return 0, err
}
}
// extract key and value from newVal
var key string
var val interface{}
switch newVal.(type) {
case map[string]interface{}, Map:
switch newVal.(type) { // "fallthrough is not permitted in type switch" (Spec)
case Map:
newVal = newVal.(Map).Old()
}
if len(newVal.(map[string]interface{})) != 1 {
return 0, fmt.Errorf("newVal map can only have len == 1 - %+v", newVal)
}
for key, val = range newVal.(map[string]interface{}) {
}
case string: // split it as a key:value pair
ss := strings.Split(newVal.(string), fieldSep)
n := len(ss)
if n < 2 || n > 3 {
return 0, fmt.Errorf("unknown newVal spec - %+v", newVal)
}
key = ss[0]
if n == 2 {
val = interface{}(ss[1])
} else if n == 3 {
switch ss[2] {
case "bool", "boolean":
nv, err := strconv.ParseBool(ss[1])
if err != nil {
return 0, fmt.Errorf("can't convert newVal to bool - %+v", newVal)
}
val = interface{}(nv)
case "num", "numeric", "float", "int":
nv, err := strconv.ParseFloat(ss[1], 64)
if err != nil {
return 0, fmt.Errorf("can't convert newVal to float64 - %+v", newVal)
}
val = interface{}(nv)
default:
return 0, fmt.Errorf("unknown type for newVal value - %+v", newVal)
}
}
default:
return 0, fmt.Errorf("invalid newVal type - %+v", newVal)
}
// parse path
keys := strings.Split(path, ".")
var count int
updateValuesForKeyPath(key, val, m, keys, subKeyMap, &count)
return count, nil
}
// navigate the path
func updateValuesForKeyPath(key string, value interface{}, m interface{}, keys []string, subkeys map[string]interface{}, cnt *int) {
// ----- at end node: looking at possible node to get 'key' ----
if len(keys) == 1 {
updateValue(key, value, m, keys[0], subkeys, cnt)
return
}
// ----- here we are navigating the path thru the penultimate node --------
// key of interest is keys[0] - the next in the path
switch keys[0] {
case "*": // wildcard - scan all values
switch m.(type) {
case map[string]interface{}:
for _, v := range m.(map[string]interface{}) {
updateValuesForKeyPath(key, value, v, keys[1:], subkeys, cnt)
}
case []interface{}:
for _, v := range m.([]interface{}) {
switch v.(type) {
// flatten out a list of maps - keys are processed
case map[string]interface{}:
for _, vv := range v.(map[string]interface{}) {
updateValuesForKeyPath(key, value, vv, keys[1:], subkeys, cnt)
}
default:
updateValuesForKeyPath(key, value, v, keys[1:], subkeys, cnt)
}
}
}
default: // key - must be map[string]interface{}
switch m.(type) {
case map[string]interface{}:
if v, ok := m.(map[string]interface{})[keys[0]]; ok {
updateValuesForKeyPath(key, value, v, keys[1:], subkeys, cnt)
}
case []interface{}: // may be buried in list
for _, v := range m.([]interface{}) {
switch v.(type) {
case map[string]interface{}:
if vv, ok := v.(map[string]interface{})[keys[0]]; ok {
updateValuesForKeyPath(key, value, vv, keys[1:], subkeys, cnt)
}
}
}
}
}
}
// change value if key and subkeys are present
func updateValue(key string, value interface{}, m interface{}, keys0 string, subkeys map[string]interface{}, cnt *int) {
// there are two possible options for the value of 'keys0': map[string]interface, []interface{}
// and 'key' is a key in the map or is a key in a map in a list.
switch m.(type) {
case map[string]interface{}: // gotta have the last key
if keys0 == "*" {
for k := range m.(map[string]interface{}) {
updateValue(key, value, m, k, subkeys, cnt)
}
return
}
endVal, _ := m.(map[string]interface{})[keys0]
// if newV key is the end of path, replace the value for path-end
// may be []interface{} - means replace just an entry w/ subkeys
// otherwise replace the keys0 value if subkeys are there
// NOTE: this will replace the subkeys, also
if key == keys0 {
switch endVal.(type) {
case map[string]interface{}:
if hasSubKeys(m, subkeys) {
(m.(map[string]interface{}))[keys0] = value
(*cnt)++
}
case []interface{}:
// without subkeys can't select list member to modify
// so key:value spec is it ...
if hasSubKeys(m, subkeys) {
(m.(map[string]interface{}))[keys0] = value
(*cnt)++
break
}
nv := make([]interface{}, 0)
var valmodified bool
for _, v := range endVal.([]interface{}) {
// check entry subkeys
if hasSubKeys(v, subkeys) {
// replace v with value
nv = append(nv, value)
valmodified = true
(*cnt)++
continue
}
nv = append(nv, v)
}
if valmodified {
(m.(map[string]interface{}))[keys0] = interface{}(nv)
}
default: // anything else is a strict replacement
if hasSubKeys(m, subkeys) {
(m.(map[string]interface{}))[keys0] = value
(*cnt)++
}
}
return
}
// so value is for an element of endVal
// if endVal is a map then 'key' must be there w/ subkeys
// if endVal is a list then 'key' must be in a list member w/ subkeys
switch endVal.(type) {
case map[string]interface{}:
if !hasSubKeys(endVal, subkeys) {
return
}
if _, ok := (endVal.(map[string]interface{}))[key]; ok {
(endVal.(map[string]interface{}))[key] = value
(*cnt)++
}
case []interface{}: // keys0 points to a list, check subkeys
for _, v := range endVal.([]interface{}) {
// got to be a map so we can replace value for 'key'
vv, vok := v.(map[string]interface{})
if !vok {
continue
}
if _, ok := vv[key]; !ok {
continue
}
if !hasSubKeys(vv, subkeys) {
continue
}
vv[key] = value
(*cnt)++
}
}
case []interface{}: // key may be in a list member
// don't need to handle keys0 == "*"; we're looking at everything, anyway.
for _, v := range m.([]interface{}) {
// only map values - we're looking for 'key'
mm, ok := v.(map[string]interface{})
if !ok {
continue
}
if _, ok := mm[key]; !ok {
continue
}
if !hasSubKeys(mm, subkeys) {
continue
}
mm[key] = value
(*cnt)++
}
}
// return
}
// Copyright 2012-2016, 2018-2019 Charles Banning. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
// xml.go - basically the core of X2j for map[string]interface{} values.
// NewMapXml, NewMapXmlReader, mv.Xml, mv.XmlWriter
// see x2j and j2x for wrappers to provide end-to-end transformation of XML and JSON messages.
package mxj
import (
"bytes"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"io"
"reflect"
"sort"
"strconv"
"strings"
"time"
)
// ------------------- NewMapXml & NewMapXmlReader ... -------------------------
// If XmlCharsetReader != nil, it will be used to decode the XML, if required.
// Note: if CustomDecoder != nil, then XmlCharsetReader is ignored;
// set the CustomDecoder attribute instead.
// import (
// charset "code.google.com/p/go-charset/charset"
// github.com/clbanning/mxj
// )
// ...
// mxj.XmlCharsetReader = charset.NewReader
// m, merr := mxj.NewMapXml(xmlValue)
var XmlCharsetReader func(charset string, input io.Reader) (io.Reader, error)
// NewMapXml - convert a XML doc into a Map
// (This is analogous to unmarshalling a JSON string to map[string]interface{} using json.Unmarshal().)
// If the optional argument 'cast' is 'true', then values will be converted to boolean or float64 if possible.
//
// Converting XML to JSON is a simple as:
// ...
// mapVal, merr := mxj.NewMapXml(xmlVal)
// if merr != nil {
// // handle error
// }
// jsonVal, jerr := mapVal.Json()
// if jerr != nil {
// // handle error
// }
//
// NOTES:
// 1. Declarations, directives, process instructions and comments are NOT parsed.
// 2. The 'xmlVal' will be parsed looking for an xml.StartElement, so BOM and other
// extraneous xml.CharData will be ignored unless io.EOF is reached first.
// 3. If CoerceKeysToLower() has been called, then all key values will be lower case.
// 4. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
// 5. If DisableTrimWhiteSpace(b bool) has been called, then all values will be trimmed or not. 'true' by default.
func NewMapXml(xmlVal []byte, cast ...bool) (Map, error) {
var r bool
if len(cast) == 1 {
r = cast[0]
}
return xmlToMap(xmlVal, r)
}
// Get next XML doc from an io.Reader as a Map value. Returns Map value.
// NOTES:
// 1. Declarations, directives, process instructions and comments are NOT parsed.
// 2. The 'xmlReader' will be parsed looking for an xml.StartElement, so BOM and other
// extraneous xml.CharData will be ignored unless io.EOF is reached first.
// 3. If CoerceKeysToLower() has been called, then all key values will be lower case.
// 4. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
func NewMapXmlReader(xmlReader io.Reader, cast ...bool) (Map, error) {
var r bool
if len(cast) == 1 {
r = cast[0]
}
// We need to put an *os.File reader in a ByteReader or the xml.NewDecoder
// will wrap it in a bufio.Reader and seek on the file beyond where the
// xml.Decoder parses!
if _, ok := xmlReader.(io.ByteReader); !ok {
xmlReader = myByteReader(xmlReader) // see code at EOF
}
// build the map
return xmlReaderToMap(xmlReader, r)
}
// Get next XML doc from an io.Reader as a Map value. Returns Map value and slice with the raw XML.
// NOTES:
// 1. Declarations, directives, process instructions and comments are NOT parsed.
// 2. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte
// using a ByteReader. If the io.Reader is an os.File, there may be significant performance impact.
// See the examples - getmetrics1.go through getmetrics4.go - for comparative use cases on a large
// data set. If the io.Reader is wrapping a []byte value in-memory, however, such as http.Request.Body
// you CAN use it to efficiently unmarshal a XML doc and retrieve the raw XML in a single call.
// 3. The 'raw' return value may be larger than the XML text value.
// 4. The 'xmlReader' will be parsed looking for an xml.StartElement, so BOM and other
// extraneous xml.CharData will be ignored unless io.EOF is reached first.
// 5. If CoerceKeysToLower() has been called, then all key values will be lower case.
// 6. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
func NewMapXmlReaderRaw(xmlReader io.Reader, cast ...bool) (Map, []byte, error) {
var r bool
if len(cast) == 1 {
r = cast[0]
}
// create TeeReader so we can retrieve raw XML
buf := make([]byte, 0)
wb := bytes.NewBuffer(buf)
trdr := myTeeReader(xmlReader, wb) // see code at EOF
m, err := xmlReaderToMap(trdr, r)
// retrieve the raw XML that was decoded
b := wb.Bytes()
if err != nil {
return nil, b, err
}
return m, b, nil
}
// xmlReaderToMap() - parse a XML io.Reader to a map[string]interface{} value
func xmlReaderToMap(rdr io.Reader, r bool) (map[string]interface{}, error) {
// parse the Reader
p := xml.NewDecoder(rdr)
if CustomDecoder != nil {
useCustomDecoder(p)
} else {
p.CharsetReader = XmlCharsetReader
}
return xmlToMapParser("", nil, p, r)
}
// xmlToMap - convert a XML doc into map[string]interface{} value
func xmlToMap(doc []byte, r bool) (map[string]interface{}, error) {
b := bytes.NewReader(doc)
p := xml.NewDecoder(b)
if CustomDecoder != nil {
useCustomDecoder(p)
} else {
p.CharsetReader = XmlCharsetReader
}
return xmlToMapParser("", nil, p, r)
}
// ===================================== where the work happens =============================
// PrependAttrWithHyphen. Prepend attribute tags with a hyphen.
// Default is 'true'. (Not applicable to NewMapXmlSeq(), mv.XmlSeq(), etc.)
// Note:
// If 'false', unmarshaling and marshaling is not symmetric. Attributes will be
// marshal'd as <attr_tag>attr</attr_tag> and may be part of a list.
func PrependAttrWithHyphen(v bool) {
if v {
attrPrefix = "-"
lenAttrPrefix = len(attrPrefix)
return
}
attrPrefix = ""
lenAttrPrefix = len(attrPrefix)
}
// Include sequence id with inner tags. - per Sean Murphy, murphysean84@gmail.com.
var includeTagSeqNum bool
// IncludeTagSeqNum - include a "_seq":N key:value pair with each inner tag, denoting
// its position when parsed. This is of limited usefulness, since list values cannot
// be tagged with "_seq" without changing their depth in the Map.
// So THIS SHOULD BE USED WITH CAUTION - see the test cases. Here's a sample of what
// you get.
/*
<Obj c="la" x="dee" h="da">
<IntObj id="3"/>
<IntObj1 id="1"/>
<IntObj id="2"/>
<StrObj>hello</StrObj>
</Obj>
parses as:
{
Obj:{
"-c":"la",
"-h":"da",
"-x":"dee",
"intObj":[
{
"-id"="3",
"_seq":"0" // if mxj.Cast is passed, then: "_seq":0
},
{
"-id"="2",
"_seq":"2"
}],
"intObj1":{
"-id":"1",
"_seq":"1"
},
"StrObj":{
"#text":"hello", // simple element value gets "#text" tag
"_seq":"3"
}
}
}
*/
func IncludeTagSeqNum(b ...bool) {
if len(b) == 0 {
includeTagSeqNum = !includeTagSeqNum
} else if len(b) == 1 {
includeTagSeqNum = b[0]
}
}
// all keys will be "lower case"
var lowerCase bool
// Coerce all tag values to keys in lower case. This is useful if you've got sources with variable
// tag capitalization, and you want to use m.ValuesForKeys(), etc., with the key or path spec
// in lower case.
// CoerceKeysToLower() will toggle the coercion flag true|false - on|off
// CoerceKeysToLower(true|false) will set the coercion flag on|off
//
// NOTE: only recognized by NewMapXml, NewMapXmlReader, and NewMapXmlReaderRaw functions as well as
// the associated HandleXmlReader and HandleXmlReaderRaw.
func CoerceKeysToLower(b ...bool) {
if len(b) == 0 {
lowerCase = !lowerCase
} else if len(b) == 1 {
lowerCase = b[0]
}
}
// disableTrimWhiteSpace sets if the white space should be removed or not
var disableTrimWhiteSpace bool
var trimRunes = "\t\r\b\n "
// DisableTrimWhiteSpace set if the white space should be trimmed or not. By default white space is always trimmed. If
// no argument is provided, trim white space will be disabled.
func DisableTrimWhiteSpace(b ...bool) {
if len(b) == 0 {
disableTrimWhiteSpace = true
} else {
disableTrimWhiteSpace = b[0]
}
if disableTrimWhiteSpace {
trimRunes = "\t\r\b\n"
} else {
trimRunes = "\t\r\b\n "
}
}
// 25jun16: Allow user to specify the "prefix" character for XML attribute key labels.
// We do this by replacing '`' constant with attrPrefix var, replacing useHyphen with attrPrefix = "",
// and adding a SetAttrPrefix(s string) function.
var attrPrefix string = `-` // the default
var lenAttrPrefix int = 1 // the default
// SetAttrPrefix changes the default, "-", to the specified value, s.
// SetAttrPrefix("") is the same as PrependAttrWithHyphen(false).
// (Not applicable for NewMapXmlSeq(), mv.XmlSeq(), etc.)
func SetAttrPrefix(s string) {
attrPrefix = s
lenAttrPrefix = len(attrPrefix)
}
// 18jan17: Allows user to specify if the map keys should be in snake case instead
// of the default hyphenated notation.
var snakeCaseKeys bool
// CoerceKeysToSnakeCase changes the default, false, to the specified value, b.
// Note: the attribute prefix will be a hyphen, '-', or what ever string value has
// been specified using SetAttrPrefix.
func CoerceKeysToSnakeCase(b ...bool) {
if len(b) == 0 {
snakeCaseKeys = !snakeCaseKeys
} else if len(b) == 1 {
snakeCaseKeys = b[0]
}
}
// 10jan19: use of pull request #57 should be conditional - legacy code assumes
// numeric values are float64.
var castToInt bool
// CastValuesToInt tries to coerce numeric valus to int64 or uint64 instead of the
// default float64. Repeated calls with no argument will toggle this on/off, or this
// handling will be set with the value of 'b'.
func CastValuesToInt(b ...bool) {
if len(b) == 0 {
castToInt = !castToInt
} else if len(b) == 1 {
castToInt = b[0]
}
}
// 05feb17: support processing XMPP streams (issue #36)
var handleXMPPStreamTag bool
// HandleXMPPStreamTag causes decoder to parse XMPP <stream:stream> elements.
// If called with no argument, XMPP stream element handling is toggled on/off.
// (See xmppStream_test.go for example.)
// If called with NewMapXml, NewMapXmlReader, New MapXmlReaderRaw the "stream"
// element will be returned as:
// map["stream"]interface{}{map[-<attrs>]interface{}}.
// If called with NewMapSeq, NewMapSeqReader, NewMapSeqReaderRaw the "stream"
// element will be returned as:
// map["stream:stream"]interface{}{map["#attr"]interface{}{map[string]interface{}}}
// where the "#attr" values have "#text" and "#seq" keys. (See NewMapXmlSeq.)
func HandleXMPPStreamTag(b ...bool) {
if len(b) == 0 {
handleXMPPStreamTag = !handleXMPPStreamTag
} else if len(b) == 1 {
handleXMPPStreamTag = b[0]
}
}
// 21jan18 - decode all values as map["#text":value] (issue #56)
var decodeSimpleValuesAsMap bool
// DecodeSimpleValuesAsMap forces all values to be decoded as map["#text":<value>].
// If called with no argument, the decoding is toggled on/off.
//
// By default the NewMapXml functions decode simple values without attributes as
// map[<tag>:<value>]. This function causes simple values without attributes to be
// decoded the same as simple values with attributes - map[<tag>:map["#text":<value>]].
func DecodeSimpleValuesAsMap(b ...bool) {
if len(b) == 0 {
decodeSimpleValuesAsMap = !decodeSimpleValuesAsMap
} else if len(b) == 1 {
decodeSimpleValuesAsMap = b[0]
}
}
// xmlToMapParser (2015.11.12) - load a 'clean' XML doc into a map[string]interface{} directly.
// A refactoring of xmlToTreeParser(), markDuplicate() and treeToMap() - here, all-in-one.
// We've removed the intermediate *node tree with the allocation and subsequent rescanning.
func xmlToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[string]interface{}, error) {
if lowerCase {
skey = strings.ToLower(skey)
}
if snakeCaseKeys {
skey = strings.Replace(skey, "-", "_", -1)
}
// NOTE: all attributes and sub-elements parsed into 'na', 'na' is returned as value for 'skey' in 'n'.
// Unless 'skey' is a simple element w/o attributes, in which case the xml.CharData value is the value.
var n, na map[string]interface{}
var seq int // for includeTagSeqNum
// Allocate maps and load attributes, if any.
// NOTE: on entry from NewMapXml(), etc., skey=="", and we fall through
// to get StartElement then recurse with skey==xml.StartElement.Name.Local
// where we begin allocating map[string]interface{} values 'n' and 'na'.
if skey != "" {
n = make(map[string]interface{}) // old n
na = make(map[string]interface{}) // old n.nodes
if len(a) > 0 {
for _, v := range a {
if snakeCaseKeys {
v.Name.Local = strings.Replace(v.Name.Local, "-", "_", -1)
}
var key string
key = attrPrefix + v.Name.Local
if lowerCase {
key = strings.ToLower(key)
}
if xmlEscapeCharsDecoder { // per issue#84
v.Value = escapeChars(v.Value)
}
na[key] = cast(v.Value, r, key)
}
}
}
// Return XMPP <stream:stream> message.
if handleXMPPStreamTag && skey == "stream" {
n[skey] = na
return n, nil
}
for {
t, err := p.Token()
if err != nil {
if err != io.EOF {
return nil, errors.New("xml.Decoder.Token() - " + err.Error())
}
return nil, err
}
switch t.(type) {
case xml.StartElement:
tt := t.(xml.StartElement)
// First call to xmlToMapParser() doesn't pass xml.StartElement - the map key.
// So when the loop is first entered, the first token is the root tag along
// with any attributes, which we process here.
//
// Subsequent calls to xmlToMapParser() will pass in tag+attributes for
// processing before getting the next token which is the element value,
// which is done above.
if skey == "" {
return xmlToMapParser(tt.Name.Local, tt.Attr, p, r)
}
// If not initializing the map, parse the element.
// len(nn) == 1, necessarily - it is just an 'n'.
nn, err := xmlToMapParser(tt.Name.Local, tt.Attr, p, r)
if err != nil {
return nil, err
}
// The nn map[string]interface{} value is a na[nn_key] value.
// We need to see if nn_key already exists - means we're parsing a list.
// This may require converting na[nn_key] value into []interface{} type.
// First, extract the key:val for the map - it's a singleton.
// Note:
// * if CoerceKeysToLower() called, then key will be lower case.
// * if CoerceKeysToSnakeCase() called, then key will be converted to snake case.
var key string
var val interface{}
for key, val = range nn {
break
}
// IncludeTagSeqNum requests that the element be augmented with a "_seq" sub-element.
// In theory, we don't need this if len(na) == 1. But, we don't know what might
// come next - we're only parsing forward. So if you ask for 'includeTagSeqNum' you
// get it on every element. (Personally, I never liked this, but I added it on request
// and did get a $50 Amazon gift card in return - now we support it for backwards compatibility!)
if includeTagSeqNum {
switch val.(type) {
case []interface{}:
// noop - There's no clean way to handle this w/o changing message structure.
case map[string]interface{}:
val.(map[string]interface{})["_seq"] = seq // will overwrite an "_seq" XML tag
seq++
case interface{}: // a non-nil simple element: string, float64, bool
v := map[string]interface{}{"#text": val}
v["_seq"] = seq
seq++
val = v
}
}
// 'na' holding sub-elements of n.
// See if 'key' already exists.
// If 'key' exists, then this is a list, if not just add key:val to na.
if v, ok := na[key]; ok {
var a []interface{}
switch v.(type) {
case []interface{}:
a = v.([]interface{})
default: // anything else - note: v.(type) != nil
a = []interface{}{v}
}
a = append(a, val)
na[key] = a
} else {
na[key] = val // save it as a singleton
}
case xml.EndElement:
// len(n) > 0 if this is a simple element w/o xml.Attrs - see xml.CharData case.
if len(n) == 0 {
// If len(na)==0 we have an empty element == "";
// it has no xml.Attr nor xml.CharData.
// Note: in original node-tree parser, val defaulted to "";
// so we always had the default if len(node.nodes) == 0.
if len(na) > 0 {
n[skey] = na
} else {
n[skey] = "" // empty element
}
} else if len(n) == 1 && len(na) > 0 {
// it's a simple element w/ no attributes w/ subelements
for _, v := range n {
na["#text"] = v
}
n[skey] = na
}
return n, nil
case xml.CharData:
// clean up possible noise
tt := strings.Trim(string(t.(xml.CharData)), trimRunes)
if xmlEscapeCharsDecoder { // issue#84
tt = escapeChars(tt)
}
if len(tt) > 0 {
if len(na) > 0 || decodeSimpleValuesAsMap {
na["#text"] = cast(tt, r, "#text")
} else if skey != "" {
n[skey] = cast(tt, r, skey)
} else {
// per Adrian (http://www.adrianlungu.com/) catch stray text
// in decoder stream -
// https://github.com/clbanning/mxj/pull/14#issuecomment-182816374
// NOTE: CharSetReader must be set to non-UTF-8 CharSet or you'll get
// a p.Token() decoding error when the BOM is UTF-16 or UTF-32.
continue
}
}
default:
// noop
}
}
}
var castNanInf bool
// Cast "Nan", "Inf", "-Inf" XML values to 'float64'.
// By default, these values will be decoded as 'string'.
func CastNanInf(b ...bool) {
if len(b) == 0 {
castNanInf = !castNanInf
} else if len(b) == 1 {
castNanInf = b[0]
}
}
// cast - try to cast string values to bool or float64
// 't' is the tag key that can be checked for 'not-casting'
func cast(s string, r bool, t string) interface{} {
if checkTagToSkip != nil && t != "" && checkTagToSkip(t) {
// call the check-function here with 't[0]'
// if 'true' return s
return s
}
if r {
// handle nan and inf
if !castNanInf {
switch strings.ToLower(s) {
case "nan", "inf", "-inf":
return s
}
}
// handle numeric strings ahead of boolean
if castToInt {
if f, err := strconv.ParseInt(s, 10, 64); err == nil {
return f
}
if f, err := strconv.ParseUint(s, 10, 64); err == nil {
return f
}
}
if castToFloat {
if f, err := strconv.ParseFloat(s, 64); err == nil {
return f
}
}
// ParseBool treats "1"==true & "0"==false, we've already scanned those
// values as float64. See if value has 't' or 'f' as initial screen to
// minimize calls to ParseBool; also, see if len(s) < 6.
if castToBool {
if len(s) > 0 && len(s) < 6 {
switch s[:1] {
case "t", "T", "f", "F":
if b, err := strconv.ParseBool(s); err == nil {
return b
}
}
}
}
}
return s
}
// pull request, #59
var castToFloat = true
// CastValuesToFloat can be used to skip casting to float64 when
// "cast" argument is 'true' in NewMapXml, etc.
// Default is true.
func CastValuesToFloat(b ...bool) {
if len(b) == 0 {
castToFloat = !castToFloat
} else if len(b) == 1 {
castToFloat = b[0]
}
}
var castToBool = true
// CastValuesToBool can be used to skip casting to bool when
// "cast" argument is 'true' in NewMapXml, etc.
// Default is true.
func CastValuesToBool(b ...bool) {
if len(b) == 0 {
castToBool = !castToBool
} else if len(b) == 1 {
castToBool = b[0]
}
}
// checkTagToSkip - switch to address Issue #58
var checkTagToSkip func(string) bool
// SetCheckTagToSkipFunc registers function to test whether the value
// for a tag should be cast to bool or float64 when "cast" argument is 'true'.
// (Dot tag path notation is not supported.)
// NOTE: key may be "#text" if it's a simple element with attributes
// or "decodeSimpleValuesAsMap == true".
// NOTE: does not apply to NewMapXmlSeq... functions.
func SetCheckTagToSkipFunc(fn func(string) bool) {
checkTagToSkip = fn
}
// ------------------ END: NewMapXml & NewMapXmlReader -------------------------
// ------------------ mv.Xml & mv.XmlWriter - from j2x ------------------------
const (
DefaultRootTag = "doc"
)
var useGoXmlEmptyElemSyntax bool
// XmlGoEmptyElemSyntax() - <tag ...></tag> rather than <tag .../>.
// Go's encoding/xml package marshals empty XML elements as <tag ...></tag>. By default this package
// encodes empty elements as <tag .../>. If you're marshaling Map values that include structures
// (which are passed to xml.Marshal for encoding), this will let you conform to the standard package.
func XmlGoEmptyElemSyntax() {
useGoXmlEmptyElemSyntax = true
}
// XmlDefaultEmptyElemSyntax() - <tag .../> rather than <tag ...></tag>.
// Return XML encoding for empty elements to the default package setting.
// Reverses effect of XmlGoEmptyElemSyntax().
func XmlDefaultEmptyElemSyntax() {
useGoXmlEmptyElemSyntax = false
}
// ------- issue #88 ----------
// xmlCheckIsValid set switch to force decoding the encoded XML to
// see if it is valid XML.
var xmlCheckIsValid bool
// XmlCheckIsValid forces the encoded XML to be checked for validity.
func XmlCheckIsValid(b ...bool) {
if len(b) == 1 {
xmlCheckIsValid = b[0]
return
}
xmlCheckIsValid = !xmlCheckIsValid
}
// Encode a Map as XML. The companion of NewMapXml().
// The following rules apply.
// - The key label "#text" is treated as the value for a simple element with attributes.
// - Map keys that begin with a hyphen, '-', are interpreted as attributes.
// It is an error if the attribute doesn't have a []byte, string, number, or boolean value.
// - Map value type encoding:
// > string, bool, float64, int, int32, int64, float32: per "%v" formating
// > []bool, []uint8: by casting to string
// > structures, etc.: handed to xml.Marshal() - if there is an error, the element
// value is "UNKNOWN"
// - Elements with only attribute values or are null are terminated using "/>".
// - If len(mv) == 1 and no rootTag is provided, then the map key is used as the root tag, possible.
// Thus, `{ "key":"value" }` encodes as "<key>value</key>".
// - To encode empty elements in a syntax consistent with encoding/xml call UseGoXmlEmptyElementSyntax().
// The attributes tag=value pairs are alphabetized by "tag". Also, when encoding map[string]interface{} values -
// complex elements, etc. - the key:value pairs are alphabetized by key so the resulting tags will appear sorted.
func (mv Map) Xml(rootTag ...string) ([]byte, error) {
m := map[string]interface{}(mv)
var err error
b := new(bytes.Buffer)
p := new(pretty) // just a stub
if len(m) == 1 && len(rootTag) == 0 {
for key, value := range m {
// if it an array, see if all values are map[string]interface{}
// we force a new root tag if we'll end up with no key:value in the list
// so: key:[string_val, bool:true] --> <doc><key>string_val</key><bool>true</bool></doc>
switch value.(type) {
case []interface{}:
for _, v := range value.([]interface{}) {
switch v.(type) {
case map[string]interface{}: // noop
default: // anything else
err = marshalMapToXmlIndent(false, b, DefaultRootTag, m, p)
goto done
}
}
}
err = marshalMapToXmlIndent(false, b, key, value, p)
}
} else if len(rootTag) == 1 {
err = marshalMapToXmlIndent(false, b, rootTag[0], m, p)
} else {
err = marshalMapToXmlIndent(false, b, DefaultRootTag, m, p)
}
done:
if xmlCheckIsValid {
d := xml.NewDecoder(bytes.NewReader(b.Bytes()))
for {
_, err = d.Token()
if err == io.EOF {
err = nil
break
} else if err != nil {
return nil, err
}
}
}
return b.Bytes(), err
}
// The following implementation is provided only for symmetry with NewMapXmlReader[Raw]
// The names will also provide a key for the number of return arguments.
// Writes the Map as XML on the Writer.
// See Xml() for encoding rules.
func (mv Map) XmlWriter(xmlWriter io.Writer, rootTag ...string) error {
x, err := mv.Xml(rootTag...)
if err != nil {
return err
}
_, err = xmlWriter.Write(x)
return err
}
// Writes the Map as XML on the Writer. []byte is the raw XML that was written.
// See Xml() for encoding rules.
/*
func (mv Map) XmlWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error) {
x, err := mv.Xml(rootTag...)
if err != nil {
return x, err
}
_, err = xmlWriter.Write(x)
return x, err
}
*/
// Writes the Map as pretty XML on the Writer.
// See Xml() for encoding rules.
func (mv Map) XmlIndentWriter(xmlWriter io.Writer, prefix, indent string, rootTag ...string) error {
x, err := mv.XmlIndent(prefix, indent, rootTag...)
if err != nil {
return err
}
_, err = xmlWriter.Write(x)
return err
}
// Writes the Map as pretty XML on the Writer. []byte is the raw XML that was written.
// See Xml() for encoding rules.
/*
func (mv Map) XmlIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error) {
x, err := mv.XmlIndent(prefix, indent, rootTag...)
if err != nil {
return x, err
}
_, err = xmlWriter.Write(x)
return x, err
}
*/
// -------------------- END: mv.Xml & mv.XmlWriter -------------------------------
// -------------- Handle XML stream by processing Map value --------------------
// Default poll delay to keep Handler from spinning on an open stream
// like sitting on os.Stdin waiting for imput.
var xhandlerPollInterval = time.Millisecond
// Bulk process XML using handlers that process a Map value.
// 'rdr' is an io.Reader for XML (stream)
// 'mapHandler' is the Map processor. Return of 'false' stops io.Reader processing.
// 'errHandler' is the error processor. Return of 'false' stops io.Reader processing and returns the error.
// Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
// This means that you can stop reading the file on error or after processing a particular message.
// To have reading and handling run concurrently, pass argument to a go routine in handler and return 'true'.
func HandleXmlReader(xmlReader io.Reader, mapHandler func(Map) bool, errHandler func(error) bool) error {
var n int
for {
m, merr := NewMapXmlReader(xmlReader)
n++
// handle error condition with errhandler
if merr != nil && merr != io.EOF {
merr = fmt.Errorf("[xmlReader: %d] %s", n, merr.Error())
if ok := errHandler(merr); !ok {
// caused reader termination
return merr
}
continue
}
// pass to maphandler
if len(m) != 0 {
if ok := mapHandler(m); !ok {
break
}
} else if merr != io.EOF {
time.Sleep(xhandlerPollInterval)
}
if merr == io.EOF {
break
}
}
return nil
}
// Bulk process XML using handlers that process a Map value and the raw XML.
// 'rdr' is an io.Reader for XML (stream)
// 'mapHandler' is the Map and raw XML - []byte - processor. Return of 'false' stops io.Reader processing.
// 'errHandler' is the error and raw XML processor. Return of 'false' stops io.Reader processing and returns the error.
// Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
// This means that you can stop reading the file on error or after processing a particular message.
// To have reading and handling run concurrently, pass argument(s) to a go routine in handler and return 'true'.
// See NewMapXmlReaderRaw for comment on performance associated with retrieving raw XML from a Reader.
func HandleXmlReaderRaw(xmlReader io.Reader, mapHandler func(Map, []byte) bool, errHandler func(error, []byte) bool) error {
var n int
for {
m, raw, merr := NewMapXmlReaderRaw(xmlReader)
n++
// handle error condition with errhandler
if merr != nil && merr != io.EOF {
merr = fmt.Errorf("[xmlReader: %d] %s", n, merr.Error())
if ok := errHandler(merr, raw); !ok {
// caused reader termination
return merr
}
continue
}
// pass to maphandler
if len(m) != 0 {
if ok := mapHandler(m, raw); !ok {
break
}
} else if merr != io.EOF {
time.Sleep(xhandlerPollInterval)
}
if merr == io.EOF {
break
}
}
return nil
}
// ----------------- END: Handle XML stream by processing Map value --------------
// -------- a hack of io.TeeReader ... need one that's an io.ByteReader for xml.NewDecoder() ----------
// This is a clone of io.TeeReader with the additional method t.ReadByte().
// Thus, this TeeReader is also an io.ByteReader.
// This is necessary because xml.NewDecoder uses a ByteReader not a Reader. It appears to have been written
// with bufio.Reader or bytes.Reader in mind ... not a generic io.Reader, which doesn't have to have ReadByte()..
// If NewDecoder is passed a Reader that does not satisfy ByteReader() it wraps the Reader with
// bufio.NewReader and uses ReadByte rather than Read that runs the TeeReader pipe logic.
type teeReader struct {
r io.Reader
w io.Writer
b []byte
}
func myTeeReader(r io.Reader, w io.Writer) io.Reader {
b := make([]byte, 1)
return &teeReader{r, w, b}
}
// need for io.Reader - but we don't use it ...
func (t *teeReader) Read(p []byte) (int, error) {
return 0, nil
}
func (t *teeReader) ReadByte() (byte, error) {
n, err := t.r.Read(t.b)
if n > 0 {
if _, err := t.w.Write(t.b[:1]); err != nil {
return t.b[0], err
}
}
return t.b[0], err
}
// For use with NewMapXmlReader & NewMapXmlSeqReader.
type byteReader struct {
r io.Reader
b []byte
}
func myByteReader(r io.Reader) io.Reader {
b := make([]byte, 1)
return &byteReader{r, b}
}
// Need for io.Reader interface ...
// Needed if reading a malformed http.Request.Body - issue #38.
func (b *byteReader) Read(p []byte) (int, error) {
return b.r.Read(p)
}
func (b *byteReader) ReadByte() (byte, error) {
_, err := b.r.Read(b.b)
if len(b.b) > 0 {
return b.b[0], nil
}
var c byte
return c, err
}
// ----------------------- END: io.TeeReader hack -----------------------------------
// ---------------------- XmlIndent - from j2x package ----------------------------
// Encode a map[string]interface{} as a pretty XML string.
// See Xml for encoding rules.
func (mv Map) XmlIndent(prefix, indent string, rootTag ...string) ([]byte, error) {
m := map[string]interface{}(mv)
var err error
b := new(bytes.Buffer)
p := new(pretty)
p.indent = indent
p.padding = prefix
if len(m) == 1 && len(rootTag) == 0 {
// this can extract the key for the single map element
// use it if it isn't a key for a list
for key, value := range m {
if _, ok := value.([]interface{}); ok {
err = marshalMapToXmlIndent(true, b, DefaultRootTag, m, p)
} else {
err = marshalMapToXmlIndent(true, b, key, value, p)
}
}
} else if len(rootTag) == 1 {
err = marshalMapToXmlIndent(true, b, rootTag[0], m, p)
} else {
err = marshalMapToXmlIndent(true, b, DefaultRootTag, m, p)
}
if xmlCheckIsValid {
d := xml.NewDecoder(bytes.NewReader(b.Bytes()))
for {
_, err = d.Token()
if err == io.EOF {
err = nil
break
} else if err != nil {
return nil, err
}
}
}
return b.Bytes(), err
}
type pretty struct {
indent string
cnt int
padding string
mapDepth int
start int
}
func (p *pretty) Indent() {
p.padding += p.indent
p.cnt++
}
func (p *pretty) Outdent() {
if p.cnt > 0 {
p.padding = p.padding[:len(p.padding)-len(p.indent)]
p.cnt--
}
}
// where the work actually happens
// returns an error if an attribute is not atomic
// NOTE: 01may20 - replaces mapToXmlIndent(); uses bytes.Buffer instead for string appends.
func marshalMapToXmlIndent(doIndent bool, b *bytes.Buffer, key string, value interface{}, pp *pretty) error {
var err error
var endTag bool
var isSimple bool
var elen int
p := &pretty{pp.indent, pp.cnt, pp.padding, pp.mapDepth, pp.start}
// per issue #48, 18apr18 - try and coerce maps to map[string]interface{}
// Don't need for mapToXmlSeqIndent, since maps there are decoded by NewMapXmlSeq().
if reflect.ValueOf(value).Kind() == reflect.Map {
switch value.(type) {
case map[string]interface{}:
default:
val := make(map[string]interface{})
vv := reflect.ValueOf(value)
keys := vv.MapKeys()
for _, k := range keys {
val[fmt.Sprint(k)] = vv.MapIndex(k).Interface()
}
value = val
}
}
// 14jul20. The following block of code has become something of a catch all for odd stuff
// that might be passed in as a result of casting an arbitrary map[<T>]<T> to an mxj.Map
// value and then call m.Xml or m.XmlIndent. See issue #71 (and #73) for such edge cases.
switch value.(type) {
// these types are handled during encoding
case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32, json.Number:
case []map[string]interface{}, []string, []float64, []bool, []int, []int32, []int64, []float32, []json.Number:
case []interface{}:
case nil:
value = ""
default:
// see if value is a struct, if so marshal using encoding/xml package
if reflect.ValueOf(value).Kind() == reflect.Struct {
if v, err := xml.Marshal(value); err != nil {
return err
} else {
value = string(v)
}
} else {
// coerce eveything else into a string value
value = fmt.Sprint(value)
}
}
// start the XML tag with required indentaton and padding
if doIndent {
if _, err = b.WriteString(p.padding); err != nil {
return err
}
}
switch value.(type) {
case []interface{}:
default:
if _, err = b.WriteString(`<` + key); err != nil {
return err
}
}
switch value.(type) {
case map[string]interface{}:
vv := value.(map[string]interface{})
lenvv := len(vv)
// scan out attributes - attribute keys have prepended attrPrefix
attrlist := make([][2]string, len(vv))
var n int
var ss string
for k, v := range vv {
if lenAttrPrefix > 0 && lenAttrPrefix < len(k) && k[:lenAttrPrefix] == attrPrefix {
switch v.(type) {
case string:
if xmlEscapeChars {
ss = escapeChars(v.(string))
} else {
ss = v.(string)
}
attrlist[n][0] = k[lenAttrPrefix:]
attrlist[n][1] = ss
case float64, bool, int, int32, int64, float32, json.Number:
attrlist[n][0] = k[lenAttrPrefix:]
attrlist[n][1] = fmt.Sprintf("%v", v)
case []byte:
if xmlEscapeChars {
ss = escapeChars(string(v.([]byte)))
} else {
ss = string(v.([]byte))
}
attrlist[n][0] = k[lenAttrPrefix:]
attrlist[n][1] = ss
default:
return fmt.Errorf("invalid attribute value for: %s:<%T>", k, v)
}
n++
}
}
if n > 0 {
attrlist = attrlist[:n]
sort.Sort(attrList(attrlist))
for _, v := range attrlist {
if _, err = b.WriteString(` ` + v[0] + `="` + v[1] + `"`); err != nil {
return err
}
}
}
// only attributes?
if n == lenvv {
if useGoXmlEmptyElemSyntax {
if _, err = b.WriteString(`</` + key + ">"); err != nil {
return err
}
} else {
if _, err = b.WriteString(`/>`); err != nil {
return err
}
}
break
}
// simple element? Note: '#text" is an invalid XML tag.
isComplex := false
if v, ok := vv["#text"]; ok && n+1 == lenvv {
// just the value and attributes
switch v.(type) {
case string:
if xmlEscapeChars {
v = escapeChars(v.(string))
} else {
v = v.(string)
}
case []byte:
if xmlEscapeChars {
v = escapeChars(string(v.([]byte)))
} else {
v = string(v.([]byte))
}
}
if _, err = b.WriteString(">" + fmt.Sprintf("%v", v)); err != nil {
return err
}
endTag = true
elen = 1
isSimple = true
break
} else if ok {
// need to handle when there are subelements in addition to the simple element value
// issue #90
switch v.(type) {
case string:
if xmlEscapeChars {
v = escapeChars(v.(string))
} else {
v = v.(string)
}
case []byte:
if xmlEscapeChars {
v = escapeChars(string(v.([]byte)))
} else {
v = string(v.([]byte))
}
}
if _, err = b.WriteString(">" + fmt.Sprintf("%v", v)); err != nil {
return err
}
isComplex = true
}
// close tag with possible attributes
if !isComplex {
if _, err = b.WriteString(">"); err != nil {
return err
}
}
if doIndent {
// *s += "\n"
if _, err = b.WriteString("\n"); err != nil {
return err
}
}
// something more complex
p.mapDepth++
// extract the map k:v pairs and sort on key
elemlist := make([][2]interface{}, len(vv))
n = 0
for k, v := range vv {
if k == "#text" {
// simple element handled above
continue
}
if lenAttrPrefix > 0 && lenAttrPrefix < len(k) && k[:lenAttrPrefix] == attrPrefix {
continue
}
elemlist[n][0] = k
elemlist[n][1] = v
n++
}
elemlist = elemlist[:n]
sort.Sort(elemList(elemlist))
var i int
for _, v := range elemlist {
switch v[1].(type) {
case []interface{}:
default:
if i == 0 && doIndent {
p.Indent()
}
}
i++
if err := marshalMapToXmlIndent(doIndent, b, v[0].(string), v[1], p); err != nil {
return err
}
switch v[1].(type) {
case []interface{}: // handled in []interface{} case
default:
if doIndent {
p.Outdent()
}
}
i--
}
p.mapDepth--
endTag = true
elen = 1 // we do have some content ...
case []interface{}:
// special case - found during implementing Issue #23
if len(value.([]interface{})) == 0 {
if doIndent {
if _, err = b.WriteString(p.padding + p.indent); err != nil {
return err
}
}
if _, err = b.WriteString("<" + key); err != nil {
return err
}
elen = 0
endTag = true
break
}
for _, v := range value.([]interface{}) {
if doIndent {
p.Indent()
}
if err := marshalMapToXmlIndent(doIndent, b, key, v, p); err != nil {
return err
}
if doIndent {
p.Outdent()
}
}
return nil
case []string:
// This was added by https://github.com/slotix ... not a type that
// would be encountered if mv generated from NewMapXml, NewMapJson.
// Could be encountered in AnyXml(), so we'll let it stay, though
// it should be merged with case []interface{}, above.
//quick fix for []string type
//[]string should be treated exaclty as []interface{}
if len(value.([]string)) == 0 {
if doIndent {
if _, err = b.WriteString(p.padding + p.indent); err != nil {
return err
}
}
if _, err = b.WriteString("<" + key); err != nil {
return err
}
elen = 0
endTag = true
break
}
for _, v := range value.([]string) {
if doIndent {
p.Indent()
}
if err := marshalMapToXmlIndent(doIndent, b, key, v, p); err != nil {
return err
}
if doIndent {
p.Outdent()
}
}
return nil
case nil:
// terminate the tag
if doIndent {
// *s += p.padding
if _, err = b.WriteString(p.padding); err != nil {
return err
}
}
if _, err = b.WriteString("<" + key); err != nil {
return err
}
endTag, isSimple = true, true
break
default: // handle anything - even goofy stuff
elen = 0
switch value.(type) {
case string:
v := value.(string)
if xmlEscapeChars {
v = escapeChars(v)
}
elen = len(v)
if elen > 0 {
// *s += ">" + v
if _, err = b.WriteString(">" + v); err != nil {
return err
}
}
case float64, bool, int, int32, int64, float32, json.Number:
v := fmt.Sprintf("%v", value)
elen = len(v) // always > 0
if _, err = b.WriteString(">" + v); err != nil {
return err
}
case []byte: // NOTE: byte is just an alias for uint8
// similar to how xml.Marshal handles []byte structure members
v := string(value.([]byte))
if xmlEscapeChars {
v = escapeChars(v)
}
elen = len(v)
if elen > 0 {
// *s += ">" + v
if _, err = b.WriteString(">" + v); err != nil {
return err
}
}
default:
if _, err = b.WriteString(">"); err != nil {
return err
}
var v []byte
var err error
if doIndent {
v, err = xml.MarshalIndent(value, p.padding, p.indent)
} else {
v, err = xml.Marshal(value)
}
if err != nil {
if _, err = b.WriteString(">UNKNOWN"); err != nil {
return err
}
} else {
elen = len(v)
if elen > 0 {
if _, err = b.Write(v); err != nil {
return err
}
}
}
}
isSimple = true
endTag = true
}
if endTag {
if doIndent {
if !isSimple {
if _, err = b.WriteString(p.padding); err != nil {
return err
}
}
}
if elen > 0 || useGoXmlEmptyElemSyntax {
if elen == 0 {
if _, err = b.WriteString(">"); err != nil {
return err
}
}
if _, err = b.WriteString(`</` + key + ">"); err != nil {
return err
}
} else {
if _, err = b.WriteString(`/>`); err != nil {
return err
}
}
}
if doIndent {
if p.cnt > p.start {
if _, err = b.WriteString("\n"); err != nil {
return err
}
}
p.Outdent()
}
return nil
}
// ============================ sort interface implementation =================
type attrList [][2]string
func (a attrList) Len() int {
return len(a)
}
func (a attrList) Swap(i, j int) {
a[i], a[j] = a[j], a[i]
}
func (a attrList) Less(i, j int) bool {
return a[i][0] <= a[j][0]
}
type elemList [][2]interface{}
func (e elemList) Len() int {
return len(e)
}
func (e elemList) Swap(i, j int) {
e[i], e[j] = e[j], e[i]
}
func (e elemList) Less(i, j int) bool {
return e[i][0].(string) <= e[j][0].(string)
}
// Copyright 2012-2016, 2019 Charles Banning. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
// xmlseq.go - version of xml.go with sequence # injection on Decoding and sorting on Encoding.
// Also, handles comments, directives and process instructions.
package mxj
import (
"bytes"
"encoding/xml"
"errors"
"fmt"
"io"
"sort"
"strings"
)
// MapSeq is like Map but contains seqencing indices to allow recovering the original order of
// the XML elements when the map[string]interface{} is marshaled. Element attributes are
// stored as a map["#attr"]map[<attr_key>]map[string]interface{}{"#text":"<value>", "#seq":<attr_index>}
// value instead of denoting the keys with a prefix character. Also, comments, directives and
// process instructions are preserved.
type MapSeq map[string]interface{}
// NoRoot is returned by NewXmlSeq, etc., when a comment, directive or procinstr element is parsed
// in the XML data stream and the element is not contained in an XML object with a root element.
var NoRoot = errors.New("no root key")
var NO_ROOT = NoRoot // maintain backwards compatibility
// ------------------- NewMapXmlSeq & NewMapXmlSeqReader ... -------------------------
// NewMapXmlSeq converts a XML doc into a MapSeq value with elements id'd with decoding sequence key represented
// as map["#seq"]<int value>.
// If the optional argument 'cast' is 'true', then values will be converted to boolean or float64 if possible.
// NOTE: "#seq" key/value pairs are removed on encoding with msv.Xml() / msv.XmlIndent().
// • attributes are a map - map["#attr"]map["attr_key"]map[string]interface{}{"#text":<aval>, "#seq":<num>}
// • all simple elements are decoded as map["#text"]interface{} with a "#seq" k:v pair, as well.
// • lists always decode as map["list_tag"][]map[string]interface{} where the array elements are maps that
// include a "#seq" k:v pair based on sequence they are decoded. Thus, XML like:
// <doc>
// <ltag>value 1</ltag>
// <newtag>value 2</newtag>
// <ltag>value 3</ltag>
// </doc>
// is decoded as:
// doc :
// ltag :[[]interface{}]
// [item: 0]
// #seq :[int] 0
// #text :[string] value 1
// [item: 1]
// #seq :[int] 2
// #text :[string] value 3
// newtag :
// #seq :[int] 1
// #text :[string] value 2
// It will encode in proper sequence even though the MapSeq representation merges all "ltag" elements in an array.
// • comments - "<!--comment-->" - are decoded as map["#comment"]map["#text"]"cmnt_text" with a "#seq" k:v pair.
// • directives - "<!text>" - are decoded as map["#directive"]map[#text"]"directive_text" with a "#seq" k:v pair.
// • process instructions - "<?instr?>" - are decoded as map["#procinst"]interface{} where the #procinst value
// is of map[string]interface{} type with the following keys: #target, #inst, and #seq.
// • comments, directives, and procinsts that are NOT part of a document with a root key will be returned as
// map[string]interface{} and the error value 'NoRoot'.
// • note: "<![CDATA[" syntax is lost in xml.Decode parser - and is not handled here, either.
// and: "\r\n" is converted to "\n"
//
// NOTES:
// 1. The 'xmlVal' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other
// extraneous xml.CharData will be ignored unless io.EOF is reached first.
// 2. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to
// re-encode the message in its original structure.
// 3. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
//
// NAME SPACES:
// 1. Keys in the MapSeq value that are parsed from a <name space prefix>:<local name> tag preserve the
// "<prefix>:" notation rather than stripping it as with NewMapXml().
// 2. Attribute keys for name space prefix declarations preserve "xmlns:<prefix>" notation.
//
// ERRORS:
// 1. If a NoRoot error, "no root key," is returned, check the initial map key for a "#comment",
// "#directive" or #procinst" key.
func NewMapXmlSeq(xmlVal []byte, cast ...bool) (MapSeq, error) {
var r bool
if len(cast) == 1 {
r = cast[0]
}
return xmlSeqToMap(xmlVal, r)
}
// NewMpaXmlSeqReader returns next XML doc from an io.Reader as a MapSeq value.
// NOTES:
// 1. The 'xmlReader' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other
// extraneous xml.CharData will be ignored unless io.EOF is reached first.
// 2. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to
// re-encode the message in its original structure.
// 3. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
//
// ERRORS:
// 1. If a NoRoot error, "no root key," is returned, check the initial map key for a "#comment",
// "#directive" or #procinst" key.
func NewMapXmlSeqReader(xmlReader io.Reader, cast ...bool) (MapSeq, error) {
var r bool
if len(cast) == 1 {
r = cast[0]
}
// We need to put an *os.File reader in a ByteReader or the xml.NewDecoder
// will wrap it in a bufio.Reader and seek on the file beyond where the
// xml.Decoder parses!
if _, ok := xmlReader.(io.ByteReader); !ok {
xmlReader = myByteReader(xmlReader) // see code at EOF
}
// build the map
return xmlSeqReaderToMap(xmlReader, r)
}
// NewMapXmlSeqReaderRaw returns the next XML doc from an io.Reader as a MapSeq value.
// Returns MapSeq value, slice with the raw XML, and any error.
// NOTES:
// 1. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte
// using a ByteReader. If the io.Reader is an os.File, there may be significant performance impact.
// See the examples - getmetrics1.go through getmetrics4.go - for comparative use cases on a large
// data set. If the io.Reader is wrapping a []byte value in-memory, however, such as http.Request.Body
// you CAN use it to efficiently unmarshal a XML doc and retrieve the raw XML in a single call.
// 2. The 'raw' return value may be larger than the XML text value.
// 3. The 'xmlReader' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other
// extraneous xml.CharData will be ignored unless io.EOF is reached first.
// 4. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to
// re-encode the message in its original structure.
// 5. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
//
// ERRORS:
// 1. If a NoRoot error, "no root key," is returned, check if the initial map key is "#comment",
// "#directive" or #procinst" key.
func NewMapXmlSeqReaderRaw(xmlReader io.Reader, cast ...bool) (MapSeq, []byte, error) {
var r bool
if len(cast) == 1 {
r = cast[0]
}
// create TeeReader so we can retrieve raw XML
buf := make([]byte, 0)
wb := bytes.NewBuffer(buf)
trdr := myTeeReader(xmlReader, wb)
m, err := xmlSeqReaderToMap(trdr, r)
// retrieve the raw XML that was decoded
b := wb.Bytes()
// err may be NoRoot
return m, b, err
}
// xmlSeqReaderToMap() - parse a XML io.Reader to a map[string]interface{} value
func xmlSeqReaderToMap(rdr io.Reader, r bool) (map[string]interface{}, error) {
// parse the Reader
p := xml.NewDecoder(rdr)
if CustomDecoder != nil {
useCustomDecoder(p)
} else {
p.CharsetReader = XmlCharsetReader
}
return xmlSeqToMapParser("", nil, p, r)
}
// xmlSeqToMap - convert a XML doc into map[string]interface{} value
func xmlSeqToMap(doc []byte, r bool) (map[string]interface{}, error) {
b := bytes.NewReader(doc)
p := xml.NewDecoder(b)
if CustomDecoder != nil {
useCustomDecoder(p)
} else {
p.CharsetReader = XmlCharsetReader
}
return xmlSeqToMapParser("", nil, p, r)
}
// ===================================== where the work happens =============================
// xmlSeqToMapParser - load a 'clean' XML doc into a map[string]interface{} directly.
// Add #seq tag value for each element decoded - to be used for Encoding later.
func xmlSeqToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[string]interface{}, error) {
if snakeCaseKeys {
skey = strings.Replace(skey, "-", "_", -1)
}
// NOTE: all attributes and sub-elements parsed into 'na', 'na' is returned as value for 'skey' in 'n'.
var n, na map[string]interface{}
var seq int // for including seq num when decoding
// Allocate maps and load attributes, if any.
// NOTE: on entry from NewMapXml(), etc., skey=="", and we fall through
// to get StartElement then recurse with skey==xml.StartElement.Name.Local
// where we begin allocating map[string]interface{} values 'n' and 'na'.
if skey != "" {
// 'n' only needs one slot - save call to runtime•hashGrow()
// 'na' we don't know
n = make(map[string]interface{}, 1)
na = make(map[string]interface{})
if len(a) > 0 {
// xml.Attr is decoded into: map["#attr"]map[<attr_label>]interface{}
// where interface{} is map[string]interface{}{"#text":<attr_val>, "#seq":<attr_seq>}
aa := make(map[string]interface{}, len(a))
for i, v := range a {
if snakeCaseKeys {
v.Name.Local = strings.Replace(v.Name.Local, "-", "_", -1)
}
if xmlEscapeCharsDecoder { // per issue#84
v.Value = escapeChars(v.Value)
}
if len(v.Name.Space) > 0 {
aa[v.Name.Space+`:`+v.Name.Local] = map[string]interface{}{"#text": cast(v.Value, r, ""), "#seq": i}
} else {
aa[v.Name.Local] = map[string]interface{}{"#text": cast(v.Value, r, ""), "#seq": i}
}
}
na["#attr"] = aa
}
}
// Return XMPP <stream:stream> message.
if handleXMPPStreamTag && skey == "stream:stream" {
n[skey] = na
return n, nil
}
for {
t, err := p.RawToken()
if err != nil {
if err != io.EOF {
return nil, errors.New("xml.Decoder.Token() - " + err.Error())
}
return nil, err
}
switch t.(type) {
case xml.StartElement:
tt := t.(xml.StartElement)
// First call to xmlSeqToMapParser() doesn't pass xml.StartElement - the map key.
// So when the loop is first entered, the first token is the root tag along
// with any attributes, which we process here.
//
// Subsequent calls to xmlSeqToMapParser() will pass in tag+attributes for
// processing before getting the next token which is the element value,
// which is done above.
if skey == "" {
if len(tt.Name.Space) > 0 {
return xmlSeqToMapParser(tt.Name.Space+`:`+tt.Name.Local, tt.Attr, p, r)
} else {
return xmlSeqToMapParser(tt.Name.Local, tt.Attr, p, r)
}
}
// If not initializing the map, parse the element.
// len(nn) == 1, necessarily - it is just an 'n'.
var nn map[string]interface{}
if len(tt.Name.Space) > 0 {
nn, err = xmlSeqToMapParser(tt.Name.Space+`:`+tt.Name.Local, tt.Attr, p, r)
} else {
nn, err = xmlSeqToMapParser(tt.Name.Local, tt.Attr, p, r)
}
if err != nil {
return nil, err
}
// The nn map[string]interface{} value is a na[nn_key] value.
// We need to see if nn_key already exists - means we're parsing a list.
// This may require converting na[nn_key] value into []interface{} type.
// First, extract the key:val for the map - it's a singleton.
var key string
var val interface{}
for key, val = range nn {
break
}
// add "#seq" k:v pair -
// Sequence number included even in list elements - this should allow us
// to properly resequence even something goofy like:
// <list>item 1</list>
// <subelement>item 2</subelement>
// <list>item 3</list>
// where all the "list" subelements are decoded into an array.
switch val.(type) {
case map[string]interface{}:
val.(map[string]interface{})["#seq"] = seq
seq++
case interface{}: // a non-nil simple element: string, float64, bool
v := map[string]interface{}{"#text": val, "#seq": seq}
seq++
val = v
}
// 'na' holding sub-elements of n.
// See if 'key' already exists.
// If 'key' exists, then this is a list, if not just add key:val to na.
if v, ok := na[key]; ok {
var a []interface{}
switch v.(type) {
case []interface{}:
a = v.([]interface{})
default: // anything else - note: v.(type) != nil
a = []interface{}{v}
}
a = append(a, val)
na[key] = a
} else {
na[key] = val // save it as a singleton
}
case xml.EndElement:
if skey != "" {
tt := t.(xml.EndElement)
if snakeCaseKeys {
tt.Name.Local = strings.Replace(tt.Name.Local, "-", "_", -1)
}
var name string
if len(tt.Name.Space) > 0 {
name = tt.Name.Space + `:` + tt.Name.Local
} else {
name = tt.Name.Local
}
if skey != name {
return nil, fmt.Errorf("element %s not properly terminated, got %s at #%d",
skey, name, p.InputOffset())
}
}
// len(n) > 0 if this is a simple element w/o xml.Attrs - see xml.CharData case.
if len(n) == 0 {
// If len(na)==0 we have an empty element == "";
// it has no xml.Attr nor xml.CharData.
// Empty element content will be map["etag"]map["#text"]""
// after #seq injection - map["etag"]map["#seq"]seq - after return.
if len(na) > 0 {
n[skey] = na
} else {
n[skey] = "" // empty element
}
}
return n, nil
case xml.CharData:
// clean up possible noise
tt := strings.Trim(string(t.(xml.CharData)), trimRunes)
if xmlEscapeCharsDecoder { // issue#84
tt = escapeChars(tt)
}
if skey == "" {
// per Adrian (http://www.adrianlungu.com/) catch stray text
// in decoder stream -
// https://github.com/clbanning/mxj/pull/14#issuecomment-182816374
// NOTE: CharSetReader must be set to non-UTF-8 CharSet or you'll get
// a p.Token() decoding error when the BOM is UTF-16 or UTF-32.
continue
}
if len(tt) > 0 {
// every simple element is a #text and has #seq associated with it
na["#text"] = cast(tt, r, "")
na["#seq"] = seq
seq++
}
case xml.Comment:
if n == nil { // no root 'key'
n = map[string]interface{}{"#comment": string(t.(xml.Comment))}
return n, NoRoot
}
cm := make(map[string]interface{}, 2)
cm["#text"] = string(t.(xml.Comment))
cm["#seq"] = seq
seq++
na["#comment"] = cm
case xml.Directive:
if n == nil { // no root 'key'
n = map[string]interface{}{"#directive": string(t.(xml.Directive))}
return n, NoRoot
}
dm := make(map[string]interface{}, 2)
dm["#text"] = string(t.(xml.Directive))
dm["#seq"] = seq
seq++
na["#directive"] = dm
case xml.ProcInst:
if n == nil {
na = map[string]interface{}{"#target": t.(xml.ProcInst).Target, "#inst": string(t.(xml.ProcInst).Inst)}
n = map[string]interface{}{"#procinst": na}
return n, NoRoot
}
pm := make(map[string]interface{}, 3)
pm["#target"] = t.(xml.ProcInst).Target
pm["#inst"] = string(t.(xml.ProcInst).Inst)
pm["#seq"] = seq
seq++
na["#procinst"] = pm
default:
// noop - shouldn't ever get here, now, since we handle all token types
}
}
}
// ------------------ END: NewMapXml & NewMapXmlReader -------------------------
// --------------------- mv.XmlSeq & mv.XmlSeqWriter -------------------------
// Xml encodes a MapSeq as XML with elements sorted on #seq. The companion of NewMapXmlSeq().
// The following rules apply.
// - The "#seq" key value is used to seqence the subelements or attributes only.
// - The "#attr" map key identifies the map of attribute map[string]interface{} values with "#text" key.
// - The "#comment" map key identifies a comment in the value "#text" map entry - <!--comment-->.
// - The "#directive" map key identifies a directive in the value "#text" map entry - <!directive>.
// - The "#procinst" map key identifies a process instruction in the value "#target" and "#inst"
// map entries - <?target inst?>.
// - Value type encoding:
// > string, bool, float64, int, int32, int64, float32: per "%v" formating
// > []bool, []uint8: by casting to string
// > structures, etc.: handed to xml.Marshal() - if there is an error, the element
// value is "UNKNOWN"
// - Elements with only attribute values or are null are terminated using "/>" unless XmlGoEmptyElemSystax() called.
// - If len(mv) == 1 and no rootTag is provided, then the map key is used as the root tag, possible.
// Thus, `{ "key":"value" }` encodes as "<key>value</key>".
func (mv MapSeq) Xml(rootTag ...string) ([]byte, error) {
m := map[string]interface{}(mv)
var err error
s := new(string)
p := new(pretty) // just a stub
if len(m) == 1 && len(rootTag) == 0 {
for key, value := range m {
// if it's an array, see if all values are map[string]interface{}
// we force a new root tag if we'll end up with no key:value in the list
// so: key:[string_val, bool:true] --> <doc><key>string_val</key><bool>true</bool></doc>
switch value.(type) {
case []interface{}:
for _, v := range value.([]interface{}) {
switch v.(type) {
case map[string]interface{}: // noop
default: // anything else
err = mapToXmlSeqIndent(false, s, DefaultRootTag, m, p)
goto done
}
}
}
err = mapToXmlSeqIndent(false, s, key, value, p)
}
} else if len(rootTag) == 1 {
err = mapToXmlSeqIndent(false, s, rootTag[0], m, p)
} else {
err = mapToXmlSeqIndent(false, s, DefaultRootTag, m, p)
}
done:
if xmlCheckIsValid {
d := xml.NewDecoder(bytes.NewReader([]byte(*s)))
for {
_, err = d.Token()
if err == io.EOF {
err = nil
break
} else if err != nil {
return nil, err
}
}
}
return []byte(*s), err
}
// The following implementation is provided only for symmetry with NewMapXmlReader[Raw]
// The names will also provide a key for the number of return arguments.
// XmlWriter Writes the MapSeq value as XML on the Writer.
// See MapSeq.Xml() for encoding rules.
func (mv MapSeq) XmlWriter(xmlWriter io.Writer, rootTag ...string) error {
x, err := mv.Xml(rootTag...)
if err != nil {
return err
}
_, err = xmlWriter.Write(x)
return err
}
// XmlWriteRaw writes the MapSeq value as XML on the Writer. []byte is the raw XML that was written.
// See Map.XmlSeq() for encoding rules.
/*
func (mv MapSeq) XmlWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error) {
x, err := mv.Xml(rootTag...)
if err != nil {
return x, err
}
_, err = xmlWriter.Write(x)
return x, err
}
*/
// XmlIndentWriter writes the MapSeq value as pretty XML on the Writer.
// See MapSeq.Xml() for encoding rules.
func (mv MapSeq) XmlIndentWriter(xmlWriter io.Writer, prefix, indent string, rootTag ...string) error {
x, err := mv.XmlIndent(prefix, indent, rootTag...)
if err != nil {
return err
}
_, err = xmlWriter.Write(x)
return err
}
// XmlIndentWriterRaw writes the Map as pretty XML on the Writer. []byte is the raw XML that was written.
// See Map.XmlSeq() for encoding rules.
/*
func (mv MapSeq) XmlIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error) {
x, err := mv.XmlSeqIndent(prefix, indent, rootTag...)
if err != nil {
return x, err
}
_, err = xmlWriter.Write(x)
return x, err
}
*/
// -------------------- END: mv.Xml & mv.XmlWriter -------------------------------
// ---------------------- XmlSeqIndent ----------------------------
// XmlIndent encodes a map[string]interface{} as a pretty XML string.
// See MapSeq.XmlSeq() for encoding rules.
func (mv MapSeq) XmlIndent(prefix, indent string, rootTag ...string) ([]byte, error) {
m := map[string]interface{}(mv)
var err error
s := new(string)
p := new(pretty)
p.indent = indent
p.padding = prefix
if len(m) == 1 && len(rootTag) == 0 {
// this can extract the key for the single map element
// use it if it isn't a key for a list
for key, value := range m {
if _, ok := value.([]interface{}); ok {
err = mapToXmlSeqIndent(true, s, DefaultRootTag, m, p)
} else {
err = mapToXmlSeqIndent(true, s, key, value, p)
}
}
} else if len(rootTag) == 1 {
err = mapToXmlSeqIndent(true, s, rootTag[0], m, p)
} else {
err = mapToXmlSeqIndent(true, s, DefaultRootTag, m, p)
}
if xmlCheckIsValid {
if _, err = NewMapXml([]byte(*s)); err != nil {
return nil, err
}
d := xml.NewDecoder(bytes.NewReader([]byte(*s)))
for {
_, err = d.Token()
if err == io.EOF {
err = nil
break
} else if err != nil {
return nil, err
}
}
}
return []byte(*s), err
}
// where the work actually happens
// returns an error if an attribute is not atomic
func mapToXmlSeqIndent(doIndent bool, s *string, key string, value interface{}, pp *pretty) error {
var endTag bool
var isSimple bool
var noEndTag bool
var elen int
var ss string
p := &pretty{pp.indent, pp.cnt, pp.padding, pp.mapDepth, pp.start}
switch value.(type) {
case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32:
if doIndent {
*s += p.padding
}
if key != "#comment" && key != "#directive" && key != "#procinst" {
*s += `<` + key
}
}
switch value.(type) {
case map[string]interface{}:
val := value.(map[string]interface{})
if key == "#comment" {
*s += `<!--` + val["#text"].(string) + `-->`
noEndTag = true
break
}
if key == "#directive" {
*s += `<!` + val["#text"].(string) + `>`
noEndTag = true
break
}
if key == "#procinst" {
*s += `<?` + val["#target"].(string) + ` ` + val["#inst"].(string) + `?>`
noEndTag = true
break
}
haveAttrs := false
// process attributes first
if v, ok := val["#attr"].(map[string]interface{}); ok {
// First, unroll the map[string]interface{} into a []keyval array.
// Then sequence it.
kv := make([]keyval, len(v))
n := 0
for ak, av := range v {
kv[n] = keyval{ak, av}
n++
}
sort.Sort(elemListSeq(kv))
// Now encode the attributes in original decoding sequence, using keyval array.
for _, a := range kv {
vv := a.v.(map[string]interface{})
switch vv["#text"].(type) {
case string:
if xmlEscapeChars {
ss = escapeChars(vv["#text"].(string))
} else {
ss = vv["#text"].(string)
}
*s += ` ` + a.k + `="` + ss + `"`
case float64, bool, int, int32, int64, float32:
*s += ` ` + a.k + `="` + fmt.Sprintf("%v", vv["#text"]) + `"`
case []byte:
if xmlEscapeChars {
ss = escapeChars(string(vv["#text"].([]byte)))
} else {
ss = string(vv["#text"].([]byte))
}
*s += ` ` + a.k + `="` + ss + `"`
default:
return fmt.Errorf("invalid attribute value for: %s", a.k)
}
}
haveAttrs = true
}
// simple element?
// every map value has, at least, "#seq" and, perhaps, "#text" and/or "#attr"
_, seqOK := val["#seq"] // have key
if v, ok := val["#text"]; ok && ((len(val) == 3 && haveAttrs) || (len(val) == 2 && !haveAttrs)) && seqOK {
if stmp, ok := v.(string); ok && stmp != "" {
if xmlEscapeChars {
stmp = escapeChars(stmp)
}
*s += ">" + stmp
endTag = true
elen = 1
}
isSimple = true
break
} else if !ok && ((len(val) == 2 && haveAttrs) || (len(val) == 1 && !haveAttrs)) && seqOK {
// here no #text but have #seq or #seq+#attr
endTag = false
break
}
// we now need to sequence everything except attributes
// 'kv' will hold everything that needs to be written
kv := make([]keyval, 0)
for k, v := range val {
if k == "#attr" { // already processed
continue
}
if k == "#seq" { // ignore - just for sorting
continue
}
switch v.(type) {
case []interface{}:
// unwind the array as separate entries
for _, vv := range v.([]interface{}) {
kv = append(kv, keyval{k, vv})
}
default:
kv = append(kv, keyval{k, v})
}
}
// close tag with possible attributes
*s += ">"
if doIndent {
*s += "\n"
}
// something more complex
p.mapDepth++
sort.Sort(elemListSeq(kv))
i := 0
for _, v := range kv {
switch v.v.(type) {
case []interface{}:
default:
if i == 0 && doIndent {
p.Indent()
}
}
i++
if err := mapToXmlSeqIndent(doIndent, s, v.k, v.v, p); err != nil {
return err
}
switch v.v.(type) {
case []interface{}: // handled in []interface{} case
default:
if doIndent {
p.Outdent()
}
}
i--
}
p.mapDepth--
endTag = true
elen = 1 // we do have some content other than attrs
case []interface{}:
for _, v := range value.([]interface{}) {
if doIndent {
p.Indent()
}
if err := mapToXmlSeqIndent(doIndent, s, key, v, p); err != nil {
return err
}
if doIndent {
p.Outdent()
}
}
return nil
case nil:
// terminate the tag
if doIndent {
*s += p.padding
}
*s += "<" + key
endTag, isSimple = true, true
break
default: // handle anything - even goofy stuff
elen = 0
switch value.(type) {
case string:
if xmlEscapeChars {
ss = escapeChars(value.(string))
} else {
ss = value.(string)
}
elen = len(ss)
if elen > 0 {
*s += ">" + ss
}
case float64, bool, int, int32, int64, float32:
v := fmt.Sprintf("%v", value)
elen = len(v)
if elen > 0 {
*s += ">" + v
}
case []byte: // NOTE: byte is just an alias for uint8
// similar to how xml.Marshal handles []byte structure members
if xmlEscapeChars {
ss = escapeChars(string(value.([]byte)))
} else {
ss = string(value.([]byte))
}
elen = len(ss)
if elen > 0 {
*s += ">" + ss
}
default:
var v []byte
var err error
if doIndent {
v, err = xml.MarshalIndent(value, p.padding, p.indent)
} else {
v, err = xml.Marshal(value)
}
if err != nil {
*s += ">UNKNOWN"
} else {
elen = len(v)
if elen > 0 {
*s += string(v)
}
}
}
isSimple = true
endTag = true
}
if endTag && !noEndTag {
if doIndent {
if !isSimple {
*s += p.padding
}
}
switch value.(type) {
case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32:
if elen > 0 || useGoXmlEmptyElemSyntax {
if elen == 0 {
*s += ">"
}
*s += `</` + key + ">"
} else {
*s += `/>`
}
}
} else if !noEndTag {
if useGoXmlEmptyElemSyntax {
*s += `</` + key + ">"
// *s += "></" + key + ">"
} else {
*s += "/>"
}
}
if doIndent {
if p.cnt > p.start {
*s += "\n"
}
p.Outdent()
}
return nil
}
// the element sort implementation
type keyval struct {
k string
v interface{}
}
type elemListSeq []keyval
func (e elemListSeq) Len() int {
return len(e)
}
func (e elemListSeq) Swap(i, j int) {
e[i], e[j] = e[j], e[i]
}
func (e elemListSeq) Less(i, j int) bool {
var iseq, jseq int
var fiseq, fjseq float64
var ok bool
if iseq, ok = e[i].v.(map[string]interface{})["#seq"].(int); !ok {
if fiseq, ok = e[i].v.(map[string]interface{})["#seq"].(float64); ok {
iseq = int(fiseq)
} else {
iseq = 9999999
}
}
if jseq, ok = e[j].v.(map[string]interface{})["#seq"].(int); !ok {
if fjseq, ok = e[j].v.(map[string]interface{})["#seq"].(float64); ok {
jseq = int(fjseq)
} else {
jseq = 9999999
}
}
return iseq <= jseq
}
// =============== https://groups.google.com/forum/#!topic/golang-nuts/lHPOHD-8qio
// BeautifyXml (re)formats an XML doc similar to Map.XmlIndent().
// It preserves comments, directives and process instructions,
func BeautifyXml(b []byte, prefix, indent string) ([]byte, error) {
x, err := NewMapXmlSeq(b)
if err != nil {
return nil, err
}
return x.XmlIndent(prefix, indent)
}
// Copyright 2012-2016, 2019 Charles Banning. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
package mxj
// ---------------- expose Map methods to MapSeq type ---------------------------
// Pretty print a Map.
func (msv MapSeq) StringIndent(offset ...int) string {
return writeMap(map[string]interface{}(msv), true, true, offset...)
}
// Pretty print a Map without the value type information - just key:value entries.
func (msv MapSeq) StringIndentNoTypeInfo(offset ...int) string {
return writeMap(map[string]interface{}(msv), false, true, offset...)
}
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
language: go
go_import_path: github.com/pkg/errors
go:
- 1.11.x
- 1.12.x
- 1.13.x
- tip
script:
- make check
Copyright (c) 2015, Dave Cheney <dave@cheney.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
PKGS := github.com/pkg/errors
SRCDIRS := $(shell go list -f '{{.Dir}}' $(PKGS))
GO := go
check: test vet gofmt misspell unconvert staticcheck ineffassign unparam
test:
$(GO) test $(PKGS)
vet: | test
$(GO) vet $(PKGS)
staticcheck:
$(GO) get honnef.co/go/tools/cmd/staticcheck
staticcheck -checks all $(PKGS)
misspell:
$(GO) get github.com/client9/misspell/cmd/misspell
misspell \
-locale GB \
-error \
*.md *.go
unconvert:
$(GO) get github.com/mdempsky/unconvert
unconvert -v $(PKGS)
ineffassign:
$(GO) get github.com/gordonklaus/ineffassign
find $(SRCDIRS) -name '*.go' | xargs ineffassign
pedantic: check errcheck
unparam:
$(GO) get mvdan.cc/unparam
unparam ./...
errcheck:
$(GO) get github.com/kisielk/errcheck
errcheck $(PKGS)
gofmt:
@echo Checking code is gofmted
@test -z "$(shell gofmt -s -l -d -e $(SRCDIRS) | tee /dev/stderr)"
# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge)
Package errors provides simple error handling primitives.
`go get github.com/pkg/errors`
The traditional error handling idiom in Go is roughly akin to
```go
if err != nil {
return err
}
```
which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error.
## Adding context to an error
The errors.Wrap function returns a new error that adds context to the original error. For example
```go
_, err := ioutil.ReadAll(r)
if err != nil {
return errors.Wrap(err, "read failed")
}
```
## Retrieving the cause of an error
Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`.
```go
type causer interface {
Cause() error
}
```
`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example:
```go
switch err := errors.Cause(err).(type) {
case *MyError:
// handle specifically
default:
// unknown error
}
```
[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors).
## Roadmap
With the upcoming [Go2 error proposals](https://go.googlesource.com/proposal/+/master/design/go2draft.md) this package is moving into maintenance mode. The roadmap for a 1.0 release is as follows:
- 0.9. Remove pre Go 1.9 and Go 1.10 support, address outstanding pull requests (if possible)
- 1.0. Final release.
## Contributing
Because of the Go2 errors changes, this package is not accepting proposals for new functionality. With that said, we welcome pull requests, bug fixes and issue reports.
Before sending a PR, please discuss your change by raising an issue.
## License
BSD-2-Clause
version: build-{build}.{branch}
clone_folder: C:\gopath\src\github.com\pkg\errors
shallow_clone: true # for startup speed
environment:
GOPATH: C:\gopath
platform:
- x64
# http://www.appveyor.com/docs/installed-software
install:
# some helpful output for debugging builds
- go version
- go env
# pre-installed MinGW at C:\MinGW is 32bit only
# but MSYS2 at C:\msys64 has mingw64
- set PATH=C:\msys64\mingw64\bin;%PATH%
- gcc --version
- g++ --version
build_script:
- go install -v ./...
test_script:
- set PATH=C:\gopath\bin;%PATH%
- go test -v ./...
#artifacts:
# - path: '%GOPATH%\bin\*.exe'
deploy: off
// Package errors provides simple error handling primitives.
//
// The traditional error handling idiom in Go is roughly akin to
//
// if err != nil {
// return err
// }
//
// which when applied recursively up the call stack results in error reports
// without context or debugging information. The errors package allows
// programmers to add context to the failure path in their code in a way
// that does not destroy the original value of the error.
//
// Adding context to an error
//
// The errors.Wrap function returns a new error that adds context to the
// original error by recording a stack trace at the point Wrap is called,
// together with the supplied message. For example
//
// _, err := ioutil.ReadAll(r)
// if err != nil {
// return errors.Wrap(err, "read failed")
// }
//
// If additional control is required, the errors.WithStack and
// errors.WithMessage functions destructure errors.Wrap into its component
// operations: annotating an error with a stack trace and with a message,
// respectively.
//
// Retrieving the cause of an error
//
// Using errors.Wrap constructs a stack of errors, adding context to the
// preceding error. Depending on the nature of the error it may be necessary
// to reverse the operation of errors.Wrap to retrieve the original error
// for inspection. Any error value which implements this interface
//
// type causer interface {
// Cause() error
// }
//
// can be inspected by errors.Cause. errors.Cause will recursively retrieve
// the topmost error that does not implement causer, which is assumed to be
// the original cause. For example:
//
// switch err := errors.Cause(err).(type) {
// case *MyError:
// // handle specifically
// default:
// // unknown error
// }
//
// Although the causer interface is not exported by this package, it is
// considered a part of its stable public interface.
//
// Formatted printing of errors
//
// All error values returned from this package implement fmt.Formatter and can
// be formatted by the fmt package. The following verbs are supported:
//
// %s print the error. If the error has a Cause it will be
// printed recursively.
// %v see %s
// %+v extended format. Each Frame of the error's StackTrace will
// be printed in detail.
//
// Retrieving the stack trace of an error or wrapper
//
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
// invoked. This information can be retrieved with the following interface:
//
// type stackTracer interface {
// StackTrace() errors.StackTrace
// }
//
// The returned errors.StackTrace type is defined as
//
// type StackTrace []Frame
//
// The Frame type represents a call site in the stack trace. Frame supports
// the fmt.Formatter interface that can be used for printing information about
// the stack trace of this error. For example:
//
// if err, ok := err.(stackTracer); ok {
// for _, f := range err.StackTrace() {
// fmt.Printf("%+s:%d\n", f, f)
// }
// }
//
// Although the stackTracer interface is not exported by this package, it is
// considered a part of its stable public interface.
//
// See the documentation for Frame.Format for more details.
package errors
import (
"fmt"
"io"
)
// New returns an error with the supplied message.
// New also records the stack trace at the point it was called.
func New(message string) error {
return &fundamental{
msg: message,
stack: callers(),
}
}
// Errorf formats according to a format specifier and returns the string
// as a value that satisfies error.
// Errorf also records the stack trace at the point it was called.
func Errorf(format string, args ...interface{}) error {
return &fundamental{
msg: fmt.Sprintf(format, args...),
stack: callers(),
}
}
// fundamental is an error that has a message and a stack, but no caller.
type fundamental struct {
msg string
*stack
}
func (f *fundamental) Error() string { return f.msg }
func (f *fundamental) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
io.WriteString(s, f.msg)
f.stack.Format(s, verb)
return
}
fallthrough
case 's':
io.WriteString(s, f.msg)
case 'q':
fmt.Fprintf(s, "%q", f.msg)
}
}
// WithStack annotates err with a stack trace at the point WithStack was called.
// If err is nil, WithStack returns nil.
func WithStack(err error) error {
if err == nil {
return nil
}
return &withStack{
err,
callers(),
}
}
type withStack struct {
error
*stack
}
func (w *withStack) Cause() error { return w.error }
// Unwrap provides compatibility for Go 1.13 error chains.
func (w *withStack) Unwrap() error { return w.error }
func (w *withStack) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
fmt.Fprintf(s, "%+v", w.Cause())
w.stack.Format(s, verb)
return
}
fallthrough
case 's':
io.WriteString(s, w.Error())
case 'q':
fmt.Fprintf(s, "%q", w.Error())
}
}
// Wrap returns an error annotating err with a stack trace
// at the point Wrap is called, and the supplied message.
// If err is nil, Wrap returns nil.
func Wrap(err error, message string) error {
if err == nil {
return nil
}
err = &withMessage{
cause: err,
msg: message,
}
return &withStack{
err,
callers(),
}
}
// Wrapf returns an error annotating err with a stack trace
// at the point Wrapf is called, and the format specifier.
// If err is nil, Wrapf returns nil.
func Wrapf(err error, format string, args ...interface{}) error {
if err == nil {
return nil
}
err = &withMessage{
cause: err,
msg: fmt.Sprintf(format, args...),
}
return &withStack{
err,
callers(),
}
}
// WithMessage annotates err with a new message.
// If err is nil, WithMessage returns nil.
func WithMessage(err error, message string) error {
if err == nil {
return nil
}
return &withMessage{
cause: err,
msg: message,
}
}
// WithMessagef annotates err with the format specifier.
// If err is nil, WithMessagef returns nil.
func WithMessagef(err error, format string, args ...interface{}) error {
if err == nil {
return nil
}
return &withMessage{
cause: err,
msg: fmt.Sprintf(format, args...),
}
}
type withMessage struct {
cause error
msg string
}
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
func (w *withMessage) Cause() error { return w.cause }
// Unwrap provides compatibility for Go 1.13 error chains.
func (w *withMessage) Unwrap() error { return w.cause }
func (w *withMessage) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
fmt.Fprintf(s, "%+v\n", w.Cause())
io.WriteString(s, w.msg)
return
}
fallthrough
case 's', 'q':
io.WriteString(s, w.Error())
}
}
// Cause returns the underlying cause of the error, if possible.
// An error value has a cause if it implements the following
// interface:
//
// type causer interface {
// Cause() error
// }
//
// If the error does not implement Cause, the original error will
// be returned. If the error is nil, nil will be returned without further
// investigation.
func Cause(err error) error {
type causer interface {
Cause() error
}
for err != nil {
cause, ok := err.(causer)
if !ok {
break
}
err = cause.Cause()
}
return err
}
// +build go1.13
package errors
import (
stderrors "errors"
)
// Is reports whether any error in err's chain matches target.
//
// The chain consists of err itself followed by the sequence of errors obtained by
// repeatedly calling Unwrap.
//
// An error is considered to match a target if it is equal to that target or if
// it implements a method Is(error) bool such that Is(target) returns true.
func Is(err, target error) bool { return stderrors.Is(err, target) }
// As finds the first error in err's chain that matches target, and if so, sets
// target to that error value and returns true.
//
// The chain consists of err itself followed by the sequence of errors obtained by
// repeatedly calling Unwrap.
//
// An error matches target if the error's concrete value is assignable to the value
// pointed to by target, or if the error has a method As(interface{}) bool such that
// As(target) returns true. In the latter case, the As method is responsible for
// setting target.
//
// As will panic if target is not a non-nil pointer to either a type that implements
// error, or to any interface type. As returns false if err is nil.
func As(err error, target interface{}) bool { return stderrors.As(err, target) }
// Unwrap returns the result of calling the Unwrap method on err, if err's
// type contains an Unwrap method returning error.
// Otherwise, Unwrap returns nil.
func Unwrap(err error) error {
return stderrors.Unwrap(err)
}
package errors
import (
"fmt"
"io"
"path"
"runtime"
"strconv"
"strings"
)
// Frame represents a program counter inside a stack frame.
// For historical reasons if Frame is interpreted as a uintptr
// its value represents the program counter + 1.
type Frame uintptr
// pc returns the program counter for this frame;
// multiple frames may have the same PC value.
func (f Frame) pc() uintptr { return uintptr(f) - 1 }
// file returns the full path to the file that contains the
// function for this Frame's pc.
func (f Frame) file() string {
fn := runtime.FuncForPC(f.pc())
if fn == nil {
return "unknown"
}
file, _ := fn.FileLine(f.pc())
return file
}
// line returns the line number of source code of the
// function for this Frame's pc.
func (f Frame) line() int {
fn := runtime.FuncForPC(f.pc())
if fn == nil {
return 0
}
_, line := fn.FileLine(f.pc())
return line
}
// name returns the name of this function, if known.
func (f Frame) name() string {
fn := runtime.FuncForPC(f.pc())
if fn == nil {
return "unknown"
}
return fn.Name()
}
// Format formats the frame according to the fmt.Formatter interface.
//
// %s source file
// %d source line
// %n function name
// %v equivalent to %s:%d
//
// Format accepts flags that alter the printing of some verbs, as follows:
//
// %+s function name and path of source file relative to the compile time
// GOPATH separated by \n\t (<funcname>\n\t<path>)
// %+v equivalent to %+s:%d
func (f Frame) Format(s fmt.State, verb rune) {
switch verb {
case 's':
switch {
case s.Flag('+'):
io.WriteString(s, f.name())
io.WriteString(s, "\n\t")
io.WriteString(s, f.file())
default:
io.WriteString(s, path.Base(f.file()))
}
case 'd':
io.WriteString(s, strconv.Itoa(f.line()))
case 'n':
io.WriteString(s, funcname(f.name()))
case 'v':
f.Format(s, 's')
io.WriteString(s, ":")
f.Format(s, 'd')
}
}
// MarshalText formats a stacktrace Frame as a text string. The output is the
// same as that of fmt.Sprintf("%+v", f), but without newlines or tabs.
func (f Frame) MarshalText() ([]byte, error) {
name := f.name()
if name == "unknown" {
return []byte(name), nil
}
return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil
}
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
type StackTrace []Frame
// Format formats the stack of Frames according to the fmt.Formatter interface.
//
// %s lists source files for each Frame in the stack
// %v lists the source file and line number for each Frame in the stack
//
// Format accepts flags that alter the printing of some verbs, as follows:
//
// %+v Prints filename, function, and line number for each Frame in the stack.
func (st StackTrace) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
switch {
case s.Flag('+'):
for _, f := range st {
io.WriteString(s, "\n")
f.Format(s, verb)
}
case s.Flag('#'):
fmt.Fprintf(s, "%#v", []Frame(st))
default:
st.formatSlice(s, verb)
}
case 's':
st.formatSlice(s, verb)
}
}
// formatSlice will format this StackTrace into the given buffer as a slice of
// Frame, only valid when called with '%s' or '%v'.
func (st StackTrace) formatSlice(s fmt.State, verb rune) {
io.WriteString(s, "[")
for i, f := range st {
if i > 0 {
io.WriteString(s, " ")
}
f.Format(s, verb)
}
io.WriteString(s, "]")
}
// stack represents a stack of program counters.
type stack []uintptr
func (s *stack) Format(st fmt.State, verb rune) {
switch verb {
case 'v':
switch {
case st.Flag('+'):
for _, pc := range *s {
f := Frame(pc)
fmt.Fprintf(st, "\n%+v", f)
}
}
}
}
func (s *stack) StackTrace() StackTrace {
f := make([]Frame, len(*s))
for i := 0; i < len(f); i++ {
f[i] = Frame((*s)[i])
}
return f
}
func callers() *stack {
const depth = 32
var pcs [depth]uintptr
n := runtime.Callers(3, pcs[:])
var st stack = pcs[0:n]
return &st
}
// funcname removes the path prefix component of a function's name reported by func.Name().
func funcname(name string) string {
i := strings.LastIndex(name, "/")
name = name[i+1:]
i = strings.Index(name, ".")
return name[i+1:]
}
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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.
test
\ No newline at end of file
/*
Copyright Suzhou Tongji Fintech Research Institute 2017 All Rights Reserved.
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.
*/
package sm3
import (
"encoding/binary"
"hash"
)
type SM3 struct {
digest [8]uint32 // digest represents the partial evaluation of V
length uint64 // length of the message
unhandleMsg []byte // uint8 //
}
func (sm3 *SM3) ff0(x, y, z uint32) uint32 { return x ^ y ^ z }
func (sm3 *SM3) ff1(x, y, z uint32) uint32 { return (x & y) | (x & z) | (y & z) }
func (sm3 *SM3) gg0(x, y, z uint32) uint32 { return x ^ y ^ z }
func (sm3 *SM3) gg1(x, y, z uint32) uint32 { return (x & y) | (^x & z) }
func (sm3 *SM3) p0(x uint32) uint32 { return x ^ sm3.leftRotate(x, 9) ^ sm3.leftRotate(x, 17) }
func (sm3 *SM3) p1(x uint32) uint32 { return x ^ sm3.leftRotate(x, 15) ^ sm3.leftRotate(x, 23) }
func (sm3 *SM3) leftRotate(x uint32, i uint32) uint32 { return x<<(i%32) | x>>(32-i%32) }
func (sm3 *SM3) pad() []byte {
msg := sm3.unhandleMsg
msg = append(msg, 0x80) // Append '1'
blockSize := 64 // Append until the resulting message length (in bits) is congruent to 448 (mod 512)
for len(msg)%blockSize != 56 {
msg = append(msg, 0x00)
}
// append message length
msg = append(msg, uint8(sm3.length>>56&0xff))
msg = append(msg, uint8(sm3.length>>48&0xff))
msg = append(msg, uint8(sm3.length>>40&0xff))
msg = append(msg, uint8(sm3.length>>32&0xff))
msg = append(msg, uint8(sm3.length>>24&0xff))
msg = append(msg, uint8(sm3.length>>16&0xff))
msg = append(msg, uint8(sm3.length>>8&0xff))
msg = append(msg, uint8(sm3.length>>0&0xff))
if len(msg)%64 != 0 {
panic("------SM3 Pad: error msgLen =")
}
return msg
}
func (sm3 *SM3) update(msg []byte) {
var w [68]uint32
var w1 [64]uint32
a, b, c, d, e, f, g, h := sm3.digest[0], sm3.digest[1], sm3.digest[2], sm3.digest[3], sm3.digest[4], sm3.digest[5], sm3.digest[6], sm3.digest[7]
for len(msg) >= 64 {
for i := 0; i < 16; i++ {
w[i] = binary.BigEndian.Uint32(msg[4*i : 4*(i+1)])
}
for i := 16; i < 68; i++ {
w[i] = sm3.p1(w[i-16]^w[i-9]^sm3.leftRotate(w[i-3], 15)) ^ sm3.leftRotate(w[i-13], 7) ^ w[i-6]
}
for i := 0; i < 64; i++ {
w1[i] = w[i] ^ w[i+4]
}
A, B, C, D, E, F, G, H := a, b, c, d, e, f, g, h
for i := 0; i < 16; i++ {
SS1 := sm3.leftRotate(sm3.leftRotate(A, 12)+E+sm3.leftRotate(0x79cc4519, uint32(i)), 7)
SS2 := SS1 ^ sm3.leftRotate(A, 12)
TT1 := sm3.ff0(A, B, C) + D + SS2 + w1[i]
TT2 := sm3.gg0(E, F, G) + H + SS1 + w[i]
D = C
C = sm3.leftRotate(B, 9)
B = A
A = TT1
H = G
G = sm3.leftRotate(F, 19)
F = E
E = sm3.p0(TT2)
}
for i := 16; i < 64; i++ {
SS1 := sm3.leftRotate(sm3.leftRotate(A, 12)+E+sm3.leftRotate(0x7a879d8a, uint32(i)), 7)
SS2 := SS1 ^ sm3.leftRotate(A, 12)
TT1 := sm3.ff1(A, B, C) + D + SS2 + w1[i]
TT2 := sm3.gg1(E, F, G) + H + SS1 + w[i]
D = C
C = sm3.leftRotate(B, 9)
B = A
A = TT1
H = G
G = sm3.leftRotate(F, 19)
F = E
E = sm3.p0(TT2)
}
a ^= A
b ^= B
c ^= C
d ^= D
e ^= E
f ^= F
g ^= G
h ^= H
msg = msg[64:]
}
sm3.digest[0], sm3.digest[1], sm3.digest[2], sm3.digest[3], sm3.digest[4], sm3.digest[5], sm3.digest[6], sm3.digest[7] = a, b, c, d, e, f, g, h
}
func (sm3 *SM3) update2(msg []byte,) [8]uint32 {
var w [68]uint32
var w1 [64]uint32
a, b, c, d, e, f, g, h := sm3.digest[0], sm3.digest[1], sm3.digest[2], sm3.digest[3], sm3.digest[4], sm3.digest[5], sm3.digest[6], sm3.digest[7]
for len(msg) >= 64 {
for i := 0; i < 16; i++ {
w[i] = binary.BigEndian.Uint32(msg[4*i : 4*(i+1)])
}
for i := 16; i < 68; i++ {
w[i] = sm3.p1(w[i-16]^w[i-9]^sm3.leftRotate(w[i-3], 15)) ^ sm3.leftRotate(w[i-13], 7) ^ w[i-6]
}
for i := 0; i < 64; i++ {
w1[i] = w[i] ^ w[i+4]
}
A, B, C, D, E, F, G, H := a, b, c, d, e, f, g, h
for i := 0; i < 16; i++ {
SS1 := sm3.leftRotate(sm3.leftRotate(A, 12)+E+sm3.leftRotate(0x79cc4519, uint32(i)), 7)
SS2 := SS1 ^ sm3.leftRotate(A, 12)
TT1 := sm3.ff0(A, B, C) + D + SS2 + w1[i]
TT2 := sm3.gg0(E, F, G) + H + SS1 + w[i]
D = C
C = sm3.leftRotate(B, 9)
B = A
A = TT1
H = G
G = sm3.leftRotate(F, 19)
F = E
E = sm3.p0(TT2)
}
for i := 16; i < 64; i++ {
SS1 := sm3.leftRotate(sm3.leftRotate(A, 12)+E+sm3.leftRotate(0x7a879d8a, uint32(i)), 7)
SS2 := SS1 ^ sm3.leftRotate(A, 12)
TT1 := sm3.ff1(A, B, C) + D + SS2 + w1[i]
TT2 := sm3.gg1(E, F, G) + H + SS1 + w[i]
D = C
C = sm3.leftRotate(B, 9)
B = A
A = TT1
H = G
G = sm3.leftRotate(F, 19)
F = E
E = sm3.p0(TT2)
}
a ^= A
b ^= B
c ^= C
d ^= D
e ^= E
f ^= F
g ^= G
h ^= H
msg = msg[64:]
}
var digest [8]uint32
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7] = a, b, c, d, e, f, g, h
return digest
}
// 创建哈希计算实例
func New() hash.Hash {
var sm3 SM3
sm3.Reset()
return &sm3
}
// BlockSize returns the hash's underlying block size.
// The Write method must be able to accept any amount
// of data, but it may operate more efficiently if all writes
// are a multiple of the block size.
func (sm3 *SM3) BlockSize() int { return 64 }
// Size returns the number of bytes Sum will return.
func (sm3 *SM3) Size() int { return 32 }
// Reset clears the internal state by zeroing bytes in the state buffer.
// This can be skipped for a newly-created hash state; the default zero-allocated state is correct.
func (sm3 *SM3) Reset() {
// Reset digest
sm3.digest[0] = 0x7380166f
sm3.digest[1] = 0x4914b2b9
sm3.digest[2] = 0x172442d7
sm3.digest[3] = 0xda8a0600
sm3.digest[4] = 0xa96f30bc
sm3.digest[5] = 0x163138aa
sm3.digest[6] = 0xe38dee4d
sm3.digest[7] = 0xb0fb0e4e
sm3.length = 0 // Reset numberic states
sm3.unhandleMsg = []byte{}
}
// Write (via the embedded io.Writer interface) adds more data to the running hash.
// It never returns an error.
func (sm3 *SM3) Write(p []byte) (int, error) {
toWrite := len(p)
sm3.length += uint64(len(p) * 8)
msg := append(sm3.unhandleMsg, p...)
nblocks := len(msg) / sm3.BlockSize()
sm3.update(msg)
// Update unhandleMsg
sm3.unhandleMsg = msg[nblocks*sm3.BlockSize():]
return toWrite, nil
}
// 返回SM3哈希算法摘要值
// Sum appends the current hash to b and returns the resulting slice.
// It does not change the underlying hash state.
func (sm3 *SM3) Sum(in []byte) []byte {
_, _ = sm3.Write(in)
msg := sm3.pad()
//Finalize
digest := sm3.update2(msg)
// save hash to in
needed := sm3.Size()
if cap(in)-len(in) < needed {
newIn := make([]byte, len(in), len(in)+needed)
copy(newIn, in)
in = newIn
}
out := in[len(in) : len(in)+needed]
for i := 0; i < 8; i++ {
binary.BigEndian.PutUint32(out[i*4:], digest[i])
}
return out
}
func Sm3Sum(data []byte) []byte {
var sm3 SM3
sm3.Reset()
_, _ = sm3.Write(data)
return sm3.Sum(nil)
}
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socks
import (
"context"
"errors"
"io"
"net"
"strconv"
"time"
)
var (
noDeadline = time.Time{}
aLongTimeAgo = time.Unix(1, 0)
)
func (d *Dialer) connect(ctx context.Context, c net.Conn, address string) (_ net.Addr, ctxErr error) {
host, port, err := splitHostPort(address)
if err != nil {
return nil, err
}
if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
c.SetDeadline(deadline)
defer c.SetDeadline(noDeadline)
}
if ctx != context.Background() {
errCh := make(chan error, 1)
done := make(chan struct{})
defer func() {
close(done)
if ctxErr == nil {
ctxErr = <-errCh
}
}()
go func() {
select {
case <-ctx.Done():
c.SetDeadline(aLongTimeAgo)
errCh <- ctx.Err()
case <-done:
errCh <- nil
}
}()
}
b := make([]byte, 0, 6+len(host)) // the size here is just an estimate
b = append(b, Version5)
if len(d.AuthMethods) == 0 || d.Authenticate == nil {
b = append(b, 1, byte(AuthMethodNotRequired))
} else {
ams := d.AuthMethods
if len(ams) > 255 {
return nil, errors.New("too many authentication methods")
}
b = append(b, byte(len(ams)))
for _, am := range ams {
b = append(b, byte(am))
}
}
if _, ctxErr = c.Write(b); ctxErr != nil {
return
}
if _, ctxErr = io.ReadFull(c, b[:2]); ctxErr != nil {
return
}
if b[0] != Version5 {
return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
}
am := AuthMethod(b[1])
if am == AuthMethodNoAcceptableMethods {
return nil, errors.New("no acceptable authentication methods")
}
if d.Authenticate != nil {
if ctxErr = d.Authenticate(ctx, c, am); ctxErr != nil {
return
}
}
b = b[:0]
b = append(b, Version5, byte(d.cmd), 0)
if ip := net.ParseIP(host); ip != nil {
if ip4 := ip.To4(); ip4 != nil {
b = append(b, AddrTypeIPv4)
b = append(b, ip4...)
} else if ip6 := ip.To16(); ip6 != nil {
b = append(b, AddrTypeIPv6)
b = append(b, ip6...)
} else {
return nil, errors.New("unknown address type")
}
} else {
if len(host) > 255 {
return nil, errors.New("FQDN too long")
}
b = append(b, AddrTypeFQDN)
b = append(b, byte(len(host)))
b = append(b, host...)
}
b = append(b, byte(port>>8), byte(port))
if _, ctxErr = c.Write(b); ctxErr != nil {
return
}
if _, ctxErr = io.ReadFull(c, b[:4]); ctxErr != nil {
return
}
if b[0] != Version5 {
return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
}
if cmdErr := Reply(b[1]); cmdErr != StatusSucceeded {
return nil, errors.New("unknown error " + cmdErr.String())
}
if b[2] != 0 {
return nil, errors.New("non-zero reserved field")
}
l := 2
var a Addr
switch b[3] {
case AddrTypeIPv4:
l += net.IPv4len
a.IP = make(net.IP, net.IPv4len)
case AddrTypeIPv6:
l += net.IPv6len
a.IP = make(net.IP, net.IPv6len)
case AddrTypeFQDN:
if _, err := io.ReadFull(c, b[:1]); err != nil {
return nil, err
}
l += int(b[0])
default:
return nil, errors.New("unknown address type " + strconv.Itoa(int(b[3])))
}
if cap(b) < l {
b = make([]byte, l)
} else {
b = b[:l]
}
if _, ctxErr = io.ReadFull(c, b); ctxErr != nil {
return
}
if a.IP != nil {
copy(a.IP, b)
} else {
a.Name = string(b[:len(b)-2])
}
a.Port = int(b[len(b)-2])<<8 | int(b[len(b)-1])
return &a, nil
}
func splitHostPort(address string) (string, int, error) {
host, port, err := net.SplitHostPort(address)
if err != nil {
return "", 0, err
}
portnum, err := strconv.Atoi(port)
if err != nil {
return "", 0, err
}
if 1 > portnum || portnum > 0xffff {
return "", 0, errors.New("port number out of range " + port)
}
return host, portnum, nil
}
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package socks provides a SOCKS version 5 client implementation.
//
// SOCKS protocol version 5 is defined in RFC 1928.
// Username/Password authentication for SOCKS version 5 is defined in
// RFC 1929.
package socks
import (
"context"
"errors"
"io"
"net"
"strconv"
)
// A Command represents a SOCKS command.
type Command int
func (cmd Command) String() string {
switch cmd {
case CmdConnect:
return "socks connect"
case cmdBind:
return "socks bind"
default:
return "socks " + strconv.Itoa(int(cmd))
}
}
// An AuthMethod represents a SOCKS authentication method.
type AuthMethod int
// A Reply represents a SOCKS command reply code.
type Reply int
func (code Reply) String() string {
switch code {
case StatusSucceeded:
return "succeeded"
case 0x01:
return "general SOCKS server failure"
case 0x02:
return "connection not allowed by ruleset"
case 0x03:
return "network unreachable"
case 0x04:
return "host unreachable"
case 0x05:
return "connection refused"
case 0x06:
return "TTL expired"
case 0x07:
return "command not supported"
case 0x08:
return "address type not supported"
default:
return "unknown code: " + strconv.Itoa(int(code))
}
}
// Wire protocol constants.
const (
Version5 = 0x05
AddrTypeIPv4 = 0x01
AddrTypeFQDN = 0x03
AddrTypeIPv6 = 0x04
CmdConnect Command = 0x01 // establishes an active-open forward proxy connection
cmdBind Command = 0x02 // establishes a passive-open forward proxy connection
AuthMethodNotRequired AuthMethod = 0x00 // no authentication required
AuthMethodUsernamePassword AuthMethod = 0x02 // use username/password
AuthMethodNoAcceptableMethods AuthMethod = 0xff // no acceptable authentication methods
StatusSucceeded Reply = 0x00
)
// An Addr represents a SOCKS-specific address.
// Either Name or IP is used exclusively.
type Addr struct {
Name string // fully-qualified domain name
IP net.IP
Port int
}
func (a *Addr) Network() string { return "socks" }
func (a *Addr) String() string {
if a == nil {
return "<nil>"
}
port := strconv.Itoa(a.Port)
if a.IP == nil {
return net.JoinHostPort(a.Name, port)
}
return net.JoinHostPort(a.IP.String(), port)
}
// A Conn represents a forward proxy connection.
type Conn struct {
net.Conn
boundAddr net.Addr
}
// BoundAddr returns the address assigned by the proxy server for
// connecting to the command target address from the proxy server.
func (c *Conn) BoundAddr() net.Addr {
if c == nil {
return nil
}
return c.boundAddr
}
// A Dialer holds SOCKS-specific options.
type Dialer struct {
cmd Command // either CmdConnect or cmdBind
proxyNetwork string // network between a proxy server and a client
proxyAddress string // proxy server address
// ProxyDial specifies the optional dial function for
// establishing the transport connection.
ProxyDial func(context.Context, string, string) (net.Conn, error)
// AuthMethods specifies the list of request authentication
// methods.
// If empty, SOCKS client requests only AuthMethodNotRequired.
AuthMethods []AuthMethod
// Authenticate specifies the optional authentication
// function. It must be non-nil when AuthMethods is not empty.
// It must return an error when the authentication is failed.
Authenticate func(context.Context, io.ReadWriter, AuthMethod) error
}
// DialContext connects to the provided address on the provided
// network.
//
// The returned error value may be a net.OpError. When the Op field of
// net.OpError contains "socks", the Source field contains a proxy
// server address and the Addr field contains a command target
// address.
//
// See func Dial of the net package of standard library for a
// description of the network and address parameters.
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
if err := d.validateTarget(network, address); err != nil {
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
}
if ctx == nil {
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
}
var err error
var c net.Conn
if d.ProxyDial != nil {
c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress)
} else {
var dd net.Dialer
c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress)
}
if err != nil {
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
}
a, err := d.connect(ctx, c, address)
if err != nil {
c.Close()
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
}
return &Conn{Conn: c, boundAddr: a}, nil
}
// DialWithConn initiates a connection from SOCKS server to the target
// network and address using the connection c that is already
// connected to the SOCKS server.
//
// It returns the connection's local address assigned by the SOCKS
// server.
func (d *Dialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) {
if err := d.validateTarget(network, address); err != nil {
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
}
if ctx == nil {
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
}
a, err := d.connect(ctx, c, address)
if err != nil {
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
}
return a, nil
}
// Dial connects to the provided address on the provided network.
//
// Unlike DialContext, it returns a raw transport connection instead
// of a forward proxy connection.
//
// Deprecated: Use DialContext or DialWithConn instead.
func (d *Dialer) Dial(network, address string) (net.Conn, error) {
if err := d.validateTarget(network, address); err != nil {
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
}
var err error
var c net.Conn
if d.ProxyDial != nil {
c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress)
} else {
c, err = net.Dial(d.proxyNetwork, d.proxyAddress)
}
if err != nil {
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
}
if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil {
c.Close()
return nil, err
}
return c, nil
}
func (d *Dialer) validateTarget(network, address string) error {
switch network {
case "tcp", "tcp6", "tcp4":
default:
return errors.New("network not implemented")
}
switch d.cmd {
case CmdConnect, cmdBind:
default:
return errors.New("command not implemented")
}
return nil
}
func (d *Dialer) pathAddrs(address string) (proxy, dst net.Addr, err error) {
for i, s := range []string{d.proxyAddress, address} {
host, port, err := splitHostPort(s)
if err != nil {
return nil, nil, err
}
a := &Addr{Port: port}
a.IP = net.ParseIP(host)
if a.IP == nil {
a.Name = host
}
if i == 0 {
proxy = a
} else {
dst = a
}
}
return
}
// NewDialer returns a new Dialer that dials through the provided
// proxy server's network and address.
func NewDialer(network, address string) *Dialer {
return &Dialer{proxyNetwork: network, proxyAddress: address, cmd: CmdConnect}
}
const (
authUsernamePasswordVersion = 0x01
authStatusSucceeded = 0x00
)
// UsernamePassword are the credentials for the username/password
// authentication method.
type UsernamePassword struct {
Username string
Password string
}
// Authenticate authenticates a pair of username and password with the
// proxy server.
func (up *UsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth AuthMethod) error {
switch auth {
case AuthMethodNotRequired:
return nil
case AuthMethodUsernamePassword:
if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) > 255 {
return errors.New("invalid username/password")
}
b := []byte{authUsernamePasswordVersion}
b = append(b, byte(len(up.Username)))
b = append(b, up.Username...)
b = append(b, byte(len(up.Password)))
b = append(b, up.Password...)
// TODO(mikio): handle IO deadlines and cancelation if
// necessary
if _, err := rw.Write(b); err != nil {
return err
}
if _, err := io.ReadFull(rw, b[:2]); err != nil {
return err
}
if b[0] != authUsernamePasswordVersion {
return errors.New("invalid username/password version")
}
if b[1] != authStatusSucceeded {
return errors.New("username/password authentication failed")
}
return nil
}
return errors.New("unsupported authentication method " + strconv.Itoa(int(auth)))
}
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package proxy
import (
"context"
"net"
)
// A ContextDialer dials using a context.
type ContextDialer interface {
DialContext(ctx context.Context, network, address string) (net.Conn, error)
}
// Dial works like DialContext on net.Dialer but using a dialer returned by FromEnvironment.
//
// The passed ctx is only used for returning the Conn, not the lifetime of the Conn.
//
// Custom dialers (registered via RegisterDialerType) that do not implement ContextDialer
// can leak a goroutine for as long as it takes the underlying Dialer implementation to timeout.
//
// A Conn returned from a successful Dial after the context has been cancelled will be immediately closed.
func Dial(ctx context.Context, network, address string) (net.Conn, error) {
d := FromEnvironment()
if xd, ok := d.(ContextDialer); ok {
return xd.DialContext(ctx, network, address)
}
return dialContext(ctx, d, network, address)
}
// WARNING: this can leak a goroutine for as long as the underlying Dialer implementation takes to timeout
// A Conn returned from a successful Dial after the context has been cancelled will be immediately closed.
func dialContext(ctx context.Context, d Dialer, network, address string) (net.Conn, error) {
var (
conn net.Conn
done = make(chan struct{}, 1)
err error
)
go func() {
conn, err = d.Dial(network, address)
close(done)
if conn != nil && ctx.Err() != nil {
conn.Close()
}
}()
select {
case <-ctx.Done():
err = ctx.Err()
case <-done:
}
return conn, err
}
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package proxy
import (
"context"
"net"
)
type direct struct{}
// Direct implements Dialer by making network connections directly using net.Dial or net.DialContext.
var Direct = direct{}
var (
_ Dialer = Direct
_ ContextDialer = Direct
)
// Dial directly invokes net.Dial with the supplied parameters.
func (direct) Dial(network, addr string) (net.Conn, error) {
return net.Dial(network, addr)
}
// DialContext instantiates a net.Dialer and invokes its DialContext receiver with the supplied parameters.
func (direct) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
var d net.Dialer
return d.DialContext(ctx, network, addr)
}
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package proxy
import (
"context"
"net"
"strings"
)
// A PerHost directs connections to a default Dialer unless the host name
// requested matches one of a number of exceptions.
type PerHost struct {
def, bypass Dialer
bypassNetworks []*net.IPNet
bypassIPs []net.IP
bypassZones []string
bypassHosts []string
}
// NewPerHost returns a PerHost Dialer that directs connections to either
// defaultDialer or bypass, depending on whether the connection matches one of
// the configured rules.
func NewPerHost(defaultDialer, bypass Dialer) *PerHost {
return &PerHost{
def: defaultDialer,
bypass: bypass,
}
}
// Dial connects to the address addr on the given network through either
// defaultDialer or bypass.
func (p *PerHost) Dial(network, addr string) (c net.Conn, err error) {
host, _, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
return p.dialerForRequest(host).Dial(network, addr)
}
// DialContext connects to the address addr on the given network through either
// defaultDialer or bypass.
func (p *PerHost) DialContext(ctx context.Context, network, addr string) (c net.Conn, err error) {
host, _, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
d := p.dialerForRequest(host)
if x, ok := d.(ContextDialer); ok {
return x.DialContext(ctx, network, addr)
}
return dialContext(ctx, d, network, addr)
}
func (p *PerHost) dialerForRequest(host string) Dialer {
if ip := net.ParseIP(host); ip != nil {
for _, net := range p.bypassNetworks {
if net.Contains(ip) {
return p.bypass
}
}
for _, bypassIP := range p.bypassIPs {
if bypassIP.Equal(ip) {
return p.bypass
}
}
return p.def
}
for _, zone := range p.bypassZones {
if strings.HasSuffix(host, zone) {
return p.bypass
}
if host == zone[1:] {
// For a zone ".example.com", we match "example.com"
// too.
return p.bypass
}
}
for _, bypassHost := range p.bypassHosts {
if bypassHost == host {
return p.bypass
}
}
return p.def
}
// AddFromString parses a string that contains comma-separated values
// specifying hosts that should use the bypass proxy. Each value is either an
// IP address, a CIDR range, a zone (*.example.com) or a host name
// (localhost). A best effort is made to parse the string and errors are
// ignored.
func (p *PerHost) AddFromString(s string) {
hosts := strings.Split(s, ",")
for _, host := range hosts {
host = strings.TrimSpace(host)
if len(host) == 0 {
continue
}
if strings.Contains(host, "/") {
// We assume that it's a CIDR address like 127.0.0.0/8
if _, net, err := net.ParseCIDR(host); err == nil {
p.AddNetwork(net)
}
continue
}
if ip := net.ParseIP(host); ip != nil {
p.AddIP(ip)
continue
}
if strings.HasPrefix(host, "*.") {
p.AddZone(host[1:])
continue
}
p.AddHost(host)
}
}
// AddIP specifies an IP address that will use the bypass proxy. Note that
// this will only take effect if a literal IP address is dialed. A connection
// to a named host will never match an IP.
func (p *PerHost) AddIP(ip net.IP) {
p.bypassIPs = append(p.bypassIPs, ip)
}
// AddNetwork specifies an IP range that will use the bypass proxy. Note that
// this will only take effect if a literal IP address is dialed. A connection
// to a named host will never match.
func (p *PerHost) AddNetwork(net *net.IPNet) {
p.bypassNetworks = append(p.bypassNetworks, net)
}
// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
// "example.com" matches "example.com" and all of its subdomains.
func (p *PerHost) AddZone(zone string) {
zone = strings.TrimSuffix(zone, ".")
if !strings.HasPrefix(zone, ".") {
zone = "." + zone
}
p.bypassZones = append(p.bypassZones, zone)
}
// AddHost specifies a host name that will use the bypass proxy.
func (p *PerHost) AddHost(host string) {
host = strings.TrimSuffix(host, ".")
p.bypassHosts = append(p.bypassHosts, host)
}
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package proxy provides support for a variety of protocols to proxy network
// data.
package proxy // import "golang.org/x/net/proxy"
import (
"errors"
"net"
"net/url"
"os"
"sync"
)
// A Dialer is a means to establish a connection.
// Custom dialers should also implement ContextDialer.
type Dialer interface {
// Dial connects to the given address via the proxy.
Dial(network, addr string) (c net.Conn, err error)
}
// Auth contains authentication parameters that specific Dialers may require.
type Auth struct {
User, Password string
}
// FromEnvironment returns the dialer specified by the proxy-related
// variables in the environment and makes underlying connections
// directly.
func FromEnvironment() Dialer {
return FromEnvironmentUsing(Direct)
}
// FromEnvironmentUsing returns the dialer specify by the proxy-related
// variables in the environment and makes underlying connections
// using the provided forwarding Dialer (for instance, a *net.Dialer
// with desired configuration).
func FromEnvironmentUsing(forward Dialer) Dialer {
allProxy := allProxyEnv.Get()
if len(allProxy) == 0 {
return forward
}
proxyURL, err := url.Parse(allProxy)
if err != nil {
return forward
}
proxy, err := FromURL(proxyURL, forward)
if err != nil {
return forward
}
noProxy := noProxyEnv.Get()
if len(noProxy) == 0 {
return proxy
}
perHost := NewPerHost(proxy, forward)
perHost.AddFromString(noProxy)
return perHost
}
// proxySchemes is a map from URL schemes to a function that creates a Dialer
// from a URL with such a scheme.
var proxySchemes map[string]func(*url.URL, Dialer) (Dialer, error)
// RegisterDialerType takes a URL scheme and a function to generate Dialers from
// a URL with that scheme and a forwarding Dialer. Registered schemes are used
// by FromURL.
func RegisterDialerType(scheme string, f func(*url.URL, Dialer) (Dialer, error)) {
if proxySchemes == nil {
proxySchemes = make(map[string]func(*url.URL, Dialer) (Dialer, error))
}
proxySchemes[scheme] = f
}
// FromURL returns a Dialer given a URL specification and an underlying
// Dialer for it to make network requests.
func FromURL(u *url.URL, forward Dialer) (Dialer, error) {
var auth *Auth
if u.User != nil {
auth = new(Auth)
auth.User = u.User.Username()
if p, ok := u.User.Password(); ok {
auth.Password = p
}
}
switch u.Scheme {
case "socks5", "socks5h":
addr := u.Hostname()
port := u.Port()
if port == "" {
port = "1080"
}
return SOCKS5("tcp", net.JoinHostPort(addr, port), auth, forward)
}
// If the scheme doesn't match any of the built-in schemes, see if it
// was registered by another package.
if proxySchemes != nil {
if f, ok := proxySchemes[u.Scheme]; ok {
return f(u, forward)
}
}
return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
}
var (
allProxyEnv = &envOnce{
names: []string{"ALL_PROXY", "all_proxy"},
}
noProxyEnv = &envOnce{
names: []string{"NO_PROXY", "no_proxy"},
}
)
// envOnce looks up an environment variable (optionally by multiple
// names) once. It mitigates expensive lookups on some platforms
// (e.g. Windows).
// (Borrowed from net/http/transport.go)
type envOnce struct {
names []string
once sync.Once
val string
}
func (e *envOnce) Get() string {
e.once.Do(e.init)
return e.val
}
func (e *envOnce) init() {
for _, n := range e.names {
e.val = os.Getenv(n)
if e.val != "" {
return
}
}
}
// reset is used by tests
func (e *envOnce) reset() {
e.once = sync.Once{}
e.val = ""
}
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package proxy
import (
"context"
"net"
"golang.org/x/net/internal/socks"
)
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given
// address with an optional username and password.
// See RFC 1928 and RFC 1929.
func SOCKS5(network, address string, auth *Auth, forward Dialer) (Dialer, error) {
d := socks.NewDialer(network, address)
if forward != nil {
if f, ok := forward.(ContextDialer); ok {
d.ProxyDial = func(ctx context.Context, network string, address string) (net.Conn, error) {
return f.DialContext(ctx, network, address)
}
} else {
d.ProxyDial = func(ctx context.Context, network string, address string) (net.Conn, error) {
return dialContext(ctx, forward, network, address)
}
}
}
if auth != nil {
up := socks.UsernamePassword{
Username: auth.User,
Password: auth.Password,
}
d.AuthMethods = []socks.AuthMethod{
socks.AuthMethodNotRequired,
socks.AuthMethodUsernamePassword,
}
d.Authenticate = up.Authenticate
}
return d, nil
}
# github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4
## explicit; go 1.14
github.com/alibabacloud-go/alibabacloud-gateway-spi/client
# github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9
## explicit; go 1.14
github.com/alibabacloud-go/darabonba-openapi/v2/client
# github.com/alibabacloud-go/debug v1.0.0
## explicit; go 1.18
github.com/alibabacloud-go/debug/debug
# github.com/alibabacloud-go/endpoint-util v1.1.0
## explicit
github.com/alibabacloud-go/endpoint-util/service
# github.com/alibabacloud-go/imm-20200930/v4 v4.5.4
## explicit; go 1.14
github.com/alibabacloud-go/imm-20200930/v4/client
# github.com/alibabacloud-go/openapi-util v0.1.1
## explicit; go 1.14
github.com/alibabacloud-go/openapi-util/service
# github.com/alibabacloud-go/tea v1.2.2
## explicit; go 1.14
github.com/alibabacloud-go/tea/tea
github.com/alibabacloud-go/tea/utils
# github.com/alibabacloud-go/tea-utils/v2 v2.0.6
## explicit; go 1.14
github.com/alibabacloud-go/tea-utils/v2/service
# github.com/alibabacloud-go/tea-xml v1.1.3
## explicit; go 1.14
github.com/alibabacloud-go/tea-xml/service
# github.com/aliyun/credentials-go v1.3.1
## explicit; go 1.14
github.com/aliyun/credentials-go/credentials
github.com/aliyun/credentials-go/credentials/request
github.com/aliyun/credentials-go/credentials/response
github.com/aliyun/credentials-go/credentials/utils
# github.com/bytedance/sonic v1.12.1 # github.com/bytedance/sonic v1.12.1
## explicit; go 1.17 ## explicit; go 1.17
github.com/bytedance/sonic github.com/bytedance/sonic
...@@ -39,6 +73,9 @@ github.com/bytedance/sonic/loader/internal/rt ...@@ -39,6 +73,9 @@ github.com/bytedance/sonic/loader/internal/rt
# github.com/cespare/xxhash/v2 v2.2.0 # github.com/cespare/xxhash/v2 v2.2.0
## explicit; go 1.11 ## explicit; go 1.11
github.com/cespare/xxhash/v2 github.com/cespare/xxhash/v2
# github.com/clbanning/mxj/v2 v2.5.5
## explicit; go 1.15
github.com/clbanning/mxj/v2
# github.com/cloudwego/base64x v0.1.4 # github.com/cloudwego/base64x v0.1.4
## explicit; go 1.16 ## explicit; go 1.16
github.com/cloudwego/base64x github.com/cloudwego/base64x
...@@ -133,6 +170,9 @@ github.com/pelletier/go-toml/v2/internal/characters ...@@ -133,6 +170,9 @@ github.com/pelletier/go-toml/v2/internal/characters
github.com/pelletier/go-toml/v2/internal/danger github.com/pelletier/go-toml/v2/internal/danger
github.com/pelletier/go-toml/v2/internal/tracker github.com/pelletier/go-toml/v2/internal/tracker
github.com/pelletier/go-toml/v2/unstable github.com/pelletier/go-toml/v2/unstable
# github.com/pkg/errors v0.9.1
## explicit
github.com/pkg/errors
# github.com/redis/go-redis/v9 v9.6.1 # github.com/redis/go-redis/v9 v9.6.1
## explicit; go 1.18 ## explicit; go 1.18
github.com/redis/go-redis/v9 github.com/redis/go-redis/v9
...@@ -143,6 +183,9 @@ github.com/redis/go-redis/v9/internal/pool ...@@ -143,6 +183,9 @@ github.com/redis/go-redis/v9/internal/pool
github.com/redis/go-redis/v9/internal/proto github.com/redis/go-redis/v9/internal/proto
github.com/redis/go-redis/v9/internal/rand github.com/redis/go-redis/v9/internal/rand
github.com/redis/go-redis/v9/internal/util github.com/redis/go-redis/v9/internal/util
# github.com/tjfoc/gmsm v1.4.1
## explicit; go 1.14
github.com/tjfoc/gmsm/sm3
# github.com/twitchyliquid64/golang-asm v0.15.1 # github.com/twitchyliquid64/golang-asm v0.15.1
## explicit; go 1.13 ## explicit; go 1.13
github.com/twitchyliquid64/golang-asm/asm/arch github.com/twitchyliquid64/golang-asm/asm/arch
...@@ -180,6 +223,8 @@ golang.org/x/net/http2 ...@@ -180,6 +223,8 @@ golang.org/x/net/http2
golang.org/x/net/http2/h2c golang.org/x/net/http2/h2c
golang.org/x/net/http2/hpack golang.org/x/net/http2/hpack
golang.org/x/net/idna golang.org/x/net/idna
golang.org/x/net/internal/socks
golang.org/x/net/proxy
# golang.org/x/sys v0.24.0 # golang.org/x/sys v0.24.0
## explicit; go 1.18 ## explicit; go 1.18
golang.org/x/sys/cpu golang.org/x/sys/cpu
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment