diff --git a/go.mod b/go.mod
index 0d07982232b..2497c6d905b 100644
--- a/go.mod
+++ b/go.mod
@@ -50,8 +50,8 @@ require (
github.com/spf13/afero v1.15.0
github.com/stretchr/testify v1.11.1
github.com/thanos-io/objstore v0.0.0-20250804093838-71d60dfee488
- github.com/thanos-io/promql-engine v0.0.0-20260429105430-454f16264be9
- github.com/thanos-io/thanos v0.41.0
+ github.com/thanos-io/promql-engine v0.0.0-20260513095632-c5f6038078e3
+ github.com/thanos-io/thanos v0.42.0
github.com/uber/jaeger-client-go v2.30.0+incompatible
github.com/weaveworks/common v0.0.0-20230728070032-dd9e68f319d5
go.etcd.io/etcd/api/v3 v3.5.17
@@ -116,8 +116,9 @@ require (
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect
- github.com/VictoriaMetrics/easyproto v0.1.4 // indirect
+ github.com/VictoriaMetrics/easyproto v1.1.3 // indirect
github.com/alecthomas/kingpin/v2 v2.4.0 // indirect
+ github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.19.25 // indirect
@@ -134,11 +135,13 @@ require (
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.7 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.43.4 // indirect
github.com/aws/smithy-go v1.27.1 // indirect
+ github.com/baidubce/bce-sdk-go v0.9.230 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/caio/go-tdigest v3.1.0+incompatible // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
+ github.com/clbanning/mxj v1.8.4 // indirect
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect
github.com/coder/quartz v0.3.1 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
@@ -180,10 +183,12 @@ require (
github.com/go-openapi/validate v0.25.2 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
+ github.com/gofrs/flock v0.13.0 // indirect
github.com/gogo/googleapis v1.4.1 // indirect
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/btree v1.1.3 // indirect
+ github.com/google/go-querystring v1.1.0 // indirect
github.com/google/pprof v0.0.0-20251213031049-b05bdaca462f // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect
@@ -199,6 +204,7 @@ require (
github.com/hashicorp/go-version v1.9.0 // indirect
github.com/hashicorp/golang-lru v0.6.0 // indirect
github.com/hashicorp/serf v0.10.1 // indirect
+ github.com/huaweicloud/huaweicloud-sdk-go-obs v3.25.4+incompatible // indirect
github.com/jessevdk/go-flags v1.6.1 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/julienschmidt/httprouter v1.3.0 // indirect
@@ -222,15 +228,18 @@ require (
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
+ github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
+ github.com/mozillazg/go-httpheader v0.4.0 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
github.com/ncw/swift v1.0.53 // indirect
github.com/oklog/run v1.2.0 // indirect
github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.142.0 // indirect
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.142.0 // indirect
github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.142.0 // indirect
+ github.com/oracle/oci-go-sdk/v65 v65.93.1 // indirect
github.com/parquet-go/bitpack v1.0.0 // indirect
github.com/parquet-go/jsonlite v1.0.0 // indirect
github.com/philhofer/fwd v1.2.0 // indirect
@@ -253,6 +262,7 @@ require (
github.com/soheilhy/cmux v0.1.5 // indirect
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
+ github.com/tencentyun/cos-go-sdk-v5 v0.7.66 // indirect
github.com/tinylib/msgp v1.6.1 // indirect
github.com/twmb/franz-go v1.21.2 // indirect
github.com/twmb/franz-go/pkg/kmsg v1.13.1 // indirect
@@ -262,6 +272,7 @@ require (
github.com/vimeo/galaxycache v1.3.1 // indirect
github.com/weaveworks/promrus v1.2.0 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
+ github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
github.com/yuin/gopher-lua v1.1.1 // indirect
github.com/zeebo/xxh3 v1.1.0 // indirect
go.opencensus.io v0.24.0 // indirect
@@ -304,7 +315,7 @@ require (
google.golang.org/api v0.257.0 // indirect
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260615183401-62b3387ff324 // indirect
gopkg.in/ini.v1 v1.67.2 // indirect
gopkg.in/telebot.v3 v3.3.8 // indirect
k8s.io/apimachinery v0.34.3 // indirect
diff --git a/go.sum b/go.sum
index 4e235810228..6bf2223791f 100644
--- a/go.sum
+++ b/go.sum
@@ -121,8 +121,9 @@ github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA4
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/VictoriaMetrics/easyproto v0.1.4 h1:r8cNvo8o6sR4QShBXQd1bKw/VVLSQma/V2KhTBPf+Sc=
-github.com/VictoriaMetrics/easyproto v0.1.4/go.mod h1:QlGlzaJnDfFd8Lk6Ci/fuLxfTo3/GThPs2KH23mv710=
+github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
+github.com/VictoriaMetrics/easyproto v1.1.3 h1:gRSA3ZQs7n4+5I+SniDWD59jde1jVq4JmgQ9HUUyvk4=
+github.com/VictoriaMetrics/easyproto v1.1.3/go.mod h1:QlGlzaJnDfFd8Lk6Ci/fuLxfTo3/GThPs2KH23mv710=
github.com/VictoriaMetrics/fastcache v1.13.3 h1:rBabE0iIxcqKEMCwUmwHZ9dgEqXerg8FRbRDUvC7OVc=
github.com/VictoriaMetrics/fastcache v1.13.3/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
github.com/alecthomas/assert/v2 v2.10.0 h1:jjRCHsj6hBJhkmhznrCzoNpbA3zqy0fYiUcYZP/GkPY=
@@ -441,6 +442,7 @@ github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
@@ -451,6 +453,7 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/gogo/status v1.1.1 h1:DuHXlSFHNKqTQ+/ACf5Vs6r4X/dH2EgIzR9Vr+H65kg=
github.com/gogo/status v1.1.1/go.mod h1:jpG3dM5QPcqu19Hg8lkUhBFBa3TcLs1DG7+2Jqci7oU=
+github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang-migrate/migrate/v4 v4.19.1 h1:OCyb44lFuQfYXYLx1SCxPZQGU7mcaZ7gH9yH4jSFbBA=
@@ -515,6 +518,7 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -546,6 +550,7 @@ github.com/google/pprof v0.0.0-20251213031049-b05bdaca462f/go.mod h1:67FPmZWbr+K
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -789,6 +794,7 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFd
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
+github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
github.com/mozillazg/go-httpheader v0.4.0 h1:aBn6aRXtFzyDLZ4VIRLsZbbJloagQfMnCiYgOq6hK4w=
github.com/mozillazg/go-httpheader v0.4.0/go.mod h1:PuT8h0pw6efvp8ZeUec1Rs7dwjK08bt6gKSReGMqtdA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
@@ -944,6 +950,7 @@ github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
+github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/sony/gobreaker v1.0.0 h1:feX5fGGXSl3dYd4aHZItw+FpHLvvoaqkawKjVNiFMNQ=
github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
@@ -982,16 +989,18 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
github.com/tencentyun/cos-go-sdk-v5 v0.7.66 h1:O4O6EsozBoDjxWbltr3iULgkI7WPj/BFNlYTXDuE64E=
github.com/tencentyun/cos-go-sdk-v5 v0.7.66/go.mod h1:8+hG+mQMuRP/OIS9d83syAvXvrMj9HhkND6Q1fLghw0=
github.com/thanos-community/galaxycache v0.0.0-20211122094458-3a32041a1f1e h1:f1Zsv7OAU9iQhZwigp50Yl38W10g/vd5NC8Rdk1Jzng=
github.com/thanos-community/galaxycache v0.0.0-20211122094458-3a32041a1f1e/go.mod h1:jXcofnrSln/cLI6/dhlBxPQZEEQHVPCcFaH75M+nSzM=
github.com/thanos-io/objstore v0.0.0-20250804093838-71d60dfee488 h1:khBsQLLRoF1KzXgTlwFZa6mC32bwYUUAu/AeP49V7UM=
github.com/thanos-io/objstore v0.0.0-20250804093838-71d60dfee488/go.mod h1:uDHLkMKOGDAnlN75EAz8VrRzob1+VbgYSuUleatWuF0=
-github.com/thanos-io/promql-engine v0.0.0-20260429105430-454f16264be9 h1:1lLMuRO2RR068rcJ1Fwl86stfd8N1o01Oa/3MTEqVuc=
-github.com/thanos-io/promql-engine v0.0.0-20260429105430-454f16264be9/go.mod h1:uzn40oZHPXvfdP498h+MiRL2fN7RF519gNaV3LyhChc=
-github.com/thanos-io/thanos v0.41.0 h1:GDPGynjHBa8ORAX7DfluBFjHbMeY1BzjLTGdviFvo7Q=
-github.com/thanos-io/thanos v0.41.0/go.mod h1:ppdHafpAT8WAbcwgLiNU4jNtNe17Ct3xX9dXq+h6g2k=
+github.com/thanos-io/promql-engine v0.0.0-20260513095632-c5f6038078e3 h1:vO9SBpSzzp7jlDaWqJ34Y7zumD5OrPTcCZcWgDgzC6M=
+github.com/thanos-io/promql-engine v0.0.0-20260513095632-c5f6038078e3/go.mod h1:uzn40oZHPXvfdP498h+MiRL2fN7RF519gNaV3LyhChc=
+github.com/thanos-io/thanos v0.42.0 h1:9d+cAEq/lCuhQY6VK3FzxFmXhE/1Qy0Gv45xSYn1EAM=
+github.com/thanos-io/thanos v0.42.0/go.mod h1:9BZsUdHl3x8L/zTrc9+kCEupUHglPi6ldBZvHLj1q6o=
github.com/tinylib/msgp v1.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY=
github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
github.com/tjhop/slog-gokit v0.2.0 h1:tUNkuukDjpswQ2abhsugEobRRxN1aHEW8h4rvwdHMqU=
@@ -1028,6 +1037,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
@@ -1172,8 +1182,11 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
+golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1213,6 +1226,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/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/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1263,6 +1278,10 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+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.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -1299,6 +1318,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/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/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1382,12 +1403,21 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/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.1.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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
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.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
+golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1399,6 +1429,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/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.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1462,6 +1495,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+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/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1604,8 +1639,8 @@ google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuO
google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8=
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260615183401-62b3387ff324 h1:9HZDLIdYBJXAnaFOr9WHrKVycfpY+75s9HGadC0305A=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260615183401-62b3387ff324/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
diff --git a/vendor/github.com/VictoriaMetrics/easyproto/reader.go b/vendor/github.com/VictoriaMetrics/easyproto/reader.go
index c525c1f17af..ea77038f58c 100644
--- a/vendor/github.com/VictoriaMetrics/easyproto/reader.go
+++ b/vendor/github.com/VictoriaMetrics/easyproto/reader.go
@@ -155,6 +155,21 @@ const (
wireTypeI32 = wireType(5)
)
+func (wt wireType) String() string {
+ switch wt {
+ case wireTypeVarint:
+ return "varint"
+ case wireTypeI64:
+ return "i64"
+ case wireTypeLen:
+ return "len"
+ case wireTypeI32:
+ return "i32"
+ default:
+ return fmt.Sprintf("unknown (%d)", int(wt))
+ }
+}
+
// Int32 returns int32 value for fc.
//
// False is returned if fc doesn't contain int32 value.
@@ -231,6 +246,16 @@ func (fc *FieldContext) Bool() (bool, bool) {
return getBool(fc.intValue)
}
+// Enum returns enum value for fc.
+//
+// False is returned if fc doesn't contain enum value.
+func (fc *FieldContext) Enum() (int32, bool) {
+ if fc.wireType != wireTypeVarint {
+ return 0, false
+ }
+ return getInt32(fc.intValue)
+}
+
// Fixed64 returns fixed64 value for fc.
//
// False is returned if fc doesn't contain fixed64 value.
@@ -688,6 +713,360 @@ func (fc *FieldContext) UnpackFloats(dst []float32) ([]float32, bool) {
return dst, true
}
+func (fc *FieldContext) getField(src []byte, fieldNum uint32, neededWireType wireType) (bool, error) {
+ for len(src) > 0 {
+ var err error
+ src, err = fc.NextField(src)
+ if err != nil {
+ return false, fmt.Errorf("cannot read the next field: %w", err)
+ }
+ if fc.FieldNum != fieldNum {
+ continue
+ }
+ if fc.wireType != neededWireType {
+ return false, fmt.Errorf("fieldNum=%d contains unexpected wireType; got %s; want %s", fieldNum, fc.wireType, neededWireType)
+ }
+ return true, nil
+ }
+ return false, nil
+}
+
+// GetInt32 returns the int32 value for the given fieldNum from protobuf-encoded message at src.
+//
+// ok=false is returned if src doesn't contain the given fieldNum.
+//
+// This function is useful when only a single message with the given fieldNum must be obtained from protobuf-encoded src.
+// Otherwise use FieldContext for obtaining multiple message from protobuf-encoded src.
+func GetInt32(src []byte, fieldNum uint32) (n int32, ok bool, err error) {
+ var fc FieldContext
+ ok, err = fc.getField(src, fieldNum, wireTypeVarint)
+ if err != nil {
+ return 0, false, err
+ }
+ if !ok {
+ return 0, false, nil
+ }
+ n, ok = getInt32(fc.intValue)
+ if !ok {
+ return 0, false, fmt.Errorf("fieldNum=%d contains too big integer %d, which cannot be converted to int32", fieldNum, fc.intValue)
+ }
+ return n, true, nil
+}
+
+// GetInt64 returns the int64 value for the given fieldNum from protobuf-encoded message at src.
+//
+// ok=false is returned if src doesn't contain the given fieldNum.
+//
+// This function is useful when only a single message with the given fieldNum must be obtained from protobuf-encoded src.
+// Otherwise use FieldContext for obtaining multiple message from protobuf-encoded src.
+func GetInt64(src []byte, fieldNum uint32) (n int64, ok bool, err error) {
+ var fc FieldContext
+ ok, err = fc.getField(src, fieldNum, wireTypeVarint)
+ if err != nil {
+ return 0, false, err
+ }
+ if !ok {
+ return 0, false, nil
+ }
+ return int64(fc.intValue), true, nil
+}
+
+// GetUint32 returns the uint32 value for the given fieldNum from protobuf-encoded message at src.
+//
+// ok=false is returned if src doesn't contain the given fieldNum.
+//
+// This function is useful when only a single message with the given fieldNum must be obtained from protobuf-encoded src.
+// Otherwise use FieldContext for obtaining multiple message from protobuf-encoded src.
+func GetUint32(src []byte, fieldNum uint32) (n uint32, ok bool, err error) {
+ var fc FieldContext
+ ok, err = fc.getField(src, fieldNum, wireTypeVarint)
+ if err != nil {
+ return 0, false, err
+ }
+ if !ok {
+ return 0, false, nil
+ }
+ n, ok = getUint32(fc.intValue)
+ if !ok {
+ return 0, false, fmt.Errorf("fieldNum=%d contains too big integer %d, which cannot be converted to uint32", fieldNum, fc.intValue)
+ }
+ return n, true, nil
+}
+
+// GetUint64 returns the int64 value for the given fieldNum from protobuf-encoded message at src.
+//
+// ok=false is returned if src doesn't contain the given fieldNum.
+//
+// This function is useful when only a single message with the given fieldNum must be obtained from protobuf-encoded src.
+// Otherwise use FieldContext for obtaining multiple message from protobuf-encoded src.
+func GetUint64(src []byte, fieldNum uint32) (n uint64, ok bool, err error) {
+ var fc FieldContext
+ ok, err = fc.getField(src, fieldNum, wireTypeVarint)
+ if err != nil {
+ return 0, false, err
+ }
+ if !ok {
+ return 0, false, nil
+ }
+ return fc.intValue, true, nil
+}
+
+// GetSint32 returns sint32 value for the given fieldNum from protobuf-encoded message at src.
+//
+// ok=false is returned if src doesn't contain the given fieldNum.
+//
+// This function is useful when only a single message with the given fieldNum must be obtained from protobuf-encoded src.
+// Otherwise use FieldContext for obtaining multiple message from protobuf-encoded src.
+func GetSint32(src []byte, fieldNum uint32) (n int32, ok bool, err error) {
+ var fc FieldContext
+ ok, err = fc.getField(src, fieldNum, wireTypeVarint)
+ if err != nil {
+ return 0, false, err
+ }
+ if !ok {
+ return 0, false, nil
+ }
+ u32, ok := getUint32(fc.intValue)
+ if !ok {
+ return 0, false, fmt.Errorf("fieldNum=%d contains too big integer %d, which cannot be converted to uint32", fieldNum, fc.intValue)
+ }
+ n = decodeZigZagInt32(u32)
+ return n, true, nil
+}
+
+// GetSint64 returns sint64 value for the given fieldNum from protobuf-encoded message at src.
+//
+// ok=false is returned if src doesn't contain the given fieldNum.
+//
+// This function is useful when only a single message with the given fieldNum must be obtained from protobuf-encoded src.
+// Otherwise use FieldContext for obtaining multiple message from protobuf-encoded src.
+func GetSint64(src []byte, fieldNum uint32) (n int64, ok bool, err error) {
+ var fc FieldContext
+ ok, err = fc.getField(src, fieldNum, wireTypeVarint)
+ if err != nil {
+ return 0, false, err
+ }
+ if !ok {
+ return 0, false, nil
+ }
+ n = decodeZigZagInt64(fc.intValue)
+ return n, true, nil
+}
+
+// GetBool returns bool value for the given fieldNum from protobuf-encoded message at src.
+//
+// ok=false is returned if src doesn't contain the given fieldNum.
+//
+// This function is useful when only a single message with the given fieldNum must be obtained from protobuf-encoded src.
+// Otherwise use FieldContext for obtaining multiple message from protobuf-encoded src.
+func GetBool(src []byte, fieldNum uint32) (b bool, ok bool, err error) {
+ var fc FieldContext
+ ok, err = fc.getField(src, fieldNum, wireTypeVarint)
+ if err != nil {
+ return false, false, err
+ }
+ if !ok {
+ return false, false, nil
+ }
+ b, ok = getBool(fc.intValue)
+ if !ok {
+ return false, false, fmt.Errorf("fieldNum=%d contains invalid integer %d, which cannot be converted to bool", fieldNum, fc.intValue)
+ }
+ return b, true, nil
+}
+
+// GetEnum returns enum value for the given fieldNum from protobuf-encoded message at src.
+//
+// ok=false is returned if src doesn't contain the given fieldNum.
+//
+// This function is useful when only a single message with the given fieldNum must be obtained from protobuf-encoded src.
+// Otherwise use FieldContext for obtaining multiple message from protobuf-encoded src.
+func GetEnum(src []byte, fieldNum uint32) (n int32, ok bool, err error) {
+ var fc FieldContext
+ ok, err = fc.getField(src, fieldNum, wireTypeVarint)
+ if err != nil {
+ return 0, false, err
+ }
+ if !ok {
+ return 0, false, nil
+ }
+ n, ok = getInt32(fc.intValue)
+ if !ok {
+ return 0, false, fmt.Errorf("fieldNum=%d contains invalid integer %d, which cannot be converted to enum", fieldNum, fc.intValue)
+ }
+ return n, true, nil
+}
+
+// GetFixed64 returns fixed64 value for the given fieldNum from protobuf-encoded message at src.
+//
+// ok=false is returned if src doesn't contain the given fieldNum.
+//
+// This function is useful when only a single message with the given fieldNum must be obtained from protobuf-encoded src.
+// Otherwise use FieldContext for obtaining multiple message from protobuf-encoded src.
+func GetFixed64(src []byte, fieldNum uint32) (n uint64, ok bool, err error) {
+ var fc FieldContext
+ ok, err = fc.getField(src, fieldNum, wireTypeI64)
+ if err != nil {
+ return 0, false, err
+ }
+ if !ok {
+ return 0, false, nil
+ }
+ return fc.intValue, true, nil
+}
+
+// GetSfixed64 returns sfixed64 value for the given fieldNum from protobuf-encoded message at src.
+//
+// ok=false is returned if src doesn't contain the given fieldNum.
+//
+// This function is useful when only a single message with the given fieldNum must be obtained from protobuf-encoded src.
+// Otherwise use FieldContext for obtaining multiple message from protobuf-encoded src.
+func GetSfixed64(src []byte, fieldNum uint32) (n int64, ok bool, err error) {
+ var fc FieldContext
+ ok, err = fc.getField(src, fieldNum, wireTypeI64)
+ if err != nil {
+ return 0, false, err
+ }
+ if !ok {
+ return 0, false, nil
+ }
+ return int64(fc.intValue), true, nil
+}
+
+// GetDouble returns double value for the given fieldNum from protobuf-encoded message at src.
+//
+// ok=false is returned if src doesn't contain the given fieldNum.
+//
+// This function is useful when only a single message with the given fieldNum must be obtained from protobuf-encoded src.
+// Otherwise use FieldContext for obtaining multiple message from protobuf-encoded src.
+func GetDouble(src []byte, fieldNum uint32) (f float64, ok bool, err error) {
+ var fc FieldContext
+ ok, err = fc.getField(src, fieldNum, wireTypeI64)
+ if err != nil {
+ return 0, false, err
+ }
+ if !ok {
+ return 0, false, nil
+ }
+ f = math.Float64frombits(fc.intValue)
+ return f, true, nil
+}
+
+// GetString returns string value for the given fieldNum from protobuf-encoded message at src.
+//
+// ok=false is returned if src doesn't contain the given fieldNum.
+// The returned string is valid until src is changed.
+//
+// This function is useful when only a single message with the given fieldNum must be obtained from protobuf-encoded src.
+// Otherwise use FieldContext for obtaining multiple message from protobuf-encoded src.
+func GetString(src []byte, fieldNum uint32) (s string, ok bool, err error) {
+ var fc FieldContext
+ ok, err = fc.getField(src, fieldNum, wireTypeLen)
+ if err != nil {
+ return "", false, err
+ }
+ if !ok {
+ return "", false, nil
+ }
+ return unsafeBytesToString(fc.data), true, nil
+}
+
+// GetBytes returns bytes slice for the given fieldNum from protobuf-encoded message at src.
+//
+// ok=false is returned if src doesn't contain the given fieldNum.
+// The returned bytes slice is valid until src is changed.
+//
+// This function is useful when only a single message with the given fieldNum must be obtained from protobuf-encoded src.
+// Otherwise use FieldContext for obtaining multiple message from protobuf-encoded src.
+func GetBytes(src []byte, fieldNum uint32) (b []byte, ok bool, err error) {
+ var fc FieldContext
+ ok, err = fc.getField(src, fieldNum, wireTypeLen)
+ if err != nil {
+ return nil, false, err
+ }
+ if !ok {
+ return nil, false, nil
+ }
+ return fc.data, true, nil
+}
+
+// GetMessageData returns message data for the given fieldNum from protobuf-encoded message at src.
+//
+// ok=false is returned if src doesn't contain the given fieldNum.
+// The returned message data is valid until src is changed.
+//
+// This function is useful when only a single message with the given fieldNum must be obtained from protobuf-encoded src.
+// Otherwise use FieldContext for obtaining multiple message from protobuf-encoded src.
+func GetMessageData(src []byte, fieldNum uint32) (data []byte, ok bool, err error) {
+ var fc FieldContext
+ ok, err = fc.getField(src, fieldNum, wireTypeLen)
+ if err != nil {
+ return nil, false, err
+ }
+ if !ok {
+ return nil, false, nil
+ }
+ return fc.data, true, nil
+}
+
+// GetFixed32 returns fixed32 value for the given fieldNum from protobuf-encoded message at src.
+//
+// ok=false is returned if src doesn't contain the given fieldNum.
+//
+// This function is useful when only a single message with the given fieldNum must be obtained from protobuf-encoded src.
+// Otherwise use FieldContext for obtaining multiple message from protobuf-encoded src.
+func GetFixed32(src []byte, fieldNum uint32) (n uint32, ok bool, err error) {
+ var fc FieldContext
+ ok, err = fc.getField(src, fieldNum, wireTypeI32)
+ if err != nil {
+ return 0, false, err
+ }
+ if !ok {
+ return 0, false, nil
+ }
+ n = mustGetUint32(fc.intValue)
+ return n, true, nil
+}
+
+// GetSfixed32 returns sfixed32 value for the given fieldNum from protobuf-encoded message at src.
+//
+// ok=false is returned if src doesn't contain the given fieldNum.
+//
+// This function is useful when only a single message with the given fieldNum must be obtained from protobuf-encoded src.
+// Otherwise use FieldContext for obtaining multiple message from protobuf-encoded src.
+func GetSfixed32(src []byte, fieldNum uint32) (n int32, ok bool, err error) {
+ var fc FieldContext
+ ok, err = fc.getField(src, fieldNum, wireTypeI32)
+ if err != nil {
+ return 0, false, err
+ }
+ if !ok {
+ return 0, false, nil
+ }
+ n = mustGetInt32(fc.intValue)
+ return n, true, nil
+}
+
+// GetFloat returns float32 value for the given fieldNum from protobuf-encoded message at src.
+//
+// ok=false is returned if src doesn't contain the given fieldNum.
+//
+// This function is useful when only a single message with the given fieldNum must be obtained from protobuf-encoded src.
+// Otherwise use FieldContext for obtaining multiple message from protobuf-encoded src.
+func GetFloat(src []byte, fieldNum uint32) (f float32, ok bool, err error) {
+ var fc FieldContext
+ ok, err = fc.getField(src, fieldNum, wireTypeI32)
+ if err != nil {
+ return 0, false, err
+ }
+ if !ok {
+ return 0, false, nil
+ }
+ u32 := mustGetUint32(fc.intValue)
+ f = math.Float32frombits(u32)
+ return f, true, nil
+}
+
func decodeZigZagInt64(u64 uint64) int64 {
return int64(u64>>1) ^ (int64(u64<<63) >> 63)
}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/LICENSE b/vendor/github.com/aliyun/aliyun-oss-go-sdk/LICENSE
new file mode 100644
index 00000000000..d46e9d128cd
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/LICENSE
@@ -0,0 +1,14 @@
+Copyright (c) 2015 aliyun.com
+
+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.
\ No newline at end of file
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/auth.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/auth.go
new file mode 100644
index 00000000000..2eccf316310
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/auth.go
@@ -0,0 +1,339 @@
+package oss
+
+import (
+ "bytes"
+ "crypto/hmac"
+ "crypto/sha1"
+ "crypto/sha256"
+ "encoding/base64"
+ "encoding/hex"
+ "fmt"
+ "hash"
+ "io"
+ "net/http"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// headerSorter defines the key-value structure for storing the sorted data in signHeader.
+type headerSorter struct {
+ Keys []string
+ Vals []string
+}
+
+// getAdditionalHeaderKeys get exist key in http header
+func (conn Conn) getAdditionalHeaderKeys(req *http.Request) ([]string, map[string]string) {
+ var keysList []string
+ keysMap := make(map[string]string)
+ srcKeys := make(map[string]string)
+
+ for k := range req.Header {
+ srcKeys[strings.ToLower(k)] = ""
+ }
+
+ for _, v := range conn.config.AdditionalHeaders {
+ if _, ok := srcKeys[strings.ToLower(v)]; ok {
+ keysMap[strings.ToLower(v)] = ""
+ }
+ }
+
+ for k := range keysMap {
+ keysList = append(keysList, k)
+ }
+ sort.Strings(keysList)
+ return keysList, keysMap
+}
+
+// getAdditionalHeaderKeysV4 get exist key in http header
+func (conn Conn) getAdditionalHeaderKeysV4(req *http.Request) ([]string, map[string]string) {
+ var keysList []string
+ keysMap := make(map[string]string)
+ srcKeys := make(map[string]string)
+
+ for k := range req.Header {
+ srcKeys[strings.ToLower(k)] = ""
+ }
+
+ for _, v := range conn.config.AdditionalHeaders {
+ if _, ok := srcKeys[strings.ToLower(v)]; ok {
+ if !strings.EqualFold(v, HTTPHeaderContentMD5) && !strings.EqualFold(v, HTTPHeaderContentType) {
+ keysMap[strings.ToLower(v)] = ""
+ }
+ }
+ }
+
+ for k := range keysMap {
+ keysList = append(keysList, k)
+ }
+ sort.Strings(keysList)
+ return keysList, keysMap
+}
+
+// signHeader signs the header and sets it as the authorization header.
+func (conn Conn) signHeader(req *http.Request, canonicalizedResource string, credentials Credentials) {
+ akIf := credentials
+ authorizationStr := ""
+ if conn.config.AuthVersion == AuthV4 {
+ strDay := ""
+ strDate := req.Header.Get(HttpHeaderOssDate)
+ if strDate == "" {
+ strDate = req.Header.Get(HTTPHeaderDate)
+ t, _ := time.Parse(http.TimeFormat, strDate)
+ strDay = t.Format("20060102")
+ } else {
+ t, _ := time.Parse(timeFormatV4, strDate)
+ strDay = t.Format("20060102")
+ }
+ signHeaderProduct := conn.config.GetSignProduct()
+ signHeaderRegion := conn.config.GetSignRegion()
+
+ additionalList, _ := conn.getAdditionalHeaderKeysV4(req)
+ if len(additionalList) > 0 {
+ authorizationFmt := "OSS4-HMAC-SHA256 Credential=%v/%v/%v/" + signHeaderProduct + "/aliyun_v4_request,AdditionalHeaders=%v,Signature=%v"
+ additionnalHeadersStr := strings.Join(additionalList, ";")
+ authorizationStr = fmt.Sprintf(authorizationFmt, akIf.GetAccessKeyID(), strDay, signHeaderRegion, additionnalHeadersStr, conn.getSignedStrV4(req, canonicalizedResource, akIf.GetAccessKeySecret(), nil))
+ } else {
+ authorizationFmt := "OSS4-HMAC-SHA256 Credential=%v/%v/%v/" + signHeaderProduct + "/aliyun_v4_request,Signature=%v"
+ authorizationStr = fmt.Sprintf(authorizationFmt, akIf.GetAccessKeyID(), strDay, signHeaderRegion, conn.getSignedStrV4(req, canonicalizedResource, akIf.GetAccessKeySecret(), nil))
+ }
+ } else if conn.config.AuthVersion == AuthV2 {
+ additionalList, _ := conn.getAdditionalHeaderKeys(req)
+ if len(additionalList) > 0 {
+ authorizationFmt := "OSS2 AccessKeyId:%v,AdditionalHeaders:%v,Signature:%v"
+ additionnalHeadersStr := strings.Join(additionalList, ";")
+ authorizationStr = fmt.Sprintf(authorizationFmt, akIf.GetAccessKeyID(), additionnalHeadersStr, conn.getSignedStr(req, canonicalizedResource, akIf.GetAccessKeySecret()))
+ } else {
+ authorizationFmt := "OSS2 AccessKeyId:%v,Signature:%v"
+ authorizationStr = fmt.Sprintf(authorizationFmt, akIf.GetAccessKeyID(), conn.getSignedStr(req, canonicalizedResource, akIf.GetAccessKeySecret()))
+ }
+ } else {
+ // Get the final authorization string
+ authorizationStr = "OSS " + akIf.GetAccessKeyID() + ":" + conn.getSignedStr(req, canonicalizedResource, akIf.GetAccessKeySecret())
+ }
+
+ // Give the parameter "Authorization" value
+ req.Header.Set(HTTPHeaderAuthorization, authorizationStr)
+}
+
+func (conn Conn) getSignedStr(req *http.Request, canonicalizedResource string, keySecret string) string {
+ // Find out the "x-oss-"'s address in header of the request
+ ossHeadersMap := make(map[string]string)
+ additionalList, additionalMap := conn.getAdditionalHeaderKeys(req)
+ for k, v := range req.Header {
+ if strings.HasPrefix(strings.ToLower(k), "x-oss-") {
+ ossHeadersMap[strings.ToLower(k)] = v[0]
+ } else if conn.config.AuthVersion == AuthV2 {
+ if _, ok := additionalMap[strings.ToLower(k)]; ok {
+ ossHeadersMap[strings.ToLower(k)] = v[0]
+ }
+ }
+ }
+ hs := newHeaderSorter(ossHeadersMap)
+
+ // Sort the ossHeadersMap 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 := req.Header.Get(HTTPHeaderDate)
+ contentType := req.Header.Get(HTTPHeaderContentType)
+ contentMd5 := req.Header.Get(HTTPHeaderContentMD5)
+
+ // default is v1 signature
+ signStr := req.Method + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + canonicalizedResource
+ h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(keySecret))
+
+ // v2 signature
+ if conn.config.AuthVersion == AuthV2 {
+ signStr = req.Method + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + strings.Join(additionalList, ";") + "\n" + canonicalizedResource
+ h = hmac.New(func() hash.Hash { return sha256.New() }, []byte(keySecret))
+ }
+
+ if conn.config.LogLevel >= Debug {
+ conn.config.WriteLog(Debug, "[Req:%p]signStr:%s\n", req, EscapeLFString(signStr))
+ }
+
+ io.WriteString(h, signStr)
+ signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))
+
+ return signedStr
+}
+
+func (conn Conn) getSignedStrV4(req *http.Request, canonicalizedResource string, keySecret string, signingTime *time.Time) string {
+ // Find out the "x-oss-"'s address in header of the request
+ ossHeadersMap := make(map[string]string)
+ additionalList, additionalMap := conn.getAdditionalHeaderKeysV4(req)
+ for k, v := range req.Header {
+ lowKey := strings.ToLower(k)
+ if strings.EqualFold(lowKey, HTTPHeaderContentMD5) ||
+ strings.EqualFold(lowKey, HTTPHeaderContentType) ||
+ strings.HasPrefix(lowKey, "x-oss-") {
+ ossHeadersMap[lowKey] = strings.Trim(v[0], " ")
+ } else {
+ if _, ok := additionalMap[lowKey]; ok {
+ ossHeadersMap[lowKey] = strings.Trim(v[0], " ")
+ }
+ }
+ }
+
+ // get day,eg 20210914
+ //signingTime
+ signDate := ""
+ strDay := ""
+ if signingTime != nil {
+ signDate = signingTime.Format(timeFormatV4)
+ strDay = signingTime.Format(shortTimeFormatV4)
+ } else {
+ var t time.Time
+ // Required parameters
+ if date := req.Header.Get(HTTPHeaderDate); date != "" {
+ signDate = date
+ t, _ = time.Parse(http.TimeFormat, date)
+ }
+
+ if ossDate := req.Header.Get(HttpHeaderOssDate); ossDate != "" {
+ signDate = ossDate
+ t, _ = time.Parse(timeFormatV4, ossDate)
+ }
+
+ strDay = t.Format("20060102")
+ }
+
+ hs := newHeaderSorter(ossHeadersMap)
+
+ // Sort the ossHeadersMap by the ascending order
+ hs.Sort()
+
+ // Get the canonicalizedOSSHeaders
+ canonicalizedOSSHeaders := ""
+ for i := range hs.Keys {
+ canonicalizedOSSHeaders += hs.Keys[i] + ":" + hs.Vals[i] + "\n"
+ }
+
+ signStr := ""
+
+ // v4 signature
+ hashedPayload := DefaultContentSha256
+ if val := req.Header.Get(HttpHeaderOssContentSha256); val != "" {
+ hashedPayload = val
+ }
+
+ // subResource
+ resource := canonicalizedResource
+ subResource := ""
+ subPos := strings.LastIndex(canonicalizedResource, "?")
+ if subPos != -1 {
+ subResource = canonicalizedResource[subPos+1:]
+ resource = canonicalizedResource[0:subPos]
+ }
+
+ // get canonical request
+ canonicalReuqest := req.Method + "\n" + resource + "\n" + subResource + "\n" + canonicalizedOSSHeaders + "\n" + strings.Join(additionalList, ";") + "\n" + hashedPayload
+ rh := sha256.New()
+ io.WriteString(rh, canonicalReuqest)
+ hashedRequest := hex.EncodeToString(rh.Sum(nil))
+
+ if conn.config.LogLevel >= Debug {
+ conn.config.WriteLog(Debug, "[Req:%p]CanonicalRequest:%s\n", req, EscapeLFString(canonicalReuqest))
+ }
+
+ // Product & Region
+ signedStrV4Product := conn.config.GetSignProduct()
+ signedStrV4Region := conn.config.GetSignRegion()
+
+ signStr = "OSS4-HMAC-SHA256" + "\n" + signDate + "\n" + strDay + "/" + signedStrV4Region + "/" + signedStrV4Product + "/aliyun_v4_request" + "\n" + hashedRequest
+ if conn.config.LogLevel >= Debug {
+ conn.config.WriteLog(Debug, "[Req:%p]signStr:%s\n", req, EscapeLFString(signStr))
+ }
+
+ h1 := hmac.New(func() hash.Hash { return sha256.New() }, []byte("aliyun_v4"+keySecret))
+ io.WriteString(h1, strDay)
+ h1Key := h1.Sum(nil)
+
+ h2 := hmac.New(func() hash.Hash { return sha256.New() }, h1Key)
+ io.WriteString(h2, signedStrV4Region)
+ h2Key := h2.Sum(nil)
+
+ h3 := hmac.New(func() hash.Hash { return sha256.New() }, h2Key)
+ io.WriteString(h3, signedStrV4Product)
+ h3Key := h3.Sum(nil)
+
+ h4 := hmac.New(func() hash.Hash { return sha256.New() }, h3Key)
+ io.WriteString(h4, "aliyun_v4_request")
+ h4Key := h4.Sum(nil)
+
+ h := hmac.New(func() hash.Hash { return sha256.New() }, h4Key)
+ io.WriteString(h, signStr)
+ return fmt.Sprintf("%x", h.Sum(nil))
+}
+
+func (conn Conn) getRtmpSignedStr(bucketName, channelName, playlistName string, expiration int64, keySecret string, params map[string]interface{}) string {
+ if params[HTTPParamAccessKeyID] == nil {
+ return ""
+ }
+
+ canonResource := fmt.Sprintf("/%s/%s", bucketName, channelName)
+ canonParamsKeys := []string{}
+ for key := range params {
+ if key != HTTPParamAccessKeyID && key != HTTPParamSignature && key != HTTPParamExpires && key != HTTPParamSecurityToken {
+ canonParamsKeys = append(canonParamsKeys, key)
+ }
+ }
+
+ sort.Strings(canonParamsKeys)
+ canonParamsStr := ""
+ for _, key := range canonParamsKeys {
+ canonParamsStr = fmt.Sprintf("%s%s:%s\n", canonParamsStr, key, params[key].(string))
+ }
+
+ expireStr := strconv.FormatInt(expiration, 10)
+ signStr := expireStr + "\n" + canonParamsStr + canonResource
+
+ h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(keySecret))
+ io.WriteString(h, signStr)
+ signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))
+ return signedStr
+}
+
+// newHeaderSorter is an additional function for function SignHeader.
+func newHeaderSorter(m map[string]string) *headerSorter {
+ hs := &headerSorter{
+ 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 *headerSorter) Sort() {
+ sort.Sort(hs)
+}
+
+// Len is an additional function for function SignHeader.
+func (hs *headerSorter) Len() int {
+ return len(hs.Vals)
+}
+
+// Less is an additional function for function SignHeader.
+func (hs *headerSorter) 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 *headerSorter) 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]
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/bucket.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/bucket.go
new file mode 100644
index 00000000000..26dea3bb202
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/bucket.go
@@ -0,0 +1,1321 @@
+package oss
+
+import (
+ "bytes"
+ "context"
+ "crypto/md5"
+ "encoding/base64"
+ "encoding/xml"
+ "fmt"
+ "hash"
+ "hash/crc64"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "os"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// Bucket implements the operations of object.
+type Bucket struct {
+ Client Client
+ BucketName string
+}
+
+// PutObject creates a new object and it will overwrite the original one if it exists already.
+//
+// objectKey the object key in UTF-8 encoding. The length must be between 1 and 1023, and cannot start with "/" or "\".
+// reader io.Reader instance for reading the data for uploading
+// options the options for uploading the object. The valid options here are CacheControl, ContentDisposition, ContentEncoding
+//
+// Expires, ServerSideEncryption, ObjectACL and Meta. Refer to the link below for more details.
+// https://www.alibabacloud.com/help/en/object-storage-service/latest/putobject
+//
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) PutObject(objectKey string, reader io.Reader, options ...Option) error {
+ opts := AddContentType(options, objectKey)
+
+ request := &PutObjectRequest{
+ ObjectKey: objectKey,
+ Reader: reader,
+ }
+ resp, err := bucket.DoPutObject(request, opts)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ return err
+}
+
+// PutObjectFromFile creates a new object from the local file.
+//
+// objectKey object key.
+// filePath the local file path to upload.
+// options the options for uploading the object. Refer to the parameter options in PutObject for more details.
+//
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) PutObjectFromFile(objectKey, filePath string, options ...Option) error {
+ fd, err := os.Open(filePath)
+ if err != nil {
+ return err
+ }
+ defer fd.Close()
+
+ opts := AddContentType(options, filePath, objectKey)
+
+ request := &PutObjectRequest{
+ ObjectKey: objectKey,
+ Reader: fd,
+ }
+ resp, err := bucket.DoPutObject(request, opts)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ return err
+}
+
+// DoPutObject does the actual upload work.
+//
+// request the request instance for uploading an object.
+// options the options for uploading an object.
+//
+// Response the response from OSS.
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) DoPutObject(request *PutObjectRequest, options []Option) (*Response, error) {
+ isOptSet, _, _ := IsOptionSet(options, HTTPHeaderContentType)
+ if !isOptSet {
+ options = AddContentType(options, request.ObjectKey)
+ }
+
+ listener := GetProgressListener(options)
+
+ params := map[string]interface{}{}
+ resp, err := bucket.do("PUT", request.ObjectKey, params, options, request.Reader, listener)
+ if err != nil {
+ return nil, err
+ }
+ if bucket.GetConfig().IsEnableCRC {
+ err = CheckCRC(resp, "DoPutObject")
+ if err != nil {
+ return resp, err
+ }
+ }
+ err = CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+ body, _ := ioutil.ReadAll(resp.Body)
+ if len(body) > 0 {
+ if err != nil {
+ err = tryConvertServiceError(body, resp, err)
+ } else {
+ rb, _ := FindOption(options, responseBody, nil)
+ if rb != nil {
+ if rbody, ok := rb.(*[]byte); ok {
+ *rbody = body
+ }
+ }
+ }
+ }
+ return resp, err
+}
+
+// GetObject downloads the object.
+//
+// objectKey the object key.
+// options the options for downloading the object. The valid values are: Range, IfModifiedSince, IfUnmodifiedSince, IfMatch,
+//
+// IfNoneMatch, AcceptEncoding. For more details, please check out:
+// https://www.alibabacloud.com/help/en/object-storage-service/latest/getobject
+//
+// io.ReadCloser reader instance for reading data from response. It must be called close() after the usage and only valid when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) GetObject(objectKey string, options ...Option) (io.ReadCloser, error) {
+ result, err := bucket.DoGetObject(&GetObjectRequest{objectKey}, options)
+ if err != nil {
+ return nil, err
+ }
+
+ return result.Response, nil
+}
+
+// GetObjectToFile downloads the data to a local file.
+//
+// objectKey the object key to download.
+// filePath the local file to store the object data.
+// options the options for downloading the object. Refer to the parameter options in method GetObject for more details.
+//
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) GetObjectToFile(objectKey, filePath string, options ...Option) error {
+ tempFilePath := filePath + TempFileSuffix
+
+ // Calls the API to actually download the object. Returns the result instance.
+ result, err := bucket.DoGetObject(&GetObjectRequest{objectKey}, options)
+ if err != nil {
+ return err
+ }
+ defer result.Response.Close()
+
+ // If the local file does not exist, create a new one. If it exists, overwrite it.
+ fd, err := os.OpenFile(tempFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, FilePermMode)
+ if err != nil {
+ return err
+ }
+
+ // Copy the data to the local file path.
+ _, err = io.Copy(fd, result.Response.Body)
+ fd.Close()
+ if err != nil {
+ return err
+ }
+
+ // Compares the CRC value
+ hasRange, _, _ := IsOptionSet(options, HTTPHeaderRange)
+ encodeOpt, _ := FindOption(options, HTTPHeaderAcceptEncoding, nil)
+ acceptEncoding := ""
+ if encodeOpt != nil {
+ acceptEncoding = encodeOpt.(string)
+ }
+ if bucket.GetConfig().IsEnableCRC && !hasRange && acceptEncoding != "gzip" {
+ result.Response.ClientCRC = result.ClientCRC.Sum64()
+ err = CheckCRC(result.Response, "GetObjectToFile")
+ if err != nil {
+ os.Remove(tempFilePath)
+ return err
+ }
+ }
+
+ return os.Rename(tempFilePath, filePath)
+}
+
+// DoGetObject is the actual API that gets the object. It's the internal function called by other public APIs.
+//
+// request the request to download the object.
+// options the options for downloading the file. Checks out the parameter options in method GetObject.
+//
+// GetObjectResult the result instance of getting the object.
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) DoGetObject(request *GetObjectRequest, options []Option) (*GetObjectResult, error) {
+ params, _ := GetRawParams(options)
+ resp, err := bucket.do("GET", request.ObjectKey, params, options, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ result := &GetObjectResult{
+ Response: resp,
+ }
+
+ // CRC
+ var crcCalc hash.Hash64
+ hasRange, _, _ := IsOptionSet(options, HTTPHeaderRange)
+ if bucket.GetConfig().IsEnableCRC && !hasRange {
+ crcCalc = crc64.New(CrcTable())
+ result.ServerCRC = resp.ServerCRC
+ result.ClientCRC = crcCalc
+ }
+
+ // Progress
+ listener := GetProgressListener(options)
+
+ contentLen, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderContentLength), 10, 64)
+ resp.Body = TeeReader(resp.Body, crcCalc, contentLen, listener, nil)
+
+ return result, nil
+}
+
+// CopyObject copies the object inside the bucket.
+//
+// srcObjectKey the source object to copy.
+// destObjectKey the target object to copy.
+// options options for copying an object. You can specify the conditions of copy. The valid conditions are CopySourceIfMatch,
+//
+// CopySourceIfNoneMatch, CopySourceIfModifiedSince, CopySourceIfUnmodifiedSince, MetadataDirective.
+// Also you can specify the target object's attributes, such as CacheControl, ContentDisposition, ContentEncoding, Expires,
+// ServerSideEncryption, ObjectACL, Meta. Refer to the link below for more details :
+// https://www.alibabacloud.com/help/en/object-storage-service/latest/copyobject
+//
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) CopyObject(srcObjectKey, destObjectKey string, options ...Option) (CopyObjectResult, error) {
+ var out CopyObjectResult
+
+ //first find version id
+ versionIdKey := "versionId"
+ versionId, _ := FindOption(options, versionIdKey, nil)
+ if versionId == nil {
+ options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
+ } else {
+ options = DeleteOption(options, versionIdKey)
+ options = append(options, CopySourceVersion(bucket.BucketName, url.QueryEscape(srcObjectKey), versionId.(string)))
+ }
+
+ params := map[string]interface{}{}
+ resp, err := bucket.do("PUT", destObjectKey, params, options, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// CopyObjectTo copies the object to another bucket.
+//
+// srcObjectKey source object key. The source bucket is Bucket.BucketName .
+// destBucketName target bucket name.
+// destObjectKey target object name.
+// options copy options, check out parameter options in function CopyObject for more details.
+//
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) CopyObjectTo(destBucketName, destObjectKey, srcObjectKey string, options ...Option) (CopyObjectResult, error) {
+ return bucket.copy(srcObjectKey, destBucketName, destObjectKey, options...)
+}
+
+// CopyObjectFrom copies the object to another bucket.
+//
+// srcBucketName source bucket name.
+// srcObjectKey source object name.
+// destObjectKey target object name. The target bucket name is Bucket.BucketName.
+// options copy options. Check out parameter options in function CopyObject.
+//
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) CopyObjectFrom(srcBucketName, srcObjectKey, destObjectKey string, options ...Option) (CopyObjectResult, error) {
+ destBucketName := bucket.BucketName
+ var out CopyObjectResult
+ srcBucket, err := bucket.Client.Bucket(srcBucketName)
+ if err != nil {
+ return out, err
+ }
+
+ return srcBucket.copy(srcObjectKey, destBucketName, destObjectKey, options...)
+}
+
+func (bucket Bucket) copy(srcObjectKey, destBucketName, destObjectKey string, options ...Option) (CopyObjectResult, error) {
+ var out CopyObjectResult
+
+ //first find version id
+ versionIdKey := "versionId"
+ versionId, _ := FindOption(options, versionIdKey, nil)
+ if versionId == nil {
+ options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
+ } else {
+ options = DeleteOption(options, versionIdKey)
+ options = append(options, CopySourceVersion(bucket.BucketName, url.QueryEscape(srcObjectKey), versionId.(string)))
+ }
+
+ headers := make(map[string]string)
+ err := handleOptions(headers, options)
+ if err != nil {
+ return out, err
+ }
+ params := map[string]interface{}{}
+
+ ctxArg, _ := FindOption(options, contextArg, nil)
+ ctx, _ := ctxArg.(context.Context)
+
+ resp, err := bucket.Client.Conn.DoWithContext(ctx, "PUT", destBucketName, destObjectKey, params, headers, nil, 0, nil)
+
+ // get response header
+ respHeader, _ := FindOption(options, responseHeader, nil)
+ if respHeader != nil {
+ pRespHeader := respHeader.(*http.Header)
+ if resp != nil {
+ *pRespHeader = resp.Headers
+ }
+ }
+
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// AppendObject uploads the data in the way of appending an existing or new object.
+//
+// AppendObject the parameter appendPosition specifies which postion (in the target object) to append. For the first append (to a non-existing file),
+// the appendPosition should be 0. The appendPosition in the subsequent calls will be the current object length.
+// For example, the first appendObject's appendPosition is 0 and it uploaded 65536 bytes data, then the second call's position is 65536.
+// The response header x-oss-next-append-position after each successful request also specifies the next call's append position (so the caller need not to maintain this information).
+//
+// objectKey the target object to append to.
+// reader io.Reader. The read instance for reading the data to append.
+// appendPosition the start position to append.
+// destObjectProperties the options for the first appending, such as CacheControl, ContentDisposition, ContentEncoding,
+//
+// Expires, ServerSideEncryption, ObjectACL.
+//
+// int64 the next append position, it's valid when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) AppendObject(objectKey string, reader io.Reader, appendPosition int64, options ...Option) (int64, error) {
+ request := &AppendObjectRequest{
+ ObjectKey: objectKey,
+ Reader: reader,
+ Position: appendPosition,
+ }
+
+ result, err := bucket.DoAppendObject(request, options)
+ if err != nil {
+ return appendPosition, err
+ }
+
+ return result.NextPosition, err
+}
+
+// DoAppendObject is the actual API that does the object append.
+//
+// request the request object for appending object.
+// options the options for appending object.
+//
+// AppendObjectResult the result object for appending object.
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) DoAppendObject(request *AppendObjectRequest, options []Option) (*AppendObjectResult, error) {
+ params := map[string]interface{}{}
+ params["append"] = nil
+ params["position"] = strconv.FormatInt(request.Position, 10)
+ headers := make(map[string]string)
+
+ opts := AddContentType(options, request.ObjectKey)
+ handleOptions(headers, opts)
+
+ var initCRC uint64
+ isCRCSet, initCRCOpt, _ := IsOptionSet(options, initCRC64)
+ if isCRCSet {
+ initCRC = initCRCOpt.(uint64)
+ }
+
+ listener := GetProgressListener(options)
+
+ handleOptions(headers, opts)
+
+ ctxArg, _ := FindOption(options, contextArg, nil)
+ ctx, _ := ctxArg.(context.Context)
+
+ resp, err := bucket.Client.Conn.DoWithContext(ctx, "POST", bucket.BucketName, request.ObjectKey, params, headers,
+ request.Reader, initCRC, listener)
+
+ // get response header
+ respHeader, _ := FindOption(options, responseHeader, nil)
+ if respHeader != nil {
+ pRespHeader := respHeader.(*http.Header)
+ if resp != nil {
+ *pRespHeader = resp.Headers
+ }
+ }
+
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ nextPosition, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderOssNextAppendPosition), 10, 64)
+ result := &AppendObjectResult{
+ NextPosition: nextPosition,
+ CRC: resp.ServerCRC,
+ }
+
+ if bucket.GetConfig().IsEnableCRC && isCRCSet {
+ err = CheckCRC(resp, "AppendObject")
+ if err != nil {
+ return result, err
+ }
+ }
+
+ return result, nil
+}
+
+// DeleteObject deletes the object.
+//
+// objectKey the object key to delete.
+//
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) DeleteObject(objectKey string, options ...Option) error {
+ params, _ := GetRawParams(options)
+ resp, err := bucket.do("DELETE", objectKey, params, options, nil, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+// DeleteObjects deletes multiple objects.
+//
+// objectKeys the object keys to delete.
+// options the options for deleting objects.
+//
+// Supported option is DeleteObjectsQuiet which means it will not return error even deletion failed (not recommended). By default it's not used.
+//
+// DeleteObjectsResult the result object.
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) DeleteObjects(objectKeys []string, options ...Option) (DeleteObjectsResult, error) {
+ out := DeleteObjectsResult{}
+ dxml := deleteXML{}
+ for _, key := range objectKeys {
+ dxml.Objects = append(dxml.Objects, DeleteObject{Key: key})
+ }
+ isQuiet, _ := FindOption(options, deleteObjectsQuiet, false)
+ dxml.Quiet = isQuiet.(bool)
+ xmlData := marshalDeleteObjectToXml(dxml)
+ body, err := bucket.DeleteMultipleObjectsXml(xmlData, options...)
+ if err != nil {
+ return out, err
+ }
+ deletedResult := DeleteObjectVersionsResult{}
+ if !dxml.Quiet {
+ if err = xmlUnmarshal(strings.NewReader(body), &deletedResult); err == nil {
+ err = decodeDeleteObjectsResult(&deletedResult)
+ }
+ }
+ // Keep compatibility:need convert to struct DeleteObjectsResult
+ out.XMLName = deletedResult.XMLName
+ for _, v := range deletedResult.DeletedObjectsDetail {
+ out.DeletedObjects = append(out.DeletedObjects, v.Key)
+ }
+ return out, err
+}
+
+// DeleteObjectVersions deletes multiple object versions.
+//
+// objectVersions the object keys and versions to delete.
+// options the options for deleting objects.
+//
+// Supported option is DeleteObjectsQuiet which means it will not return error even deletion failed (not recommended). By default it's not used.
+//
+// DeleteObjectVersionsResult the result object.
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) DeleteObjectVersions(objectVersions []DeleteObject, options ...Option) (DeleteObjectVersionsResult, error) {
+ out := DeleteObjectVersionsResult{}
+ dxml := deleteXML{}
+ dxml.Objects = objectVersions
+ isQuiet, _ := FindOption(options, deleteObjectsQuiet, false)
+ dxml.Quiet = isQuiet.(bool)
+ xmlData := marshalDeleteObjectToXml(dxml)
+ body, err := bucket.DeleteMultipleObjectsXml(xmlData, options...)
+ if err != nil {
+ return out, err
+ }
+ if !dxml.Quiet {
+ if err = xmlUnmarshal(strings.NewReader(body), &out); err == nil {
+ err = decodeDeleteObjectsResult(&out)
+ }
+ }
+ return out, err
+}
+
+// DeleteMultipleObjectsXml deletes multiple object or deletes multiple object versions.
+//
+// xmlData the object keys and versions to delete as the xml format.
+// options the options for deleting objects.
+//
+// string the result response body.
+// error it's nil if no error, otherwise it's an error.
+func (bucket Bucket) DeleteMultipleObjectsXml(xmlData string, options ...Option) (string, error) {
+ buffer := new(bytes.Buffer)
+ bs := []byte(xmlData)
+ buffer.Write(bs)
+ options = append(options, ContentType("application/xml"))
+ sum := md5.Sum(bs)
+ b64 := base64.StdEncoding.EncodeToString(sum[:])
+ options = append(options, ContentMD5(b64))
+ params := map[string]interface{}{}
+ params["delete"] = nil
+ params["encoding-type"] = "url"
+ resp, err := bucket.doInner("POST", "", params, options, buffer, nil)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ body, err := ioutil.ReadAll(resp.Body)
+ out := string(body)
+ return out, err
+}
+
+// IsObjectExist checks if the object exists.
+//
+// bool flag of object's existence (true:exists; false:non-exist) when error is nil.
+//
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) IsObjectExist(objectKey string, options ...Option) (bool, error) {
+ _, err := bucket.GetObjectMeta(objectKey, options...)
+ if err == nil {
+ return true, nil
+ }
+
+ switch err.(type) {
+ case ServiceError:
+ if err.(ServiceError).StatusCode == 404 {
+ return false, nil
+ }
+ }
+
+ return false, err
+}
+
+// ListObjects lists the objects under the current bucket.
+//
+// options it contains all the filters for listing objects.
+//
+// It could specify a prefix filter on object keys, the max keys count to return and the object key marker and the delimiter for grouping object names.
+// The key marker means the returned objects' key must be greater than it in lexicographic order.
+//
+// For example, if the bucket has 8 objects, my-object-1, my-object-11, my-object-2, my-object-21,
+// my-object-22, my-object-3, my-object-31, my-object-32. If the prefix is my-object-2 (no other filters), then it returns
+// my-object-2, my-object-21, my-object-22 three objects. If the marker is my-object-22 (no other filters), then it returns
+// my-object-3, my-object-31, my-object-32 three objects. If the max keys is 5, then it returns 5 objects.
+// The three filters could be used together to achieve filter and paging functionality.
+// If the prefix is the folder name, then it could list all files under this folder (including the files under its subfolders).
+// But if the delimiter is specified with '/', then it only returns that folder's files (no subfolder's files). The direct subfolders are in the commonPrefixes properties.
+// For example, if the bucket has three objects fun/test.jpg, fun/movie/001.avi, fun/movie/007.avi. And if the prefix is "fun/", then it returns all three objects.
+// But if the delimiter is '/', then only "fun/test.jpg" is returned as files and fun/movie/ is returned as common prefix.
+//
+// For common usage scenario, check out sample/list_object.go.
+//
+// ListObjectsResult the return value after operation succeeds (only valid when error is nil).
+func (bucket Bucket) ListObjects(options ...Option) (ListObjectsResult, error) {
+ var out ListObjectsResult
+
+ options = append(options, EncodingType("url"))
+ params, err := GetRawParams(options)
+ if err != nil {
+ return out, err
+ }
+
+ resp, err := bucket.doInner("GET", "", params, options, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ if err != nil {
+ return out, err
+ }
+
+ err = decodeListObjectsResult(&out)
+ return out, err
+}
+
+// ListObjectsV2 lists the objects under the current bucket.
+// Recommend to use ListObjectsV2 to replace ListObjects
+// ListObjectsResultV2 the return value after operation succeeds (only valid when error is nil).
+func (bucket Bucket) ListObjectsV2(options ...Option) (ListObjectsResultV2, error) {
+ var out ListObjectsResultV2
+
+ options = append(options, EncodingType("url"))
+ options = append(options, ListType(2))
+ params, err := GetRawParams(options)
+ if err != nil {
+ return out, err
+ }
+
+ resp, err := bucket.doInner("GET", "", params, options, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ if err != nil {
+ return out, err
+ }
+
+ err = decodeListObjectsResultV2(&out)
+ return out, err
+}
+
+// ListObjectVersions lists objects of all versions under the current bucket.
+func (bucket Bucket) ListObjectVersions(options ...Option) (ListObjectVersionsResult, error) {
+ var out ListObjectVersionsResult
+
+ options = append(options, EncodingType("url"))
+ params, err := GetRawParams(options)
+ if err != nil {
+ return out, err
+ }
+ params["versions"] = nil
+
+ resp, err := bucket.doInner("GET", "", params, options, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ if err != nil {
+ return out, err
+ }
+
+ err = decodeListObjectVersionsResult(&out)
+ return out, err
+}
+
+// SetObjectMeta sets the metadata of the Object.
+//
+// objectKey object
+// options options for setting the metadata. The valid options are CacheControl, ContentDisposition, ContentEncoding, Expires,
+//
+// ServerSideEncryption, and custom metadata.
+//
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) SetObjectMeta(objectKey string, options ...Option) error {
+ options = append(options, MetadataDirective(MetaReplace))
+ _, err := bucket.CopyObject(objectKey, objectKey, options...)
+ return err
+}
+
+// GetObjectDetailedMeta gets the object's detailed metadata
+//
+// objectKey object key.
+// options the constraints of the object. Only when the object meets the requirements this method will return the metadata. Otherwise returns error. Valid options are IfModifiedSince, IfUnmodifiedSince,
+//
+// IfMatch, IfNoneMatch. For more details check out https://www.alibabacloud.com/help/en/object-storage-service/latest/headobject
+//
+// http.Header object meta when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) GetObjectDetailedMeta(objectKey string, options ...Option) (http.Header, error) {
+ params, _ := GetRawParams(options)
+ resp, err := bucket.do("HEAD", objectKey, params, options, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ return resp.Headers, nil
+}
+
+// GetObjectMeta gets object metadata.
+//
+// GetObjectMeta is more lightweight than GetObjectDetailedMeta as it only returns basic metadata including ETag
+// size, LastModified. The size information is in the HTTP header Content-Length.
+//
+// objectKey object key
+//
+// http.Header the object's metadata, valid when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) GetObjectMeta(objectKey string, options ...Option) (http.Header, error) {
+ params, _ := GetRawParams(options)
+ params["objectMeta"] = nil
+ //resp, err := bucket.do("GET", objectKey, "?objectMeta", "", nil, nil, nil)
+ resp, err := bucket.do("HEAD", objectKey, params, options, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ return resp.Headers, nil
+}
+
+// SetObjectACL updates the object's ACL.
+//
+// Only the bucket's owner could update object's ACL which priority is higher than bucket's ACL.
+// For example, if the bucket ACL is private and object's ACL is public-read-write.
+// Then object's ACL is used and it means all users could read or write that object.
+// When the object's ACL is not set, then bucket's ACL is used as the object's ACL.
+//
+// Object read operations include GetObject, HeadObject, CopyObject and UploadPartCopy on the source object;
+// Object write operations include PutObject, PostObject, AppendObject, DeleteObject, DeleteMultipleObjects,
+// CompleteMultipartUpload and CopyObject on target object.
+//
+// objectKey the target object key (to set the ACL on)
+// objectAcl object ACL. Valid options are PrivateACL, PublicReadACL, PublicReadWriteACL.
+//
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) SetObjectACL(objectKey string, objectACL ACLType, options ...Option) error {
+ options = append(options, ObjectACL(objectACL))
+ params, _ := GetRawParams(options)
+ params["acl"] = nil
+ resp, err := bucket.do("PUT", objectKey, params, options, nil, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetObjectACL gets object's ACL
+//
+// objectKey the object to get ACL from.
+//
+// GetObjectACLResult the result object when error is nil. GetObjectACLResult.Acl is the object ACL.
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) GetObjectACL(objectKey string, options ...Option) (GetObjectACLResult, error) {
+ var out GetObjectACLResult
+ params, _ := GetRawParams(options)
+ params["acl"] = nil
+ resp, err := bucket.do("GET", objectKey, params, options, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// PutSymlink creates a symlink (to point to an existing object)
+//
+// Symlink cannot point to another symlink.
+// When creating a symlink, it does not check the existence of the target file, and does not check if the target file is symlink.
+// Neither it checks the caller's permission on the target file. All these checks are deferred to the actual GetObject call via this symlink.
+// If trying to add an existing file, as long as the caller has the write permission, the existing one will be overwritten.
+// If the x-oss-meta- is specified, it will be added as the metadata of the symlink file.
+//
+// symObjectKey the symlink object's key.
+// targetObjectKey the target object key to point to.
+//
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) PutSymlink(symObjectKey string, targetObjectKey string, options ...Option) error {
+ options = append(options, symlinkTarget(url.QueryEscape(targetObjectKey)))
+ params, _ := GetRawParams(options)
+ params["symlink"] = nil
+ resp, err := bucket.do("PUT", symObjectKey, params, options, nil, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetSymlink gets the symlink object with the specified key.
+// If the symlink object does not exist, returns 404.
+//
+// objectKey the symlink object's key.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+// When error is nil, the target file key is in the X-Oss-Symlink-Target header of the returned object.
+func (bucket Bucket) GetSymlink(objectKey string, options ...Option) (http.Header, error) {
+ params, _ := GetRawParams(options)
+ params["symlink"] = nil
+ resp, err := bucket.do("GET", objectKey, params, options, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ targetObjectKey := resp.Headers.Get(HTTPHeaderOssSymlinkTarget)
+ targetObjectKey, err = url.QueryUnescape(targetObjectKey)
+ if err != nil {
+ return resp.Headers, err
+ }
+ resp.Headers.Set(HTTPHeaderOssSymlinkTarget, targetObjectKey)
+ return resp.Headers, err
+}
+
+// RestoreObject restores the object from the archive storage.
+//
+// An archive object is in cold status by default and it cannot be accessed.
+// When restore is called on the cold object, it will become available for access after some time.
+// If multiple restores are called on the same file when the object is being restored, server side does nothing for additional calls but returns success.
+// By default, the restored object is available for access for one day. After that it will be unavailable again.
+// But if another RestoreObject are called after the file is restored, then it will extend one day's access time of that object, up to 7 days.
+//
+// objectKey object key to restore.
+//
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) RestoreObject(objectKey string, options ...Option) error {
+ params, _ := GetRawParams(options)
+ params["restore"] = nil
+ resp, err := bucket.do("POST", objectKey, params, options, nil, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK, http.StatusAccepted})
+}
+
+// RestoreObjectDetail support more features than RestoreObject
+func (bucket Bucket) RestoreObjectDetail(objectKey string, restoreConfig RestoreConfiguration, options ...Option) error {
+ if restoreConfig.Tier == "" {
+ // Expedited, Standard, Bulk
+ restoreConfig.Tier = string(RestoreStandard)
+ }
+
+ if restoreConfig.Days == 0 {
+ restoreConfig.Days = 1
+ }
+
+ bs, err := xml.Marshal(restoreConfig)
+ if err != nil {
+ return err
+ }
+
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ options = append(options, ContentType(contentType))
+
+ params, _ := GetRawParams(options)
+ params["restore"] = nil
+
+ resp, err := bucket.do("POST", objectKey, params, options, buffer, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK, http.StatusAccepted})
+}
+
+// RestoreObjectXML support more features than RestoreObject
+func (bucket Bucket) RestoreObjectXML(objectKey, configXML string, options ...Option) error {
+ buffer := new(bytes.Buffer)
+ buffer.Write([]byte(configXML))
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ options = append(options, ContentType(contentType))
+
+ params, _ := GetRawParams(options)
+ params["restore"] = nil
+
+ resp, err := bucket.do("POST", objectKey, params, options, buffer, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK, http.StatusAccepted})
+}
+
+// SignURL signs the URL. Users could access the object directly with this URL without getting the AK.
+//
+// objectKey the target object to sign.
+// signURLConfig the configuration for the signed URL
+//
+// string returns the signed URL, when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) SignURL(objectKey string, method HTTPMethod, expiredInSec int64, options ...Option) (string, error) {
+ err := CheckObjectNameEx(objectKey, isVerifyObjectStrict(bucket.GetConfig()))
+ if err != nil {
+ return "", err
+ }
+
+ if expiredInSec < 0 {
+ return "", fmt.Errorf("invalid expires: %d, expires must bigger than 0", expiredInSec)
+ }
+ expiration := time.Now().Unix() + expiredInSec
+
+ params, err := GetRawParams(options)
+ if err != nil {
+ return "", err
+ }
+
+ headers := make(map[string]string)
+ err = handleOptions(headers, options)
+ if err != nil {
+ return "", err
+ }
+
+ return bucket.Client.Conn.signURL(method, bucket.BucketName, objectKey, expiration, params, headers)
+}
+
+// PutObjectWithURL uploads an object with the URL. If the object exists, it will be overwritten.
+// PutObjectWithURL It will not generate minetype according to the key name.
+//
+// signedURL signed URL.
+// reader io.Reader the read instance for reading the data for the upload.
+// options the options for uploading the data. The valid options are CacheControl, ContentDisposition, ContentEncoding,
+//
+// Expires, ServerSideEncryption, ObjectACL and custom metadata. Check out the following link for details:
+// https://www.alibabacloud.com/help/en/object-storage-service/latest/putobject
+//
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) PutObjectWithURL(signedURL string, reader io.Reader, options ...Option) error {
+ resp, err := bucket.DoPutObjectWithURL(signedURL, reader, options)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ return err
+}
+
+// PutObjectFromFileWithURL uploads an object from a local file with the signed URL.
+// PutObjectFromFileWithURL It does not generate mimetype according to object key's name or the local file name.
+//
+// signedURL the signed URL.
+// filePath local file path, such as dirfile.txt, for uploading.
+// options options for uploading, same as the options in PutObject function.
+//
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) PutObjectFromFileWithURL(signedURL, filePath string, options ...Option) error {
+ fd, err := os.Open(filePath)
+ if err != nil {
+ return err
+ }
+ defer fd.Close()
+
+ resp, err := bucket.DoPutObjectWithURL(signedURL, fd, options)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ return err
+}
+
+// DoPutObjectWithURL is the actual API that does the upload with URL work(internal for SDK)
+//
+// signedURL the signed URL.
+// reader io.Reader the read instance for getting the data to upload.
+// options options for uploading.
+//
+// Response the response object which contains the HTTP response.
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) DoPutObjectWithURL(signedURL string, reader io.Reader, options []Option) (*Response, error) {
+ listener := GetProgressListener(options)
+
+ params := map[string]interface{}{}
+ resp, err := bucket.doURL("PUT", signedURL, params, options, reader, listener)
+ if err != nil {
+ return nil, err
+ }
+
+ if bucket.GetConfig().IsEnableCRC {
+ err = CheckCRC(resp, "DoPutObjectWithURL")
+ if err != nil {
+ return resp, err
+ }
+ }
+
+ err = CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+
+ return resp, err
+}
+
+// GetObjectWithURL downloads the object and returns the reader instance, with the signed URL.
+//
+// signedURL the signed URL.
+// options options for downloading the object. Valid options are IfModifiedSince, IfUnmodifiedSince, IfMatch,
+//
+// IfNoneMatch, AcceptEncoding. For more information, check out the following link:
+// https://www.alibabacloud.com/help/en/object-storage-service/latest/getobject
+//
+// io.ReadCloser the reader object for getting the data from response. It needs be closed after the usage. It's only valid when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) GetObjectWithURL(signedURL string, options ...Option) (io.ReadCloser, error) {
+ result, err := bucket.DoGetObjectWithURL(signedURL, options)
+ if err != nil {
+ return nil, err
+ }
+ return result.Response, nil
+}
+
+// GetObjectToFileWithURL downloads the object into a local file with the signed URL.
+//
+// signedURL the signed URL
+// filePath the local file path to download to.
+// options the options for downloading object. Check out the parameter options in function GetObject for the reference.
+//
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) GetObjectToFileWithURL(signedURL, filePath string, options ...Option) error {
+ tempFilePath := filePath + TempFileSuffix
+
+ // Get the object's content
+ result, err := bucket.DoGetObjectWithURL(signedURL, options)
+ if err != nil {
+ return err
+ }
+ defer result.Response.Close()
+
+ // If the file does not exist, create one. If exists, then overwrite it.
+ fd, err := os.OpenFile(tempFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, FilePermMode)
+ if err != nil {
+ return err
+ }
+
+ // Save the data to the file.
+ _, err = io.Copy(fd, result.Response.Body)
+ fd.Close()
+ if err != nil {
+ return err
+ }
+
+ // Compare the CRC value. If CRC values do not match, return error.
+ hasRange, _, _ := IsOptionSet(options, HTTPHeaderRange)
+ encodeOpt, _ := FindOption(options, HTTPHeaderAcceptEncoding, nil)
+ acceptEncoding := ""
+ if encodeOpt != nil {
+ acceptEncoding = encodeOpt.(string)
+ }
+
+ if bucket.GetConfig().IsEnableCRC && !hasRange && acceptEncoding != "gzip" {
+ result.Response.ClientCRC = result.ClientCRC.Sum64()
+ err = CheckCRC(result.Response, "GetObjectToFileWithURL")
+ if err != nil {
+ os.Remove(tempFilePath)
+ return err
+ }
+ }
+
+ return os.Rename(tempFilePath, filePath)
+}
+
+// DoGetObjectWithURL is the actual API that downloads the file with the signed URL.
+//
+// signedURL the signed URL.
+// options the options for getting object. Check out parameter options in GetObject for the reference.
+//
+// GetObjectResult the result object when the error is nil.
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) DoGetObjectWithURL(signedURL string, options []Option) (*GetObjectResult, error) {
+ params, _ := GetRawParams(options)
+ resp, err := bucket.doURL("GET", signedURL, params, options, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ result := &GetObjectResult{
+ Response: resp,
+ }
+
+ // CRC
+ var crcCalc hash.Hash64
+ hasRange, _, _ := IsOptionSet(options, HTTPHeaderRange)
+ if bucket.GetConfig().IsEnableCRC && !hasRange {
+ crcCalc = crc64.New(CrcTable())
+ result.ServerCRC = resp.ServerCRC
+ result.ClientCRC = crcCalc
+ }
+
+ // Progress
+ listener := GetProgressListener(options)
+
+ contentLen, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderContentLength), 10, 64)
+ resp.Body = TeeReader(resp.Body, crcCalc, contentLen, listener, nil)
+
+ return result, nil
+}
+
+// ProcessObject apply process on the specified image file.
+//
+// The supported process includes resize, rotate, crop, watermark, format,
+// udf, customized style, etc.
+//
+// objectKey object key to process.
+// process process string, such as "image/resize,w_100|sys/saveas,o_dGVzdC5qcGc,b_dGVzdA"
+//
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) ProcessObject(objectKey string, process string, options ...Option) (ProcessObjectResult, error) {
+ var out ProcessObjectResult
+ params, _ := GetRawParams(options)
+ params["x-oss-process"] = nil
+ processData := fmt.Sprintf("%v=%v", "x-oss-process", process)
+ data := strings.NewReader(processData)
+ resp, err := bucket.do("POST", objectKey, params, nil, data, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = jsonUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// AsyncProcessObject apply async process on the specified image file.
+//
+// The supported process includes resize, rotate, crop, watermark, format,
+// udf, customized style, etc.
+//
+// objectKey object key to process.
+// asyncProcess process string, such as "image/resize,w_100|sys/saveas,o_dGVzdC5qcGc,b_dGVzdA"
+//
+// error it's nil if no error, otherwise it's an error object.
+func (bucket Bucket) AsyncProcessObject(objectKey string, asyncProcess string, options ...Option) (AsyncProcessObjectResult, error) {
+ var out AsyncProcessObjectResult
+ params, _ := GetRawParams(options)
+ params["x-oss-async-process"] = nil
+ processData := fmt.Sprintf("%v=%v", "x-oss-async-process", asyncProcess)
+ data := strings.NewReader(processData)
+
+ resp, err := bucket.do("POST", objectKey, params, nil, data, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = jsonUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// PutObjectTagging add tagging to object
+//
+// objectKey object key to add tagging
+// tagging tagging to be added
+//
+// error nil if success, otherwise error
+func (bucket Bucket) PutObjectTagging(objectKey string, tagging Tagging, options ...Option) error {
+ bs, err := xml.Marshal(tagging)
+ if err != nil {
+ return err
+ }
+
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ params, _ := GetRawParams(options)
+ params["tagging"] = nil
+ resp, err := bucket.do("PUT", objectKey, params, options, buffer, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ return nil
+}
+
+//
+// GetObjectTagging get tagging of the object
+//
+// objectKey object key to get tagging
+//
+// Tagging
+// error nil if success, otherwise error
+
+func (bucket Bucket) GetObjectTagging(objectKey string, options ...Option) (GetObjectTaggingResult, error) {
+ var out GetObjectTaggingResult
+ params, _ := GetRawParams(options)
+ params["tagging"] = nil
+
+ resp, err := bucket.do("GET", objectKey, params, options, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// DeleteObjectTagging delete object taggging
+//
+// objectKey object key to delete tagging
+//
+// error nil if success, otherwise error
+func (bucket Bucket) DeleteObjectTagging(objectKey string, options ...Option) error {
+ params, _ := GetRawParams(options)
+ params["tagging"] = nil
+ resp, err := bucket.do("DELETE", objectKey, params, options, nil, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+func (bucket Bucket) OptionsMethod(objectKey string, options ...Option) (http.Header, error) {
+ var out http.Header
+ resp, err := bucket.doInner("OPTIONS", objectKey, nil, options, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+ out = resp.Headers
+ return out, nil
+}
+
+// public
+func (bucket Bucket) Do(method, objectName string, params map[string]interface{}, options []Option,
+ data io.Reader, listener ProgressListener) (*Response, error) {
+ return bucket.doInner(method, objectName, params, options, data, listener)
+}
+
+// Private
+func (bucket Bucket) doInner(method, objectName string, params map[string]interface{}, options []Option,
+ data io.Reader, listener ProgressListener) (*Response, error) {
+ headers := make(map[string]string)
+ err := handleOptions(headers, options)
+ if err != nil {
+ return nil, err
+ }
+
+ err = CheckBucketName(bucket.BucketName)
+ if len(bucket.BucketName) > 0 && err != nil {
+ return nil, err
+ }
+
+ ctxArg, _ := FindOption(options, contextArg, nil)
+ ctx, _ := ctxArg.(context.Context)
+
+ resp, err := bucket.Client.Conn.DoWithContext(ctx, method, bucket.BucketName, objectName,
+ params, headers, data, 0, listener)
+
+ // get response header
+ respHeader, _ := FindOption(options, responseHeader, nil)
+ if respHeader != nil && resp != nil {
+ pRespHeader := respHeader.(*http.Header)
+ if resp != nil {
+ *pRespHeader = resp.Headers
+ }
+ }
+
+ return resp, err
+}
+
+// Private check object name before bucket.do
+func (bucket Bucket) do(method, objectName string, params map[string]interface{}, options []Option,
+ data io.Reader, listener ProgressListener) (*Response, error) {
+ err := CheckObjectName(objectName)
+ if err != nil {
+ return nil, err
+ }
+ resp, err := bucket.doInner(method, objectName, params, options, data, listener)
+ return resp, err
+}
+
+func (bucket Bucket) doURL(method HTTPMethod, signedURL string, params map[string]interface{}, options []Option,
+ data io.Reader, listener ProgressListener) (*Response, error) {
+
+ headers := make(map[string]string)
+ err := handleOptions(headers, options)
+ if err != nil {
+ return nil, err
+ }
+
+ ctxArg, _ := FindOption(options, contextArg, nil)
+ ctx, _ := ctxArg.(context.Context)
+
+ resp, err := bucket.Client.Conn.DoURLWithContext(ctx, method, signedURL, headers, data, 0, listener)
+
+ // get response header
+ respHeader, _ := FindOption(options, responseHeader, nil)
+ if respHeader != nil {
+ pRespHeader := respHeader.(*http.Header)
+ if resp != nil {
+ *pRespHeader = resp.Headers
+ }
+ }
+
+ return resp, err
+}
+
+func (bucket Bucket) GetConfig() *Config {
+ return bucket.Client.Config
+}
+
+func AddContentType(options []Option, keys ...string) []Option {
+ typ := TypeByExtension("")
+ for _, key := range keys {
+ typ = TypeByExtension(key)
+ if typ != "" {
+ break
+ }
+ }
+
+ if typ == "" {
+ typ = "application/octet-stream"
+ }
+
+ opts := []Option{ContentType(typ)}
+ opts = append(opts, options...)
+
+ return opts
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/client.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/client.go
new file mode 100644
index 00000000000..5910bb3bfbd
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/client.go
@@ -0,0 +1,2956 @@
+// Package oss implements functions for access oss service.
+// It has two main struct Client and Bucket.
+package oss
+
+import (
+ "bytes"
+ "encoding/xml"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "net"
+ "net/http"
+ "strings"
+ "time"
+)
+
+// Client SDK's entry point. It's for bucket related options such as create/delete/set bucket (such as set/get ACL/lifecycle/referer/logging/website).
+// Object related operations are done by Bucket class.
+// Users use oss.New to create Client instance.
+//
+type (
+ // Client OSS client
+ Client struct {
+ Config *Config // OSS client configuration
+ Conn *Conn // Send HTTP request
+ HTTPClient *http.Client //http.Client to use - if nil will make its own
+ }
+
+ // ClientOption client option such as UseCname, Timeout, SecurityToken.
+ ClientOption func(*Client)
+)
+
+// New creates a new client.
+//
+// endpoint the OSS datacenter endpoint such as http://oss-cn-hangzhou.aliyuncs.com .
+// accessKeyId access key Id.
+// accessKeySecret access key secret.
+//
+// Client creates the new client instance, the returned value is valid when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+//
+func New(endpoint, accessKeyID, accessKeySecret string, options ...ClientOption) (*Client, error) {
+ // Configuration
+ config := getDefaultOssConfig()
+ config.Endpoint = endpoint
+ config.AccessKeyID = accessKeyID
+ config.AccessKeySecret = accessKeySecret
+
+ // URL parse
+ url := &urlMaker{}
+
+ // HTTP connect
+ conn := &Conn{config: config, url: url}
+
+ // OSS client
+ client := &Client{
+ Config: config,
+ Conn: conn,
+ }
+
+ // Client options parse
+ for _, option := range options {
+ option(client)
+ }
+
+ err := url.InitExt(config.Endpoint, config.IsCname, config.IsUseProxy, config.IsPathStyle)
+ if err != nil {
+ return nil, err
+ }
+
+ if config.AuthVersion != AuthV1 && config.AuthVersion != AuthV2 && config.AuthVersion != AuthV4 {
+ return nil, fmt.Errorf("Init client Error, invalid Auth version: %v", config.AuthVersion)
+ }
+
+ // Create HTTP connection
+ err = conn.init(config, url, client.HTTPClient)
+
+ return client, err
+}
+
+// SetRegion set region for client
+//
+// region the region, such as cn-hangzhou
+func (client *Client) SetRegion(region string) {
+ client.Config.Region = region
+}
+
+// SetCloudBoxId set CloudBoxId for client
+//
+// cloudBoxId the id of cloudBox
+func (client *Client) SetCloudBoxId(cloudBoxId string) {
+ client.Config.CloudBoxId = cloudBoxId
+}
+
+// SetProduct set Product type for client
+//
+// Product product type
+func (client *Client) SetProduct(product string) {
+ client.Config.Product = product
+}
+
+// Bucket gets the bucket instance.
+//
+// bucketName the bucket name.
+// Bucket the bucket object, when error is nil.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) Bucket(bucketName string) (*Bucket, error) {
+ err := CheckBucketName(bucketName)
+ if err != nil {
+ return nil, err
+ }
+
+ return &Bucket{
+ client,
+ bucketName,
+ }, nil
+}
+
+// CreateBucket creates a bucket.
+//
+// bucketName the bucket name, it's globably unique and immutable. The bucket name can only consist of lowercase letters, numbers and dash ('-').
+// It must start with lowercase letter or number and the length can only be between 3 and 255.
+// options options for creating the bucket, with optional ACL. The ACL could be ACLPrivate, ACLPublicRead, and ACLPublicReadWrite. By default it's ACLPrivate.
+// It could also be specified with StorageClass option, which supports StorageStandard, StorageIA(infrequent access), StorageArchive.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) CreateBucket(bucketName string, options ...Option) error {
+ headers := make(map[string]string)
+ handleOptions(headers, options)
+
+ buffer := new(bytes.Buffer)
+
+ var cbConfig createBucketConfiguration
+ cbConfig.StorageClass = StorageStandard
+
+ isStorageSet, valStroage, _ := IsOptionSet(options, storageClass)
+ isRedundancySet, valRedundancy, _ := IsOptionSet(options, redundancyType)
+ isObjectHashFuncSet, valHashFunc, _ := IsOptionSet(options, objectHashFunc)
+ if isStorageSet {
+ cbConfig.StorageClass = valStroage.(StorageClassType)
+ }
+
+ if isRedundancySet {
+ cbConfig.DataRedundancyType = valRedundancy.(DataRedundancyType)
+ }
+
+ if isObjectHashFuncSet {
+ cbConfig.ObjectHashFunction = valHashFunc.(ObjecthashFuncType)
+ }
+
+ bs, err := xml.Marshal(cbConfig)
+ if err != nil {
+ return err
+ }
+ buffer.Write(bs)
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// create bucket xml
+func (client Client) CreateBucketXml(bucketName string, xmlBody string, options ...Option) error {
+ buffer := new(bytes.Buffer)
+ buffer.Write([]byte(xmlBody))
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// ListBuckets lists buckets of the current account under the given endpoint, with optional filters.
+//
+// options specifies the filters such as Prefix, Marker and MaxKeys. Prefix is the bucket name's prefix filter.
+// And marker makes sure the returned buckets' name are greater than it in lexicographic order.
+// Maxkeys limits the max keys to return, and by default it's 100 and up to 1000.
+// For the common usage scenario, please check out list_bucket.go in the sample.
+// ListBucketsResponse the response object if error is nil.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) ListBuckets(options ...Option) (ListBucketsResult, error) {
+ var out ListBucketsResult
+
+ params, err := GetRawParams(options)
+ if err != nil {
+ return out, err
+ }
+
+ resp, err := client.do("GET", "", params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// ListCloudBoxes lists cloud boxes of the current account under the given endpoint, with optional filters.
+//
+// options specifies the filters such as Prefix, Marker and MaxKeys. Prefix is the bucket name's prefix filter.
+// And marker makes sure the returned buckets' name are greater than it in lexicographic order.
+// Maxkeys limits the max keys to return, and by default it's 100 and up to 1000.
+// For the common usage scenario, please check out list_bucket.go in the sample.
+// ListBucketsResponse the response object if error is nil.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) ListCloudBoxes(options ...Option) (ListCloudBoxResult, error) {
+ var out ListCloudBoxResult
+
+ params, err := GetRawParams(options)
+ if err != nil {
+ return out, err
+ }
+
+ params["cloudboxes"] = nil
+
+ resp, err := client.do("GET", "", params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// IsBucketExist checks if the bucket exists
+//
+// bucketName the bucket name.
+//
+// bool true if it exists, and it's only valid when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) IsBucketExist(bucketName string) (bool, error) {
+ listRes, err := client.ListBuckets(Prefix(bucketName), MaxKeys(1))
+ if err != nil {
+ return false, err
+ }
+
+ if len(listRes.Buckets) == 1 && listRes.Buckets[0].Name == bucketName {
+ return true, nil
+ }
+ return false, nil
+}
+
+// DeleteBucket deletes the bucket. Only empty bucket can be deleted (no object and parts).
+//
+// bucketName the bucket name.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) DeleteBucket(bucketName string, options ...Option) error {
+ params := map[string]interface{}{}
+ resp, err := client.do("DELETE", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return err
+ }
+
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+// GetBucketLocation gets the bucket location.
+//
+// Checks out the following link for more information :
+// https://www.alibabacloud.com/help/en/object-storage-service/latest/getbucketlocation
+//
+// bucketName the bucket name
+//
+// string bucket's datacenter location
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetBucketLocation(bucketName string, options ...Option) (string, error) {
+ params := map[string]interface{}{}
+ params["location"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ var LocationConstraint string
+ err = xmlUnmarshal(resp.Body, &LocationConstraint)
+ return LocationConstraint, err
+}
+
+// SetBucketACL sets bucket's ACL.
+//
+// bucketName the bucket name
+// bucketAcl the bucket ACL: ACLPrivate, ACLPublicRead and ACLPublicReadWrite.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) SetBucketACL(bucketName string, bucketACL ACLType, options ...Option) error {
+ headers := map[string]string{HTTPHeaderOssACL: string(bucketACL)}
+ params := map[string]interface{}{}
+ params["acl"] = nil
+ resp, err := client.do("PUT", bucketName, params, headers, nil, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetBucketACL gets the bucket ACL.
+//
+// bucketName the bucket name.
+//
+// GetBucketAclResponse the result object, and it's only valid when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetBucketACL(bucketName string, options ...Option) (GetBucketACLResult, error) {
+ var out GetBucketACLResult
+ params := map[string]interface{}{}
+ params["acl"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// SetBucketLifecycle sets the bucket's lifecycle.
+//
+// For more information, checks out following link:
+// https://www.alibabacloud.com/help/en/object-storage-service/latest/putbucketlifecycle
+//
+// bucketName the bucket name.
+// rules the lifecycle rules. There're two kind of rules: absolute time expiration and relative time expiration in days and day/month/year respectively.
+// Check out sample/bucket_lifecycle.go for more details.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) SetBucketLifecycle(bucketName string, rules []LifecycleRule, options ...Option) error {
+ err := verifyLifecycleRules(rules)
+ if err != nil {
+ return err
+ }
+ lifecycleCfg := LifecycleConfiguration{Rules: rules}
+ bs, err := xml.Marshal(lifecycleCfg)
+ if err != nil {
+ return err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["lifecycle"] = nil
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// SetBucketLifecycleXml sets the bucket's lifecycle rule from xml config
+func (client Client) SetBucketLifecycleXml(bucketName string, xmlBody string, options ...Option) error {
+ buffer := new(bytes.Buffer)
+ buffer.Write([]byte(xmlBody))
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["lifecycle"] = nil
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// DeleteBucketLifecycle deletes the bucket's lifecycle.
+//
+//
+// bucketName the bucket name.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) DeleteBucketLifecycle(bucketName string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["lifecycle"] = nil
+ resp, err := client.do("DELETE", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+// GetBucketLifecycle gets the bucket's lifecycle settings.
+//
+// bucketName the bucket name.
+//
+// GetBucketLifecycleResponse the result object upon successful request. It's only valid when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetBucketLifecycle(bucketName string, options ...Option) (GetBucketLifecycleResult, error) {
+ var out GetBucketLifecycleResult
+ params := map[string]interface{}{}
+ params["lifecycle"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+
+ // NonVersionTransition is not suggested to use
+ // to keep compatible
+ for k, rule := range out.Rules {
+ if len(rule.NonVersionTransitions) > 0 {
+ out.Rules[k].NonVersionTransition = &(out.Rules[k].NonVersionTransitions[0])
+ }
+ }
+ return out, err
+}
+
+func (client Client) GetBucketLifecycleXml(bucketName string, options ...Option) (string, error) {
+ params := map[string]interface{}{}
+ params["lifecycle"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ body, err := ioutil.ReadAll(resp.Body)
+ out := string(body)
+ return out, err
+}
+
+// SetBucketReferer sets the bucket's referer whitelist and the flag if allowing empty referrer.
+//
+// To avoid stealing link on OSS data, OSS supports the HTTP referrer header. A whitelist referrer could be set either by API or web console, as well as
+// the allowing empty referrer flag. Note that this applies to requests from web browser only.
+// For example, for a bucket os-example and its referrer http://www.aliyun.com, all requests from this URL could access the bucket.
+// For more information, please check out this link :
+// https://www.alibabacloud.com/help/en/object-storage-service/latest/putbucketreferer
+//
+// bucketName the bucket name.
+// referrers the referrer white list. A bucket could have a referrer list and each referrer supports one '*' and multiple '?' as wildcards.
+// The sample could be found in sample/bucket_referer.go
+// allowEmptyReferer the flag of allowing empty referrer. By default it's true.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) SetBucketReferer(bucketName string, referrers []string, allowEmptyReferer bool, options ...Option) error {
+ rxml := RefererXML{}
+ rxml.AllowEmptyReferer = allowEmptyReferer
+ if referrers == nil {
+ rxml.RefererList = append(rxml.RefererList, "")
+ } else {
+ for _, referrer := range referrers {
+ rxml.RefererList = append(rxml.RefererList, referrer)
+ }
+ }
+
+ bs, err := xml.Marshal(rxml)
+ if err != nil {
+ return err
+ }
+
+ return client.PutBucketRefererXml(bucketName, string(bs), options...)
+}
+
+// SetBucketRefererV2 gets the bucket's referer white list.
+//
+// setBucketReferer SetBucketReferer bucket referer config in struct format.
+//
+// GetBucketRefererResponse the result object upon successful request. It's only valid when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) SetBucketRefererV2(bucketName string, setBucketReferer RefererXML, options ...Option) error {
+ bs, err := xml.Marshal(setBucketReferer)
+ if err != nil {
+ return err
+ }
+ return client.PutBucketRefererXml(bucketName, string(bs), options...)
+}
+
+// PutBucketRefererXml set bucket's style
+// bucketName the bucket name.
+// xmlData the style in xml format
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) PutBucketRefererXml(bucketName, xmlData string, options ...Option) error {
+ buffer := new(bytes.Buffer)
+ buffer.Write([]byte(xmlData))
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["referer"] = nil
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetBucketReferer gets the bucket's referrer white list.
+// bucketName the bucket name.
+// GetBucketRefererResult the result object upon successful request. It's only valid when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) GetBucketReferer(bucketName string, options ...Option) (GetBucketRefererResult, error) {
+ var out GetBucketRefererResult
+ body, err := client.GetBucketRefererXml(bucketName, options...)
+ if err != nil {
+ return out, err
+ }
+ err = xmlUnmarshal(strings.NewReader(body), &out)
+ return out, err
+}
+
+// GetBucketRefererXml gets the bucket's referrer white list.
+// bucketName the bucket name.
+// GetBucketRefererResponse the bucket referer config result in xml format.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) GetBucketRefererXml(bucketName string, options ...Option) (string, error) {
+ params := map[string]interface{}{}
+ params["referer"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+ body, err := ioutil.ReadAll(resp.Body)
+ return string(body), err
+}
+
+// SetBucketLogging sets the bucket logging settings.
+//
+// OSS could automatically store the access log. Only the bucket owner could enable the logging.
+// Once enabled, OSS would save all the access log into hourly log files in a specified bucket.
+// For more information, please check out https://www.alibabacloud.com/help/en/object-storage-service/latest/putbucketlogging
+//
+// bucketName bucket name to enable the log.
+// targetBucket the target bucket name to store the log files.
+// targetPrefix the log files' prefix.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) SetBucketLogging(bucketName, targetBucket, targetPrefix string,
+ isEnable bool, options ...Option) error {
+ var err error
+ var bs []byte
+ if isEnable {
+ lxml := LoggingXML{}
+ lxml.LoggingEnabled.TargetBucket = targetBucket
+ lxml.LoggingEnabled.TargetPrefix = targetPrefix
+ bs, err = xml.Marshal(lxml)
+ } else {
+ lxml := loggingXMLEmpty{}
+ bs, err = xml.Marshal(lxml)
+ }
+
+ if err != nil {
+ return err
+ }
+
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["logging"] = nil
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// DeleteBucketLogging deletes the logging configuration to disable the logging on the bucket.
+//
+// bucketName the bucket name to disable the logging.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) DeleteBucketLogging(bucketName string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["logging"] = nil
+ resp, err := client.do("DELETE", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+// GetBucketLogging gets the bucket's logging settings
+//
+// bucketName the bucket name
+// GetBucketLoggingResponse the result object upon successful request. It's only valid when error is nil.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetBucketLogging(bucketName string, options ...Option) (GetBucketLoggingResult, error) {
+ var out GetBucketLoggingResult
+ params := map[string]interface{}{}
+ params["logging"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// SetBucketWebsite sets the bucket's static website's index and error page.
+//
+// OSS supports static web site hosting for the bucket data. When the bucket is enabled with that, you can access the file in the bucket like the way to access a static website.
+// For more information, please check out: https://www.alibabacloud.com/help/en/object-storage-service/latest/putbucketwebsite
+//
+// bucketName the bucket name to enable static web site.
+// indexDocument index page.
+// errorDocument error page.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) SetBucketWebsite(bucketName, indexDocument, errorDocument string, options ...Option) error {
+ wxml := WebsiteXML{}
+ wxml.IndexDocument.Suffix = indexDocument
+ wxml.ErrorDocument.Key = errorDocument
+
+ bs, err := xml.Marshal(wxml)
+ if err != nil {
+ return err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := make(map[string]string)
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["website"] = nil
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// SetBucketWebsiteDetail sets the bucket's static website's detail
+//
+// OSS supports static web site hosting for the bucket data. When the bucket is enabled with that, you can access the file in the bucket like the way to access a static website.
+// For more information, please check out: https://www.alibabacloud.com/help/en/object-storage-service/latest/putbucketwebsite
+//
+// bucketName the bucket name to enable static web site.
+//
+// wxml the website's detail
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) SetBucketWebsiteDetail(bucketName string, wxml WebsiteXML, options ...Option) error {
+ bs, err := xml.Marshal(wxml)
+ if err != nil {
+ return err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := make(map[string]string)
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["website"] = nil
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// SetBucketWebsiteXml sets the bucket's static website's rule
+//
+// OSS supports static web site hosting for the bucket data. When the bucket is enabled with that, you can access the file in the bucket like the way to access a static website.
+// For more information, please check out: https://www.alibabacloud.com/help/en/object-storage-service/latest/putbucketwebsite
+//
+// bucketName the bucket name to enable static web site.
+//
+// wxml the website's detail
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) SetBucketWebsiteXml(bucketName string, webXml string, options ...Option) error {
+ buffer := new(bytes.Buffer)
+ buffer.Write([]byte(webXml))
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := make(map[string]string)
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["website"] = nil
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// DeleteBucketWebsite deletes the bucket's static web site settings.
+//
+// bucketName the bucket name.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) DeleteBucketWebsite(bucketName string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["website"] = nil
+ resp, err := client.do("DELETE", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+// OpenMetaQuery Enables the metadata management feature for a bucket.
+//
+// bucketName the bucket name.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) OpenMetaQuery(bucketName string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["metaQuery"] = nil
+ params["comp"] = "add"
+ resp, err := client.do("POST", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetMetaQueryStatus Queries the information about the metadata index library of a bucket.
+//
+// bucketName the bucket name
+//
+// GetMetaQueryStatusResult the result object upon successful request. It's only valid when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetMetaQueryStatus(bucketName string, options ...Option) (GetMetaQueryStatusResult, error) {
+ var out GetMetaQueryStatusResult
+ params := map[string]interface{}{}
+ params["metaQuery"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// DoMetaQuery Queries the objects that meet specified conditions and lists the information about objects based on specified fields and sorting methods.
+//
+// bucketName the bucket name
+//
+// metaQuery the option of query
+//
+// DoMetaQueryResult the result object upon successful request. It's only valid when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) DoMetaQuery(bucketName string, metaQuery MetaQuery, options ...Option) (DoMetaQueryResult, error) {
+ var out DoMetaQueryResult
+ bs, err := xml.Marshal(metaQuery)
+ if err != nil {
+ return out, err
+ }
+ out, err = client.DoMetaQueryXml(bucketName, string(bs), options...)
+ return out, err
+}
+
+// DoMetaQueryXml Queries the objects that meet specified conditions and lists the information about objects based on specified fields and sorting methods.
+//
+// bucketName the bucket name
+//
+// metaQuery the option of query
+//
+// DoMetaQueryResult the result object upon successful request. It's only valid when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) DoMetaQueryXml(bucketName string, metaQueryXml string, options ...Option) (DoMetaQueryResult, error) {
+ var out DoMetaQueryResult
+ buffer := new(bytes.Buffer)
+ buffer.Write([]byte(metaQueryXml))
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["metaQuery"] = nil
+ params["comp"] = "query"
+ resp, err := client.do("POST", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// CloseMetaQuery Disables the metadata management feature for a bucket.
+//
+// bucketName the bucket name.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) CloseMetaQuery(bucketName string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["metaQuery"] = nil
+ params["comp"] = "delete"
+ resp, err := client.do("POST", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetBucketWebsite gets the bucket's default page (index page) and the error page.
+//
+// bucketName the bucket name
+//
+// GetBucketWebsiteResponse the result object upon successful request. It's only valid when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetBucketWebsite(bucketName string, options ...Option) (GetBucketWebsiteResult, error) {
+ var out GetBucketWebsiteResult
+ params := map[string]interface{}{}
+ params["website"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// GetBucketWebsiteXml gets the bucket's website config xml config.
+//
+// bucketName the bucket name
+//
+// string the bucket's xml config, It's only valid when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetBucketWebsiteXml(bucketName string, options ...Option) (string, error) {
+ params := map[string]interface{}{}
+ params["website"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ body, err := ioutil.ReadAll(resp.Body)
+
+ out := string(body)
+ return out, err
+}
+
+// SetBucketCORS sets the bucket's CORS rules
+//
+// For more information, please check out https://help.aliyun.com/document_detail/oss/user_guide/security_management/cors.html
+//
+// bucketName the bucket name
+// corsRules the CORS rules to set. The related sample code is in sample/bucket_cors.go.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) SetBucketCORS(bucketName string, corsRules []CORSRule, options ...Option) error {
+ corsxml := CORSXML{}
+ for _, v := range corsRules {
+ cr := CORSRule{}
+ cr.AllowedMethod = v.AllowedMethod
+ cr.AllowedOrigin = v.AllowedOrigin
+ cr.AllowedHeader = v.AllowedHeader
+ cr.ExposeHeader = v.ExposeHeader
+ cr.MaxAgeSeconds = v.MaxAgeSeconds
+ corsxml.CORSRules = append(corsxml.CORSRules, cr)
+ }
+
+ bs, err := xml.Marshal(corsxml)
+ if err != nil {
+ return err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["cors"] = nil
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// SetBucketCORSV2 sets the bucket's CORS rules
+//
+// bucketName the bucket name
+// putBucketCORS the CORS rules to set.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) SetBucketCORSV2(bucketName string, putBucketCORS PutBucketCORS, options ...Option) error {
+ bs, err := xml.Marshal(putBucketCORS)
+ if err != nil {
+ return err
+ }
+ err = client.SetBucketCORSXml(bucketName, string(bs), options...)
+ return err
+}
+
+func (client Client) SetBucketCORSXml(bucketName string, xmlBody string, options ...Option) error {
+ buffer := new(bytes.Buffer)
+ buffer.Write([]byte(xmlBody))
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["cors"] = nil
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// DeleteBucketCORS deletes the bucket's static website settings.
+//
+// bucketName the bucket name.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) DeleteBucketCORS(bucketName string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["cors"] = nil
+ resp, err := client.do("DELETE", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+// GetBucketCORS gets the bucket's CORS settings.
+//
+// bucketName the bucket name.
+// GetBucketCORSResult the result object upon successful request. It's only valid when error is nil.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetBucketCORS(bucketName string, options ...Option) (GetBucketCORSResult, error) {
+ var out GetBucketCORSResult
+ params := map[string]interface{}{}
+ params["cors"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+func (client Client) GetBucketCORSXml(bucketName string, options ...Option) (string, error) {
+ params := map[string]interface{}{}
+ params["cors"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ body, err := ioutil.ReadAll(resp.Body)
+ out := string(body)
+ return out, err
+}
+
+// GetBucketInfo gets the bucket information.
+//
+// bucketName the bucket name.
+// GetBucketInfoResult the result object upon successful request. It's only valid when error is nil.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetBucketInfo(bucketName string, options ...Option) (GetBucketInfoResult, error) {
+ var out GetBucketInfoResult
+ params := map[string]interface{}{}
+ params["bucketInfo"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+
+ // convert None to ""
+ if err == nil {
+ if out.BucketInfo.SseRule.KMSMasterKeyID == "None" {
+ out.BucketInfo.SseRule.KMSMasterKeyID = ""
+ }
+
+ if out.BucketInfo.SseRule.SSEAlgorithm == "None" {
+ out.BucketInfo.SseRule.SSEAlgorithm = ""
+ }
+
+ if out.BucketInfo.SseRule.KMSDataEncryption == "None" {
+ out.BucketInfo.SseRule.KMSDataEncryption = ""
+ }
+ }
+ return out, err
+}
+
+// SetBucketVersioning set bucket versioning:Enabled、Suspended
+// bucketName the bucket name.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) SetBucketVersioning(bucketName string, versioningConfig VersioningConfig, options ...Option) error {
+ var err error
+ var bs []byte
+ bs, err = xml.Marshal(versioningConfig)
+
+ if err != nil {
+ return err
+ }
+
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["versioning"] = nil
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetBucketVersioning get bucket versioning status:Enabled、Suspended
+// bucketName the bucket name.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) GetBucketVersioning(bucketName string, options ...Option) (GetBucketVersioningResult, error) {
+ var out GetBucketVersioningResult
+ params := map[string]interface{}{}
+ params["versioning"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// SetBucketEncryption set bucket encryption config
+// bucketName the bucket name.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) SetBucketEncryption(bucketName string, encryptionRule ServerEncryptionRule, options ...Option) error {
+ var err error
+ var bs []byte
+ bs, err = xml.Marshal(encryptionRule)
+
+ if err != nil {
+ return err
+ }
+
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["encryption"] = nil
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetBucketEncryption get bucket encryption
+// bucketName the bucket name.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) GetBucketEncryption(bucketName string, options ...Option) (GetBucketEncryptionResult, error) {
+ var out GetBucketEncryptionResult
+ params := map[string]interface{}{}
+ params["encryption"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// DeleteBucketEncryption delete bucket encryption config
+// bucketName the bucket name.
+// error it's nil if no error, otherwise it's an error bucket
+func (client Client) DeleteBucketEncryption(bucketName string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["encryption"] = nil
+ resp, err := client.do("DELETE", bucketName, params, nil, nil, options...)
+
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+//
+// SetBucketTagging add tagging to bucket
+// bucketName name of bucket
+// tagging tagging to be added
+// error nil if success, otherwise error
+func (client Client) SetBucketTagging(bucketName string, tagging Tagging, options ...Option) error {
+ var err error
+ var bs []byte
+ bs, err = xml.Marshal(tagging)
+
+ if err != nil {
+ return err
+ }
+
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["tagging"] = nil
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetBucketTagging get tagging of the bucket
+// bucketName name of bucket
+// error nil if success, otherwise error
+func (client Client) GetBucketTagging(bucketName string, options ...Option) (GetBucketTaggingResult, error) {
+ var out GetBucketTaggingResult
+ params := map[string]interface{}{}
+ params["tagging"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// DeleteBucketTagging delete bucket tagging
+// bucketName name of bucket
+// error nil if success, otherwise error
+//
+func (client Client) DeleteBucketTagging(bucketName string, options ...Option) error {
+ key, _ := FindOption(options, "tagging", nil)
+ params := map[string]interface{}{}
+ if key == nil {
+ params["tagging"] = nil
+ } else {
+ params["tagging"] = key.(string)
+ }
+
+ resp, err := client.do("DELETE", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+// GetBucketStat get bucket stat
+// bucketName the bucket name.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) GetBucketStat(bucketName string, options ...Option) (GetBucketStatResult, error) {
+ var out GetBucketStatResult
+ params := map[string]interface{}{}
+ params["stat"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// GetBucketPolicy API operation for Object Storage Service.
+//
+// Get the policy from the bucket.
+//
+// bucketName the bucket name.
+//
+// string return the bucket's policy, and it's only valid when error is nil.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetBucketPolicy(bucketName string, options ...Option) (string, error) {
+ params := map[string]interface{}{}
+ params["policy"] = nil
+
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ body, err := ioutil.ReadAll(resp.Body)
+
+ out := string(body)
+ return out, err
+}
+
+// SetBucketPolicy API operation for Object Storage Service.
+//
+// Set the policy from the bucket.
+//
+// bucketName the bucket name.
+//
+// policy the bucket policy.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) SetBucketPolicy(bucketName string, policy string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["policy"] = nil
+
+ buffer := strings.NewReader(policy)
+
+ resp, err := client.do("PUT", bucketName, params, nil, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// DeleteBucketPolicy API operation for Object Storage Service.
+//
+// Deletes the policy from the bucket.
+//
+// bucketName the bucket name.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) DeleteBucketPolicy(bucketName string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["policy"] = nil
+ resp, err := client.do("DELETE", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return err
+ }
+
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+// SetBucketRequestPayment API operation for Object Storage Service.
+//
+// Set the requestPayment of bucket
+//
+// bucketName the bucket name.
+//
+// paymentConfig the payment configuration
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) SetBucketRequestPayment(bucketName string, paymentConfig RequestPaymentConfiguration, options ...Option) error {
+ params := map[string]interface{}{}
+ params["requestPayment"] = nil
+
+ var bs []byte
+ bs, err := xml.Marshal(paymentConfig)
+
+ if err != nil {
+ return err
+ }
+
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetBucketRequestPayment API operation for Object Storage Service.
+//
+// Get bucket requestPayment
+//
+// bucketName the bucket name.
+//
+// RequestPaymentConfiguration the payment configuration
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetBucketRequestPayment(bucketName string, options ...Option) (RequestPaymentConfiguration, error) {
+ var out RequestPaymentConfiguration
+ params := map[string]interface{}{}
+ params["requestPayment"] = nil
+
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// GetUserQoSInfo API operation for Object Storage Service.
+//
+// Get user qos.
+//
+// UserQoSConfiguration the User Qos and range Information.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetUserQoSInfo(options ...Option) (UserQoSConfiguration, error) {
+ var out UserQoSConfiguration
+ params := map[string]interface{}{}
+ params["qosInfo"] = nil
+
+ resp, err := client.do("GET", "", params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// SetBucketQoSInfo API operation for Object Storage Service.
+//
+// Set Bucket Qos information.
+//
+// bucketName the bucket name.
+//
+// qosConf the qos configuration.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) SetBucketQoSInfo(bucketName string, qosConf BucketQoSConfiguration, options ...Option) error {
+ params := map[string]interface{}{}
+ params["qosInfo"] = nil
+
+ var bs []byte
+ bs, err := xml.Marshal(qosConf)
+ if err != nil {
+ return err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentTpye := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentTpye
+
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetBucketQosInfo API operation for Object Storage Service.
+//
+// Get Bucket Qos information.
+//
+// bucketName the bucket name.
+//
+// BucketQoSConfiguration the return qos configuration.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetBucketQosInfo(bucketName string, options ...Option) (BucketQoSConfiguration, error) {
+ var out BucketQoSConfiguration
+ params := map[string]interface{}{}
+ params["qosInfo"] = nil
+
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// DeleteBucketQosInfo API operation for Object Storage Service.
+//
+// Delete Bucket QoS information.
+//
+// bucketName the bucket name.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) DeleteBucketQosInfo(bucketName string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["qosInfo"] = nil
+
+ resp, err := client.do("DELETE", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+// SetBucketInventory API operation for Object Storage Service
+//
+// Set the Bucket inventory.
+//
+// bucketName the bucket name.
+//
+// inventoryConfig the inventory configuration.
+//
+// error it's nil if no error, otherwise it's an error.
+//
+func (client Client) SetBucketInventory(bucketName string, inventoryConfig InventoryConfiguration, options ...Option) error {
+ params := map[string]interface{}{}
+ params["inventoryId"] = inventoryConfig.Id
+ params["inventory"] = nil
+
+ var bs []byte
+ bs, err := xml.Marshal(inventoryConfig)
+
+ if err != nil {
+ return err
+ }
+
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := make(map[string]string)
+ headers[HTTPHeaderContentType] = contentType
+
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+
+ if err != nil {
+ return err
+ }
+
+ defer resp.Body.Close()
+
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// SetBucketInventoryXml API operation for Object Storage Service
+//
+// Set the Bucket inventory
+//
+// bucketName the bucket name.
+//
+// xmlBody the inventory configuration.
+//
+// error it's nil if no error, otherwise it's an error.
+//
+func (client Client) SetBucketInventoryXml(bucketName string, xmlBody string, options ...Option) error {
+ var inventoryConfig InventoryConfiguration
+ err := xml.Unmarshal([]byte(xmlBody), &inventoryConfig)
+ if err != nil {
+ return err
+ }
+
+ if inventoryConfig.Id == "" {
+ return fmt.Errorf("inventory id is empty in xml")
+ }
+
+ params := map[string]interface{}{}
+ params["inventoryId"] = inventoryConfig.Id
+ params["inventory"] = nil
+
+ buffer := new(bytes.Buffer)
+ buffer.Write([]byte(xmlBody))
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := make(map[string]string)
+ headers[HTTPHeaderContentType] = contentType
+
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetBucketInventory API operation for Object Storage Service
+//
+// Get the Bucket inventory.
+//
+// bucketName tht bucket name.
+//
+// strInventoryId the inventory id.
+//
+// InventoryConfiguration the inventory configuration.
+//
+// error it's nil if no error, otherwise it's an error.
+//
+func (client Client) GetBucketInventory(bucketName string, strInventoryId string, options ...Option) (InventoryConfiguration, error) {
+ var out InventoryConfiguration
+ params := map[string]interface{}{}
+ params["inventory"] = nil
+ params["inventoryId"] = strInventoryId
+
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// GetBucketInventoryXml API operation for Object Storage Service
+//
+// Get the Bucket inventory.
+//
+// bucketName tht bucket name.
+//
+// strInventoryId the inventory id.
+//
+// InventoryConfiguration the inventory configuration.
+//
+// error it's nil if no error, otherwise it's an error.
+//
+func (client Client) GetBucketInventoryXml(bucketName string, strInventoryId string, options ...Option) (string, error) {
+ params := map[string]interface{}{}
+ params["inventory"] = nil
+ params["inventoryId"] = strInventoryId
+
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+ body, err := ioutil.ReadAll(resp.Body)
+ out := string(body)
+ return out, err
+}
+
+// ListBucketInventory API operation for Object Storage Service
+//
+// List the Bucket inventory.
+//
+// bucketName tht bucket name.
+//
+// continuationToken the users token.
+//
+// ListInventoryConfigurationsResult list all inventory configuration by .
+//
+// error it's nil if no error, otherwise it's an error.
+//
+func (client Client) ListBucketInventory(bucketName, continuationToken string, options ...Option) (ListInventoryConfigurationsResult, error) {
+ var out ListInventoryConfigurationsResult
+ params := map[string]interface{}{}
+ params["inventory"] = nil
+ if continuationToken == "" {
+ params["continuation-token"] = nil
+ } else {
+ params["continuation-token"] = continuationToken
+ }
+
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// ListBucketInventoryXml API operation for Object Storage Service
+//
+// List the Bucket inventory.
+//
+// bucketName tht bucket name.
+//
+// continuationToken the users token.
+//
+// ListInventoryConfigurationsResult list all inventory configuration by .
+//
+// error it's nil if no error, otherwise it's an error.
+//
+func (client Client) ListBucketInventoryXml(bucketName, continuationToken string, options ...Option) (string, error) {
+ params := map[string]interface{}{}
+ params["inventory"] = nil
+ if continuationToken == "" {
+ params["continuation-token"] = nil
+ } else {
+ params["continuation-token"] = continuationToken
+ }
+
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+ body, err := ioutil.ReadAll(resp.Body)
+ out := string(body)
+ return out, err
+}
+
+// DeleteBucketInventory API operation for Object Storage Service.
+//
+// Delete Bucket inventory information.
+//
+// bucketName tht bucket name.
+//
+// strInventoryId the inventory id.
+//
+// error it's nil if no error, otherwise it's an error.
+//
+func (client Client) DeleteBucketInventory(bucketName, strInventoryId string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["inventory"] = nil
+ params["inventoryId"] = strInventoryId
+
+ resp, err := client.do("DELETE", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+// SetBucketAsyncTask API operation for set async fetch task
+//
+// bucketName tht bucket name.
+//
+// asynConf configruation
+//
+// error it's nil if success, otherwise it's an error.
+func (client Client) SetBucketAsyncTask(bucketName string, asynConf AsyncFetchTaskConfiguration, options ...Option) (AsyncFetchTaskResult, error) {
+ var out AsyncFetchTaskResult
+ params := map[string]interface{}{}
+ params["asyncFetch"] = nil
+
+ var bs []byte
+ bs, err := xml.Marshal(asynConf)
+
+ if err != nil {
+ return out, err
+ }
+
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := make(map[string]string)
+ headers[HTTPHeaderContentType] = contentType
+
+ resp, err := client.do("POST", bucketName, params, headers, buffer, options...)
+
+ if err != nil {
+ return out, err
+ }
+
+ defer resp.Body.Close()
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// GetBucketAsyncTask API operation for set async fetch task
+//
+// bucketName tht bucket name.
+//
+// taskid returned by SetBucketAsyncTask
+//
+// error it's nil if success, otherwise it's an error.
+func (client Client) GetBucketAsyncTask(bucketName string, taskID string, options ...Option) (AsynFetchTaskInfo, error) {
+ var out AsynFetchTaskInfo
+ params := map[string]interface{}{}
+ params["asyncFetch"] = nil
+
+ headers := make(map[string]string)
+ headers[HTTPHeaderOssTaskID] = taskID
+ resp, err := client.do("GET", bucketName, params, headers, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// InitiateBucketWorm creates bucket worm Configuration
+// bucketName the bucket name.
+// retentionDays the retention period in days
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) InitiateBucketWorm(bucketName string, retentionDays int, options ...Option) (string, error) {
+ var initiateWormConf InitiateWormConfiguration
+ initiateWormConf.RetentionPeriodInDays = retentionDays
+
+ var respHeader http.Header
+ isOptSet, _, _ := IsOptionSet(options, responseHeader)
+ if !isOptSet {
+ options = append(options, GetResponseHeader(&respHeader))
+ }
+
+ bs, err := xml.Marshal(initiateWormConf)
+ if err != nil {
+ return "", err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := make(map[string]string)
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["worm"] = nil
+
+ resp, err := client.do("POST", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ respOpt, _ := FindOption(options, responseHeader, nil)
+ wormID := ""
+ err = CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+ if err == nil && respOpt != nil {
+ wormID = (respOpt.(*http.Header)).Get("x-oss-worm-id")
+ }
+ return wormID, err
+}
+
+// AbortBucketWorm delete bucket worm Configuration
+// bucketName the bucket name.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) AbortBucketWorm(bucketName string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["worm"] = nil
+ resp, err := client.do("DELETE", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+// CompleteBucketWorm complete bucket worm Configuration
+// bucketName the bucket name.
+// wormID the worm id
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) CompleteBucketWorm(bucketName string, wormID string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["wormId"] = wormID
+ resp, err := client.do("POST", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// ExtendBucketWorm exetend bucket worm Configuration
+// bucketName the bucket name.
+// retentionDays the retention period in days
+// wormID the worm id
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) ExtendBucketWorm(bucketName string, retentionDays int, wormID string, options ...Option) error {
+ var extendWormConf ExtendWormConfiguration
+ extendWormConf.RetentionPeriodInDays = retentionDays
+
+ bs, err := xml.Marshal(extendWormConf)
+ if err != nil {
+ return err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := make(map[string]string)
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["wormId"] = wormID
+ params["wormExtend"] = nil
+
+ resp, err := client.do("POST", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetBucketWorm get bucket worm Configuration
+// bucketName the bucket name.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetBucketWorm(bucketName string, options ...Option) (WormConfiguration, error) {
+ var out WormConfiguration
+ params := map[string]interface{}{}
+ params["worm"] = nil
+
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// SetBucketTransferAcc set bucket transfer acceleration configuration
+// bucketName the bucket name.
+// accConf bucket transfer acceleration configuration
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) SetBucketTransferAcc(bucketName string, accConf TransferAccConfiguration, options ...Option) error {
+ bs, err := xml.Marshal(accConf)
+ if err != nil {
+ return err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := make(map[string]string)
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["transferAcceleration"] = nil
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetBucketTransferAcc get bucket transfer acceleration configuration
+// bucketName the bucket name.
+// accConf bucket transfer acceleration configuration
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetBucketTransferAcc(bucketName string, options ...Option) (TransferAccConfiguration, error) {
+ var out TransferAccConfiguration
+ params := map[string]interface{}{}
+ params["transferAcceleration"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// DeleteBucketTransferAcc delete bucket transfer acceleration configuration
+// bucketName the bucket name.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) DeleteBucketTransferAcc(bucketName string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["transferAcceleration"] = nil
+ resp, err := client.do("DELETE", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+// PutBucketReplication put bucket replication configuration
+// bucketName the bucket name.
+// xmlBody the replication configuration.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) PutBucketReplication(bucketName string, xmlBody string, options ...Option) error {
+ buffer := new(bytes.Buffer)
+ buffer.Write([]byte(xmlBody))
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["replication"] = nil
+ params["comp"] = "add"
+ resp, err := client.do("POST", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// PutBucketRTC put bucket replication rtc
+// bucketName the bucket name.
+// rtc the bucket rtc config.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) PutBucketRTC(bucketName string, rtc PutBucketRTC, options ...Option) error {
+ bs, err := xml.Marshal(rtc)
+ if err != nil {
+ return err
+ }
+ err = client.PutBucketRTCXml(bucketName, string(bs), options...)
+ return err
+}
+
+// PutBucketRTCXml put bucket rtc configuration
+// bucketName the bucket name.
+// xmlBody the rtc configuration in xml format.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) PutBucketRTCXml(bucketName string, xmlBody string, options ...Option) error {
+ buffer := new(bytes.Buffer)
+ buffer.Write([]byte(xmlBody))
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["rtc"] = nil
+ resp, err := client.do("PUT", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetBucketReplication get bucket replication configuration
+// bucketName the bucket name.
+// string the replication configuration.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetBucketReplication(bucketName string, options ...Option) (string, error) {
+ params := map[string]interface{}{}
+ params["replication"] = nil
+
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ data, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return "", err
+ }
+ return string(data), err
+}
+
+// DeleteBucketReplication delete bucket replication configuration
+// bucketName the bucket name.
+// ruleId the ID of the replication configuration.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) DeleteBucketReplication(bucketName string, ruleId string, options ...Option) error {
+ replicationxml := ReplicationXML{}
+ replicationxml.ID = ruleId
+
+ bs, err := xml.Marshal(replicationxml)
+ if err != nil {
+ return err
+ }
+
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ params := map[string]interface{}{}
+ params["replication"] = nil
+ params["comp"] = "delete"
+ resp, err := client.do("POST", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetBucketReplicationLocation get the locations of the target bucket that can be copied to
+// bucketName the bucket name.
+// string the locations of the target bucket that can be copied to.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetBucketReplicationLocation(bucketName string, options ...Option) (string, error) {
+ params := map[string]interface{}{}
+ params["replicationLocation"] = nil
+
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ data, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return "", err
+ }
+ return string(data), err
+}
+
+// GetBucketReplicationProgress get the replication progress of bucket
+// bucketName the bucket name.
+// ruleId the ID of the replication configuration.
+// string the replication progress of bucket.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (client Client) GetBucketReplicationProgress(bucketName string, ruleId string, options ...Option) (string, error) {
+ params := map[string]interface{}{}
+ params["replicationProgress"] = nil
+ if ruleId != "" {
+ params["rule-id"] = ruleId
+ }
+
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ data, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return "", err
+ }
+ return string(data), err
+}
+
+// GetBucketAccessMonitor get bucket's access monitor config
+// bucketName the bucket name.
+// GetBucketAccessMonitorResult the access monitor configuration result of bucket.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) GetBucketAccessMonitor(bucketName string, options ...Option) (GetBucketAccessMonitorResult, error) {
+ var out GetBucketAccessMonitorResult
+ body, err := client.GetBucketAccessMonitorXml(bucketName, options...)
+ if err != nil {
+ return out, err
+ }
+ err = xmlUnmarshal(strings.NewReader(body), &out)
+ return out, err
+}
+
+// GetBucketAccessMonitorXml get bucket's access monitor config
+// bucketName the bucket name.
+// string the access monitor configuration result of bucket xml foramt.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) GetBucketAccessMonitorXml(bucketName string, options ...Option) (string, error) {
+ params := map[string]interface{}{}
+ params["accessmonitor"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+ body, err := ioutil.ReadAll(resp.Body)
+ out := string(body)
+ return out, err
+}
+
+// PutBucketAccessMonitor get bucket's access monitor config
+// bucketName the bucket name.
+// accessMonitor the access monitor configuration of bucket.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) PutBucketAccessMonitor(bucketName string, accessMonitor PutBucketAccessMonitor, options ...Option) error {
+ bs, err := xml.Marshal(accessMonitor)
+ if err != nil {
+ return err
+ }
+ err = client.PutBucketAccessMonitorXml(bucketName, string(bs), options...)
+ return err
+}
+
+// PutBucketAccessMonitorXml get bucket's access monitor config
+// bucketName the bucket name.
+// xmlData the access monitor configuration in xml foramt
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) PutBucketAccessMonitorXml(bucketName string, xmlData string, options ...Option) error {
+ buffer := new(bytes.Buffer)
+ buffer.Write([]byte(xmlData))
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+ params := map[string]interface{}{}
+ params["accessmonitor"] = nil
+ resp, err := client.do("PUT", bucketName, params, nil, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// ListBucketCname list bucket's binding cname
+// bucketName the bucket name.
+// string the xml configuration of bucket.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) ListBucketCname(bucketName string, options ...Option) (ListBucketCnameResult, error) {
+ var out ListBucketCnameResult
+ body, err := client.GetBucketCname(bucketName, options...)
+ if err != nil {
+ return out, err
+ }
+ err = xmlUnmarshal(strings.NewReader(body), &out)
+ return out, err
+}
+
+// GetBucketCname get bucket's binding cname
+// bucketName the bucket name.
+// string the xml configuration of bucket.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) GetBucketCname(bucketName string, options ...Option) (string, error) {
+ params := map[string]interface{}{}
+ params["cname"] = nil
+
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ data, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return "", err
+ }
+ return string(data), err
+}
+
+// CreateBucketCnameToken create a token for the cname.
+// bucketName the bucket name.
+// cname a custom domain name.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) CreateBucketCnameToken(bucketName string, cname string, options ...Option) (CreateBucketCnameTokenResult, error) {
+ var out CreateBucketCnameTokenResult
+ params := map[string]interface{}{}
+ params["cname"] = nil
+ params["comp"] = "token"
+
+ rxml := CnameConfigurationXML{}
+ rxml.Domain = cname
+
+ bs, err := xml.Marshal(rxml)
+ if err != nil {
+ return out, err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ resp, err := client.do("POST", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// GetBucketCnameToken get a token for the cname
+// bucketName the bucket name.
+// cname a custom domain name.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) GetBucketCnameToken(bucketName string, cname string, options ...Option) (GetBucketCnameTokenResult, error) {
+ var out GetBucketCnameTokenResult
+ params := map[string]interface{}{}
+ params["cname"] = cname
+ params["comp"] = "token"
+
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// PutBucketCnameXml map a custom domain name to a bucket
+// bucketName the bucket name.
+// xmlBody the cname configuration in xml foramt
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) PutBucketCnameXml(bucketName string, xmlBody string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["cname"] = nil
+ params["comp"] = "add"
+
+ buffer := new(bytes.Buffer)
+ buffer.Write([]byte(xmlBody))
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ resp, err := client.do("POST", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// PutBucketCname map a custom domain name to a bucket
+// bucketName the bucket name.
+// cname a custom domain name.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) PutBucketCname(bucketName string, cname string, options ...Option) error {
+ rxml := CnameConfigurationXML{}
+ rxml.Domain = cname
+ bs, err := xml.Marshal(rxml)
+ if err != nil {
+ return err
+ }
+ return client.PutBucketCnameXml(bucketName, string(bs), options...)
+}
+
+// PutBucketCnameWithCertificate map a custom domain name to a bucket
+// bucketName the bucket name.
+// PutBucketCname the bucket cname config in struct format.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) PutBucketCnameWithCertificate(bucketName string, putBucketCname PutBucketCname, options ...Option) error {
+ bs, err := xml.Marshal(putBucketCname)
+ if err != nil {
+ return err
+ }
+ return client.PutBucketCnameXml(bucketName, string(bs), options...)
+}
+
+// DeleteBucketCname remove the mapping of the custom domain name from a bucket.
+// bucketName the bucket name.
+// cname a custom domain name.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) DeleteBucketCname(bucketName string, cname string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["cname"] = nil
+ params["comp"] = "delete"
+
+ rxml := CnameConfigurationXML{}
+ rxml.Domain = cname
+
+ bs, err := xml.Marshal(rxml)
+ if err != nil {
+ return err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ resp, err := client.do("POST", bucketName, params, headers, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// PutBucketResourceGroup set bucket's resource group
+// bucketName the bucket name.
+// resourceGroup the resource group configuration of bucket.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) PutBucketResourceGroup(bucketName string, resourceGroup PutBucketResourceGroup, options ...Option) error {
+ bs, err := xml.Marshal(resourceGroup)
+ if err != nil {
+ return err
+ }
+ err = client.PutBucketResourceGroupXml(bucketName, string(bs), options...)
+ return err
+}
+
+// PutBucketResourceGroupXml set bucket's resource group
+// bucketName the bucket name.
+// xmlData the resource group in xml format
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) PutBucketResourceGroupXml(bucketName string, xmlData string, options ...Option) error {
+ buffer := new(bytes.Buffer)
+ buffer.Write([]byte(xmlData))
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+ params := map[string]interface{}{}
+ params["resourceGroup"] = nil
+ resp, err := client.do("PUT", bucketName, params, nil, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetBucketResourceGroup get bucket's resource group
+// bucketName the bucket name.
+// GetBucketResourceGroupResult the resource group configuration result of bucket.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) GetBucketResourceGroup(bucketName string, options ...Option) (GetBucketResourceGroupResult, error) {
+ var out GetBucketResourceGroupResult
+ body, err := client.GetBucketResourceGroupXml(bucketName, options...)
+ if err != nil {
+ return out, err
+ }
+ err = xmlUnmarshal(strings.NewReader(body), &out)
+ return out, err
+}
+
+// GetBucketResourceGroupXml get bucket's resource group
+// bucketName the bucket name.
+// string the resource group result of bucket xml format.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) GetBucketResourceGroupXml(bucketName string, options ...Option) (string, error) {
+ params := map[string]interface{}{}
+ params["resourceGroup"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+ body, err := ioutil.ReadAll(resp.Body)
+ out := string(body)
+ return out, err
+}
+
+// PutBucketStyle set bucket's style
+// bucketName the bucket name.
+// styleContent the style content.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) PutBucketStyle(bucketName, styleName string, styleContent string, options ...Option) error {
+ bs := fmt.Sprintf("", styleContent)
+ err := client.PutBucketStyleXml(bucketName, styleName, bs, options...)
+ return err
+}
+
+// PutBucketStyleXml set bucket's style
+// bucketName the bucket name.
+// styleName the style name.
+// xmlData the style in xml format
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) PutBucketStyleXml(bucketName, styleName, xmlData string, options ...Option) error {
+ buffer := new(bytes.Buffer)
+ buffer.Write([]byte(xmlData))
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+ params := map[string]interface{}{}
+ params["style"] = nil
+ params["styleName"] = styleName
+ resp, err := client.do("PUT", bucketName, params, nil, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetBucketStyle get bucket's style
+// bucketName the bucket name.
+// styleName the bucket style name.
+// GetBucketStyleResult the style result of bucket.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) GetBucketStyle(bucketName, styleName string, options ...Option) (GetBucketStyleResult, error) {
+ var out GetBucketStyleResult
+ body, err := client.GetBucketStyleXml(bucketName, styleName, options...)
+ if err != nil {
+ return out, err
+ }
+ err = xmlUnmarshal(strings.NewReader(body), &out)
+ return out, err
+}
+
+// GetBucketStyleXml get bucket's style
+// bucketName the bucket name.
+// styleName the bucket style name.
+// string the style result of bucket in xml format.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) GetBucketStyleXml(bucketName, styleName string, options ...Option) (string, error) {
+ params := map[string]interface{}{}
+ params["style"] = nil
+ params["styleName"] = styleName
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+ body, err := ioutil.ReadAll(resp.Body)
+ out := string(body)
+ return out, err
+}
+
+// ListBucketStyle get bucket's styles
+// bucketName the bucket name.
+// GetBucketListStyleResult the list style result of bucket.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) ListBucketStyle(bucketName string, options ...Option) (GetBucketListStyleResult, error) {
+ var out GetBucketListStyleResult
+ body, err := client.ListBucketStyleXml(bucketName, options...)
+ if err != nil {
+ return out, err
+ }
+ err = xmlUnmarshal(strings.NewReader(body), &out)
+ return out, err
+}
+
+// ListBucketStyleXml get bucket's list style
+// bucketName the bucket name.
+// string the style result of bucket in xml format.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) ListBucketStyleXml(bucketName string, options ...Option) (string, error) {
+ params := map[string]interface{}{}
+ params["style"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+ body, err := ioutil.ReadAll(resp.Body)
+ out := string(body)
+ return out, err
+}
+
+// DeleteBucketStyle delete bucket's style
+// bucketName the bucket name.
+// styleName the bucket style name.
+// string the style result of bucket in xml format.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) DeleteBucketStyle(bucketName, styleName string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["style"] = bucketName
+ params["styleName"] = styleName
+ resp, err := client.do("DELETE", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+// PutBucketResponseHeader set bucket response header
+// bucketName the bucket name.
+// xmlData the resource group in xml format
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) PutBucketResponseHeader(bucketName string, responseHeader PutBucketResponseHeader, options ...Option) error {
+ bs, err := xml.Marshal(responseHeader)
+ if err != nil {
+ return err
+ }
+ err = client.PutBucketResponseHeaderXml(bucketName, string(bs), options...)
+ return err
+}
+
+// PutBucketResponseHeaderXml set bucket response header
+// bucketName the bucket name.
+// xmlData the bucket response header in xml format
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) PutBucketResponseHeaderXml(bucketName, xmlData string, options ...Option) error {
+ buffer := new(bytes.Buffer)
+ buffer.Write([]byte(xmlData))
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+ params := map[string]interface{}{}
+ params["responseHeader"] = nil
+ resp, err := client.do("PUT", bucketName, params, nil, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetBucketResponseHeader get bucket's response header.
+// bucketName the bucket name.
+// GetBucketResponseHeaderResult the response header result of bucket.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) GetBucketResponseHeader(bucketName string, options ...Option) (GetBucketResponseHeaderResult, error) {
+ var out GetBucketResponseHeaderResult
+ body, err := client.GetBucketResponseHeaderXml(bucketName, options...)
+ if err != nil {
+ return out, err
+ }
+ err = xmlUnmarshal(strings.NewReader(body), &out)
+ return out, err
+}
+
+// GetBucketResponseHeaderXml get bucket's resource group
+// bucketName the bucket name.
+// string the response header result of bucket xml format.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) GetBucketResponseHeaderXml(bucketName string, options ...Option) (string, error) {
+ params := map[string]interface{}{}
+ params["responseHeader"] = nil
+ resp, err := client.do("GET", bucketName, params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+ body, err := ioutil.ReadAll(resp.Body)
+ out := string(body)
+ return out, err
+}
+
+// DeleteBucketResponseHeader delete response header from a bucket.
+// bucketName the bucket name.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) DeleteBucketResponseHeader(bucketName string, options ...Option) error {
+ params := map[string]interface{}{}
+ params["responseHeader"] = nil
+ resp, err := client.do("DELETE", bucketName, params, nil, nil, options...)
+
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+// DescribeRegions get describe regions
+// GetDescribeRegionsResult the result of bucket in xml format.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) DescribeRegions(options ...Option) (DescribeRegionsResult, error) {
+ var out DescribeRegionsResult
+ body, err := client.DescribeRegionsXml(options...)
+ if err != nil {
+ return out, err
+ }
+ err = xmlUnmarshal(strings.NewReader(body), &out)
+ return out, err
+}
+
+// DescribeRegionsXml get describe regions
+// string the style result of bucket in xml format.
+// error it's nil if no error, otherwise it's an error object.
+func (client Client) DescribeRegionsXml(options ...Option) (string, error) {
+ params, err := GetRawParams(options)
+ if err != nil {
+ return "", err
+ }
+ if params["regions"] == nil {
+ params["regions"] = nil
+ }
+ resp, err := client.do("GET", "", params, nil, nil, options...)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+ body, err := ioutil.ReadAll(resp.Body)
+ out := string(body)
+ return out, err
+}
+
+// LimitUploadSpeed set upload bandwidth limit speed,default is 0,unlimited
+// upSpeed KB/s, 0 is unlimited,default is 0
+// error it's nil if success, otherwise failure
+func (client Client) LimitUploadSpeed(upSpeed int) error {
+ if client.Config == nil {
+ return fmt.Errorf("client config is nil")
+ }
+ return client.Config.LimitUploadSpeed(upSpeed)
+}
+
+// LimitDownloadSpeed set download bandwidth limit speed,default is 0,unlimited
+// downSpeed KB/s, 0 is unlimited,default is 0
+// error it's nil if success, otherwise failure
+func (client Client) LimitDownloadSpeed(downSpeed int) error {
+ if client.Config == nil {
+ return fmt.Errorf("client config is nil")
+ }
+ return client.Config.LimitDownloadSpeed(downSpeed)
+}
+
+// UseCname sets the flag of using CName. By default it's false.
+//
+// isUseCname true: the endpoint has the CName, false: the endpoint does not have cname. Default is false.
+//
+func UseCname(isUseCname bool) ClientOption {
+ return func(client *Client) {
+ client.Config.IsCname = isUseCname
+ }
+}
+
+// ForcePathStyle sets the flag of using Path Style. By default it's false.
+//
+// isPathStyle true: the endpoint has the Path Style, false: the endpoint does not have Path Style. Default is false.
+//
+func ForcePathStyle(isPathStyle bool) ClientOption {
+ return func(client *Client) {
+ client.Config.IsPathStyle = isPathStyle
+ }
+}
+
+// Timeout sets the HTTP timeout in seconds.
+//
+// connectTimeoutSec HTTP timeout in seconds. Default is 10 seconds. 0 means infinite (not recommended)
+// readWriteTimeout HTTP read or write's timeout in seconds. Default is 20 seconds. 0 means infinite.
+//
+func Timeout(connectTimeoutSec, readWriteTimeout int64) ClientOption {
+ return func(client *Client) {
+ client.Config.HTTPTimeout.ConnectTimeout =
+ time.Second * time.Duration(connectTimeoutSec)
+ client.Config.HTTPTimeout.ReadWriteTimeout =
+ time.Second * time.Duration(readWriteTimeout)
+ client.Config.HTTPTimeout.HeaderTimeout =
+ time.Second * time.Duration(readWriteTimeout)
+ client.Config.HTTPTimeout.IdleConnTimeout =
+ time.Second * time.Duration(readWriteTimeout)
+ client.Config.HTTPTimeout.LongTimeout =
+ time.Second * time.Duration(readWriteTimeout*10)
+ }
+}
+
+// MaxConns sets the HTTP max connections for a client.
+//
+// maxIdleConns controls the maximum number of idle (keep-alive) connections across all hosts. Default is 100.
+// maxIdleConnsPerHost controls the maximum idle (keep-alive) connections to keep per-host. Default is 100.
+// maxConnsPerHost limits the total number of connections per host. Default is no limit.
+//
+func MaxConns(maxIdleConns, maxIdleConnsPerHost, maxConnsPerHost int) ClientOption {
+ return func(client *Client) {
+ client.Config.HTTPMaxConns.MaxIdleConns = maxIdleConns
+ client.Config.HTTPMaxConns.MaxIdleConnsPerHost = maxIdleConnsPerHost
+ client.Config.HTTPMaxConns.MaxConnsPerHost = maxConnsPerHost
+ }
+}
+
+// SecurityToken sets the temporary user's SecurityToken.
+//
+// token STS token
+//
+func SecurityToken(token string) ClientOption {
+ return func(client *Client) {
+ client.Config.SecurityToken = strings.TrimSpace(token)
+ }
+}
+
+// EnableMD5 enables MD5 validation.
+//
+// isEnableMD5 true: enable MD5 validation; false: disable MD5 validation.
+//
+func EnableMD5(isEnableMD5 bool) ClientOption {
+ return func(client *Client) {
+ client.Config.IsEnableMD5 = isEnableMD5
+ }
+}
+
+// MD5ThresholdCalcInMemory sets the memory usage threshold for computing the MD5, default is 16MB.
+//
+// threshold the memory threshold in bytes. When the uploaded content is more than 16MB, the temp file is used for computing the MD5.
+//
+func MD5ThresholdCalcInMemory(threshold int64) ClientOption {
+ return func(client *Client) {
+ client.Config.MD5Threshold = threshold
+ }
+}
+
+// EnableCRC enables the CRC checksum. Default is true.
+//
+// isEnableCRC true: enable CRC checksum; false: disable the CRC checksum.
+//
+func EnableCRC(isEnableCRC bool) ClientOption {
+ return func(client *Client) {
+ client.Config.IsEnableCRC = isEnableCRC
+ }
+}
+
+// UserAgent specifies UserAgent. The default is aliyun-sdk-go/1.2.0 (windows/-/amd64;go1.5.2).
+//
+// userAgent the user agent string.
+//
+func UserAgent(userAgent string) ClientOption {
+ return func(client *Client) {
+ client.Config.UserAgent = userAgent
+ client.Config.UserSetUa = true
+ }
+}
+
+// Proxy sets the proxy (optional). The default is not using proxy.
+//
+// proxyHost the proxy host in the format "host:port". For example, proxy.com:80 .
+//
+func Proxy(proxyHost string) ClientOption {
+ return func(client *Client) {
+ client.Config.IsUseProxy = true
+ client.Config.ProxyHost = proxyHost
+ }
+}
+
+// AuthProxy sets the proxy information with user name and password.
+//
+// proxyHost the proxy host in the format "host:port". For example, proxy.com:80 .
+// proxyUser the proxy user name.
+// proxyPassword the proxy password.
+//
+func AuthProxy(proxyHost, proxyUser, proxyPassword string) ClientOption {
+ return func(client *Client) {
+ client.Config.IsUseProxy = true
+ client.Config.ProxyHost = proxyHost
+ client.Config.IsAuthProxy = true
+ client.Config.ProxyUser = proxyUser
+ client.Config.ProxyPassword = proxyPassword
+ }
+}
+
+//
+// HTTPClient sets the http.Client in use to the one passed in
+//
+func HTTPClient(HTTPClient *http.Client) ClientOption {
+ return func(client *Client) {
+ client.HTTPClient = HTTPClient
+ }
+}
+
+//
+// SetLogLevel sets the oss sdk log level
+//
+func SetLogLevel(LogLevel int) ClientOption {
+ return func(client *Client) {
+ client.Config.LogLevel = LogLevel
+ }
+}
+
+//
+// SetLogger sets the oss sdk logger
+//
+func SetLogger(Logger *log.Logger) ClientOption {
+ return func(client *Client) {
+ client.Config.Logger = Logger
+ }
+}
+
+// SetCredentialsProvider sets function for get the user's ak
+func SetCredentialsProvider(provider CredentialsProvider) ClientOption {
+ return func(client *Client) {
+ client.Config.CredentialsProvider = provider
+ }
+}
+
+// SetLocalAddr sets function for local addr
+func SetLocalAddr(localAddr net.Addr) ClientOption {
+ return func(client *Client) {
+ client.Config.LocalAddr = localAddr
+ }
+}
+
+// AuthVersion sets auth version: v1 or v2 signature which oss_server needed
+func AuthVersion(authVersion AuthVersionType) ClientOption {
+ return func(client *Client) {
+ client.Config.AuthVersion = authVersion
+ }
+}
+
+// AdditionalHeaders sets special http headers needed to be signed
+func AdditionalHeaders(headers []string) ClientOption {
+ return func(client *Client) {
+ client.Config.AdditionalHeaders = headers
+ }
+}
+
+// RedirectEnabled only effective from go1.7 onward,RedirectEnabled set http redirect enabled or not
+func RedirectEnabled(enabled bool) ClientOption {
+ return func(client *Client) {
+ client.Config.RedirectEnabled = enabled
+ }
+}
+
+// InsecureSkipVerify skip verifying tls certificate file
+func InsecureSkipVerify(enabled bool) ClientOption {
+ return func(client *Client) {
+ client.Config.InsecureSkipVerify = enabled
+ }
+}
+
+// Region set region
+func Region(region string) ClientOption {
+ return func(client *Client) {
+ client.Config.Region = region
+ }
+}
+
+// CloudBoxId set cloudBox id
+func CloudBoxId(cloudBoxId string) ClientOption {
+ return func(client *Client) {
+ client.Config.CloudBoxId = cloudBoxId
+ }
+}
+
+// Product set product type
+func Product(product string) ClientOption {
+ return func(client *Client) {
+ client.Config.Product = product
+ }
+}
+
+// VerifyObjectStrict sets the flag of verifying object name strictly.
+func VerifyObjectStrict(enable bool) ClientOption {
+ return func(client *Client) {
+ client.Config.VerifyObjectStrict = enable
+ }
+}
+
+// Private
+func (client Client) do(method, bucketName string, params map[string]interface{},
+ headers map[string]string, data io.Reader, options ...Option) (*Response, error) {
+ err := CheckBucketName(bucketName)
+ if len(bucketName) > 0 && err != nil {
+ return nil, err
+ }
+
+ // option headers
+ addHeaders := make(map[string]string)
+ err = handleOptions(addHeaders, options)
+ if err != nil {
+ return nil, err
+ }
+
+ // merge header
+ if headers == nil {
+ headers = make(map[string]string)
+ }
+
+ for k, v := range addHeaders {
+ if _, ok := headers[k]; !ok {
+ headers[k] = v
+ }
+ }
+
+ resp, err := client.Conn.Do(method, bucketName, "", params, headers, data, 0, nil)
+
+ // get response header
+ respHeader, _ := FindOption(options, responseHeader, nil)
+ if respHeader != nil {
+ pRespHeader := respHeader.(*http.Header)
+ if resp != nil {
+ *pRespHeader = resp.Headers
+ }
+ }
+
+ return resp, err
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/conf.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/conf.go
new file mode 100644
index 00000000000..305fbcf1223
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/conf.go
@@ -0,0 +1,301 @@
+package oss
+
+import (
+ "bytes"
+ "fmt"
+ "log"
+ "net"
+ "os"
+ "time"
+)
+
+// Define the level of the output log
+const (
+ LogOff = iota
+ Error
+ Warn
+ Info
+ Debug
+)
+
+// LogTag Tag for each level of log
+var LogTag = []string{"[error]", "[warn]", "[info]", "[debug]"}
+
+// HTTPTimeout defines HTTP timeout.
+type HTTPTimeout struct {
+ ConnectTimeout time.Duration
+ ReadWriteTimeout time.Duration
+ HeaderTimeout time.Duration
+ LongTimeout time.Duration
+ IdleConnTimeout time.Duration
+}
+
+// HTTPMaxConns defines max idle connections and max idle connections per host
+type HTTPMaxConns struct {
+ MaxIdleConns int
+ MaxIdleConnsPerHost int
+ MaxConnsPerHost int
+}
+
+// Credentials is interface for get AccessKeyID,AccessKeySecret,SecurityToken
+type Credentials interface {
+ GetAccessKeyID() string
+ GetAccessKeySecret() string
+ GetSecurityToken() string
+}
+
+// CredentialsProvider is interface for get Credential Info
+type CredentialsProvider interface {
+ GetCredentials() Credentials
+}
+
+type CredentialsProviderE interface {
+ CredentialsProvider
+ GetCredentialsE() (Credentials, error)
+}
+
+type defaultCredentials struct {
+ config *Config
+}
+
+func (defCre *defaultCredentials) GetAccessKeyID() string {
+ return defCre.config.AccessKeyID
+}
+
+func (defCre *defaultCredentials) GetAccessKeySecret() string {
+ return defCre.config.AccessKeySecret
+}
+
+func (defCre *defaultCredentials) GetSecurityToken() string {
+ return defCre.config.SecurityToken
+}
+
+type defaultCredentialsProvider struct {
+ config *Config
+}
+
+func (defBuild *defaultCredentialsProvider) GetCredentials() Credentials {
+ return &defaultCredentials{config: defBuild.config}
+}
+
+type envCredentials struct {
+ AccessKeyId string
+ AccessKeySecret string
+ SecurityToken string
+}
+
+type EnvironmentVariableCredentialsProvider struct {
+ cred Credentials
+}
+
+func (credentials *envCredentials) GetAccessKeyID() string {
+ return credentials.AccessKeyId
+}
+
+func (credentials *envCredentials) GetAccessKeySecret() string {
+ return credentials.AccessKeySecret
+}
+
+func (credentials *envCredentials) GetSecurityToken() string {
+ return credentials.SecurityToken
+}
+
+func (defBuild *EnvironmentVariableCredentialsProvider) GetCredentials() Credentials {
+ var accessID, accessKey, token string
+ if defBuild.cred == nil {
+ accessID = os.Getenv("OSS_ACCESS_KEY_ID")
+ accessKey = os.Getenv("OSS_ACCESS_KEY_SECRET")
+ token = os.Getenv("OSS_SESSION_TOKEN")
+ } else {
+ accessID = defBuild.cred.GetAccessKeyID()
+ accessKey = defBuild.cred.GetAccessKeySecret()
+ token = defBuild.cred.GetSecurityToken()
+ }
+
+ return &envCredentials{
+ AccessKeyId: accessID,
+ AccessKeySecret: accessKey,
+ SecurityToken: token,
+ }
+}
+
+func NewEnvironmentVariableCredentialsProvider() (EnvironmentVariableCredentialsProvider, error) {
+ var provider EnvironmentVariableCredentialsProvider
+ accessID := os.Getenv("OSS_ACCESS_KEY_ID")
+ if accessID == "" {
+ return provider, fmt.Errorf("access key id is empty!")
+ }
+ accessKey := os.Getenv("OSS_ACCESS_KEY_SECRET")
+ if accessKey == "" {
+ return provider, fmt.Errorf("access key secret is empty!")
+ }
+ token := os.Getenv("OSS_SESSION_TOKEN")
+ envCredential := &envCredentials{
+ AccessKeyId: accessID,
+ AccessKeySecret: accessKey,
+ SecurityToken: token,
+ }
+ return EnvironmentVariableCredentialsProvider{
+ cred: envCredential,
+ }, nil
+}
+
+// Config defines oss configuration
+type Config struct {
+ Endpoint string // OSS endpoint
+ AccessKeyID string // AccessId
+ AccessKeySecret string // AccessKey
+ RetryTimes uint // Retry count by default it's 5.
+ UserAgent string // SDK name/version/system information
+ IsDebug bool // Enable debug mode. Default is false.
+ Timeout uint // Timeout in seconds. By default it's 60.
+ SecurityToken string // STS Token
+ IsCname bool // If cname is in the endpoint.
+ IsPathStyle bool // If Path Style is in the endpoint.
+ HTTPTimeout HTTPTimeout // HTTP timeout
+ HTTPMaxConns HTTPMaxConns // Http max connections
+ IsUseProxy bool // Flag of using proxy.
+ ProxyHost string // Flag of using proxy host.
+ IsAuthProxy bool // Flag of needing authentication.
+ ProxyUser string // Proxy user
+ ProxyPassword string // Proxy password
+ IsEnableMD5 bool // Flag of enabling MD5 for upload.
+ MD5Threshold int64 // Memory footprint threshold for each MD5 computation (16MB is the default), in byte. When the data is more than that, temp file is used.
+ IsEnableCRC bool // Flag of enabling CRC for upload.
+ LogLevel int // Log level
+ Logger *log.Logger // For write log
+ UploadLimitSpeed int // Upload limit speed:KB/s, 0 is unlimited
+ UploadLimiter *OssLimiter // Bandwidth limit reader for upload
+ DownloadLimitSpeed int // Download limit speed:KB/s, 0 is unlimited
+ DownloadLimiter *OssLimiter // Bandwidth limit reader for download
+ CredentialsProvider CredentialsProvider // User provides interface to get AccessKeyID, AccessKeySecret, SecurityToken
+ LocalAddr net.Addr // local client host info
+ UserSetUa bool // UserAgent is set by user or not
+ AuthVersion AuthVersionType // v1 or v2, v4 signature,default is v1
+ AdditionalHeaders []string // special http headers needed to be sign
+ RedirectEnabled bool // only effective from go1.7 onward, enable http redirect or not
+ InsecureSkipVerify bool // for https, Whether to skip verifying the server certificate file
+ Region string // such as cn-hangzhou
+ CloudBoxId string //
+ Product string // oss or oss-cloudbox, default is oss
+ VerifyObjectStrict bool // a flag of verifying object name strictly. Default is enable.
+}
+
+// LimitUploadSpeed uploadSpeed:KB/s, 0 is unlimited,default is 0
+func (config *Config) LimitUploadSpeed(uploadSpeed int) error {
+ if uploadSpeed < 0 {
+ return fmt.Errorf("invalid argument, the value of uploadSpeed is less than 0")
+ } else if uploadSpeed == 0 {
+ config.UploadLimitSpeed = 0
+ config.UploadLimiter = nil
+ return nil
+ }
+
+ var err error
+ config.UploadLimiter, err = GetOssLimiter(uploadSpeed)
+ if err == nil {
+ config.UploadLimitSpeed = uploadSpeed
+ }
+ return err
+}
+
+// LimitDownLoadSpeed downloadSpeed:KB/s, 0 is unlimited,default is 0
+func (config *Config) LimitDownloadSpeed(downloadSpeed int) error {
+ if downloadSpeed < 0 {
+ return fmt.Errorf("invalid argument, the value of downloadSpeed is less than 0")
+ } else if downloadSpeed == 0 {
+ config.DownloadLimitSpeed = 0
+ config.DownloadLimiter = nil
+ return nil
+ }
+
+ var err error
+ config.DownloadLimiter, err = GetOssLimiter(downloadSpeed)
+ if err == nil {
+ config.DownloadLimitSpeed = downloadSpeed
+ }
+ return err
+}
+
+// WriteLog output log function
+func (config *Config) WriteLog(LogLevel int, format string, a ...interface{}) {
+ if config.LogLevel < LogLevel || config.Logger == nil {
+ return
+ }
+
+ var logBuffer bytes.Buffer
+ logBuffer.WriteString(LogTag[LogLevel-1])
+ logBuffer.WriteString(fmt.Sprintf(format, a...))
+ config.Logger.Printf("%s", logBuffer.String())
+}
+
+// for get Credentials
+func (config *Config) GetCredentials() Credentials {
+ return config.CredentialsProvider.GetCredentials()
+}
+
+// for get Sign Product
+func (config *Config) GetSignProduct() string {
+ if config.CloudBoxId != "" {
+ return "oss-cloudbox"
+ }
+ return "oss"
+}
+
+// for get Sign Region
+func (config *Config) GetSignRegion() string {
+ if config.CloudBoxId != "" {
+ return config.CloudBoxId
+ }
+ return config.Region
+}
+
+// getDefaultOssConfig gets the default configuration.
+func getDefaultOssConfig() *Config {
+ config := Config{}
+
+ config.Endpoint = ""
+ config.AccessKeyID = ""
+ config.AccessKeySecret = ""
+ config.RetryTimes = 5
+ config.IsDebug = false
+ config.UserAgent = userAgent()
+ config.Timeout = 60 // Seconds
+ config.SecurityToken = ""
+ config.IsCname = false
+ config.IsPathStyle = false
+
+ config.HTTPTimeout.ConnectTimeout = time.Second * 30 // 30s
+ config.HTTPTimeout.ReadWriteTimeout = time.Second * 60 // 60s
+ config.HTTPTimeout.HeaderTimeout = time.Second * 60 // 60s
+ config.HTTPTimeout.LongTimeout = time.Second * 300 // 300s
+ config.HTTPTimeout.IdleConnTimeout = time.Second * 50 // 50s
+ config.HTTPMaxConns.MaxIdleConns = 100
+ config.HTTPMaxConns.MaxIdleConnsPerHost = 100
+
+ config.IsUseProxy = false
+ config.ProxyHost = ""
+ config.IsAuthProxy = false
+ config.ProxyUser = ""
+ config.ProxyPassword = ""
+
+ config.MD5Threshold = 16 * 1024 * 1024 // 16MB
+ config.IsEnableMD5 = false
+ config.IsEnableCRC = true
+
+ config.LogLevel = LogOff
+ config.Logger = log.New(os.Stdout, "", log.LstdFlags)
+
+ provider := &defaultCredentialsProvider{config: &config}
+ config.CredentialsProvider = provider
+
+ config.AuthVersion = AuthV1
+ config.RedirectEnabled = true
+ config.InsecureSkipVerify = false
+
+ config.Product = "oss"
+
+ config.VerifyObjectStrict = true
+
+ return &config
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/conn.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/conn.go
new file mode 100644
index 00000000000..86e2b6f2f9d
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/conn.go
@@ -0,0 +1,1021 @@
+package oss
+
+import (
+ "bytes"
+ "context"
+ "crypto/md5"
+ "encoding/base64"
+ "encoding/json"
+ "encoding/xml"
+ "fmt"
+ "hash"
+ "io"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "net/url"
+ "os"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// Conn defines OSS Conn
+type Conn struct {
+ config *Config
+ url *urlMaker
+ client *http.Client
+}
+
+var signKeyList = []string{"acl", "uploads", "location", "cors",
+ "logging", "website", "referer", "lifecycle",
+ "delete", "append", "tagging", "objectMeta",
+ "uploadId", "partNumber", "security-token",
+ "position", "img", "style", "styleName",
+ "replication", "replicationProgress",
+ "replicationLocation", "cname", "bucketInfo",
+ "comp", "qos", "live", "status", "vod",
+ "startTime", "endTime", "symlink",
+ "x-oss-process", "response-content-type", "x-oss-traffic-limit",
+ "response-content-language", "response-expires",
+ "response-cache-control", "response-content-disposition",
+ "response-content-encoding", "udf", "udfName", "udfImage",
+ "udfId", "udfImageDesc", "udfApplication", "comp",
+ "udfApplicationLog", "restore", "callback", "callback-var", "qosInfo",
+ "policy", "stat", "encryption", "versions", "versioning", "versionId", "requestPayment",
+ "x-oss-request-payer", "sequential",
+ "inventory", "inventoryId", "continuation-token", "asyncFetch",
+ "worm", "wormId", "wormExtend", "withHashContext",
+ "x-oss-enable-md5", "x-oss-enable-sha1", "x-oss-enable-sha256",
+ "x-oss-hash-ctx", "x-oss-md5-ctx", "transferAcceleration",
+ "regionList", "cloudboxes", "x-oss-ac-source-ip", "x-oss-ac-subnet-mask", "x-oss-ac-vpc-id", "x-oss-ac-forward-allow",
+ "metaQuery", "resourceGroup", "rtc", "x-oss-async-process", "responseHeader",
+}
+
+const (
+ timeFormatV4 = "20060102T150405Z"
+ shortTimeFormatV4 = "20060102"
+ signingAlgorithmV4 = "OSS4-HMAC-SHA256"
+)
+
+// init initializes Conn
+func (conn *Conn) init(config *Config, urlMaker *urlMaker, client *http.Client) error {
+ if client == nil {
+ // New transport
+ transport := newTransport(conn, config)
+
+ // Proxy
+ if conn.config.IsUseProxy {
+ proxyURL, err := url.Parse(config.ProxyHost)
+ if err != nil {
+ return err
+ }
+ if config.IsAuthProxy {
+ if config.ProxyPassword != "" {
+ proxyURL.User = url.UserPassword(config.ProxyUser, config.ProxyPassword)
+ } else {
+ proxyURL.User = url.User(config.ProxyUser)
+ }
+ }
+ transport.Proxy = http.ProxyURL(proxyURL)
+ }
+ client = &http.Client{Transport: transport}
+ if !config.RedirectEnabled {
+ disableHTTPRedirect(client)
+ }
+ }
+
+ conn.config = config
+ conn.url = urlMaker
+ conn.client = client
+
+ return nil
+}
+
+// Do sends request and returns the response
+func (conn Conn) Do(method, bucketName, objectName string, params map[string]interface{}, headers map[string]string,
+ data io.Reader, initCRC uint64, listener ProgressListener) (*Response, error) {
+ return conn.DoWithContext(nil, method, bucketName, objectName, params, headers, data, initCRC, listener)
+}
+
+// DoWithContext sends request and returns the response with context
+func (conn Conn) DoWithContext(ctx context.Context, method, bucketName, objectName string, params map[string]interface{}, headers map[string]string,
+ data io.Reader, initCRC uint64, listener ProgressListener) (*Response, error) {
+ urlParams := conn.getURLParams(params)
+ subResource := conn.getSubResource(params)
+ uri := conn.url.getURL(bucketName, objectName, urlParams)
+
+ resource := ""
+ if conn.config.AuthVersion != AuthV4 {
+ resource = conn.getResource(bucketName, objectName, subResource)
+ } else {
+ resource = conn.getResourceV4(bucketName, objectName, subResource)
+ }
+
+ return conn.doRequest(ctx, method, uri, resource, headers, data, initCRC, listener)
+}
+
+// DoURL sends the request with signed URL and returns the response result.
+func (conn Conn) DoURL(method HTTPMethod, signedURL string, headers map[string]string,
+ data io.Reader, initCRC uint64, listener ProgressListener) (*Response, error) {
+ return conn.DoURLWithContext(nil, method, signedURL, headers, data, initCRC, listener)
+}
+
+// DoURLWithContext sends the request with signed URL and context and returns the response result.
+func (conn Conn) DoURLWithContext(ctx context.Context, method HTTPMethod, signedURL string, headers map[string]string,
+ data io.Reader, initCRC uint64, listener ProgressListener) (*Response, error) {
+ // Get URI from signedURL
+ uri, err := url.ParseRequestURI(signedURL)
+ if err != nil {
+ return nil, err
+ }
+
+ m := strings.ToUpper(string(method))
+ req := &http.Request{
+ Method: m,
+ URL: uri,
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Header: make(http.Header),
+ Host: uri.Host,
+ }
+
+ if ctx != nil {
+ req = req.WithContext(ctx)
+ }
+ tracker := &readerTracker{completedBytes: 0}
+ fd, crc := conn.handleBody(req, data, initCRC, listener, tracker)
+ if fd != nil {
+ defer func() {
+ fd.Close()
+ os.Remove(fd.Name())
+ }()
+ }
+
+ if conn.config.IsAuthProxy {
+ auth := conn.config.ProxyUser + ":" + conn.config.ProxyPassword
+ basic := "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
+ req.Header.Set("Proxy-Authorization", basic)
+ }
+
+ req.Header.Set(HTTPHeaderHost, req.Host)
+ req.Header.Set(HTTPHeaderUserAgent, conn.config.UserAgent)
+
+ if headers != nil {
+ for k, v := range headers {
+ req.Header.Set(k, v)
+ }
+ }
+
+ // Transfer started
+ event := newProgressEvent(TransferStartedEvent, 0, req.ContentLength, 0)
+ publishProgress(listener, event)
+
+ if conn.config.LogLevel >= Debug {
+ conn.LoggerHTTPReq(req)
+ }
+
+ resp, err := conn.client.Do(req)
+ if err != nil {
+ // Transfer failed
+ conn.config.WriteLog(Debug, "[Resp:%p]http error:%s\n", req, err.Error())
+ event = newProgressEvent(TransferFailedEvent, tracker.completedBytes, req.ContentLength, 0)
+ publishProgress(listener, event)
+
+ return nil, err
+ }
+
+ if conn.config.LogLevel >= Debug {
+ //print out http resp
+ conn.LoggerHTTPResp(req, resp)
+ }
+
+ // Transfer completed
+ event = newProgressEvent(TransferCompletedEvent, tracker.completedBytes, req.ContentLength, 0)
+ publishProgress(listener, event)
+
+ return conn.handleResponse(resp, crc)
+}
+
+func (conn Conn) getURLParams(params map[string]interface{}) string {
+ // Sort
+ keys := make([]string, 0, len(params))
+ for k := range params {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+
+ // Serialize
+ var buf bytes.Buffer
+ for _, k := range keys {
+ if buf.Len() > 0 {
+ buf.WriteByte('&')
+ }
+ buf.WriteString(url.QueryEscape(k))
+ if params[k] != nil && params[k].(string) != "" {
+ buf.WriteString("=" + strings.Replace(url.QueryEscape(params[k].(string)), "+", "%20", -1))
+ }
+ }
+
+ return buf.String()
+}
+
+func (conn Conn) getSubResource(params map[string]interface{}) string {
+ // Sort
+ keys := make([]string, 0, len(params))
+ signParams := make(map[string]string)
+ for k := range params {
+ if conn.config.AuthVersion == AuthV2 || conn.config.AuthVersion == AuthV4 {
+ encodedKey := url.QueryEscape(k)
+ keys = append(keys, encodedKey)
+ if params[k] != nil && params[k] != "" {
+ signParams[encodedKey] = strings.Replace(url.QueryEscape(params[k].(string)), "+", "%20", -1)
+ }
+ } else if conn.isParamSign(k) {
+ keys = append(keys, k)
+ if params[k] != nil {
+ signParams[k] = params[k].(string)
+ }
+ }
+ }
+ sort.Strings(keys)
+
+ // Serialize
+ var buf bytes.Buffer
+ for _, k := range keys {
+ if buf.Len() > 0 {
+ buf.WriteByte('&')
+ }
+ buf.WriteString(k)
+ if _, ok := signParams[k]; ok {
+ if signParams[k] != "" {
+ buf.WriteString("=" + signParams[k])
+ }
+ }
+ }
+ return buf.String()
+}
+
+func (conn Conn) isParamSign(paramKey string) bool {
+ for _, k := range signKeyList {
+ if paramKey == k {
+ return true
+ }
+ }
+ return false
+}
+
+// getResource gets canonicalized resource
+func (conn Conn) getResource(bucketName, objectName, subResource string) string {
+ if subResource != "" {
+ subResource = "?" + subResource
+ }
+ if bucketName == "" {
+ if conn.config.AuthVersion == AuthV2 {
+ return url.QueryEscape("/") + subResource
+ }
+ return fmt.Sprintf("/%s%s", bucketName, subResource)
+ }
+ if conn.config.AuthVersion == AuthV2 {
+ return url.QueryEscape("/"+bucketName+"/") + strings.Replace(url.QueryEscape(objectName), "+", "%20", -1) + subResource
+ }
+ return fmt.Sprintf("/%s/%s%s", bucketName, objectName, subResource)
+}
+
+// getResource gets canonicalized resource
+func (conn Conn) getResourceV4(bucketName, objectName, subResource string) string {
+ if subResource != "" {
+ subResource = "?" + subResource
+ }
+
+ if bucketName == "" {
+ return fmt.Sprintf("/%s", subResource)
+ }
+
+ if objectName != "" {
+ objectName = url.QueryEscape(objectName)
+ objectName = strings.Replace(objectName, "+", "%20", -1)
+ objectName = strings.Replace(objectName, "%2F", "/", -1)
+ return fmt.Sprintf("/%s/%s%s", bucketName, objectName, subResource)
+ }
+ return fmt.Sprintf("/%s/%s", bucketName, subResource)
+}
+
+func (conn Conn) doRequest(ctx context.Context, method string, uri *url.URL, canonicalizedResource string, headers map[string]string,
+ data io.Reader, initCRC uint64, listener ProgressListener) (*Response, error) {
+ method = strings.ToUpper(method)
+ var req *http.Request
+ var err error
+ req = &http.Request{
+ Method: method,
+ URL: uri,
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Header: make(http.Header),
+ Host: uri.Host,
+ }
+ if ctx != nil {
+ req = req.WithContext(ctx)
+ }
+ tracker := &readerTracker{completedBytes: 0}
+ fd, crc := conn.handleBody(req, data, initCRC, listener, tracker)
+ if fd != nil {
+ defer func() {
+ fd.Close()
+ os.Remove(fd.Name())
+ }()
+ }
+
+ if conn.config.IsAuthProxy {
+ auth := conn.config.ProxyUser + ":" + conn.config.ProxyPassword
+ basic := "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
+ req.Header.Set("Proxy-Authorization", basic)
+ }
+
+ stNow := time.Now().UTC()
+ req.Header.Set(HTTPHeaderDate, stNow.Format(http.TimeFormat))
+ req.Header.Set(HTTPHeaderHost, req.Host)
+ req.Header.Set(HTTPHeaderUserAgent, conn.config.UserAgent)
+
+ if conn.config.AuthVersion == AuthV4 {
+ req.Header.Set(HttpHeaderOssContentSha256, DefaultContentSha256)
+ }
+
+ var akIf Credentials
+ if providerE, ok := conn.config.CredentialsProvider.(CredentialsProviderE); ok {
+ if akIf, err = providerE.GetCredentialsE(); err != nil {
+ return nil, err
+ }
+ } else {
+ akIf = conn.config.GetCredentials()
+ }
+
+ if akIf.GetSecurityToken() != "" {
+ req.Header.Set(HTTPHeaderOssSecurityToken, akIf.GetSecurityToken())
+ }
+
+ if headers != nil {
+ for k, v := range headers {
+ req.Header.Set(k, v)
+ }
+ }
+
+ conn.signHeader(req, canonicalizedResource, akIf)
+
+ // Transfer started
+ event := newProgressEvent(TransferStartedEvent, 0, req.ContentLength, 0)
+ publishProgress(listener, event)
+
+ if conn.config.LogLevel >= Debug {
+ conn.LoggerHTTPReq(req)
+ }
+
+ resp, err := conn.client.Do(req)
+
+ if err != nil {
+ conn.config.WriteLog(Debug, "[Resp:%p]http error:%s\n", req, err.Error())
+ // Transfer failed
+ event = newProgressEvent(TransferFailedEvent, tracker.completedBytes, req.ContentLength, 0)
+ publishProgress(listener, event)
+ return nil, err
+ }
+
+ if conn.config.LogLevel >= Debug {
+ //print out http resp
+ conn.LoggerHTTPResp(req, resp)
+ }
+
+ // Transfer completed
+ event = newProgressEvent(TransferCompletedEvent, tracker.completedBytes, req.ContentLength, 0)
+ publishProgress(listener, event)
+
+ return conn.handleResponse(resp, crc)
+}
+
+func (conn Conn) signURL(method HTTPMethod, bucketName, objectName string, expiration int64, params map[string]interface{}, headers map[string]string) (string, error) {
+ var akIf Credentials
+ var err error
+ if providerE, ok := conn.config.CredentialsProvider.(CredentialsProviderE); ok {
+ if akIf, err = providerE.GetCredentialsE(); err != nil {
+ return "", err
+ }
+ } else {
+ akIf = conn.config.GetCredentials()
+ }
+
+ m := strings.ToUpper(string(method))
+ req := &http.Request{
+ Method: m,
+ Header: make(http.Header),
+ }
+
+ if conn.config.IsAuthProxy {
+ auth := conn.config.ProxyUser + ":" + conn.config.ProxyPassword
+ basic := "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
+ req.Header.Set("Proxy-Authorization", basic)
+ }
+
+ if conn.config.AuthVersion == AuthV4 {
+ if akIf.GetSecurityToken() != "" {
+ params[HTTPParamOssSecurityToken] = akIf.GetSecurityToken()
+ }
+
+ if headers != nil {
+ for k, v := range headers {
+ req.Header.Set(k, v)
+ }
+ }
+
+ now := time.Now().UTC()
+ expires := expiration - now.Unix()
+ product := conn.config.GetSignProduct()
+ region := conn.config.GetSignRegion()
+ strDay := now.Format(shortTimeFormatV4)
+ additionalList, _ := conn.getAdditionalHeaderKeys(req)
+
+ params[HTTPParamSignatureVersion] = signingAlgorithmV4
+ params[HTTPParamCredential] = fmt.Sprintf("%s/%s/%s/%s/aliyun_v4_request", akIf.GetAccessKeyID(), strDay, region, product)
+ params[HTTPParamDate] = now.Format(timeFormatV4)
+ params[HTTPParamExpiresV2] = strconv.FormatInt(expires, 10)
+ if len(additionalList) > 0 {
+ params[HTTPParamAdditionalHeadersV2] = strings.Join(additionalList, ";")
+ }
+
+ subResource := conn.getSubResource(params)
+ canonicalizedResource := conn.getResourceV4(bucketName, objectName, subResource)
+ authorizationStr := conn.getSignedStrV4(req, canonicalizedResource, akIf.GetAccessKeySecret(), &now)
+ params[HTTPParamSignatureV2] = authorizationStr
+ } else {
+ if akIf.GetSecurityToken() != "" {
+ params[HTTPParamSecurityToken] = akIf.GetSecurityToken()
+ }
+
+ req.Header.Set(HTTPHeaderDate, strconv.FormatInt(expiration, 10))
+
+ if headers != nil {
+ for k, v := range headers {
+ req.Header.Set(k, v)
+ }
+ }
+
+ if conn.config.AuthVersion == AuthV2 {
+ params[HTTPParamSignatureVersion] = "OSS2"
+ params[HTTPParamExpiresV2] = strconv.FormatInt(expiration, 10)
+ params[HTTPParamAccessKeyIDV2] = conn.config.AccessKeyID
+ additionalList, _ := conn.getAdditionalHeaderKeys(req)
+ if len(additionalList) > 0 {
+ params[HTTPParamAdditionalHeadersV2] = strings.Join(additionalList, ";")
+ }
+ }
+
+ subResource := conn.getSubResource(params)
+ canonicalizedResource := conn.getResource(bucketName, objectName, subResource)
+ signedStr := conn.getSignedStr(req, canonicalizedResource, akIf.GetAccessKeySecret())
+
+ if conn.config.AuthVersion == AuthV1 {
+ params[HTTPParamExpires] = strconv.FormatInt(expiration, 10)
+ params[HTTPParamAccessKeyID] = akIf.GetAccessKeyID()
+ params[HTTPParamSignature] = signedStr
+ } else if conn.config.AuthVersion == AuthV2 {
+ params[HTTPParamSignatureV2] = signedStr
+ }
+ }
+
+ urlParams := conn.getURLParams(params)
+ return conn.url.getSignURL(bucketName, objectName, urlParams), nil
+}
+
+func (conn Conn) signRtmpURL(bucketName, channelName, playlistName string, expiration int64) string {
+ params := map[string]interface{}{}
+ if playlistName != "" {
+ params[HTTPParamPlaylistName] = playlistName
+ }
+ expireStr := strconv.FormatInt(expiration, 10)
+ params[HTTPParamExpires] = expireStr
+
+ akIf := conn.config.GetCredentials()
+ if akIf.GetAccessKeyID() != "" {
+ params[HTTPParamAccessKeyID] = akIf.GetAccessKeyID()
+ if akIf.GetSecurityToken() != "" {
+ params[HTTPParamSecurityToken] = akIf.GetSecurityToken()
+ }
+ signedStr := conn.getRtmpSignedStr(bucketName, channelName, playlistName, expiration, akIf.GetAccessKeySecret(), params)
+ params[HTTPParamSignature] = signedStr
+ }
+
+ urlParams := conn.getURLParams(params)
+ return conn.url.getSignRtmpURL(bucketName, channelName, urlParams)
+}
+
+// handleBody handles request body
+func (conn Conn) handleBody(req *http.Request, body io.Reader, initCRC uint64,
+ listener ProgressListener, tracker *readerTracker) (*os.File, hash.Hash64) {
+ var file *os.File
+ var crc hash.Hash64
+ reader := body
+ readerLen, err := GetReaderLen(reader)
+ if err == nil {
+ req.ContentLength = readerLen
+ }
+ req.Header.Set(HTTPHeaderContentLength, strconv.FormatInt(req.ContentLength, 10))
+
+ // MD5
+ if body != nil && conn.config.IsEnableMD5 && req.Header.Get(HTTPHeaderContentMD5) == "" {
+ md5 := ""
+ reader, md5, file, _ = calcMD5(body, req.ContentLength, conn.config.MD5Threshold)
+ req.Header.Set(HTTPHeaderContentMD5, md5)
+ }
+
+ // CRC
+ if reader != nil && conn.config.IsEnableCRC {
+ crc = NewCRC(CrcTable(), initCRC)
+ reader = TeeReader(reader, crc, req.ContentLength, listener, tracker)
+ }
+
+ // HTTP body
+ rc, ok := reader.(io.ReadCloser)
+ if !ok && reader != nil {
+ rc = ioutil.NopCloser(reader)
+ }
+
+ if conn.isUploadLimitReq(req) {
+ limitReader := &LimitSpeedReader{
+ reader: rc,
+ ossLimiter: conn.config.UploadLimiter,
+ }
+ req.Body = limitReader
+ } else {
+ req.Body = rc
+ }
+ return file, crc
+}
+
+// isUploadLimitReq: judge limit upload speed or not
+func (conn Conn) isUploadLimitReq(req *http.Request) bool {
+ if conn.config.UploadLimitSpeed == 0 || conn.config.UploadLimiter == nil {
+ return false
+ }
+
+ if req.Method != "GET" && req.Method != "DELETE" && req.Method != "HEAD" {
+ if req.ContentLength > 0 {
+ return true
+ }
+ }
+ return false
+}
+
+func tryGetFileSize(f *os.File) int64 {
+ fInfo, _ := f.Stat()
+ return fInfo.Size()
+}
+
+// handleResponse handles response
+func (conn Conn) handleResponse(resp *http.Response, crc hash.Hash64) (*Response, error) {
+ var cliCRC uint64
+ var srvCRC uint64
+
+ statusCode := resp.StatusCode
+ if statusCode/100 != 2 {
+ if statusCode >= 400 && statusCode <= 505 {
+ // 4xx and 5xx indicate that the operation has error occurred
+ var respBody []byte
+ var errorXml []byte
+ respBody, err := readResponseBody(resp)
+ if err != nil {
+ return nil, err
+ }
+ errorXml = respBody
+ if len(respBody) == 0 && len(resp.Header.Get(HTTPHeaderOssErr)) > 0 {
+ errorXml, err = base64.StdEncoding.DecodeString(resp.Header.Get(HTTPHeaderOssErr))
+ if err != nil {
+ errorXml = respBody
+ }
+ }
+ if len(errorXml) == 0 {
+ err = ServiceError{
+ StatusCode: statusCode,
+ RequestID: resp.Header.Get(HTTPHeaderOssRequestID),
+ Ec: resp.Header.Get(HTTPHeaderOssEc),
+ }
+ } else {
+ srvErr, errIn := serviceErrFromXML(errorXml, resp.StatusCode,
+ resp.Header.Get(HTTPHeaderOssRequestID))
+ if errIn != nil { // error unmarshal the error response
+ if len(resp.Header.Get(HTTPHeaderOssEc)) > 0 {
+ err = fmt.Errorf("oss: service returned invalid response body, status = %s, RequestId = %s, ec = %s", resp.Status, resp.Header.Get(HTTPHeaderOssRequestID), resp.Header.Get(HTTPHeaderOssEc))
+ } else {
+ err = fmt.Errorf("oss: service returned invalid response body, status = %s, RequestId = %s", resp.Status, resp.Header.Get(HTTPHeaderOssRequestID))
+ }
+ } else {
+ err = srvErr
+ }
+ }
+ return &Response{
+ StatusCode: resp.StatusCode,
+ Headers: resp.Header,
+ Body: ioutil.NopCloser(bytes.NewReader(respBody)), // restore the body
+ }, err
+ } else if statusCode >= 300 && statusCode <= 307 {
+ // OSS use 3xx, but response has no body
+ err := fmt.Errorf("oss: service returned %d,%s", resp.StatusCode, resp.Status)
+ return &Response{
+ StatusCode: resp.StatusCode,
+ Headers: resp.Header,
+ Body: resp.Body,
+ }, err
+ } else {
+ // (0,300) [308,400) [506,)
+ // Other extended http StatusCode
+ var respBody []byte
+ var errorXml []byte
+ respBody, err := readResponseBody(resp)
+ if err != nil {
+ return &Response{StatusCode: resp.StatusCode, Headers: resp.Header, Body: ioutil.NopCloser(bytes.NewReader(respBody))}, err
+ }
+ errorXml = respBody
+ if len(respBody) == 0 && len(resp.Header.Get(HTTPHeaderOssErr)) > 0 {
+ errorXml, err = base64.StdEncoding.DecodeString(resp.Header.Get(HTTPHeaderOssErr))
+ if err != nil {
+ errorXml = respBody
+ }
+ }
+ if len(errorXml) == 0 {
+ err = ServiceError{
+ StatusCode: statusCode,
+ RequestID: resp.Header.Get(HTTPHeaderOssRequestID),
+ Ec: resp.Header.Get(HTTPHeaderOssEc),
+ }
+ } else {
+ srvErr, errIn := serviceErrFromXML(errorXml, resp.StatusCode,
+ resp.Header.Get(HTTPHeaderOssRequestID))
+ if errIn != nil { // error unmarshal the error response
+ if len(resp.Header.Get(HTTPHeaderOssEc)) > 0 {
+ err = fmt.Errorf("unknown response body, status = %s, RequestId = %s, ec = %s", resp.Status, resp.Header.Get(HTTPHeaderOssRequestID), resp.Header.Get(HTTPHeaderOssEc))
+ } else {
+ err = fmt.Errorf("unknown response body, status = %s, RequestId = %s", resp.Status, resp.Header.Get(HTTPHeaderOssRequestID))
+ }
+ } else {
+ err = srvErr
+ }
+ }
+ return &Response{
+ StatusCode: resp.StatusCode,
+ Headers: resp.Header,
+ Body: ioutil.NopCloser(bytes.NewReader(respBody)), // restore the body
+ }, err
+ }
+ } else {
+ if conn.config.IsEnableCRC && crc != nil {
+ cliCRC = crc.Sum64()
+ }
+ srvCRC, _ = strconv.ParseUint(resp.Header.Get(HTTPHeaderOssCRC64), 10, 64)
+
+ realBody := resp.Body
+ if conn.isDownloadLimitResponse(resp) {
+ limitReader := &LimitSpeedReader{
+ reader: realBody,
+ ossLimiter: conn.config.DownloadLimiter,
+ }
+ realBody = limitReader
+ }
+
+ // 2xx, successful
+ return &Response{
+ StatusCode: resp.StatusCode,
+ Headers: resp.Header,
+ Body: realBody,
+ ClientCRC: cliCRC,
+ ServerCRC: srvCRC,
+ }, nil
+ }
+}
+
+// isUploadLimitReq: judge limit upload speed or not
+func (conn Conn) isDownloadLimitResponse(resp *http.Response) bool {
+ if resp == nil || conn.config.DownloadLimitSpeed == 0 || conn.config.DownloadLimiter == nil {
+ return false
+ }
+
+ if strings.EqualFold(resp.Request.Method, "GET") {
+ return true
+ }
+ return false
+}
+
+// LoggerHTTPReq Print the header information of the http request
+func (conn Conn) LoggerHTTPReq(req *http.Request) {
+ var logBuffer bytes.Buffer
+ logBuffer.WriteString(fmt.Sprintf("[Req:%p]Method:%s\t", req, req.Method))
+ logBuffer.WriteString(fmt.Sprintf("Host:%s\t", req.URL.Host))
+ logBuffer.WriteString(fmt.Sprintf("Path:%s\t", req.URL.Path))
+ logBuffer.WriteString(fmt.Sprintf("Query:%s\t", req.URL.RawQuery))
+ logBuffer.WriteString(fmt.Sprintf("Header info:"))
+
+ for k, v := range req.Header {
+ var valueBuffer bytes.Buffer
+ for j := 0; j < len(v); j++ {
+ if j > 0 {
+ valueBuffer.WriteString(" ")
+ }
+ valueBuffer.WriteString(v[j])
+ }
+ logBuffer.WriteString(fmt.Sprintf("\t%s:%s", k, valueBuffer.String()))
+ }
+ conn.config.WriteLog(Debug, "%s\n", logBuffer.String())
+}
+
+// LoggerHTTPResp Print Response to http request
+func (conn Conn) LoggerHTTPResp(req *http.Request, resp *http.Response) {
+ var logBuffer bytes.Buffer
+ logBuffer.WriteString(fmt.Sprintf("[Resp:%p]StatusCode:%d\t", req, resp.StatusCode))
+ logBuffer.WriteString(fmt.Sprintf("Header info:"))
+ for k, v := range resp.Header {
+ var valueBuffer bytes.Buffer
+ for j := 0; j < len(v); j++ {
+ if j > 0 {
+ valueBuffer.WriteString(" ")
+ }
+ valueBuffer.WriteString(v[j])
+ }
+ logBuffer.WriteString(fmt.Sprintf("\t%s:%s", k, valueBuffer.String()))
+ }
+ conn.config.WriteLog(Debug, "%s\n", logBuffer.String())
+}
+
+func calcMD5(body io.Reader, contentLen, md5Threshold int64) (reader io.Reader, b64 string, tempFile *os.File, err error) {
+ if contentLen == 0 || contentLen > md5Threshold {
+ // Huge body, use temporary file
+ tempFile, err = ioutil.TempFile(os.TempDir(), TempFilePrefix)
+ if tempFile != nil {
+ io.Copy(tempFile, body)
+ tempFile.Seek(0, os.SEEK_SET)
+ md5 := md5.New()
+ io.Copy(md5, tempFile)
+ sum := md5.Sum(nil)
+ b64 = base64.StdEncoding.EncodeToString(sum[:])
+ tempFile.Seek(0, os.SEEK_SET)
+ reader = tempFile
+ }
+ } else {
+ // Small body, use memory
+ buf, _ := ioutil.ReadAll(body)
+ sum := md5.Sum(buf)
+ b64 = base64.StdEncoding.EncodeToString(sum[:])
+ reader = bytes.NewReader(buf)
+ }
+ return
+}
+
+func readResponseBody(resp *http.Response) ([]byte, error) {
+ defer resp.Body.Close()
+ out, err := ioutil.ReadAll(resp.Body)
+ if err == io.EOF {
+ err = nil
+ }
+ return out, err
+}
+
+func serviceErrFromXML(body []byte, statusCode int, requestID string) (ServiceError, error) {
+ var storageErr ServiceError
+
+ if err := xml.Unmarshal(body, &storageErr); err != nil {
+ return storageErr, err
+ }
+
+ storageErr.StatusCode = statusCode
+ storageErr.RequestID = requestID
+ storageErr.RawMessage = string(body)
+ return storageErr, nil
+}
+
+func xmlUnmarshal(body io.Reader, v interface{}) error {
+ data, err := ioutil.ReadAll(body)
+ if err != nil {
+ return err
+ }
+ return xml.Unmarshal(data, v)
+}
+
+func jsonUnmarshal(body io.Reader, v interface{}) error {
+ data, err := ioutil.ReadAll(body)
+ if err != nil {
+ return err
+ }
+ return json.Unmarshal(data, v)
+}
+
+// timeoutConn handles HTTP timeout
+type timeoutConn struct {
+ conn net.Conn
+ timeout time.Duration
+ longTimeout time.Duration
+}
+
+func newTimeoutConn(conn net.Conn, timeout time.Duration, longTimeout time.Duration) *timeoutConn {
+ conn.SetReadDeadline(time.Now().Add(longTimeout))
+ return &timeoutConn{
+ conn: conn,
+ timeout: timeout,
+ longTimeout: longTimeout,
+ }
+}
+
+func (c *timeoutConn) Read(b []byte) (n int, err error) {
+ c.SetReadDeadline(time.Now().Add(c.timeout))
+ n, err = c.conn.Read(b)
+ c.SetReadDeadline(time.Now().Add(c.longTimeout))
+ return n, err
+}
+
+func (c *timeoutConn) Write(b []byte) (n int, err error) {
+ c.SetWriteDeadline(time.Now().Add(c.timeout))
+ n, err = c.conn.Write(b)
+ c.SetReadDeadline(time.Now().Add(c.longTimeout))
+ return n, err
+}
+
+func (c *timeoutConn) Close() error {
+ return c.conn.Close()
+}
+
+func (c *timeoutConn) LocalAddr() net.Addr {
+ return c.conn.LocalAddr()
+}
+
+func (c *timeoutConn) RemoteAddr() net.Addr {
+ return c.conn.RemoteAddr()
+}
+
+func (c *timeoutConn) SetDeadline(t time.Time) error {
+ return c.conn.SetDeadline(t)
+}
+
+func (c *timeoutConn) SetReadDeadline(t time.Time) error {
+ return c.conn.SetReadDeadline(t)
+}
+
+func (c *timeoutConn) SetWriteDeadline(t time.Time) error {
+ return c.conn.SetWriteDeadline(t)
+}
+
+// UrlMaker builds URL and resource
+const (
+ urlTypeCname = 1
+ urlTypeIP = 2
+ urlTypeAliyun = 3
+ urlTypePathStyle = 4
+)
+
+type urlMaker struct {
+ Scheme string // HTTP or HTTPS
+ NetLoc string // Host or IP
+ Type int // 1 CNAME, 2 IP, 3 ALIYUN
+ IsProxy bool // Proxy
+}
+
+// Init parses endpoint
+func (um *urlMaker) Init(endpoint string, isCname bool, isProxy bool) error {
+ return um.InitExt(endpoint, isCname, isProxy, false)
+}
+
+// InitExt parses endpoint
+func (um *urlMaker) InitExt(endpoint string, isCname bool, isProxy bool, isPathStyle bool) error {
+ if strings.HasPrefix(endpoint, "http://") {
+ um.Scheme = "http"
+ um.NetLoc = endpoint[len("http://"):]
+ } else if strings.HasPrefix(endpoint, "https://") {
+ um.Scheme = "https"
+ um.NetLoc = endpoint[len("https://"):]
+ } else {
+ um.Scheme = "http"
+ um.NetLoc = endpoint
+ }
+
+ //use url.Parse() to get real host
+ strUrl := um.Scheme + "://" + um.NetLoc
+ url, err := url.Parse(strUrl)
+ if err != nil {
+ return err
+ }
+
+ um.NetLoc = url.Host
+ host, _, err := net.SplitHostPort(um.NetLoc)
+ if err != nil {
+ host = um.NetLoc
+ if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' {
+ host = host[1 : len(host)-1]
+ }
+ }
+
+ ip := net.ParseIP(host)
+ if ip != nil {
+ um.Type = urlTypeIP
+ } else if isCname {
+ um.Type = urlTypeCname
+ } else if isPathStyle {
+ um.Type = urlTypePathStyle
+ } else {
+ um.Type = urlTypeAliyun
+ }
+ um.IsProxy = isProxy
+
+ return nil
+}
+
+// getURL gets URL
+func (um urlMaker) getURL(bucket, object, params string) *url.URL {
+ host, path := um.buildURL(bucket, object)
+ addr := ""
+ if params == "" {
+ addr = fmt.Sprintf("%s://%s%s", um.Scheme, host, path)
+ } else {
+ addr = fmt.Sprintf("%s://%s%s?%s", um.Scheme, host, path, params)
+ }
+ uri, _ := url.ParseRequestURI(addr)
+ return uri
+}
+
+// getSignURL gets sign URL
+func (um urlMaker) getSignURL(bucket, object, params string) string {
+ host, path := um.buildURL(bucket, object)
+ return fmt.Sprintf("%s://%s%s?%s", um.Scheme, host, path, params)
+}
+
+// getSignRtmpURL Build Sign Rtmp URL
+func (um urlMaker) getSignRtmpURL(bucket, channelName, params string) string {
+ host, path := um.buildURL(bucket, "live")
+
+ channelName = url.QueryEscape(channelName)
+ channelName = strings.Replace(channelName, "+", "%20", -1)
+
+ return fmt.Sprintf("rtmp://%s%s/%s?%s", host, path, channelName, params)
+}
+
+// buildURL builds URL
+func (um urlMaker) buildURL(bucket, object string) (string, string) {
+ var host = ""
+ var path = ""
+
+ object = url.QueryEscape(object)
+ object = strings.Replace(object, "+", "%20", -1)
+
+ if um.Type == urlTypeCname {
+ host = um.NetLoc
+ path = "/" + object
+ } else if um.Type == urlTypeIP || um.Type == urlTypePathStyle {
+ if bucket == "" {
+ host = um.NetLoc
+ path = "/"
+ } else {
+ host = um.NetLoc
+ path = fmt.Sprintf("/%s/%s", bucket, object)
+ }
+ } else {
+ if bucket == "" {
+ host = um.NetLoc
+ path = "/"
+ } else {
+ host = bucket + "." + um.NetLoc
+ path = "/" + object
+ }
+ }
+
+ return host, path
+}
+
+// buildURL builds URL
+func (um urlMaker) buildURLV4(bucket, object string) (string, string) {
+ var host = ""
+ var path = ""
+
+ object = url.QueryEscape(object)
+ object = strings.Replace(object, "+", "%20", -1)
+
+ // no escape /
+ object = strings.Replace(object, "%2F", "/", -1)
+
+ if um.Type == urlTypeCname {
+ host = um.NetLoc
+ path = "/" + object
+ } else if um.Type == urlTypeIP || um.Type == urlTypePathStyle {
+ if bucket == "" {
+ host = um.NetLoc
+ path = "/"
+ } else {
+ host = um.NetLoc
+ path = fmt.Sprintf("/%s/%s", bucket, object)
+ }
+ } else {
+ if bucket == "" {
+ host = um.NetLoc
+ path = "/"
+ } else {
+ host = bucket + "." + um.NetLoc
+ path = fmt.Sprintf("/%s/%s", bucket, object)
+ }
+ }
+ return host, path
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/const.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/const.go
new file mode 100644
index 00000000000..400f7cf9512
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/const.go
@@ -0,0 +1,273 @@
+package oss
+
+import "os"
+
+// ACLType bucket/object ACL
+type ACLType string
+
+const (
+ // ACLPrivate definition : private read and write
+ ACLPrivate ACLType = "private"
+
+ // ACLPublicRead definition : public read and private write
+ ACLPublicRead ACLType = "public-read"
+
+ // ACLPublicReadWrite definition : public read and public write
+ ACLPublicReadWrite ACLType = "public-read-write"
+
+ // ACLDefault Object. It's only applicable for object.
+ ACLDefault ACLType = "default"
+)
+
+// bucket versioning status
+type VersioningStatus string
+
+const (
+ // Versioning Status definition: Enabled
+ VersionEnabled VersioningStatus = "Enabled"
+
+ // Versioning Status definition: Suspended
+ VersionSuspended VersioningStatus = "Suspended"
+)
+
+// MetadataDirectiveType specifying whether use the metadata of source object when copying object.
+type MetadataDirectiveType string
+
+const (
+ // MetaCopy the target object's metadata is copied from the source one
+ MetaCopy MetadataDirectiveType = "COPY"
+
+ // MetaReplace the target object's metadata is created as part of the copy request (not same as the source one)
+ MetaReplace MetadataDirectiveType = "REPLACE"
+)
+
+// TaggingDirectiveType specifying whether use the tagging of source object when copying object.
+type TaggingDirectiveType string
+
+const (
+ // TaggingCopy the target object's tagging is copied from the source one
+ TaggingCopy TaggingDirectiveType = "COPY"
+
+ // TaggingReplace the target object's tagging is created as part of the copy request (not same as the source one)
+ TaggingReplace TaggingDirectiveType = "REPLACE"
+)
+
+// AlgorithmType specifying the server side encryption algorithm name
+type AlgorithmType string
+
+const (
+ KMSAlgorithm AlgorithmType = "KMS"
+ AESAlgorithm AlgorithmType = "AES256"
+ SM4Algorithm AlgorithmType = "SM4"
+)
+
+// StorageClassType bucket storage type
+type StorageClassType string
+
+const (
+ // StorageStandard standard
+ StorageStandard StorageClassType = "Standard"
+
+ // StorageIA infrequent access
+ StorageIA StorageClassType = "IA"
+
+ // StorageArchive archive
+ StorageArchive StorageClassType = "Archive"
+
+ // StorageColdArchive cold archive
+ StorageColdArchive StorageClassType = "ColdArchive"
+
+ // StorageDeepColdArchive deep cold archive
+ StorageDeepColdArchive StorageClassType = "DeepColdArchive"
+)
+
+//RedundancyType bucket data Redundancy type
+type DataRedundancyType string
+
+const (
+ // RedundancyLRS Local redundancy, default value
+ RedundancyLRS DataRedundancyType = "LRS"
+
+ // RedundancyZRS Same city redundancy
+ RedundancyZRS DataRedundancyType = "ZRS"
+)
+
+//ObjecthashFuncType
+type ObjecthashFuncType string
+
+const (
+ HashFuncSha1 ObjecthashFuncType = "SHA-1"
+ HashFuncSha256 ObjecthashFuncType = "SHA-256"
+)
+
+// PayerType the type of request payer
+type PayerType string
+
+const (
+ // Requester the requester who send the request
+ Requester PayerType = "Requester"
+
+ // BucketOwner the requester who send the request
+ BucketOwner PayerType = "BucketOwner"
+)
+
+//RestoreMode the restore mode for coldArchive object
+type RestoreMode string
+
+const (
+ //RestoreExpedited object will be restored in 1 hour
+ RestoreExpedited RestoreMode = "Expedited"
+
+ //RestoreStandard object will be restored in 2-5 hours
+ RestoreStandard RestoreMode = "Standard"
+
+ //RestoreBulk object will be restored in 5-10 hours
+ RestoreBulk RestoreMode = "Bulk"
+)
+
+// HTTPMethod HTTP request method
+type HTTPMethod string
+
+const (
+ // HTTPGet HTTP GET
+ HTTPGet HTTPMethod = "GET"
+
+ // HTTPPut HTTP PUT
+ HTTPPut HTTPMethod = "PUT"
+
+ // HTTPHead HTTP HEAD
+ HTTPHead HTTPMethod = "HEAD"
+
+ // HTTPPost HTTP POST
+ HTTPPost HTTPMethod = "POST"
+
+ // HTTPDelete HTTP DELETE
+ HTTPDelete HTTPMethod = "DELETE"
+)
+
+// HTTP headers
+const (
+ HTTPHeaderAcceptEncoding string = "Accept-Encoding"
+ HTTPHeaderAuthorization = "Authorization"
+ HTTPHeaderCacheControl = "Cache-Control"
+ HTTPHeaderContentDisposition = "Content-Disposition"
+ HTTPHeaderContentEncoding = "Content-Encoding"
+ HTTPHeaderContentLength = "Content-Length"
+ HTTPHeaderContentMD5 = "Content-MD5"
+ HTTPHeaderContentType = "Content-Type"
+ HTTPHeaderContentLanguage = "Content-Language"
+ HTTPHeaderDate = "Date"
+ HTTPHeaderEtag = "ETag"
+ HTTPHeaderExpires = "Expires"
+ HTTPHeaderHost = "Host"
+ HTTPHeaderLastModified = "Last-Modified"
+ HTTPHeaderRange = "Range"
+ HTTPHeaderLocation = "Location"
+ HTTPHeaderOrigin = "Origin"
+ HTTPHeaderServer = "Server"
+ HTTPHeaderUserAgent = "User-Agent"
+ HTTPHeaderIfModifiedSince = "If-Modified-Since"
+ HTTPHeaderIfUnmodifiedSince = "If-Unmodified-Since"
+ HTTPHeaderIfMatch = "If-Match"
+ HTTPHeaderIfNoneMatch = "If-None-Match"
+ HTTPHeaderACReqMethod = "Access-Control-Request-Method"
+ HTTPHeaderACReqHeaders = "Access-Control-Request-Headers"
+
+ HTTPHeaderOssACL = "X-Oss-Acl"
+ HTTPHeaderOssMetaPrefix = "X-Oss-Meta-"
+ HTTPHeaderOssObjectACL = "X-Oss-Object-Acl"
+ HTTPHeaderOssSecurityToken = "X-Oss-Security-Token"
+ HTTPHeaderOssServerSideEncryption = "X-Oss-Server-Side-Encryption"
+ HTTPHeaderOssServerSideEncryptionKeyID = "X-Oss-Server-Side-Encryption-Key-Id"
+ HTTPHeaderOssServerSideDataEncryption = "X-Oss-Server-Side-Data-Encryption"
+ HTTPHeaderSSECAlgorithm = "X-Oss-Server-Side-Encryption-Customer-Algorithm"
+ HTTPHeaderSSECKey = "X-Oss-Server-Side-Encryption-Customer-Key"
+ HTTPHeaderSSECKeyMd5 = "X-Oss-Server-Side-Encryption-Customer-Key-MD5"
+ HTTPHeaderOssCopySource = "X-Oss-Copy-Source"
+ HTTPHeaderOssCopySourceRange = "X-Oss-Copy-Source-Range"
+ HTTPHeaderOssCopySourceIfMatch = "X-Oss-Copy-Source-If-Match"
+ HTTPHeaderOssCopySourceIfNoneMatch = "X-Oss-Copy-Source-If-None-Match"
+ HTTPHeaderOssCopySourceIfModifiedSince = "X-Oss-Copy-Source-If-Modified-Since"
+ HTTPHeaderOssCopySourceIfUnmodifiedSince = "X-Oss-Copy-Source-If-Unmodified-Since"
+ HTTPHeaderOssMetadataDirective = "X-Oss-Metadata-Directive"
+ HTTPHeaderOssNextAppendPosition = "X-Oss-Next-Append-Position"
+ HTTPHeaderOssRequestID = "X-Oss-Request-Id"
+ HTTPHeaderOssCRC64 = "X-Oss-Hash-Crc64ecma"
+ HTTPHeaderOssSymlinkTarget = "X-Oss-Symlink-Target"
+ HTTPHeaderOssStorageClass = "X-Oss-Storage-Class"
+ HTTPHeaderOssCallback = "X-Oss-Callback"
+ HTTPHeaderOssCallbackVar = "X-Oss-Callback-Var"
+ HTTPHeaderOssRequester = "X-Oss-Request-Payer"
+ HTTPHeaderOssTagging = "X-Oss-Tagging"
+ HTTPHeaderOssTaggingDirective = "X-Oss-Tagging-Directive"
+ HTTPHeaderOssTrafficLimit = "X-Oss-Traffic-Limit"
+ HTTPHeaderOssForbidOverWrite = "X-Oss-Forbid-Overwrite"
+ HTTPHeaderOssRangeBehavior = "X-Oss-Range-Behavior"
+ HTTPHeaderOssTaskID = "X-Oss-Task-Id"
+ HTTPHeaderOssHashCtx = "X-Oss-Hash-Ctx"
+ HTTPHeaderOssMd5Ctx = "X-Oss-Md5-Ctx"
+ HTTPHeaderAllowSameActionOverLap = "X-Oss-Allow-Same-Action-Overlap"
+ HttpHeaderOssDate = "X-Oss-Date"
+ HttpHeaderOssContentSha256 = "X-Oss-Content-Sha256"
+ HttpHeaderOssNotification = "X-Oss-Notification"
+ HTTPHeaderOssEc = "X-Oss-Ec"
+ HTTPHeaderOssErr = "X-Oss-Err"
+)
+
+// HTTP Param
+const (
+ HTTPParamExpires = "Expires"
+ HTTPParamAccessKeyID = "OSSAccessKeyId"
+ HTTPParamSignature = "Signature"
+ HTTPParamSecurityToken = "security-token"
+ HTTPParamPlaylistName = "playlistName"
+
+ HTTPParamSignatureVersion = "x-oss-signature-version"
+ HTTPParamExpiresV2 = "x-oss-expires"
+ HTTPParamAccessKeyIDV2 = "x-oss-access-key-id"
+ HTTPParamSignatureV2 = "x-oss-signature"
+ HTTPParamAdditionalHeadersV2 = "x-oss-additional-headers"
+ HTTPParamCredential = "x-oss-credential"
+ HTTPParamDate = "x-oss-date"
+ HTTPParamOssSecurityToken = "x-oss-security-token"
+)
+
+// Other constants
+const (
+ MaxPartSize = 5 * 1024 * 1024 * 1024 // Max part size, 5GB
+ MinPartSize = 100 * 1024 // Min part size, 100KB
+
+ FilePermMode = os.FileMode(0664) // Default file permission
+
+ TempFilePrefix = "oss-go-temp-" // Temp file prefix
+ TempFileSuffix = ".temp" // Temp file suffix
+
+ CheckpointFileSuffix = ".cp" // Checkpoint file suffix
+
+ NullVersion = "null"
+
+ DefaultContentSha256 = "UNSIGNED-PAYLOAD" // for v4 signature
+
+ Version = "v3.0.2" // Go SDK version
+)
+
+// FrameType
+const (
+ DataFrameType = 8388609
+ ContinuousFrameType = 8388612
+ EndFrameType = 8388613
+ MetaEndFrameCSVType = 8388614
+ MetaEndFrameJSONType = 8388615
+)
+
+// AuthVersion the version of auth
+type AuthVersionType string
+
+const (
+ // AuthV1 v1
+ AuthV1 AuthVersionType = "v1"
+ // AuthV2 v2
+ AuthV2 AuthVersionType = "v2"
+ // AuthV4 v4
+ AuthV4 AuthVersionType = "v4"
+)
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/crc.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/crc.go
new file mode 100644
index 00000000000..c96694f2850
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/crc.go
@@ -0,0 +1,123 @@
+package oss
+
+import (
+ "hash"
+ "hash/crc64"
+)
+
+// digest represents the partial evaluation of a checksum.
+type digest struct {
+ crc uint64
+ tab *crc64.Table
+}
+
+// NewCRC creates a new hash.Hash64 computing the CRC64 checksum
+// using the polynomial represented by the Table.
+func NewCRC(tab *crc64.Table, init uint64) hash.Hash64 { return &digest{init, tab} }
+
+// Size returns the number of bytes sum will return.
+func (d *digest) Size() int { return crc64.Size }
+
+// 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 (d *digest) BlockSize() int { return 1 }
+
+// Reset resets the hash to its initial state.
+func (d *digest) Reset() { d.crc = 0 }
+
+// Write (via the embedded io.Writer interface) adds more data to the running hash.
+// It never returns an error.
+func (d *digest) Write(p []byte) (n int, err error) {
+ d.crc = crc64.Update(d.crc, d.tab, p)
+ return len(p), nil
+}
+
+// Sum64 returns CRC64 value.
+func (d *digest) Sum64() uint64 { return d.crc }
+
+// Sum returns hash value.
+func (d *digest) Sum(in []byte) []byte {
+ s := d.Sum64()
+ return append(in, byte(s>>56), byte(s>>48), byte(s>>40), byte(s>>32), byte(s>>24), byte(s>>16), byte(s>>8), byte(s))
+}
+
+// gf2Dim dimension of GF(2) vectors (length of CRC)
+const gf2Dim int = 64
+
+func gf2MatrixTimes(mat []uint64, vec uint64) uint64 {
+ var sum uint64
+ for i := 0; vec != 0; i++ {
+ if vec&1 != 0 {
+ sum ^= mat[i]
+ }
+
+ vec >>= 1
+ }
+ return sum
+}
+
+func gf2MatrixSquare(square []uint64, mat []uint64) {
+ for n := 0; n < gf2Dim; n++ {
+ square[n] = gf2MatrixTimes(mat, mat[n])
+ }
+}
+
+// CRC64Combine combines CRC64
+func CRC64Combine(crc1 uint64, crc2 uint64, len2 uint64) uint64 {
+ var even [gf2Dim]uint64 // Even-power-of-two zeros operator
+ var odd [gf2Dim]uint64 // Odd-power-of-two zeros operator
+
+ // Degenerate case
+ if len2 == 0 {
+ return crc1
+ }
+
+ // Put operator for one zero bit in odd
+ odd[0] = crc64.ECMA // CRC64 polynomial
+ var row uint64 = 1
+ for n := 1; n < gf2Dim; n++ {
+ odd[n] = row
+ row <<= 1
+ }
+
+ // Put operator for two zero bits in even
+ gf2MatrixSquare(even[:], odd[:])
+
+ // Put operator for four zero bits in odd
+ gf2MatrixSquare(odd[:], even[:])
+
+ // Apply len2 zeros to crc1, first square will put the operator for one zero byte, eight zero bits, in even
+ for {
+ // Apply zeros operator for this bit of len2
+ gf2MatrixSquare(even[:], odd[:])
+
+ if len2&1 != 0 {
+ crc1 = gf2MatrixTimes(even[:], crc1)
+ }
+
+ len2 >>= 1
+
+ // If no more bits set, then done
+ if len2 == 0 {
+ break
+ }
+
+ // Another iteration of the loop with odd and even swapped
+ gf2MatrixSquare(odd[:], even[:])
+ if len2&1 != 0 {
+ crc1 = gf2MatrixTimes(odd[:], crc1)
+ }
+ len2 >>= 1
+
+ // If no more bits set, then done
+ if len2 == 0 {
+ break
+ }
+ }
+
+ // Return combined CRC
+ crc1 ^= crc2
+ return crc1
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/download.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/download.go
new file mode 100644
index 00000000000..90c1b633d9e
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/download.go
@@ -0,0 +1,567 @@
+package oss
+
+import (
+ "crypto/md5"
+ "encoding/base64"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "hash"
+ "hash/crc64"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strconv"
+ "time"
+)
+
+// DownloadFile downloads files with multipart download.
+//
+// objectKey the object key.
+// filePath the local file to download from objectKey in OSS.
+// partSize the part size in bytes.
+// options object's constraints, check out GetObject for the reference.
+//
+// error it's nil when the call succeeds, otherwise it's an error object.
+//
+func (bucket Bucket) DownloadFile(objectKey, filePath string, partSize int64, options ...Option) error {
+ if partSize < 1 {
+ return errors.New("oss: part size smaller than 1")
+ }
+
+ uRange, err := GetRangeConfig(options)
+ if err != nil {
+ return err
+ }
+
+ cpConf := getCpConfig(options)
+ routines := getRoutines(options)
+
+ var strVersionId string
+ versionId, _ := FindOption(options, "versionId", nil)
+ if versionId != nil {
+ strVersionId = versionId.(string)
+ }
+
+ if cpConf != nil && cpConf.IsEnable {
+ cpFilePath := getDownloadCpFilePath(cpConf, bucket.BucketName, objectKey, strVersionId, filePath)
+ if cpFilePath != "" {
+ return bucket.downloadFileWithCp(objectKey, filePath, partSize, options, cpFilePath, routines, uRange)
+ }
+ }
+
+ return bucket.downloadFile(objectKey, filePath, partSize, options, routines, uRange)
+}
+
+func getDownloadCpFilePath(cpConf *cpConfig, srcBucket, srcObject, versionId, destFile string) string {
+ if cpConf.FilePath == "" && cpConf.DirPath != "" {
+ src := fmt.Sprintf("oss://%v/%v", srcBucket, srcObject)
+ absPath, _ := filepath.Abs(destFile)
+ cpFileName := getCpFileName(src, absPath, versionId)
+ cpConf.FilePath = cpConf.DirPath + string(os.PathSeparator) + cpFileName
+ }
+ return cpConf.FilePath
+}
+
+// downloadWorkerArg is download worker's parameters
+type downloadWorkerArg struct {
+ bucket *Bucket
+ key string
+ filePath string
+ options []Option
+ hook downloadPartHook
+ enableCRC bool
+}
+
+// downloadPartHook is hook for test
+type downloadPartHook func(part downloadPart) error
+
+var downloadPartHooker downloadPartHook = defaultDownloadPartHook
+
+func defaultDownloadPartHook(part downloadPart) error {
+ return nil
+}
+
+// defaultDownloadProgressListener defines default ProgressListener, shields the ProgressListener in options of GetObject.
+type defaultDownloadProgressListener struct {
+}
+
+// ProgressChanged no-ops
+func (listener *defaultDownloadProgressListener) ProgressChanged(event *ProgressEvent) {
+}
+
+// downloadWorker
+func downloadWorker(id int, arg downloadWorkerArg, jobs <-chan downloadPart, results chan<- downloadPart, failed chan<- error, die <-chan bool) {
+ for part := range jobs {
+ if err := arg.hook(part); err != nil {
+ failed <- err
+ break
+ }
+
+ // Resolve options
+ r := Range(part.Start, part.End)
+ p := Progress(&defaultDownloadProgressListener{})
+
+ var respHeader http.Header
+ opts := make([]Option, len(arg.options)+3)
+ // Append orderly, can not be reversed!
+ opts = append(opts, arg.options...)
+ opts = append(opts, r, p, GetResponseHeader(&respHeader))
+
+ rd, err := arg.bucket.GetObject(arg.key, opts...)
+ if err != nil {
+ failed <- err
+ break
+ }
+ defer rd.Close()
+
+ var crcCalc hash.Hash64
+ if arg.enableCRC {
+ crcCalc = crc64.New(CrcTable())
+ contentLen := part.End - part.Start + 1
+ rd = ioutil.NopCloser(TeeReader(rd, crcCalc, contentLen, nil, nil))
+ }
+ defer rd.Close()
+
+ select {
+ case <-die:
+ return
+ default:
+ }
+
+ fd, err := os.OpenFile(arg.filePath, os.O_WRONLY, FilePermMode)
+ if err != nil {
+ failed <- err
+ break
+ }
+
+ _, err = fd.Seek(part.Start-part.Offset, os.SEEK_SET)
+ if err != nil {
+ fd.Close()
+ failed <- err
+ break
+ }
+
+ startT := time.Now().UnixNano() / 1000 / 1000 / 1000
+ _, err = io.Copy(fd, rd)
+ endT := time.Now().UnixNano() / 1000 / 1000 / 1000
+ if err != nil {
+ arg.bucket.Client.Config.WriteLog(Debug, "download part error,cost:%d second,part number:%d,request id:%s,error:%s.\n", endT-startT, part.Index, GetRequestId(respHeader), err.Error())
+ fd.Close()
+ failed <- err
+ break
+ }
+
+ if arg.enableCRC {
+ part.CRC64 = crcCalc.Sum64()
+ }
+
+ fd.Close()
+ results <- part
+ }
+}
+
+// downloadScheduler
+func downloadScheduler(jobs chan downloadPart, parts []downloadPart) {
+ for _, part := range parts {
+ jobs <- part
+ }
+ close(jobs)
+}
+
+// downloadPart defines download part
+type downloadPart struct {
+ Index int // Part number, starting from 0
+ Start int64 // Start index
+ End int64 // End index
+ Offset int64 // Offset
+ CRC64 uint64 // CRC check value of part
+}
+
+// getDownloadParts gets download parts
+func getDownloadParts(objectSize, partSize int64, uRange *UnpackedRange) []downloadPart {
+ parts := []downloadPart{}
+ part := downloadPart{}
+ i := 0
+ start, end := AdjustRange(uRange, objectSize)
+ for offset := start; offset < end; offset += partSize {
+ part.Index = i
+ part.Start = offset
+ part.End = GetPartEnd(offset, end, partSize)
+ part.Offset = start
+ part.CRC64 = 0
+ parts = append(parts, part)
+ i++
+ }
+ return parts
+}
+
+// getObjectBytes gets object bytes length
+func getObjectBytes(parts []downloadPart) int64 {
+ var ob int64
+ for _, part := range parts {
+ ob += (part.End - part.Start + 1)
+ }
+ return ob
+}
+
+// combineCRCInParts caculates the total CRC of continuous parts
+func combineCRCInParts(dps []downloadPart) uint64 {
+ if dps == nil || len(dps) == 0 {
+ return 0
+ }
+
+ crc := dps[0].CRC64
+ for i := 1; i < len(dps); i++ {
+ crc = CRC64Combine(crc, dps[i].CRC64, (uint64)(dps[i].End-dps[i].Start+1))
+ }
+
+ return crc
+}
+
+// downloadFile downloads file concurrently without checkpoint.
+func (bucket Bucket) downloadFile(objectKey, filePath string, partSize int64, options []Option, routines int, uRange *UnpackedRange) error {
+ tempFilePath := filePath + TempFileSuffix
+ listener := GetProgressListener(options)
+
+ // If the file does not exist, create one. If exists, the download will overwrite it.
+ fd, err := os.OpenFile(tempFilePath, os.O_WRONLY|os.O_CREATE, FilePermMode)
+ if err != nil {
+ return err
+ }
+ fd.Close()
+
+ // Get the object detailed meta for object whole size
+ // must delete header:range to get whole object size
+ skipOptions := DeleteOption(options, HTTPHeaderRange)
+ meta, err := bucket.GetObjectDetailedMeta(objectKey, skipOptions...)
+ if err != nil {
+ return err
+ }
+
+ objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 64)
+ if err != nil {
+ return err
+ }
+
+ enableCRC := false
+ expectedCRC := (uint64)(0)
+ if bucket.GetConfig().IsEnableCRC && meta.Get(HTTPHeaderOssCRC64) != "" {
+ if uRange == nil || (!uRange.HasStart && !uRange.HasEnd) {
+ enableCRC = true
+ expectedCRC, _ = strconv.ParseUint(meta.Get(HTTPHeaderOssCRC64), 10, 64)
+ }
+ }
+
+ // Get the parts of the file
+ parts := getDownloadParts(objectSize, partSize, uRange)
+ jobs := make(chan downloadPart, len(parts))
+ results := make(chan downloadPart, len(parts))
+ failed := make(chan error)
+ die := make(chan bool)
+
+ var completedBytes int64
+ totalBytes := getObjectBytes(parts)
+ event := newProgressEvent(TransferStartedEvent, 0, totalBytes, 0)
+ publishProgress(listener, event)
+
+ // Start the download workers
+ arg := downloadWorkerArg{&bucket, objectKey, tempFilePath, options, downloadPartHooker, enableCRC}
+ for w := 1; w <= routines; w++ {
+ go downloadWorker(w, arg, jobs, results, failed, die)
+ }
+
+ // Download parts concurrently
+ go downloadScheduler(jobs, parts)
+
+ // Waiting for parts download finished
+ completed := 0
+ for completed < len(parts) {
+ select {
+ case part := <-results:
+ completed++
+ downBytes := (part.End - part.Start + 1)
+ completedBytes += downBytes
+ parts[part.Index].CRC64 = part.CRC64
+ event = newProgressEvent(TransferDataEvent, completedBytes, totalBytes, downBytes)
+ publishProgress(listener, event)
+ case err := <-failed:
+ close(die)
+ event = newProgressEvent(TransferFailedEvent, completedBytes, totalBytes, 0)
+ publishProgress(listener, event)
+ return err
+ }
+
+ if completed >= len(parts) {
+ break
+ }
+ }
+
+ event = newProgressEvent(TransferCompletedEvent, completedBytes, totalBytes, 0)
+ publishProgress(listener, event)
+
+ if enableCRC {
+ actualCRC := combineCRCInParts(parts)
+ err = CheckDownloadCRC(actualCRC, expectedCRC)
+ if err != nil {
+ return err
+ }
+ }
+
+ return os.Rename(tempFilePath, filePath)
+}
+
+// ----- Concurrent download with chcekpoint -----
+
+const downloadCpMagic = "92611BED-89E2-46B6-89E5-72F273D4B0A3"
+
+type downloadCheckpoint struct {
+ Magic string // Magic
+ MD5 string // Checkpoint content MD5
+ FilePath string // Local file
+ Object string // Key
+ ObjStat objectStat // Object status
+ Parts []downloadPart // All download parts
+ PartStat []bool // Parts' download status
+ Start int64 // Start point of the file
+ End int64 // End point of the file
+ enableCRC bool // Whether has CRC check
+ CRC uint64 // CRC check value
+}
+
+type objectStat struct {
+ Size int64 // Object size
+ LastModified string // Last modified time
+ Etag string // Etag
+}
+
+// isValid flags of checkpoint data is valid. It returns true when the data is valid and the checkpoint is valid and the object is not updated.
+func (cp downloadCheckpoint) isValid(meta http.Header, uRange *UnpackedRange) (bool, error) {
+ // Compare the CP's Magic and the MD5
+ cpb := cp
+ cpb.MD5 = ""
+ js, _ := json.Marshal(cpb)
+ sum := md5.Sum(js)
+ b64 := base64.StdEncoding.EncodeToString(sum[:])
+
+ if cp.Magic != downloadCpMagic || b64 != cp.MD5 {
+ return false, nil
+ }
+
+ objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 64)
+ if err != nil {
+ return false, err
+ }
+
+ // Compare the object size, last modified time and etag
+ if cp.ObjStat.Size != objectSize ||
+ cp.ObjStat.LastModified != meta.Get(HTTPHeaderLastModified) ||
+ cp.ObjStat.Etag != meta.Get(HTTPHeaderEtag) {
+ return false, nil
+ }
+
+ // Check the download range
+ if uRange != nil {
+ start, end := AdjustRange(uRange, objectSize)
+ if start != cp.Start || end != cp.End {
+ return false, nil
+ }
+ }
+
+ return true, nil
+}
+
+// load checkpoint from local file
+func (cp *downloadCheckpoint) load(filePath string) error {
+ contents, err := ioutil.ReadFile(filePath)
+ if err != nil {
+ return err
+ }
+
+ err = json.Unmarshal(contents, cp)
+ return err
+}
+
+// dump funciton dumps to file
+func (cp *downloadCheckpoint) dump(filePath string) error {
+ bcp := *cp
+
+ // Calculate MD5
+ bcp.MD5 = ""
+ js, err := json.Marshal(bcp)
+ if err != nil {
+ return err
+ }
+ sum := md5.Sum(js)
+ b64 := base64.StdEncoding.EncodeToString(sum[:])
+ bcp.MD5 = b64
+
+ // Serialize
+ js, err = json.Marshal(bcp)
+ if err != nil {
+ return err
+ }
+
+ // Dump
+ return ioutil.WriteFile(filePath, js, FilePermMode)
+}
+
+// todoParts gets unfinished parts
+func (cp downloadCheckpoint) todoParts() []downloadPart {
+ dps := []downloadPart{}
+ for i, ps := range cp.PartStat {
+ if !ps {
+ dps = append(dps, cp.Parts[i])
+ }
+ }
+ return dps
+}
+
+// getCompletedBytes gets completed size
+func (cp downloadCheckpoint) getCompletedBytes() int64 {
+ var completedBytes int64
+ for i, part := range cp.Parts {
+ if cp.PartStat[i] {
+ completedBytes += (part.End - part.Start + 1)
+ }
+ }
+ return completedBytes
+}
+
+// prepare initiates download tasks
+func (cp *downloadCheckpoint) prepare(meta http.Header, bucket *Bucket, objectKey, filePath string, partSize int64, uRange *UnpackedRange) error {
+ // CP
+ cp.Magic = downloadCpMagic
+ cp.FilePath = filePath
+ cp.Object = objectKey
+
+ objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 64)
+ if err != nil {
+ return err
+ }
+
+ cp.ObjStat.Size = objectSize
+ cp.ObjStat.LastModified = meta.Get(HTTPHeaderLastModified)
+ cp.ObjStat.Etag = meta.Get(HTTPHeaderEtag)
+
+ if bucket.GetConfig().IsEnableCRC && meta.Get(HTTPHeaderOssCRC64) != "" {
+ if uRange == nil || (!uRange.HasStart && !uRange.HasEnd) {
+ cp.enableCRC = true
+ cp.CRC, _ = strconv.ParseUint(meta.Get(HTTPHeaderOssCRC64), 10, 64)
+ }
+ }
+
+ // Parts
+ cp.Parts = getDownloadParts(objectSize, partSize, uRange)
+ cp.PartStat = make([]bool, len(cp.Parts))
+ for i := range cp.PartStat {
+ cp.PartStat[i] = false
+ }
+
+ return nil
+}
+
+func (cp *downloadCheckpoint) complete(cpFilePath, downFilepath string) error {
+ err := os.Rename(downFilepath, cp.FilePath)
+ if err != nil {
+ return err
+ }
+ return os.Remove(cpFilePath)
+}
+
+// downloadFileWithCp downloads files with checkpoint.
+func (bucket Bucket) downloadFileWithCp(objectKey, filePath string, partSize int64, options []Option, cpFilePath string, routines int, uRange *UnpackedRange) error {
+ tempFilePath := filePath + TempFileSuffix
+ listener := GetProgressListener(options)
+
+ // Load checkpoint data.
+ dcp := downloadCheckpoint{}
+ err := dcp.load(cpFilePath)
+ if err != nil {
+ os.Remove(cpFilePath)
+ }
+
+ // Get the object detailed meta for object whole size
+ // must delete header:range to get whole object size
+ skipOptions := DeleteOption(options, HTTPHeaderRange)
+ meta, err := bucket.GetObjectDetailedMeta(objectKey, skipOptions...)
+ if err != nil {
+ return err
+ }
+
+ // Load error or data invalid. Re-initialize the download.
+ valid, err := dcp.isValid(meta, uRange)
+ if err != nil || !valid {
+ if err = dcp.prepare(meta, &bucket, objectKey, filePath, partSize, uRange); err != nil {
+ return err
+ }
+ os.Remove(cpFilePath)
+ }
+
+ // Create the file if not exists. Otherwise the parts download will overwrite it.
+ fd, err := os.OpenFile(tempFilePath, os.O_WRONLY|os.O_CREATE, FilePermMode)
+ if err != nil {
+ return err
+ }
+ fd.Close()
+
+ // Unfinished parts
+ parts := dcp.todoParts()
+ jobs := make(chan downloadPart, len(parts))
+ results := make(chan downloadPart, len(parts))
+ failed := make(chan error)
+ die := make(chan bool)
+
+ completedBytes := dcp.getCompletedBytes()
+ event := newProgressEvent(TransferStartedEvent, completedBytes, dcp.ObjStat.Size, 0)
+ publishProgress(listener, event)
+
+ // Start the download workers routine
+ arg := downloadWorkerArg{&bucket, objectKey, tempFilePath, options, downloadPartHooker, dcp.enableCRC}
+ for w := 1; w <= routines; w++ {
+ go downloadWorker(w, arg, jobs, results, failed, die)
+ }
+
+ // Concurrently downloads parts
+ go downloadScheduler(jobs, parts)
+
+ // Wait for the parts download finished
+ completed := 0
+ for completed < len(parts) {
+ select {
+ case part := <-results:
+ completed++
+ dcp.PartStat[part.Index] = true
+ dcp.Parts[part.Index].CRC64 = part.CRC64
+ dcp.dump(cpFilePath)
+ downBytes := (part.End - part.Start + 1)
+ completedBytes += downBytes
+ event = newProgressEvent(TransferDataEvent, completedBytes, dcp.ObjStat.Size, downBytes)
+ publishProgress(listener, event)
+ case err := <-failed:
+ close(die)
+ event = newProgressEvent(TransferFailedEvent, completedBytes, dcp.ObjStat.Size, 0)
+ publishProgress(listener, event)
+ return err
+ }
+
+ if completed >= len(parts) {
+ break
+ }
+ }
+
+ event = newProgressEvent(TransferCompletedEvent, completedBytes, dcp.ObjStat.Size, 0)
+ publishProgress(listener, event)
+
+ if dcp.enableCRC {
+ actualCRC := combineCRCInParts(dcp.Parts)
+ err = CheckDownloadCRC(actualCRC, dcp.CRC)
+ if err != nil {
+ return err
+ }
+ }
+
+ return dcp.complete(cpFilePath, tempFilePath)
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/error.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/error.go
new file mode 100644
index 00000000000..aaa24f28a9b
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/error.go
@@ -0,0 +1,136 @@
+package oss
+
+import (
+ "encoding/xml"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "strconv"
+ "strings"
+)
+
+// ServiceError contains fields of the error response from Oss Service REST API.
+type ServiceError struct {
+ XMLName xml.Name `xml:"Error"`
+ Code string `xml:"Code"` // The error code returned from OSS to the caller
+ Message string `xml:"Message"` // The detail error message from OSS
+ RequestID string `xml:"RequestId"` // The UUID used to uniquely identify the request
+ HostID string `xml:"HostId"` // The OSS server cluster's Id
+ Endpoint string `xml:"Endpoint"`
+ Ec string `xml:"EC"`
+ RawMessage string // The raw messages from OSS
+ StatusCode int // HTTP status code
+
+}
+
+// Error implements interface error
+func (e ServiceError) Error() string {
+ errorStr := fmt.Sprintf("oss: service returned error: StatusCode=%d, ErrorCode=%s, ErrorMessage=\"%s\", RequestId=%s", e.StatusCode, e.Code, e.Message, e.RequestID)
+ if len(e.Endpoint) > 0 {
+ errorStr = fmt.Sprintf("%s, Endpoint=%s", errorStr, e.Endpoint)
+ }
+ if len(e.Ec) > 0 {
+ errorStr = fmt.Sprintf("%s, Ec=%s", errorStr, e.Ec)
+ }
+ return errorStr
+}
+
+// UnexpectedStatusCodeError is returned when a storage service responds with neither an error
+// nor with an HTTP status code indicating success.
+type UnexpectedStatusCodeError struct {
+ allowed []int // The expected HTTP stats code returned from OSS
+ got int // The actual HTTP status code from OSS
+}
+
+// Error implements interface error
+func (e UnexpectedStatusCodeError) Error() string {
+ s := func(i int) string { return fmt.Sprintf("%d %s", i, http.StatusText(i)) }
+
+ got := s(e.got)
+ expected := []string{}
+ for _, v := range e.allowed {
+ expected = append(expected, s(v))
+ }
+ return fmt.Sprintf("oss: status code from service response is %s; was expecting %s",
+ got, strings.Join(expected, " or "))
+}
+
+// Got is the actual status code returned by oss.
+func (e UnexpectedStatusCodeError) Got() int {
+ return e.got
+}
+
+// CheckRespCode returns UnexpectedStatusError if the given response code is not
+// one of the allowed status codes; otherwise nil.
+func CheckRespCode(respCode int, allowed []int) error {
+ for _, v := range allowed {
+ if respCode == v {
+ return nil
+ }
+ }
+ return UnexpectedStatusCodeError{allowed, respCode}
+}
+
+// CheckCallbackResp return error if the given response code is not 200
+func CheckCallbackResp(resp *Response) error {
+ var err error
+ contentLengthStr := resp.Headers.Get("Content-Length")
+ contentLength, _ := strconv.Atoi(contentLengthStr)
+ var bodyBytes []byte
+ if contentLength > 0 {
+ bodyBytes, _ = ioutil.ReadAll(resp.Body)
+ }
+ if len(bodyBytes) > 0 {
+ srvErr, errIn := serviceErrFromXML(bodyBytes, resp.StatusCode,
+ resp.Headers.Get(HTTPHeaderOssRequestID))
+ if errIn != nil {
+ if len(resp.Headers.Get(HTTPHeaderOssEc)) > 0 {
+ err = fmt.Errorf("unknown response body, status code = %d, RequestId = %s, ec = %s", resp.StatusCode, resp.Headers.Get(HTTPHeaderOssRequestID), resp.Headers.Get(HTTPHeaderOssEc))
+ } else {
+ err = fmt.Errorf("unknown response body, status code= %d, RequestId = %s", resp.StatusCode, resp.Headers.Get(HTTPHeaderOssRequestID))
+ }
+ } else {
+ err = srvErr
+ }
+ }
+ return err
+}
+
+func tryConvertServiceError(data []byte, resp *Response, def error) (err error) {
+ err = def
+ if len(data) > 0 {
+ srvErr, errIn := serviceErrFromXML(data, resp.StatusCode, resp.Headers.Get(HTTPHeaderOssRequestID))
+ if errIn == nil {
+ err = srvErr
+ }
+ }
+ return err
+}
+
+// CRCCheckError is returned when crc check is inconsistent between client and server
+type CRCCheckError struct {
+ clientCRC uint64 // Calculated CRC64 in client
+ serverCRC uint64 // Calculated CRC64 in server
+ operation string // Upload operations such as PutObject/AppendObject/UploadPart, etc
+ requestID string // The request id of this operation
+}
+
+// Error implements interface error
+func (e CRCCheckError) Error() string {
+ return fmt.Sprintf("oss: the crc of %s is inconsistent, client %d but server %d; request id is %s",
+ e.operation, e.clientCRC, e.serverCRC, e.requestID)
+}
+
+func CheckDownloadCRC(clientCRC, serverCRC uint64) error {
+ if clientCRC == serverCRC {
+ return nil
+ }
+ return CRCCheckError{clientCRC, serverCRC, "DownloadFile", ""}
+}
+
+func CheckCRC(resp *Response, operation string) error {
+ if resp.Headers.Get(HTTPHeaderOssCRC64) == "" || resp.ClientCRC == resp.ServerCRC {
+ return nil
+ }
+ return CRCCheckError{resp.ClientCRC, resp.ServerCRC, operation, resp.Headers.Get(HTTPHeaderOssRequestID)}
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/limit_reader_1_6.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/limit_reader_1_6.go
new file mode 100644
index 00000000000..2293f99043e
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/limit_reader_1_6.go
@@ -0,0 +1,29 @@
+//go:build !go1.7
+// +build !go1.7
+
+// "golang.org/x/time/rate" is depended on golang context package go1.7 onward
+// this file is only for build,not supports limit upload speed
+package oss
+
+import (
+ "fmt"
+ "io"
+)
+
+const (
+ perTokenBandwidthSize int = 1024
+)
+
+type OssLimiter struct {
+}
+
+type LimitSpeedReader struct {
+ io.ReadCloser
+ reader io.Reader
+ ossLimiter *OssLimiter
+}
+
+func GetOssLimiter(uploadSpeed int) (ossLimiter *OssLimiter, err error) {
+ err = fmt.Errorf("rate.Limiter is not supported below version go1.7")
+ return nil, err
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/limit_reader_1_7.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/limit_reader_1_7.go
new file mode 100644
index 00000000000..ea0826ceca0
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/limit_reader_1_7.go
@@ -0,0 +1,91 @@
+//go:build go1.7
+// +build go1.7
+
+package oss
+
+import (
+ "fmt"
+ "io"
+ "math"
+ "time"
+
+ "golang.org/x/time/rate"
+)
+
+const (
+ perTokenBandwidthSize int = 1024
+)
+
+// OssLimiter wrapper rate.Limiter
+type OssLimiter struct {
+ limiter *rate.Limiter
+}
+
+// GetOssLimiter create OssLimiter
+// uploadSpeed KB/s
+func GetOssLimiter(uploadSpeed int) (ossLimiter *OssLimiter, err error) {
+ limiter := rate.NewLimiter(rate.Limit(uploadSpeed), uploadSpeed)
+
+ // first consume the initial full token,the limiter will behave more accurately
+ limiter.AllowN(time.Now(), uploadSpeed)
+
+ return &OssLimiter{
+ limiter: limiter,
+ }, nil
+}
+
+// LimitSpeedReader for limit bandwidth upload
+type LimitSpeedReader struct {
+ io.ReadCloser
+ reader io.Reader
+ ossLimiter *OssLimiter
+}
+
+// Read
+func (r *LimitSpeedReader) Read(p []byte) (n int, err error) {
+ n = 0
+ err = nil
+ start := 0
+ burst := r.ossLimiter.limiter.Burst()
+ var end int
+ var tmpN int
+ var tc int
+ for start < len(p) {
+ if start+burst*perTokenBandwidthSize < len(p) {
+ end = start + burst*perTokenBandwidthSize
+ } else {
+ end = len(p)
+ }
+
+ tmpN, err = r.reader.Read(p[start:end])
+ if tmpN > 0 {
+ n += tmpN
+ start = n
+ }
+
+ if err != nil {
+ return
+ }
+
+ tc = int(math.Ceil(float64(tmpN) / float64(perTokenBandwidthSize)))
+ now := time.Now()
+ re := r.ossLimiter.limiter.ReserveN(now, tc)
+ if !re.OK() {
+ err = fmt.Errorf("LimitSpeedReader.Read() failure,ReserveN error,start:%d,end:%d,burst:%d,perTokenBandwidthSize:%d",
+ start, end, burst, perTokenBandwidthSize)
+ return
+ }
+ timeDelay := re.Delay()
+ time.Sleep(timeDelay)
+ }
+ return
+}
+
+// Close ...
+func (r *LimitSpeedReader) Close() error {
+ rc, ok := r.reader.(io.ReadCloser)
+ if ok {
+ return rc.Close()
+ }
+ return nil
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/livechannel.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/livechannel.go
new file mode 100644
index 00000000000..1fd30fdfd6e
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/livechannel.go
@@ -0,0 +1,257 @@
+package oss
+
+import (
+ "bytes"
+ "encoding/xml"
+ "fmt"
+ "io"
+ "net/http"
+ "strconv"
+ "time"
+)
+
+//
+// CreateLiveChannel create a live-channel
+//
+// channelName the name of the channel
+// config configuration of the channel
+//
+// CreateLiveChannelResult the result of create live-channel
+// error nil if success, otherwise error
+//
+func (bucket Bucket) CreateLiveChannel(channelName string, config LiveChannelConfiguration) (CreateLiveChannelResult, error) {
+ var out CreateLiveChannelResult
+
+ bs, err := xml.Marshal(config)
+ if err != nil {
+ return out, err
+ }
+
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ params := map[string]interface{}{}
+ params["live"] = nil
+ resp, err := bucket.do("PUT", channelName, params, nil, buffer, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// PutLiveChannelStatus Set the status of the live-channel: enabled/disabled
+//
+// channelName the name of the channel
+// status enabled/disabled
+//
+// error nil if success, otherwise error
+//
+func (bucket Bucket) PutLiveChannelStatus(channelName, status string) error {
+ params := map[string]interface{}{}
+ params["live"] = nil
+ params["status"] = status
+
+ resp, err := bucket.do("PUT", channelName, params, nil, nil, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// PostVodPlaylist create an playlist based on the specified playlist name, startTime and endTime
+//
+// channelName the name of the channel
+// playlistName the name of the playlist, must end with ".m3u8"
+// startTime the start time of the playlist
+// endTime the endtime of the playlist
+//
+// error nil if success, otherwise error
+//
+func (bucket Bucket) PostVodPlaylist(channelName, playlistName string, startTime, endTime time.Time) error {
+ params := map[string]interface{}{}
+ params["vod"] = nil
+ params["startTime"] = strconv.FormatInt(startTime.Unix(), 10)
+ params["endTime"] = strconv.FormatInt(endTime.Unix(), 10)
+
+ key := fmt.Sprintf("%s/%s", channelName, playlistName)
+ resp, err := bucket.do("POST", key, params, nil, nil, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ return CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetVodPlaylist get the playlist based on the specified channelName, startTime and endTime
+//
+// channelName the name of the channel
+// startTime the start time of the playlist
+// endTime the endtime of the playlist
+//
+// io.ReadCloser reader instance for reading data from response. It must be called close() after the usage and only valid when error is nil.
+// error nil if success, otherwise error
+//
+func (bucket Bucket) GetVodPlaylist(channelName string, startTime, endTime time.Time) (io.ReadCloser, error) {
+ params := map[string]interface{}{}
+ params["vod"] = nil
+ params["startTime"] = strconv.FormatInt(startTime.Unix(), 10)
+ params["endTime"] = strconv.FormatInt(endTime.Unix(), 10)
+
+ resp, err := bucket.do("GET", channelName, params, nil, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return resp.Body, nil
+}
+
+//
+// GetLiveChannelStat Get the state of the live-channel
+//
+// channelName the name of the channel
+//
+// LiveChannelStat the state of the live-channel
+// error nil if success, otherwise error
+//
+func (bucket Bucket) GetLiveChannelStat(channelName string) (LiveChannelStat, error) {
+ var out LiveChannelStat
+ params := map[string]interface{}{}
+ params["live"] = nil
+ params["comp"] = "stat"
+
+ resp, err := bucket.do("GET", channelName, params, nil, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// GetLiveChannelInfo Get the configuration info of the live-channel
+//
+// channelName the name of the channel
+//
+// LiveChannelConfiguration the configuration info of the live-channel
+// error nil if success, otherwise error
+//
+func (bucket Bucket) GetLiveChannelInfo(channelName string) (LiveChannelConfiguration, error) {
+ var out LiveChannelConfiguration
+ params := map[string]interface{}{}
+ params["live"] = nil
+
+ resp, err := bucket.do("GET", channelName, params, nil, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// GetLiveChannelHistory Get push records of live-channel
+//
+// channelName the name of the channel
+//
+// LiveChannelHistory push records
+// error nil if success, otherwise error
+//
+func (bucket Bucket) GetLiveChannelHistory(channelName string) (LiveChannelHistory, error) {
+ var out LiveChannelHistory
+ params := map[string]interface{}{}
+ params["live"] = nil
+ params["comp"] = "history"
+
+ resp, err := bucket.do("GET", channelName, params, nil, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// ListLiveChannel list the live-channels
+//
+// options Prefix: filter by the name start with the value of "Prefix"
+// MaxKeys: the maximum count returned
+// Marker: cursor from which starting list
+//
+// ListLiveChannelResult live-channel list
+// error nil if success, otherwise error
+//
+func (bucket Bucket) ListLiveChannel(options ...Option) (ListLiveChannelResult, error) {
+ var out ListLiveChannelResult
+
+ params, err := GetRawParams(options)
+ if err != nil {
+ return out, err
+ }
+
+ params["live"] = nil
+
+ resp, err := bucket.doInner("GET", "", params, nil, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// DeleteLiveChannel Delete the live-channel. When a client trying to stream the live-channel, the operation will fail. it will only delete the live-channel itself and the object generated by the live-channel will not be deleted.
+//
+// channelName the name of the channel
+//
+// error nil if success, otherwise error
+//
+func (bucket Bucket) DeleteLiveChannel(channelName string) error {
+ params := map[string]interface{}{}
+ params["live"] = nil
+
+ if channelName == "" {
+ return fmt.Errorf("invalid argument: channel name is empty")
+ }
+
+ resp, err := bucket.do("DELETE", channelName, params, nil, nil, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+//
+// SignRtmpURL Generate a RTMP push-stream signature URL for the trusted user to push the RTMP stream to the live-channel.
+//
+// channelName the name of the channel
+// playlistName the name of the playlist, must end with ".m3u8"
+// expires expiration (in seconds)
+//
+// string singed rtmp push stream url
+// error nil if success, otherwise error
+//
+func (bucket Bucket) SignRtmpURL(channelName, playlistName string, expires int64) (string, error) {
+ if expires <= 0 {
+ return "", fmt.Errorf("invalid argument: %d, expires must greater than 0", expires)
+ }
+ expiration := time.Now().Unix() + expires
+
+ return bucket.Client.Conn.signRtmpURL(bucket.BucketName, channelName, playlistName, expiration), nil
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/mime.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/mime.go
new file mode 100644
index 00000000000..96a9ee7aa71
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/mime.go
@@ -0,0 +1,594 @@
+package oss
+
+import (
+ "mime"
+ "path"
+ "strings"
+)
+
+var extToMimeType = map[string]string{
+ ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ ".xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
+ ".potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
+ ".ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
+ ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+ ".sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide",
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ ".dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
+ ".xlam": "application/vnd.ms-excel.addin.macroEnabled.12",
+ ".xlsb": "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
+ ".apk": "application/vnd.android.package-archive",
+ ".hqx": "application/mac-binhex40",
+ ".cpt": "application/mac-compactpro",
+ ".doc": "application/msword",
+ ".ogg": "application/ogg",
+ ".pdf": "application/pdf",
+ ".rtf": "text/rtf",
+ ".mif": "application/vnd.mif",
+ ".xls": "application/vnd.ms-excel",
+ ".ppt": "application/vnd.ms-powerpoint",
+ ".odc": "application/vnd.oasis.opendocument.chart",
+ ".odb": "application/vnd.oasis.opendocument.database",
+ ".odf": "application/vnd.oasis.opendocument.formula",
+ ".odg": "application/vnd.oasis.opendocument.graphics",
+ ".otg": "application/vnd.oasis.opendocument.graphics-template",
+ ".odi": "application/vnd.oasis.opendocument.image",
+ ".odp": "application/vnd.oasis.opendocument.presentation",
+ ".otp": "application/vnd.oasis.opendocument.presentation-template",
+ ".ods": "application/vnd.oasis.opendocument.spreadsheet",
+ ".ots": "application/vnd.oasis.opendocument.spreadsheet-template",
+ ".odt": "application/vnd.oasis.opendocument.text",
+ ".odm": "application/vnd.oasis.opendocument.text-master",
+ ".ott": "application/vnd.oasis.opendocument.text-template",
+ ".oth": "application/vnd.oasis.opendocument.text-web",
+ ".sxw": "application/vnd.sun.xml.writer",
+ ".stw": "application/vnd.sun.xml.writer.template",
+ ".sxc": "application/vnd.sun.xml.calc",
+ ".stc": "application/vnd.sun.xml.calc.template",
+ ".sxd": "application/vnd.sun.xml.draw",
+ ".std": "application/vnd.sun.xml.draw.template",
+ ".sxi": "application/vnd.sun.xml.impress",
+ ".sti": "application/vnd.sun.xml.impress.template",
+ ".sxg": "application/vnd.sun.xml.writer.global",
+ ".sxm": "application/vnd.sun.xml.math",
+ ".sis": "application/vnd.symbian.install",
+ ".wbxml": "application/vnd.wap.wbxml",
+ ".wmlc": "application/vnd.wap.wmlc",
+ ".wmlsc": "application/vnd.wap.wmlscriptc",
+ ".bcpio": "application/x-bcpio",
+ ".torrent": "application/x-bittorrent",
+ ".bz2": "application/x-bzip2",
+ ".vcd": "application/x-cdlink",
+ ".pgn": "application/x-chess-pgn",
+ ".cpio": "application/x-cpio",
+ ".csh": "application/x-csh",
+ ".dvi": "application/x-dvi",
+ ".spl": "application/x-futuresplash",
+ ".gtar": "application/x-gtar",
+ ".hdf": "application/x-hdf",
+ ".jar": "application/x-java-archive",
+ ".jnlp": "application/x-java-jnlp-file",
+ ".js": "application/x-javascript",
+ ".ksp": "application/x-kspread",
+ ".chrt": "application/x-kchart",
+ ".kil": "application/x-killustrator",
+ ".latex": "application/x-latex",
+ ".rpm": "application/x-rpm",
+ ".sh": "application/x-sh",
+ ".shar": "application/x-shar",
+ ".swf": "application/x-shockwave-flash",
+ ".sit": "application/x-stuffit",
+ ".sv4cpio": "application/x-sv4cpio",
+ ".sv4crc": "application/x-sv4crc",
+ ".tar": "application/x-tar",
+ ".tcl": "application/x-tcl",
+ ".tex": "application/x-tex",
+ ".man": "application/x-troff-man",
+ ".me": "application/x-troff-me",
+ ".ms": "application/x-troff-ms",
+ ".ustar": "application/x-ustar",
+ ".src": "application/x-wais-source",
+ ".zip": "application/zip",
+ ".m3u": "audio/x-mpegurl",
+ ".ra": "audio/x-pn-realaudio",
+ ".wav": "audio/x-wav",
+ ".wma": "audio/x-ms-wma",
+ ".wax": "audio/x-ms-wax",
+ ".pdb": "chemical/x-pdb",
+ ".xyz": "chemical/x-xyz",
+ ".bmp": "image/bmp",
+ ".gif": "image/gif",
+ ".ief": "image/ief",
+ ".png": "image/png",
+ ".wbmp": "image/vnd.wap.wbmp",
+ ".ras": "image/x-cmu-raster",
+ ".pnm": "image/x-portable-anymap",
+ ".pbm": "image/x-portable-bitmap",
+ ".pgm": "image/x-portable-graymap",
+ ".ppm": "image/x-portable-pixmap",
+ ".rgb": "image/x-rgb",
+ ".xbm": "image/x-xbitmap",
+ ".xpm": "image/x-xpixmap",
+ ".xwd": "image/x-xwindowdump",
+ ".css": "text/css",
+ ".rtx": "text/richtext",
+ ".tsv": "text/tab-separated-values",
+ ".jad": "text/vnd.sun.j2me.app-descriptor",
+ ".wml": "text/vnd.wap.wml",
+ ".wmls": "text/vnd.wap.wmlscript",
+ ".etx": "text/x-setext",
+ ".mxu": "video/vnd.mpegurl",
+ ".flv": "video/x-flv",
+ ".wm": "video/x-ms-wm",
+ ".wmv": "video/x-ms-wmv",
+ ".wmx": "video/x-ms-wmx",
+ ".wvx": "video/x-ms-wvx",
+ ".avi": "video/x-msvideo",
+ ".movie": "video/x-sgi-movie",
+ ".ice": "x-conference/x-cooltalk",
+ ".3gp": "video/3gpp",
+ ".ai": "application/postscript",
+ ".aif": "audio/x-aiff",
+ ".aifc": "audio/x-aiff",
+ ".aiff": "audio/x-aiff",
+ ".asc": "text/plain",
+ ".atom": "application/atom+xml",
+ ".au": "audio/basic",
+ ".bin": "application/octet-stream",
+ ".cdf": "application/x-netcdf",
+ ".cgm": "image/cgm",
+ ".class": "application/octet-stream",
+ ".dcr": "application/x-director",
+ ".dif": "video/x-dv",
+ ".dir": "application/x-director",
+ ".djv": "image/vnd.djvu",
+ ".djvu": "image/vnd.djvu",
+ ".dll": "application/octet-stream",
+ ".dmg": "application/octet-stream",
+ ".dms": "application/octet-stream",
+ ".dtd": "application/xml-dtd",
+ ".dv": "video/x-dv",
+ ".dxr": "application/x-director",
+ ".eps": "application/postscript",
+ ".exe": "application/octet-stream",
+ ".ez": "application/andrew-inset",
+ ".gram": "application/srgs",
+ ".grxml": "application/srgs+xml",
+ ".gz": "application/x-gzip",
+ ".htm": "text/html",
+ ".html": "text/html",
+ ".ico": "image/x-icon",
+ ".ics": "text/calendar",
+ ".ifb": "text/calendar",
+ ".iges": "model/iges",
+ ".igs": "model/iges",
+ ".jp2": "image/jp2",
+ ".jpe": "image/jpeg",
+ ".jpeg": "image/jpeg",
+ ".jpg": "image/jpeg",
+ ".kar": "audio/midi",
+ ".lha": "application/octet-stream",
+ ".lzh": "application/octet-stream",
+ ".m4a": "audio/mp4a-latm",
+ ".m4p": "audio/mp4a-latm",
+ ".m4u": "video/vnd.mpegurl",
+ ".m4v": "video/x-m4v",
+ ".mac": "image/x-macpaint",
+ ".mathml": "application/mathml+xml",
+ ".mesh": "model/mesh",
+ ".mid": "audio/midi",
+ ".midi": "audio/midi",
+ ".mov": "video/quicktime",
+ ".mp2": "audio/mpeg",
+ ".mp3": "audio/mpeg",
+ ".mp4": "video/mp4",
+ ".mpe": "video/mpeg",
+ ".mpeg": "video/mpeg",
+ ".mpg": "video/mpeg",
+ ".mpga": "audio/mpeg",
+ ".msh": "model/mesh",
+ ".nc": "application/x-netcdf",
+ ".oda": "application/oda",
+ ".ogv": "video/ogv",
+ ".pct": "image/pict",
+ ".pic": "image/pict",
+ ".pict": "image/pict",
+ ".pnt": "image/x-macpaint",
+ ".pntg": "image/x-macpaint",
+ ".ps": "application/postscript",
+ ".qt": "video/quicktime",
+ ".qti": "image/x-quicktime",
+ ".qtif": "image/x-quicktime",
+ ".ram": "audio/x-pn-realaudio",
+ ".rdf": "application/rdf+xml",
+ ".rm": "application/vnd.rn-realmedia",
+ ".roff": "application/x-troff",
+ ".sgm": "text/sgml",
+ ".sgml": "text/sgml",
+ ".silo": "model/mesh",
+ ".skd": "application/x-koan",
+ ".skm": "application/x-koan",
+ ".skp": "application/x-koan",
+ ".skt": "application/x-koan",
+ ".smi": "application/smil",
+ ".smil": "application/smil",
+ ".snd": "audio/basic",
+ ".so": "application/octet-stream",
+ ".svg": "image/svg+xml",
+ ".t": "application/x-troff",
+ ".texi": "application/x-texinfo",
+ ".texinfo": "application/x-texinfo",
+ ".tif": "image/tiff",
+ ".tiff": "image/tiff",
+ ".tr": "application/x-troff",
+ ".txt": "text/plain",
+ ".vrml": "model/vrml",
+ ".vxml": "application/voicexml+xml",
+ ".webm": "video/webm",
+ ".wrl": "model/vrml",
+ ".xht": "application/xhtml+xml",
+ ".xhtml": "application/xhtml+xml",
+ ".xml": "application/xml",
+ ".xsl": "application/xml",
+ ".xslt": "application/xslt+xml",
+ ".xul": "application/vnd.mozilla.xul+xml",
+ ".webp": "image/webp",
+ ".323": "text/h323",
+ ".aab": "application/x-authoware-bin",
+ ".aam": "application/x-authoware-map",
+ ".aas": "application/x-authoware-seg",
+ ".acx": "application/internet-property-stream",
+ ".als": "audio/X-Alpha5",
+ ".amc": "application/x-mpeg",
+ ".ani": "application/octet-stream",
+ ".asd": "application/astound",
+ ".asf": "video/x-ms-asf",
+ ".asn": "application/astound",
+ ".asp": "application/x-asap",
+ ".asr": "video/x-ms-asf",
+ ".asx": "video/x-ms-asf",
+ ".avb": "application/octet-stream",
+ ".awb": "audio/amr-wb",
+ ".axs": "application/olescript",
+ ".bas": "text/plain",
+ ".bin ": "application/octet-stream",
+ ".bld": "application/bld",
+ ".bld2": "application/bld2",
+ ".bpk": "application/octet-stream",
+ ".c": "text/plain",
+ ".cal": "image/x-cals",
+ ".cat": "application/vnd.ms-pkiseccat",
+ ".ccn": "application/x-cnc",
+ ".cco": "application/x-cocoa",
+ ".cer": "application/x-x509-ca-cert",
+ ".cgi": "magnus-internal/cgi",
+ ".chat": "application/x-chat",
+ ".clp": "application/x-msclip",
+ ".cmx": "image/x-cmx",
+ ".co": "application/x-cult3d-object",
+ ".cod": "image/cis-cod",
+ ".conf": "text/plain",
+ ".cpp": "text/plain",
+ ".crd": "application/x-mscardfile",
+ ".crl": "application/pkix-crl",
+ ".crt": "application/x-x509-ca-cert",
+ ".csm": "chemical/x-csml",
+ ".csml": "chemical/x-csml",
+ ".cur": "application/octet-stream",
+ ".dcm": "x-lml/x-evm",
+ ".dcx": "image/x-dcx",
+ ".der": "application/x-x509-ca-cert",
+ ".dhtml": "text/html",
+ ".dot": "application/msword",
+ ".dwf": "drawing/x-dwf",
+ ".dwg": "application/x-autocad",
+ ".dxf": "application/x-autocad",
+ ".ebk": "application/x-expandedbook",
+ ".emb": "chemical/x-embl-dl-nucleotide",
+ ".embl": "chemical/x-embl-dl-nucleotide",
+ ".epub": "application/epub+zip",
+ ".eri": "image/x-eri",
+ ".es": "audio/echospeech",
+ ".esl": "audio/echospeech",
+ ".etc": "application/x-earthtime",
+ ".evm": "x-lml/x-evm",
+ ".evy": "application/envoy",
+ ".fh4": "image/x-freehand",
+ ".fh5": "image/x-freehand",
+ ".fhc": "image/x-freehand",
+ ".fif": "application/fractals",
+ ".flr": "x-world/x-vrml",
+ ".fm": "application/x-maker",
+ ".fpx": "image/x-fpx",
+ ".fvi": "video/isivideo",
+ ".gau": "chemical/x-gaussian-input",
+ ".gca": "application/x-gca-compressed",
+ ".gdb": "x-lml/x-gdb",
+ ".gps": "application/x-gps",
+ ".h": "text/plain",
+ ".hdm": "text/x-hdml",
+ ".hdml": "text/x-hdml",
+ ".hlp": "application/winhlp",
+ ".hta": "application/hta",
+ ".htc": "text/x-component",
+ ".hts": "text/html",
+ ".htt": "text/webviewhtml",
+ ".ifm": "image/gif",
+ ".ifs": "image/ifs",
+ ".iii": "application/x-iphone",
+ ".imy": "audio/melody",
+ ".ins": "application/x-internet-signup",
+ ".ips": "application/x-ipscript",
+ ".ipx": "application/x-ipix",
+ ".isp": "application/x-internet-signup",
+ ".it": "audio/x-mod",
+ ".itz": "audio/x-mod",
+ ".ivr": "i-world/i-vrml",
+ ".j2k": "image/j2k",
+ ".jam": "application/x-jam",
+ ".java": "text/plain",
+ ".jfif": "image/pipeg",
+ ".jpz": "image/jpeg",
+ ".jwc": "application/jwc",
+ ".kjx": "application/x-kjx",
+ ".lak": "x-lml/x-lak",
+ ".lcc": "application/fastman",
+ ".lcl": "application/x-digitalloca",
+ ".lcr": "application/x-digitalloca",
+ ".lgh": "application/lgh",
+ ".lml": "x-lml/x-lml",
+ ".lmlpack": "x-lml/x-lmlpack",
+ ".log": "text/plain",
+ ".lsf": "video/x-la-asf",
+ ".lsx": "video/x-la-asf",
+ ".m13": "application/x-msmediaview",
+ ".m14": "application/x-msmediaview",
+ ".m15": "audio/x-mod",
+ ".m3url": "audio/x-mpegurl",
+ ".m4b": "audio/mp4a-latm",
+ ".ma1": "audio/ma1",
+ ".ma2": "audio/ma2",
+ ".ma3": "audio/ma3",
+ ".ma5": "audio/ma5",
+ ".map": "magnus-internal/imagemap",
+ ".mbd": "application/mbedlet",
+ ".mct": "application/x-mascot",
+ ".mdb": "application/x-msaccess",
+ ".mdz": "audio/x-mod",
+ ".mel": "text/x-vmel",
+ ".mht": "message/rfc822",
+ ".mhtml": "message/rfc822",
+ ".mi": "application/x-mif",
+ ".mil": "image/x-cals",
+ ".mio": "audio/x-mio",
+ ".mmf": "application/x-skt-lbs",
+ ".mng": "video/x-mng",
+ ".mny": "application/x-msmoney",
+ ".moc": "application/x-mocha",
+ ".mocha": "application/x-mocha",
+ ".mod": "audio/x-mod",
+ ".mof": "application/x-yumekara",
+ ".mol": "chemical/x-mdl-molfile",
+ ".mop": "chemical/x-mopac-input",
+ ".mpa": "video/mpeg",
+ ".mpc": "application/vnd.mpohun.certificate",
+ ".mpg4": "video/mp4",
+ ".mpn": "application/vnd.mophun.application",
+ ".mpp": "application/vnd.ms-project",
+ ".mps": "application/x-mapserver",
+ ".mpv2": "video/mpeg",
+ ".mrl": "text/x-mrml",
+ ".mrm": "application/x-mrm",
+ ".msg": "application/vnd.ms-outlook",
+ ".mts": "application/metastream",
+ ".mtx": "application/metastream",
+ ".mtz": "application/metastream",
+ ".mvb": "application/x-msmediaview",
+ ".mzv": "application/metastream",
+ ".nar": "application/zip",
+ ".nbmp": "image/nbmp",
+ ".ndb": "x-lml/x-ndb",
+ ".ndwn": "application/ndwn",
+ ".nif": "application/x-nif",
+ ".nmz": "application/x-scream",
+ ".nokia-op-logo": "image/vnd.nok-oplogo-color",
+ ".npx": "application/x-netfpx",
+ ".nsnd": "audio/nsnd",
+ ".nva": "application/x-neva1",
+ ".nws": "message/rfc822",
+ ".oom": "application/x-AtlasMate-Plugin",
+ ".p10": "application/pkcs10",
+ ".p12": "application/x-pkcs12",
+ ".p7b": "application/x-pkcs7-certificates",
+ ".p7c": "application/x-pkcs7-mime",
+ ".p7m": "application/x-pkcs7-mime",
+ ".p7r": "application/x-pkcs7-certreqresp",
+ ".p7s": "application/x-pkcs7-signature",
+ ".pac": "audio/x-pac",
+ ".pae": "audio/x-epac",
+ ".pan": "application/x-pan",
+ ".pcx": "image/x-pcx",
+ ".pda": "image/x-pda",
+ ".pfr": "application/font-tdpfr",
+ ".pfx": "application/x-pkcs12",
+ ".pko": "application/ynd.ms-pkipko",
+ ".pm": "application/x-perl",
+ ".pma": "application/x-perfmon",
+ ".pmc": "application/x-perfmon",
+ ".pmd": "application/x-pmd",
+ ".pml": "application/x-perfmon",
+ ".pmr": "application/x-perfmon",
+ ".pmw": "application/x-perfmon",
+ ".pnz": "image/png",
+ ".pot,": "application/vnd.ms-powerpoint",
+ ".pps": "application/vnd.ms-powerpoint",
+ ".pqf": "application/x-cprplayer",
+ ".pqi": "application/cprplayer",
+ ".prc": "application/x-prc",
+ ".prf": "application/pics-rules",
+ ".prop": "text/plain",
+ ".proxy": "application/x-ns-proxy-autoconfig",
+ ".ptlk": "application/listenup",
+ ".pub": "application/x-mspublisher",
+ ".pvx": "video/x-pv-pvx",
+ ".qcp": "audio/vnd.qcelp",
+ ".r3t": "text/vnd.rn-realtext3d",
+ ".rar": "application/octet-stream",
+ ".rc": "text/plain",
+ ".rf": "image/vnd.rn-realflash",
+ ".rlf": "application/x-richlink",
+ ".rmf": "audio/x-rmf",
+ ".rmi": "audio/mid",
+ ".rmm": "audio/x-pn-realaudio",
+ ".rmvb": "audio/x-pn-realaudio",
+ ".rnx": "application/vnd.rn-realplayer",
+ ".rp": "image/vnd.rn-realpix",
+ ".rt": "text/vnd.rn-realtext",
+ ".rte": "x-lml/x-gps",
+ ".rtg": "application/metastream",
+ ".rv": "video/vnd.rn-realvideo",
+ ".rwc": "application/x-rogerwilco",
+ ".s3m": "audio/x-mod",
+ ".s3z": "audio/x-mod",
+ ".sca": "application/x-supercard",
+ ".scd": "application/x-msschedule",
+ ".sct": "text/scriptlet",
+ ".sdf": "application/e-score",
+ ".sea": "application/x-stuffit",
+ ".setpay": "application/set-payment-initiation",
+ ".setreg": "application/set-registration-initiation",
+ ".shtml": "text/html",
+ ".shtm": "text/html",
+ ".shw": "application/presentations",
+ ".si6": "image/si6",
+ ".si7": "image/vnd.stiwap.sis",
+ ".si9": "image/vnd.lgtwap.sis",
+ ".slc": "application/x-salsa",
+ ".smd": "audio/x-smd",
+ ".smp": "application/studiom",
+ ".smz": "audio/x-smd",
+ ".spc": "application/x-pkcs7-certificates",
+ ".spr": "application/x-sprite",
+ ".sprite": "application/x-sprite",
+ ".sdp": "application/sdp",
+ ".spt": "application/x-spt",
+ ".sst": "application/vnd.ms-pkicertstore",
+ ".stk": "application/hyperstudio",
+ ".stl": "application/vnd.ms-pkistl",
+ ".stm": "text/html",
+ ".svf": "image/vnd",
+ ".svh": "image/svh",
+ ".svr": "x-world/x-svr",
+ ".swfl": "application/x-shockwave-flash",
+ ".tad": "application/octet-stream",
+ ".talk": "text/x-speech",
+ ".taz": "application/x-tar",
+ ".tbp": "application/x-timbuktu",
+ ".tbt": "application/x-timbuktu",
+ ".tgz": "application/x-compressed",
+ ".thm": "application/vnd.eri.thm",
+ ".tki": "application/x-tkined",
+ ".tkined": "application/x-tkined",
+ ".toc": "application/toc",
+ ".toy": "image/toy",
+ ".trk": "x-lml/x-gps",
+ ".trm": "application/x-msterminal",
+ ".tsi": "audio/tsplayer",
+ ".tsp": "application/dsptype",
+ ".ttf": "application/octet-stream",
+ ".ttz": "application/t-time",
+ ".uls": "text/iuls",
+ ".ult": "audio/x-mod",
+ ".uu": "application/x-uuencode",
+ ".uue": "application/x-uuencode",
+ ".vcf": "text/x-vcard",
+ ".vdo": "video/vdo",
+ ".vib": "audio/vib",
+ ".viv": "video/vivo",
+ ".vivo": "video/vivo",
+ ".vmd": "application/vocaltec-media-desc",
+ ".vmf": "application/vocaltec-media-file",
+ ".vmi": "application/x-dreamcast-vms-info",
+ ".vms": "application/x-dreamcast-vms",
+ ".vox": "audio/voxware",
+ ".vqe": "audio/x-twinvq-plugin",
+ ".vqf": "audio/x-twinvq",
+ ".vql": "audio/x-twinvq",
+ ".vre": "x-world/x-vream",
+ ".vrt": "x-world/x-vrt",
+ ".vrw": "x-world/x-vream",
+ ".vts": "workbook/formulaone",
+ ".wcm": "application/vnd.ms-works",
+ ".wdb": "application/vnd.ms-works",
+ ".web": "application/vnd.xara",
+ ".wi": "image/wavelet",
+ ".wis": "application/x-InstallShield",
+ ".wks": "application/vnd.ms-works",
+ ".wmd": "application/x-ms-wmd",
+ ".wmf": "application/x-msmetafile",
+ ".wmlscript": "text/vnd.wap.wmlscript",
+ ".wmz": "application/x-ms-wmz",
+ ".wpng": "image/x-up-wpng",
+ ".wps": "application/vnd.ms-works",
+ ".wpt": "x-lml/x-gps",
+ ".wri": "application/x-mswrite",
+ ".wrz": "x-world/x-vrml",
+ ".ws": "text/vnd.wap.wmlscript",
+ ".wsc": "application/vnd.wap.wmlscriptc",
+ ".wv": "video/wavelet",
+ ".wxl": "application/x-wxl",
+ ".x-gzip": "application/x-gzip",
+ ".xaf": "x-world/x-vrml",
+ ".xar": "application/vnd.xara",
+ ".xdm": "application/x-xdma",
+ ".xdma": "application/x-xdma",
+ ".xdw": "application/vnd.fujixerox.docuworks",
+ ".xhtm": "application/xhtml+xml",
+ ".xla": "application/vnd.ms-excel",
+ ".xlc": "application/vnd.ms-excel",
+ ".xll": "application/x-excel",
+ ".xlm": "application/vnd.ms-excel",
+ ".xlt": "application/vnd.ms-excel",
+ ".xlw": "application/vnd.ms-excel",
+ ".xm": "audio/x-mod",
+ ".xmz": "audio/x-mod",
+ ".xof": "x-world/x-vrml",
+ ".xpi": "application/x-xpinstall",
+ ".xsit": "text/xml",
+ ".yz1": "application/x-yz1",
+ ".z": "application/x-compress",
+ ".zac": "application/x-zaurus-zac",
+ ".json": "application/json",
+}
+
+// TypeByExtension returns the MIME type associated with the file extension ext.
+// gets the file's MIME type for HTTP header Content-Type
+func TypeByExtension(filePath string) string {
+ typ := mime.TypeByExtension(path.Ext(filePath))
+ if typ == "" {
+ typ = extToMimeType[strings.ToLower(path.Ext(filePath))]
+ } else {
+ if strings.HasPrefix(typ, "text/") && strings.Contains(typ, "charset=") {
+ typ = removeCharsetInMimeType(typ)
+ }
+ }
+ return typ
+}
+
+// Remove charset from mime type
+func removeCharsetInMimeType(typ string) (str string) {
+ temArr := strings.Split(typ, ";")
+ var builder strings.Builder
+ for i, s := range temArr {
+ tmpStr := strings.Trim(s, " ")
+ if strings.Contains(tmpStr, "charset=") {
+ continue
+ }
+ if i == 0 {
+ builder.WriteString(s)
+ } else {
+ builder.WriteString("; " + s)
+ }
+ }
+ return builder.String()
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/model.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/model.go
new file mode 100644
index 00000000000..b0b4a502715
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/model.go
@@ -0,0 +1,69 @@
+package oss
+
+import (
+ "hash"
+ "io"
+ "net/http"
+)
+
+// Response defines HTTP response from OSS
+type Response struct {
+ StatusCode int
+ Headers http.Header
+ Body io.ReadCloser
+ ClientCRC uint64
+ ServerCRC uint64
+}
+
+func (r *Response) Read(p []byte) (n int, err error) {
+ return r.Body.Read(p)
+}
+
+// Close close http reponse body
+func (r *Response) Close() error {
+ return r.Body.Close()
+}
+
+// PutObjectRequest is the request of DoPutObject
+type PutObjectRequest struct {
+ ObjectKey string
+ Reader io.Reader
+}
+
+// GetObjectRequest is the request of DoGetObject
+type GetObjectRequest struct {
+ ObjectKey string
+}
+
+// GetObjectResult is the result of DoGetObject
+type GetObjectResult struct {
+ Response *Response
+ ClientCRC hash.Hash64
+ ServerCRC uint64
+}
+
+// AppendObjectRequest is the requtest of DoAppendObject
+type AppendObjectRequest struct {
+ ObjectKey string
+ Reader io.Reader
+ Position int64
+}
+
+// AppendObjectResult is the result of DoAppendObject
+type AppendObjectResult struct {
+ NextPosition int64
+ CRC uint64
+}
+
+// UploadPartRequest is the request of DoUploadPart
+type UploadPartRequest struct {
+ InitResult *InitiateMultipartUploadResult
+ Reader io.Reader
+ PartSize int64
+ PartNumber int
+}
+
+// UploadPartResult is the result of DoUploadPart
+type UploadPartResult struct {
+ Part UploadPart
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/multicopy.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/multicopy.go
new file mode 100644
index 00000000000..56ed8cadfd0
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/multicopy.go
@@ -0,0 +1,474 @@
+package oss
+
+import (
+ "crypto/md5"
+ "encoding/base64"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "strconv"
+)
+
+// CopyFile is multipart copy object
+//
+// srcBucketName source bucket name
+// srcObjectKey source object name
+// destObjectKey target object name in the form of bucketname.objectkey
+// partSize the part size in byte.
+// options object's contraints. Check out function InitiateMultipartUpload.
+//
+// error it's nil if the operation succeeds, otherwise it's an error object.
+//
+func (bucket Bucket) CopyFile(srcBucketName, srcObjectKey, destObjectKey string, partSize int64, options ...Option) error {
+ destBucketName := bucket.BucketName
+ if partSize < MinPartSize || partSize > MaxPartSize {
+ return errors.New("oss: part size invalid range (1024KB, 5GB]")
+ }
+
+ cpConf := getCpConfig(options)
+ routines := getRoutines(options)
+
+ var strVersionId string
+ versionId, _ := FindOption(options, "versionId", nil)
+ if versionId != nil {
+ strVersionId = versionId.(string)
+ }
+
+ if cpConf != nil && cpConf.IsEnable {
+ cpFilePath := getCopyCpFilePath(cpConf, srcBucketName, srcObjectKey, destBucketName, destObjectKey, strVersionId)
+ if cpFilePath != "" {
+ return bucket.copyFileWithCp(srcBucketName, srcObjectKey, destBucketName, destObjectKey, partSize, options, cpFilePath, routines)
+ }
+ }
+
+ return bucket.copyFile(srcBucketName, srcObjectKey, destBucketName, destObjectKey,
+ partSize, options, routines)
+}
+
+func getCopyCpFilePath(cpConf *cpConfig, srcBucket, srcObject, destBucket, destObject, versionId string) string {
+ if cpConf.FilePath == "" && cpConf.DirPath != "" {
+ dest := fmt.Sprintf("oss://%v/%v", destBucket, destObject)
+ src := fmt.Sprintf("oss://%v/%v", srcBucket, srcObject)
+ cpFileName := getCpFileName(src, dest, versionId)
+ cpConf.FilePath = cpConf.DirPath + string(os.PathSeparator) + cpFileName
+ }
+ return cpConf.FilePath
+}
+
+// ----- Concurrently copy without checkpoint ---------
+
+// copyWorkerArg defines the copy worker arguments
+type copyWorkerArg struct {
+ bucket *Bucket
+ imur InitiateMultipartUploadResult
+ srcBucketName string
+ srcObjectKey string
+ options []Option
+ hook copyPartHook
+}
+
+// copyPartHook is the hook for testing purpose
+type copyPartHook func(part copyPart) error
+
+var copyPartHooker copyPartHook = defaultCopyPartHook
+
+func defaultCopyPartHook(part copyPart) error {
+ return nil
+}
+
+// copyWorker copies worker
+func copyWorker(id int, arg copyWorkerArg, jobs <-chan copyPart, results chan<- UploadPart, failed chan<- error, die <-chan bool) {
+ for chunk := range jobs {
+ if err := arg.hook(chunk); err != nil {
+ failed <- err
+ break
+ }
+ chunkSize := chunk.End - chunk.Start + 1
+ part, err := arg.bucket.UploadPartCopy(arg.imur, arg.srcBucketName, arg.srcObjectKey,
+ chunk.Start, chunkSize, chunk.Number, arg.options...)
+ if err != nil {
+ failed <- err
+ break
+ }
+ select {
+ case <-die:
+ return
+ default:
+ }
+ results <- part
+ }
+}
+
+// copyScheduler
+func copyScheduler(jobs chan copyPart, parts []copyPart) {
+ for _, part := range parts {
+ jobs <- part
+ }
+ close(jobs)
+}
+
+// copyPart structure
+type copyPart struct {
+ Number int // Part number (from 1 to 10,000)
+ Start int64 // The start index in the source file.
+ End int64 // The end index in the source file
+}
+
+// getCopyParts calculates copy parts
+func getCopyParts(objectSize, partSize int64) []copyPart {
+ parts := []copyPart{}
+ part := copyPart{}
+ i := 0
+ for offset := int64(0); offset < objectSize; offset += partSize {
+ part.Number = i + 1
+ part.Start = offset
+ part.End = GetPartEnd(offset, objectSize, partSize)
+ parts = append(parts, part)
+ i++
+ }
+ return parts
+}
+
+// getSrcObjectBytes gets the source file size
+func getSrcObjectBytes(parts []copyPart) int64 {
+ var ob int64
+ for _, part := range parts {
+ ob += (part.End - part.Start + 1)
+ }
+ return ob
+}
+
+// copyFile is a concurrently copy without checkpoint
+func (bucket Bucket) copyFile(srcBucketName, srcObjectKey, destBucketName, destObjectKey string,
+ partSize int64, options []Option, routines int) error {
+ descBucket, err := bucket.Client.Bucket(destBucketName)
+ srcBucket, err := bucket.Client.Bucket(srcBucketName)
+ listener := GetProgressListener(options)
+
+ // choice valid options
+ headerOptions := ChoiceHeadObjectOption(options)
+ partOptions := ChoiceTransferPartOption(options)
+ completeOptions := ChoiceCompletePartOption(options)
+ abortOptions := ChoiceAbortPartOption(options)
+
+ meta, err := srcBucket.GetObjectDetailedMeta(srcObjectKey, headerOptions...)
+ if err != nil {
+ return err
+ }
+
+ objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 0)
+ if err != nil {
+ return err
+ }
+
+ // Get copy parts
+ parts := getCopyParts(objectSize, partSize)
+ // Initialize the multipart upload
+ imur, err := descBucket.InitiateMultipartUpload(destObjectKey, options...)
+ if err != nil {
+ return err
+ }
+
+ jobs := make(chan copyPart, len(parts))
+ results := make(chan UploadPart, len(parts))
+ failed := make(chan error)
+ die := make(chan bool)
+
+ var completedBytes int64
+ totalBytes := getSrcObjectBytes(parts)
+ event := newProgressEvent(TransferStartedEvent, 0, totalBytes, 0)
+ publishProgress(listener, event)
+
+ // Start to copy workers
+ arg := copyWorkerArg{descBucket, imur, srcBucketName, srcObjectKey, partOptions, copyPartHooker}
+ for w := 1; w <= routines; w++ {
+ go copyWorker(w, arg, jobs, results, failed, die)
+ }
+
+ // Start the scheduler
+ go copyScheduler(jobs, parts)
+
+ // Wait for the parts finished.
+ completed := 0
+ ups := make([]UploadPart, len(parts))
+ for completed < len(parts) {
+ select {
+ case part := <-results:
+ completed++
+ ups[part.PartNumber-1] = part
+ copyBytes := (parts[part.PartNumber-1].End - parts[part.PartNumber-1].Start + 1)
+ completedBytes += copyBytes
+ event = newProgressEvent(TransferDataEvent, completedBytes, totalBytes, copyBytes)
+ publishProgress(listener, event)
+ case err := <-failed:
+ close(die)
+ descBucket.AbortMultipartUpload(imur, abortOptions...)
+ event = newProgressEvent(TransferFailedEvent, completedBytes, totalBytes, 0)
+ publishProgress(listener, event)
+ return err
+ }
+
+ if completed >= len(parts) {
+ break
+ }
+ }
+
+ event = newProgressEvent(TransferCompletedEvent, completedBytes, totalBytes, 0)
+ publishProgress(listener, event)
+
+ // Complete the multipart upload
+ _, err = descBucket.CompleteMultipartUpload(imur, ups, completeOptions...)
+ if err != nil {
+ bucket.AbortMultipartUpload(imur, abortOptions...)
+ return err
+ }
+ return nil
+}
+
+// ----- Concurrently copy with checkpoint -----
+
+const copyCpMagic = "84F1F18C-FF1D-403B-A1D8-9DEB5F65910A"
+
+type copyCheckpoint struct {
+ Magic string // Magic
+ MD5 string // CP content MD5
+ SrcBucketName string // Source bucket
+ SrcObjectKey string // Source object
+ DestBucketName string // Target bucket
+ DestObjectKey string // Target object
+ CopyID string // Copy ID
+ ObjStat objectStat // Object stat
+ Parts []copyPart // Copy parts
+ CopyParts []UploadPart // The uploaded parts
+ PartStat []bool // The part status
+}
+
+// isValid checks if the data is valid which means CP is valid and object is not updated.
+func (cp copyCheckpoint) isValid(meta http.Header) (bool, error) {
+ // Compare CP's magic number and the MD5.
+ cpb := cp
+ cpb.MD5 = ""
+ js, _ := json.Marshal(cpb)
+ sum := md5.Sum(js)
+ b64 := base64.StdEncoding.EncodeToString(sum[:])
+
+ if cp.Magic != downloadCpMagic || b64 != cp.MD5 {
+ return false, nil
+ }
+
+ objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 64)
+ if err != nil {
+ return false, err
+ }
+
+ // Compare the object size and last modified time and etag.
+ if cp.ObjStat.Size != objectSize ||
+ cp.ObjStat.LastModified != meta.Get(HTTPHeaderLastModified) ||
+ cp.ObjStat.Etag != meta.Get(HTTPHeaderEtag) {
+ return false, nil
+ }
+
+ return true, nil
+}
+
+// load loads from the checkpoint file
+func (cp *copyCheckpoint) load(filePath string) error {
+ contents, err := ioutil.ReadFile(filePath)
+ if err != nil {
+ return err
+ }
+
+ err = json.Unmarshal(contents, cp)
+ return err
+}
+
+// update updates the parts status
+func (cp *copyCheckpoint) update(part UploadPart) {
+ cp.CopyParts[part.PartNumber-1] = part
+ cp.PartStat[part.PartNumber-1] = true
+}
+
+// dump dumps the CP to the file
+func (cp *copyCheckpoint) dump(filePath string) error {
+ bcp := *cp
+
+ // Calculate MD5
+ bcp.MD5 = ""
+ js, err := json.Marshal(bcp)
+ if err != nil {
+ return err
+ }
+ sum := md5.Sum(js)
+ b64 := base64.StdEncoding.EncodeToString(sum[:])
+ bcp.MD5 = b64
+
+ // Serialization
+ js, err = json.Marshal(bcp)
+ if err != nil {
+ return err
+ }
+
+ // Dump
+ return ioutil.WriteFile(filePath, js, FilePermMode)
+}
+
+// todoParts returns unfinished parts
+func (cp copyCheckpoint) todoParts() []copyPart {
+ dps := []copyPart{}
+ for i, ps := range cp.PartStat {
+ if !ps {
+ dps = append(dps, cp.Parts[i])
+ }
+ }
+ return dps
+}
+
+// getCompletedBytes returns finished bytes count
+func (cp copyCheckpoint) getCompletedBytes() int64 {
+ var completedBytes int64
+ for i, part := range cp.Parts {
+ if cp.PartStat[i] {
+ completedBytes += (part.End - part.Start + 1)
+ }
+ }
+ return completedBytes
+}
+
+// prepare initializes the multipart upload
+func (cp *copyCheckpoint) prepare(meta http.Header, srcBucket *Bucket, srcObjectKey string, destBucket *Bucket, destObjectKey string,
+ partSize int64, options []Option) error {
+ // CP
+ cp.Magic = copyCpMagic
+ cp.SrcBucketName = srcBucket.BucketName
+ cp.SrcObjectKey = srcObjectKey
+ cp.DestBucketName = destBucket.BucketName
+ cp.DestObjectKey = destObjectKey
+
+ objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 64)
+ if err != nil {
+ return err
+ }
+
+ cp.ObjStat.Size = objectSize
+ cp.ObjStat.LastModified = meta.Get(HTTPHeaderLastModified)
+ cp.ObjStat.Etag = meta.Get(HTTPHeaderEtag)
+
+ // Parts
+ cp.Parts = getCopyParts(objectSize, partSize)
+ cp.PartStat = make([]bool, len(cp.Parts))
+ for i := range cp.PartStat {
+ cp.PartStat[i] = false
+ }
+ cp.CopyParts = make([]UploadPart, len(cp.Parts))
+
+ // Init copy
+ imur, err := destBucket.InitiateMultipartUpload(destObjectKey, options...)
+ if err != nil {
+ return err
+ }
+ cp.CopyID = imur.UploadID
+
+ return nil
+}
+
+func (cp *copyCheckpoint) complete(bucket *Bucket, parts []UploadPart, cpFilePath string, options []Option) error {
+ imur := InitiateMultipartUploadResult{Bucket: cp.DestBucketName,
+ Key: cp.DestObjectKey, UploadID: cp.CopyID}
+ _, err := bucket.CompleteMultipartUpload(imur, parts, options...)
+ if err != nil {
+ return err
+ }
+ os.Remove(cpFilePath)
+ return err
+}
+
+// copyFileWithCp is concurrently copy with checkpoint
+func (bucket Bucket) copyFileWithCp(srcBucketName, srcObjectKey, destBucketName, destObjectKey string,
+ partSize int64, options []Option, cpFilePath string, routines int) error {
+ descBucket, err := bucket.Client.Bucket(destBucketName)
+ srcBucket, err := bucket.Client.Bucket(srcBucketName)
+ listener := GetProgressListener(options)
+
+ // Load CP data
+ ccp := copyCheckpoint{}
+ err = ccp.load(cpFilePath)
+ if err != nil {
+ os.Remove(cpFilePath)
+ }
+
+ // choice valid options
+ headerOptions := ChoiceHeadObjectOption(options)
+ partOptions := ChoiceTransferPartOption(options)
+ completeOptions := ChoiceCompletePartOption(options)
+
+ meta, err := srcBucket.GetObjectDetailedMeta(srcObjectKey, headerOptions...)
+ if err != nil {
+ return err
+ }
+
+ // Load error or the CP data is invalid---reinitialize
+ valid, err := ccp.isValid(meta)
+ if err != nil || !valid {
+ if err = ccp.prepare(meta, srcBucket, srcObjectKey, descBucket, destObjectKey, partSize, options); err != nil {
+ return err
+ }
+ os.Remove(cpFilePath)
+ }
+
+ // Unfinished parts
+ parts := ccp.todoParts()
+ imur := InitiateMultipartUploadResult{
+ Bucket: destBucketName,
+ Key: destObjectKey,
+ UploadID: ccp.CopyID}
+
+ jobs := make(chan copyPart, len(parts))
+ results := make(chan UploadPart, len(parts))
+ failed := make(chan error)
+ die := make(chan bool)
+
+ completedBytes := ccp.getCompletedBytes()
+ event := newProgressEvent(TransferStartedEvent, completedBytes, ccp.ObjStat.Size, 0)
+ publishProgress(listener, event)
+
+ // Start the worker coroutines
+ arg := copyWorkerArg{descBucket, imur, srcBucketName, srcObjectKey, partOptions, copyPartHooker}
+ for w := 1; w <= routines; w++ {
+ go copyWorker(w, arg, jobs, results, failed, die)
+ }
+
+ // Start the scheduler
+ go copyScheduler(jobs, parts)
+
+ // Wait for the parts completed.
+ completed := 0
+ for completed < len(parts) {
+ select {
+ case part := <-results:
+ completed++
+ ccp.update(part)
+ ccp.dump(cpFilePath)
+ copyBytes := (parts[part.PartNumber-1].End - parts[part.PartNumber-1].Start + 1)
+ completedBytes += copyBytes
+ event = newProgressEvent(TransferDataEvent, completedBytes, ccp.ObjStat.Size, copyBytes)
+ publishProgress(listener, event)
+ case err := <-failed:
+ close(die)
+ event = newProgressEvent(TransferFailedEvent, completedBytes, ccp.ObjStat.Size, 0)
+ publishProgress(listener, event)
+ return err
+ }
+
+ if completed >= len(parts) {
+ break
+ }
+ }
+
+ event = newProgressEvent(TransferCompletedEvent, completedBytes, ccp.ObjStat.Size, 0)
+ publishProgress(listener, event)
+
+ return ccp.complete(descBucket, ccp.CopyParts, cpFilePath, completeOptions)
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/multipart.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/multipart.go
new file mode 100644
index 00000000000..aea7fafb6ea
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/multipart.go
@@ -0,0 +1,320 @@
+package oss
+
+import (
+ "bytes"
+ "encoding/xml"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "os"
+ "sort"
+ "strconv"
+)
+
+// InitiateMultipartUpload initializes multipart upload
+//
+// objectKey object name
+// options the object constricts for upload. The valid options are CacheControl, ContentDisposition, ContentEncoding, Expires,
+//
+// ServerSideEncryption, Meta, check out the following link:
+// https://www.alibabacloud.com/help/en/object-storage-service/latest/initiatemultipartupload
+//
+// InitiateMultipartUploadResult the return value of the InitiateMultipartUpload, which is used for calls later on such as UploadPartFromFile,UploadPartCopy.
+// error it's nil if the operation succeeds, otherwise it's an error object.
+func (bucket Bucket) InitiateMultipartUpload(objectKey string, options ...Option) (InitiateMultipartUploadResult, error) {
+ var imur InitiateMultipartUploadResult
+ opts := AddContentType(options, objectKey)
+ params, _ := GetRawParams(options)
+ paramKeys := []string{"sequential", "withHashContext", "x-oss-enable-md5", "x-oss-enable-sha1", "x-oss-enable-sha256"}
+ ConvertEmptyValueToNil(params, paramKeys)
+ params["uploads"] = nil
+
+ resp, err := bucket.do("POST", objectKey, params, opts, nil, nil)
+ if err != nil {
+ return imur, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &imur)
+ return imur, err
+}
+
+// UploadPart uploads parts
+//
+// After initializing a Multipart Upload, the upload Id and object key could be used for uploading the parts.
+// Each part has its part number (ranges from 1 to 10,000). And for each upload Id, the part number identifies the position of the part in the whole file.
+// And thus with the same part number and upload Id, another part upload will overwrite the data.
+// Except the last one, minimal part size is 100KB. There's no limit on the last part size.
+//
+// imur the returned value of InitiateMultipartUpload.
+// reader io.Reader the reader for the part's data.
+// size the part size.
+// partNumber the part number (ranges from 1 to 10,000). Invalid part number will lead to InvalidArgument error.
+//
+// UploadPart the return value of the upload part. It consists of PartNumber and ETag. It's valid when error is nil.
+// error it's nil if the operation succeeds, otherwise it's an error object.
+func (bucket Bucket) UploadPart(imur InitiateMultipartUploadResult, reader io.Reader,
+ partSize int64, partNumber int, options ...Option) (UploadPart, error) {
+ request := &UploadPartRequest{
+ InitResult: &imur,
+ Reader: reader,
+ PartSize: partSize,
+ PartNumber: partNumber,
+ }
+
+ result, err := bucket.DoUploadPart(request, options)
+
+ return result.Part, err
+}
+
+// UploadPartFromFile uploads part from the file.
+//
+// imur the return value of a successful InitiateMultipartUpload.
+// filePath the local file path to upload.
+// startPosition the start position in the local file.
+// partSize the part size.
+// partNumber the part number (from 1 to 10,000)
+//
+// UploadPart the return value consists of PartNumber and ETag.
+// error it's nil if the operation succeeds, otherwise it's an error object.
+func (bucket Bucket) UploadPartFromFile(imur InitiateMultipartUploadResult, filePath string,
+ startPosition, partSize int64, partNumber int, options ...Option) (UploadPart, error) {
+ var part = UploadPart{}
+ fd, err := os.Open(filePath)
+ if err != nil {
+ return part, err
+ }
+ defer fd.Close()
+ fd.Seek(startPosition, os.SEEK_SET)
+
+ request := &UploadPartRequest{
+ InitResult: &imur,
+ Reader: fd,
+ PartSize: partSize,
+ PartNumber: partNumber,
+ }
+
+ result, err := bucket.DoUploadPart(request, options)
+
+ return result.Part, err
+}
+
+// DoUploadPart does the actual part upload.
+//
+// request part upload request
+//
+// UploadPartResult the result of uploading part.
+// error it's nil if the operation succeeds, otherwise it's an error object.
+func (bucket Bucket) DoUploadPart(request *UploadPartRequest, options []Option) (*UploadPartResult, error) {
+ listener := GetProgressListener(options)
+ options = append(options, ContentLength(request.PartSize))
+ params := map[string]interface{}{}
+ params["partNumber"] = strconv.Itoa(request.PartNumber)
+ params["uploadId"] = request.InitResult.UploadID
+ resp, err := bucket.do("PUT", request.InitResult.Key, params, options,
+ &io.LimitedReader{R: request.Reader, N: request.PartSize}, listener)
+ if err != nil {
+ return &UploadPartResult{}, err
+ }
+ defer resp.Body.Close()
+
+ part := UploadPart{
+ ETag: resp.Headers.Get(HTTPHeaderEtag),
+ PartNumber: request.PartNumber,
+ }
+
+ if bucket.GetConfig().IsEnableCRC {
+ err = CheckCRC(resp, "DoUploadPart")
+ if err != nil {
+ return &UploadPartResult{part}, err
+ }
+ }
+
+ return &UploadPartResult{part}, nil
+}
+
+// UploadPartCopy uploads part copy
+//
+// imur the return value of InitiateMultipartUpload
+// copySrc source Object name
+// startPosition the part's start index in the source file
+// partSize the part size
+// partNumber the part number, ranges from 1 to 10,000. If it exceeds the range OSS returns InvalidArgument error.
+// options the constraints of source object for the copy. The copy happens only when these contraints are met. Otherwise it returns error.
+//
+// CopySourceIfNoneMatch, CopySourceIfModifiedSince CopySourceIfUnmodifiedSince, check out the following link for the detail
+// https://www.alibabacloud.com/help/en/object-storage-service/latest/uploadpartcopy
+//
+// UploadPart the return value consists of PartNumber and ETag.
+// error it's nil if the operation succeeds, otherwise it's an error object.
+func (bucket Bucket) UploadPartCopy(imur InitiateMultipartUploadResult, srcBucketName, srcObjectKey string,
+ startPosition, partSize int64, partNumber int, options ...Option) (UploadPart, error) {
+ var out UploadPartCopyResult
+ var part UploadPart
+ var opts []Option
+
+ //first find version id
+ versionIdKey := "versionId"
+ versionId, _ := FindOption(options, versionIdKey, nil)
+ if versionId == nil {
+ opts = []Option{CopySource(srcBucketName, url.QueryEscape(srcObjectKey)),
+ CopySourceRange(startPosition, partSize)}
+ } else {
+ opts = []Option{CopySourceVersion(srcBucketName, url.QueryEscape(srcObjectKey), versionId.(string)),
+ CopySourceRange(startPosition, partSize)}
+ options = DeleteOption(options, versionIdKey)
+ }
+
+ opts = append(opts, options...)
+
+ params := map[string]interface{}{}
+ params["partNumber"] = strconv.Itoa(partNumber)
+ params["uploadId"] = imur.UploadID
+ resp, err := bucket.do("PUT", imur.Key, params, opts, nil, nil)
+ if err != nil {
+ return part, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ if err != nil {
+ return part, err
+ }
+ part.ETag = out.ETag
+ part.PartNumber = partNumber
+
+ return part, nil
+}
+
+// CompleteMultipartUpload completes the multipart upload.
+//
+// imur the return value of InitiateMultipartUpload.
+// parts the array of return value of UploadPart/UploadPartFromFile/UploadPartCopy.
+//
+// CompleteMultipartUploadResponse the return value when the call succeeds. Only valid when the error is nil.
+// error it's nil if the operation succeeds, otherwise it's an error object.
+func (bucket Bucket) CompleteMultipartUpload(imur InitiateMultipartUploadResult,
+ parts []UploadPart, options ...Option) (CompleteMultipartUploadResult, error) {
+ var out CompleteMultipartUploadResult
+
+ sort.Sort(UploadParts(parts))
+ cxml := completeMultipartUploadXML{}
+ cxml.Part = parts
+ bs, err := xml.Marshal(cxml)
+ if err != nil {
+ return out, err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ params := map[string]interface{}{}
+ params["uploadId"] = imur.UploadID
+ resp, err := bucket.do("POST", imur.Key, params, options, buffer, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return out, err
+ }
+ err = CheckRespCode(resp.StatusCode, []int{http.StatusOK})
+ if len(body) > 0 {
+ if err != nil {
+ err = tryConvertServiceError(body, resp, err)
+ } else {
+ callback, _ := FindOption(options, HTTPHeaderOssCallback, nil)
+ if callback == nil {
+ err = xml.Unmarshal(body, &out)
+ } else {
+ rb, _ := FindOption(options, responseBody, nil)
+ if rb != nil {
+ if rbody, ok := rb.(*[]byte); ok {
+ *rbody = body
+ }
+ }
+ }
+ }
+ }
+ return out, err
+}
+
+// AbortMultipartUpload aborts the multipart upload.
+//
+// imur the return value of InitiateMultipartUpload.
+//
+// error it's nil if the operation succeeds, otherwise it's an error object.
+func (bucket Bucket) AbortMultipartUpload(imur InitiateMultipartUploadResult, options ...Option) error {
+ params := map[string]interface{}{}
+ params["uploadId"] = imur.UploadID
+ resp, err := bucket.do("DELETE", imur.Key, params, options, nil, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return CheckRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+// ListUploadedParts lists the uploaded parts.
+//
+// imur the return value of InitiateMultipartUpload.
+//
+// ListUploadedPartsResponse the return value if it succeeds, only valid when error is nil.
+// error it's nil if the operation succeeds, otherwise it's an error object.
+func (bucket Bucket) ListUploadedParts(imur InitiateMultipartUploadResult, options ...Option) (ListUploadedPartsResult, error) {
+ var out ListUploadedPartsResult
+ options = append(options, EncodingType("url"))
+
+ params := map[string]interface{}{}
+ params, err := GetRawParams(options)
+ if err != nil {
+ return out, err
+ }
+
+ params["uploadId"] = imur.UploadID
+ resp, err := bucket.do("GET", imur.Key, params, options, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ if err != nil {
+ return out, err
+ }
+ err = decodeListUploadedPartsResult(&out)
+ return out, err
+}
+
+// ListMultipartUploads lists all ongoing multipart upload tasks
+//
+// options listObject's filter. Prefix specifies the returned object's prefix; KeyMarker specifies the returned object's start point in lexicographic order;
+//
+// MaxKeys specifies the max entries to return; Delimiter is the character for grouping object keys.
+//
+// ListMultipartUploadResponse the return value if it succeeds, only valid when error is nil.
+// error it's nil if the operation succeeds, otherwise it's an error object.
+func (bucket Bucket) ListMultipartUploads(options ...Option) (ListMultipartUploadResult, error) {
+ var out ListMultipartUploadResult
+
+ options = append(options, EncodingType("url"))
+ params, err := GetRawParams(options)
+ if err != nil {
+ return out, err
+ }
+ params["uploads"] = nil
+
+ resp, err := bucket.doInner("GET", "", params, options, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ if err != nil {
+ return out, err
+ }
+ err = decodeListMultipartUploadResult(&out)
+ return out, err
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/option.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/option.go
new file mode 100644
index 00000000000..7e517ddbed3
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/option.go
@@ -0,0 +1,735 @@
+package oss
+
+import (
+ "context"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+ "time"
+)
+
+type optionType string
+
+const (
+ optionParam optionType = "HTTPParameter" // URL parameter
+ optionHTTP optionType = "HTTPHeader" // HTTP header
+ optionContext optionType = "HTTPContext" // context
+ optionArg optionType = "FuncArgument" // Function argument
+
+)
+
+const (
+ deleteObjectsQuiet = "delete-objects-quiet"
+ routineNum = "x-routine-num"
+ checkpointConfig = "x-cp-config"
+ initCRC64 = "init-crc64"
+ progressListener = "x-progress-listener"
+ storageClass = "storage-class"
+ responseHeader = "x-response-header"
+ redundancyType = "redundancy-type"
+ objectHashFunc = "object-hash-func"
+ responseBody = "x-response-body"
+ contextArg = "x-context-arg"
+)
+
+type (
+ optionValue struct {
+ Value interface{}
+ Type optionType
+ }
+
+ // Option HTTP option
+ Option func(map[string]optionValue) error
+)
+
+// ACL is an option to set X-Oss-Acl header
+func ACL(acl ACLType) Option {
+ return setHeader(HTTPHeaderOssACL, string(acl))
+}
+
+// ContentType is an option to set Content-Type header
+func ContentType(value string) Option {
+ return setHeader(HTTPHeaderContentType, value)
+}
+
+// ContentLength is an option to set Content-Length header
+func ContentLength(length int64) Option {
+ return setHeader(HTTPHeaderContentLength, strconv.FormatInt(length, 10))
+}
+
+// CacheControl is an option to set Cache-Control header
+func CacheControl(value string) Option {
+ return setHeader(HTTPHeaderCacheControl, value)
+}
+
+// ContentDisposition is an option to set Content-Disposition header
+func ContentDisposition(value string) Option {
+ return setHeader(HTTPHeaderContentDisposition, value)
+}
+
+// ContentEncoding is an option to set Content-Encoding header
+func ContentEncoding(value string) Option {
+ return setHeader(HTTPHeaderContentEncoding, value)
+}
+
+// ContentLanguage is an option to set Content-Language header
+func ContentLanguage(value string) Option {
+ return setHeader(HTTPHeaderContentLanguage, value)
+}
+
+// ContentMD5 is an option to set Content-MD5 header
+func ContentMD5(value string) Option {
+ return setHeader(HTTPHeaderContentMD5, value)
+}
+
+// Expires is an option to set Expires header
+func Expires(t time.Time) Option {
+ return setHeader(HTTPHeaderExpires, t.Format(http.TimeFormat))
+}
+
+// Meta is an option to set Meta header
+func Meta(key, value string) Option {
+ return setHeader(HTTPHeaderOssMetaPrefix+key, value)
+}
+
+// Range is an option to set Range header, [start, end]
+func Range(start, end int64) Option {
+ return setHeader(HTTPHeaderRange, fmt.Sprintf("bytes=%d-%d", start, end))
+}
+
+// NormalizedRange is an option to set Range header, such as 1024-2048 or 1024- or -2048
+func NormalizedRange(nr string) Option {
+ return setHeader(HTTPHeaderRange, fmt.Sprintf("bytes=%s", strings.TrimSpace(nr)))
+}
+
+// AcceptEncoding is an option to set Accept-Encoding header
+func AcceptEncoding(value string) Option {
+ return setHeader(HTTPHeaderAcceptEncoding, value)
+}
+
+// IfModifiedSince is an option to set If-Modified-Since header
+func IfModifiedSince(t time.Time) Option {
+ return setHeader(HTTPHeaderIfModifiedSince, t.Format(http.TimeFormat))
+}
+
+// IfUnmodifiedSince is an option to set If-Unmodified-Since header
+func IfUnmodifiedSince(t time.Time) Option {
+ return setHeader(HTTPHeaderIfUnmodifiedSince, t.Format(http.TimeFormat))
+}
+
+// IfMatch is an option to set If-Match header
+func IfMatch(value string) Option {
+ return setHeader(HTTPHeaderIfMatch, value)
+}
+
+// IfNoneMatch is an option to set IfNoneMatch header
+func IfNoneMatch(value string) Option {
+ return setHeader(HTTPHeaderIfNoneMatch, value)
+}
+
+// CopySource is an option to set X-Oss-Copy-Source header
+func CopySource(sourceBucket, sourceObject string) Option {
+ return setHeader(HTTPHeaderOssCopySource, "/"+sourceBucket+"/"+sourceObject)
+}
+
+// CopySourceVersion is an option to set X-Oss-Copy-Source header,include versionId
+func CopySourceVersion(sourceBucket, sourceObject string, versionId string) Option {
+ return setHeader(HTTPHeaderOssCopySource, "/"+sourceBucket+"/"+sourceObject+"?"+"versionId="+versionId)
+}
+
+// CopySourceRange is an option to set X-Oss-Copy-Source header
+func CopySourceRange(startPosition, partSize int64) Option {
+ val := "bytes=" + strconv.FormatInt(startPosition, 10) + "-" +
+ strconv.FormatInt((startPosition+partSize-1), 10)
+ return setHeader(HTTPHeaderOssCopySourceRange, val)
+}
+
+// CopySourceIfMatch is an option to set X-Oss-Copy-Source-If-Match header
+func CopySourceIfMatch(value string) Option {
+ return setHeader(HTTPHeaderOssCopySourceIfMatch, value)
+}
+
+// CopySourceIfNoneMatch is an option to set X-Oss-Copy-Source-If-None-Match header
+func CopySourceIfNoneMatch(value string) Option {
+ return setHeader(HTTPHeaderOssCopySourceIfNoneMatch, value)
+}
+
+// CopySourceIfModifiedSince is an option to set X-Oss-CopySource-If-Modified-Since header
+func CopySourceIfModifiedSince(t time.Time) Option {
+ return setHeader(HTTPHeaderOssCopySourceIfModifiedSince, t.Format(http.TimeFormat))
+}
+
+// CopySourceIfUnmodifiedSince is an option to set X-Oss-Copy-Source-If-Unmodified-Since header
+func CopySourceIfUnmodifiedSince(t time.Time) Option {
+ return setHeader(HTTPHeaderOssCopySourceIfUnmodifiedSince, t.Format(http.TimeFormat))
+}
+
+// MetadataDirective is an option to set X-Oss-Metadata-Directive header
+func MetadataDirective(directive MetadataDirectiveType) Option {
+ return setHeader(HTTPHeaderOssMetadataDirective, string(directive))
+}
+
+// ServerSideEncryption is an option to set X-Oss-Server-Side-Encryption header
+func ServerSideEncryption(value string) Option {
+ return setHeader(HTTPHeaderOssServerSideEncryption, value)
+}
+
+// ServerSideEncryptionKeyID is an option to set X-Oss-Server-Side-Encryption-Key-Id header
+func ServerSideEncryptionKeyID(value string) Option {
+ return setHeader(HTTPHeaderOssServerSideEncryptionKeyID, value)
+}
+
+// ServerSideDataEncryption is an option to set X-Oss-Server-Side-Data-Encryption header
+func ServerSideDataEncryption(value string) Option {
+ return setHeader(HTTPHeaderOssServerSideDataEncryption, value)
+}
+
+// SSECAlgorithm is an option to set X-Oss-Server-Side-Encryption-Customer-Algorithm header
+func SSECAlgorithm(value string) Option {
+ return setHeader(HTTPHeaderSSECAlgorithm, value)
+}
+
+// SSECKey is an option to set X-Oss-Server-Side-Encryption-Customer-Key header
+func SSECKey(value string) Option {
+ return setHeader(HTTPHeaderSSECKey, value)
+}
+
+// SSECKeyMd5 is an option to set X-Oss-Server-Side-Encryption-Customer-Key-Md5 header
+func SSECKeyMd5(value string) Option {
+ return setHeader(HTTPHeaderSSECKeyMd5, value)
+}
+
+// ObjectACL is an option to set X-Oss-Object-Acl header
+func ObjectACL(acl ACLType) Option {
+ return setHeader(HTTPHeaderOssObjectACL, string(acl))
+}
+
+// symlinkTarget is an option to set X-Oss-Symlink-Target
+func symlinkTarget(targetObjectKey string) Option {
+ return setHeader(HTTPHeaderOssSymlinkTarget, targetObjectKey)
+}
+
+// Origin is an option to set Origin header
+func Origin(value string) Option {
+ return setHeader(HTTPHeaderOrigin, value)
+}
+
+// ObjectStorageClass is an option to set the storage class of object
+func ObjectStorageClass(storageClass StorageClassType) Option {
+ return setHeader(HTTPHeaderOssStorageClass, string(storageClass))
+}
+
+// Callback is an option to set callback values
+func Callback(callback string) Option {
+ return setHeader(HTTPHeaderOssCallback, callback)
+}
+
+// CallbackVar is an option to set callback user defined values
+func CallbackVar(callbackVar string) Option {
+ return setHeader(HTTPHeaderOssCallbackVar, callbackVar)
+}
+
+// RequestPayer is an option to set payer who pay for the request
+func RequestPayer(payerType PayerType) Option {
+ return setHeader(HTTPHeaderOssRequester, strings.ToLower(string(payerType)))
+}
+
+// RequestPayerParam is an option to set payer who pay for the request
+func RequestPayerParam(payerType PayerType) Option {
+ return addParam(strings.ToLower(HTTPHeaderOssRequester), strings.ToLower(string(payerType)))
+}
+
+// SetTagging is an option to set object tagging
+func SetTagging(tagging Tagging) Option {
+ if len(tagging.Tags) == 0 {
+ return nil
+ }
+
+ taggingValue := ""
+ for index, tag := range tagging.Tags {
+ if index != 0 {
+ taggingValue += "&"
+ }
+ taggingValue += url.QueryEscape(tag.Key) + "=" + url.QueryEscape(tag.Value)
+ }
+ return setHeader(HTTPHeaderOssTagging, taggingValue)
+}
+
+// TaggingDirective is an option to set X-Oss-Metadata-Directive header
+func TaggingDirective(directive TaggingDirectiveType) Option {
+ return setHeader(HTTPHeaderOssTaggingDirective, string(directive))
+}
+
+// ACReqMethod is an option to set Access-Control-Request-Method header
+func ACReqMethod(value string) Option {
+ return setHeader(HTTPHeaderACReqMethod, value)
+}
+
+// ACReqHeaders is an option to set Access-Control-Request-Headers header
+func ACReqHeaders(value string) Option {
+ return setHeader(HTTPHeaderACReqHeaders, value)
+}
+
+// TrafficLimitHeader is an option to set X-Oss-Traffic-Limit
+func TrafficLimitHeader(value int64) Option {
+ return setHeader(HTTPHeaderOssTrafficLimit, strconv.FormatInt(value, 10))
+}
+
+// UserAgentHeader is an option to set HTTPHeaderUserAgent
+func UserAgentHeader(ua string) Option {
+ return setHeader(HTTPHeaderUserAgent, ua)
+}
+
+// ForbidOverWrite is an option to set X-Oss-Forbid-Overwrite
+func ForbidOverWrite(forbidWrite bool) Option {
+ if forbidWrite {
+ return setHeader(HTTPHeaderOssForbidOverWrite, "true")
+ } else {
+ return setHeader(HTTPHeaderOssForbidOverWrite, "false")
+ }
+}
+
+// RangeBehavior is an option to set Range value, such as "standard"
+func RangeBehavior(value string) Option {
+ return setHeader(HTTPHeaderOssRangeBehavior, value)
+}
+
+func PartHashCtxHeader(value string) Option {
+ return setHeader(HTTPHeaderOssHashCtx, value)
+}
+
+func PartMd5CtxHeader(value string) Option {
+ return setHeader(HTTPHeaderOssMd5Ctx, value)
+}
+
+func PartHashCtxParam(value string) Option {
+ return addParam("x-oss-hash-ctx", value)
+}
+
+func PartMd5CtxParam(value string) Option {
+ return addParam("x-oss-md5-ctx", value)
+}
+
+// Delimiter is an option to set delimiler parameter
+func Delimiter(value string) Option {
+ return addParam("delimiter", value)
+}
+
+// Marker is an option to set marker parameter
+func Marker(value string) Option {
+ return addParam("marker", value)
+}
+
+// MaxKeys is an option to set maxkeys parameter
+func MaxKeys(value int) Option {
+ return addParam("max-keys", strconv.Itoa(value))
+}
+
+// Prefix is an option to set prefix parameter
+func Prefix(value string) Option {
+ return addParam("prefix", value)
+}
+
+// EncodingType is an option to set encoding-type parameter
+func EncodingType(value string) Option {
+ return addParam("encoding-type", value)
+}
+
+// MaxUploads is an option to set max-uploads parameter
+func MaxUploads(value int) Option {
+ return addParam("max-uploads", strconv.Itoa(value))
+}
+
+// KeyMarker is an option to set key-marker parameter
+func KeyMarker(value string) Option {
+ return addParam("key-marker", value)
+}
+
+// VersionIdMarker is an option to set version-id-marker parameter
+func VersionIdMarker(value string) Option {
+ return addParam("version-id-marker", value)
+}
+
+// VersionId is an option to set versionId parameter
+func VersionId(value string) Option {
+ return addParam("versionId", value)
+}
+
+// TagKey is an option to set tag key parameter
+func TagKey(value string) Option {
+ return addParam("tag-key", value)
+}
+
+// TagValue is an option to set tag value parameter
+func TagValue(value string) Option {
+ return addParam("tag-value", value)
+}
+
+// UploadIDMarker is an option to set upload-id-marker parameter
+func UploadIDMarker(value string) Option {
+ return addParam("upload-id-marker", value)
+}
+
+// MaxParts is an option to set max-parts parameter
+func MaxParts(value int) Option {
+ return addParam("max-parts", strconv.Itoa(value))
+}
+
+// PartNumberMarker is an option to set part-number-marker parameter
+func PartNumberMarker(value int) Option {
+ return addParam("part-number-marker", strconv.Itoa(value))
+}
+
+// Sequential is an option to set sequential parameter for InitiateMultipartUpload
+func Sequential() Option {
+ return addParam("sequential", "")
+}
+
+// WithHashContext is an option to set withHashContext parameter for InitiateMultipartUpload
+func WithHashContext() Option {
+ return addParam("withHashContext", "")
+}
+
+// EnableMd5 is an option to set x-oss-enable-md5 parameter for InitiateMultipartUpload
+func EnableMd5() Option {
+ return addParam("x-oss-enable-md5", "")
+}
+
+// EnableSha1 is an option to set x-oss-enable-sha1 parameter for InitiateMultipartUpload
+func EnableSha1() Option {
+ return addParam("x-oss-enable-sha1", "")
+}
+
+// EnableSha256 is an option to set x-oss-enable-sha256 parameter for InitiateMultipartUpload
+func EnableSha256() Option {
+ return addParam("x-oss-enable-sha256", "")
+}
+
+// ListType is an option to set List-type parameter for ListObjectsV2
+func ListType(value int) Option {
+ return addParam("list-type", strconv.Itoa(value))
+}
+
+// StartAfter is an option to set start-after parameter for ListObjectsV2
+func StartAfter(value string) Option {
+ return addParam("start-after", value)
+}
+
+// ContinuationToken is an option to set Continuation-token parameter for ListObjectsV2
+func ContinuationToken(value string) Option {
+ if value == "" {
+ return addParam("continuation-token", nil)
+ }
+ return addParam("continuation-token", value)
+}
+
+// FetchOwner is an option to set Fetch-owner parameter for ListObjectsV2
+func FetchOwner(value bool) Option {
+ if value {
+ return addParam("fetch-owner", "true")
+ }
+ return addParam("fetch-owner", "false")
+}
+
+// DeleteObjectsQuiet false:DeleteObjects in verbose mode; true:DeleteObjects in quite mode. Default is false.
+func DeleteObjectsQuiet(isQuiet bool) Option {
+ return addArg(deleteObjectsQuiet, isQuiet)
+}
+
+// StorageClass bucket storage class
+func StorageClass(value StorageClassType) Option {
+ return addArg(storageClass, value)
+}
+
+// RedundancyType bucket data redundancy type
+func RedundancyType(value DataRedundancyType) Option {
+ return addArg(redundancyType, value)
+}
+
+// RedundancyType bucket data redundancy type
+func ObjectHashFunc(value ObjecthashFuncType) Option {
+ return addArg(objectHashFunc, value)
+}
+
+// WithContext returns an option that sets the context for requests.
+func WithContext(ctx context.Context) Option {
+ return addArg(contextArg, ctx)
+}
+
+// Checkpoint configuration
+type cpConfig struct {
+ IsEnable bool
+ FilePath string
+ DirPath string
+}
+
+// Checkpoint sets the isEnable flag and checkpoint file path for DownloadFile/UploadFile.
+func Checkpoint(isEnable bool, filePath string) Option {
+ return addArg(checkpointConfig, &cpConfig{IsEnable: isEnable, FilePath: filePath})
+}
+
+// CheckpointDir sets the isEnable flag and checkpoint dir path for DownloadFile/UploadFile.
+func CheckpointDir(isEnable bool, dirPath string) Option {
+ return addArg(checkpointConfig, &cpConfig{IsEnable: isEnable, DirPath: dirPath})
+}
+
+// Routines DownloadFile/UploadFile routine count
+func Routines(n int) Option {
+ return addArg(routineNum, n)
+}
+
+// InitCRC Init AppendObject CRC
+func InitCRC(initCRC uint64) Option {
+ return addArg(initCRC64, initCRC)
+}
+
+// Progress set progress listener
+func Progress(listener ProgressListener) Option {
+ return addArg(progressListener, listener)
+}
+
+// GetResponseHeader for get response http header
+func GetResponseHeader(respHeader *http.Header) Option {
+ return addArg(responseHeader, respHeader)
+}
+
+// CallbackResult for get response of call back
+func CallbackResult(body *[]byte) Option {
+ return addArg(responseBody, body)
+}
+
+// ResponseContentType is an option to set response-content-type param
+func ResponseContentType(value string) Option {
+ return addParam("response-content-type", value)
+}
+
+// ResponseContentLanguage is an option to set response-content-language param
+func ResponseContentLanguage(value string) Option {
+ return addParam("response-content-language", value)
+}
+
+// ResponseExpires is an option to set response-expires param
+func ResponseExpires(value string) Option {
+ return addParam("response-expires", value)
+}
+
+// ResponseCacheControl is an option to set response-cache-control param
+func ResponseCacheControl(value string) Option {
+ return addParam("response-cache-control", value)
+}
+
+// ResponseContentDisposition is an option to set response-content-disposition param
+func ResponseContentDisposition(value string) Option {
+ return addParam("response-content-disposition", value)
+}
+
+// ResponseContentEncoding is an option to set response-content-encoding param
+func ResponseContentEncoding(value string) Option {
+ return addParam("response-content-encoding", value)
+}
+
+// Process is an option to set x-oss-process param
+func Process(value string) Option {
+ return addParam("x-oss-process", value)
+}
+
+// TrafficLimitParam is a option to set x-oss-traffic-limit
+func TrafficLimitParam(value int64) Option {
+ return addParam("x-oss-traffic-limit", strconv.FormatInt(value, 10))
+}
+
+// SetHeader Allow users to set personalized http headers
+func SetHeader(key string, value interface{}) Option {
+ return setHeader(key, value)
+}
+
+// AddParam Allow users to set personalized http params
+func AddParam(key string, value interface{}) Option {
+ return addParam(key, value)
+}
+
+func setHeader(key string, value interface{}) Option {
+ return func(params map[string]optionValue) error {
+ if value == nil {
+ return nil
+ }
+ params[key] = optionValue{value, optionHTTP}
+ return nil
+ }
+}
+
+func addParam(key string, value interface{}) Option {
+ return func(params map[string]optionValue) error {
+ if value == nil {
+ return nil
+ }
+ params[key] = optionValue{value, optionParam}
+ return nil
+ }
+}
+
+func addArg(key string, value interface{}) Option {
+ return func(params map[string]optionValue) error {
+ if value == nil {
+ return nil
+ }
+ params[key] = optionValue{value, optionArg}
+ return nil
+ }
+}
+
+func handleOptions(headers map[string]string, options []Option) error {
+ params := map[string]optionValue{}
+ for _, option := range options {
+ if option != nil {
+ if err := option(params); err != nil {
+ return err
+ }
+ }
+ }
+
+ for k, v := range params {
+ if v.Type == optionHTTP {
+ headers[k] = v.Value.(string)
+ }
+ }
+ return nil
+}
+
+func GetRawParams(options []Option) (map[string]interface{}, error) {
+ // Option
+ params := map[string]optionValue{}
+ for _, option := range options {
+ if option != nil {
+ if err := option(params); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ paramsm := map[string]interface{}{}
+ // Serialize
+ for k, v := range params {
+ if v.Type == optionParam {
+ vs := params[k]
+ paramsm[k] = vs.Value.(string)
+ }
+ }
+
+ return paramsm, nil
+}
+
+func FindOption(options []Option, param string, defaultVal interface{}) (interface{}, error) {
+ params := map[string]optionValue{}
+ for _, option := range options {
+ if option != nil {
+ if err := option(params); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ if val, ok := params[param]; ok {
+ return val.Value, nil
+ }
+ return defaultVal, nil
+}
+
+func IsOptionSet(options []Option, option string) (bool, interface{}, error) {
+ params := map[string]optionValue{}
+ for _, option := range options {
+ if option != nil {
+ if err := option(params); err != nil {
+ return false, nil, err
+ }
+ }
+ }
+
+ if val, ok := params[option]; ok {
+ return true, val.Value, nil
+ }
+ return false, nil, nil
+}
+
+func DeleteOption(options []Option, strKey string) []Option {
+ var outOption []Option
+ params := map[string]optionValue{}
+ for _, option := range options {
+ if option != nil {
+ option(params)
+ _, exist := params[strKey]
+ if !exist {
+ outOption = append(outOption, option)
+ } else {
+ delete(params, strKey)
+ }
+ }
+ }
+ return outOption
+}
+
+func GetRequestId(header http.Header) string {
+ return header.Get("x-oss-request-id")
+}
+
+func GetVersionId(header http.Header) string {
+ return header.Get("x-oss-version-id")
+}
+
+func GetCopySrcVersionId(header http.Header) string {
+ return header.Get("x-oss-copy-source-version-id")
+}
+
+func GetDeleteMark(header http.Header) bool {
+ value := header.Get("x-oss-delete-marker")
+ if strings.ToUpper(value) == "TRUE" {
+ return true
+ }
+ return false
+}
+
+func GetQosDelayTime(header http.Header) string {
+ return header.Get("x-oss-qos-delay-time")
+}
+
+// ForbidOverWrite is an option to set X-Oss-Forbid-Overwrite
+func AllowSameActionOverLap(enabled bool) Option {
+ if enabled {
+ return setHeader(HTTPHeaderAllowSameActionOverLap, "true")
+ } else {
+ return setHeader(HTTPHeaderAllowSameActionOverLap, "false")
+ }
+}
+
+func GetCallbackBody(options []Option, resp *Response, callbackSet bool) error {
+ var err error
+
+ // get response body
+ if callbackSet {
+ err = setBody(options, resp)
+ } else {
+ callback, _ := FindOption(options, HTTPHeaderOssCallback, nil)
+ if callback != nil {
+ err = setBody(options, resp)
+ }
+ }
+ return err
+}
+
+func setBody(options []Option, resp *Response) error {
+ respBody, _ := FindOption(options, responseBody, nil)
+ if respBody != nil && resp != nil {
+ pRespBody := respBody.(*[]byte)
+ pBody, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return err
+ }
+ if pBody != nil {
+ *pRespBody = pBody
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/progress.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/progress.go
new file mode 100644
index 00000000000..1b6535ba5ed
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/progress.go
@@ -0,0 +1,116 @@
+package oss
+
+import (
+ "io"
+)
+
+// ProgressEventType defines transfer progress event type
+type ProgressEventType int
+
+const (
+ // TransferStartedEvent transfer started, set TotalBytes
+ TransferStartedEvent ProgressEventType = 1 + iota
+ // TransferDataEvent transfer data, set ConsumedBytes and 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)
+ }
+}
+
+type readerTracker struct {
+ completedBytes int64
+}
+
+type teeReader struct {
+ reader io.Reader
+ writer io.Writer
+ listener ProgressListener
+ consumedBytes int64
+ totalBytes int64
+ tracker *readerTracker
+}
+
+// TeeReader returns a Reader that writes to w what it reads from r.
+// All reads from r performed through it are matched with
+// corresponding writes to w. There is no internal buffering -
+// the write must complete before the read completes.
+// Any error encountered while writing is reported as a read error.
+func TeeReader(reader io.Reader, writer io.Writer, totalBytes int64, listener ProgressListener, tracker *readerTracker) io.ReadCloser {
+ return &teeReader{
+ reader: reader,
+ writer: writer,
+ listener: listener,
+ consumedBytes: 0,
+ totalBytes: totalBytes,
+ tracker: tracker,
+ }
+}
+
+func (t *teeReader) Read(p []byte) (n int, err error) {
+ n, err = t.reader.Read(p)
+
+ // Read encountered error
+ if err != nil && err != io.EOF {
+ event := newProgressEvent(TransferFailedEvent, t.consumedBytes, t.totalBytes, 0)
+ publishProgress(t.listener, event)
+ }
+
+ if n > 0 {
+ t.consumedBytes += int64(n)
+ // CRC
+ if t.writer != nil {
+ if n, err := t.writer.Write(p[:n]); err != nil {
+ return n, err
+ }
+ }
+ // Progress
+ if t.listener != nil {
+ event := newProgressEvent(TransferDataEvent, t.consumedBytes, t.totalBytes, int64(n))
+ publishProgress(t.listener, event)
+ }
+ // Track
+ if t.tracker != nil {
+ t.tracker.completedBytes = t.consumedBytes
+ }
+ }
+
+ return
+}
+
+func (t *teeReader) Close() error {
+ if rc, ok := t.reader.(io.ReadCloser); ok {
+ return rc.Close()
+ }
+ return nil
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/redirect_1_6.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/redirect_1_6.go
new file mode 100644
index 00000000000..ea17d5bd5ce
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/redirect_1_6.go
@@ -0,0 +1,12 @@
+//go:build !go1.7
+// +build !go1.7
+
+package oss
+
+import "net/http"
+
+// http.ErrUseLastResponse only is defined go1.7 onward
+
+func disableHTTPRedirect(client *http.Client) {
+
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/redirect_1_7.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/redirect_1_7.go
new file mode 100644
index 00000000000..5d1442dd184
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/redirect_1_7.go
@@ -0,0 +1,13 @@
+//go:build go1.7
+// +build go1.7
+
+package oss
+
+import "net/http"
+
+// http.ErrUseLastResponse only is defined go1.7 onward
+func disableHTTPRedirect(client *http.Client) {
+ client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
+ return http.ErrUseLastResponse
+ }
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/select_object.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/select_object.go
new file mode 100644
index 00000000000..2e0da4637f1
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/select_object.go
@@ -0,0 +1,197 @@
+package oss
+
+import (
+ "bytes"
+ "encoding/xml"
+ "hash/crc32"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "strings"
+)
+
+// CreateSelectCsvObjectMeta is Creating csv object meta
+//
+// key the object key.
+// csvMeta the csv file meta
+// options the options for create csv Meta of the object.
+//
+// MetaEndFrameCSV the csv file meta info
+// error it's nil if no error, otherwise it's an error object.
+//
+func (bucket Bucket) CreateSelectCsvObjectMeta(key string, csvMeta CsvMetaRequest, options ...Option) (MetaEndFrameCSV, error) {
+ var endFrame MetaEndFrameCSV
+ params := map[string]interface{}{}
+ params["x-oss-process"] = "csv/meta"
+
+ csvMeta.encodeBase64()
+ bs, err := xml.Marshal(csvMeta)
+ if err != nil {
+ return endFrame, err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ resp, err := bucket.DoPostSelectObject(key, params, buffer, options...)
+ if err != nil {
+ return endFrame, err
+ }
+ defer resp.Body.Close()
+
+ _, err = ioutil.ReadAll(resp)
+
+ return resp.Frame.MetaEndFrameCSV, err
+}
+
+// CreateSelectJsonObjectMeta is Creating json object meta
+//
+// key the object key.
+// csvMeta the json file meta
+// options the options for create json Meta of the object.
+//
+// MetaEndFrameJSON the json file meta info
+// error it's nil if no error, otherwise it's an error object.
+//
+func (bucket Bucket) CreateSelectJsonObjectMeta(key string, jsonMeta JsonMetaRequest, options ...Option) (MetaEndFrameJSON, error) {
+ var endFrame MetaEndFrameJSON
+ params := map[string]interface{}{}
+ params["x-oss-process"] = "json/meta"
+
+ bs, err := xml.Marshal(jsonMeta)
+ if err != nil {
+ return endFrame, err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ resp, err := bucket.DoPostSelectObject(key, params, buffer, options...)
+ if err != nil {
+ return endFrame, err
+ }
+ defer resp.Body.Close()
+
+ _, err = ioutil.ReadAll(resp)
+
+ return resp.Frame.MetaEndFrameJSON, err
+}
+
+// SelectObject is the select object api, approve csv and json file.
+//
+// key the object key.
+// selectReq the request data for select object
+// options the options for select file of the object.
+//
+// o.ReadCloser reader instance for reading data from response. It must be called close() after the usage and only valid when error is nil.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (bucket Bucket) SelectObject(key string, selectReq SelectRequest, options ...Option) (io.ReadCloser, error) {
+ params := map[string]interface{}{}
+ if selectReq.InputSerializationSelect.JsonBodyInput.JsonIsEmpty() {
+ params["x-oss-process"] = "csv/select" // default select csv file
+ } else {
+ params["x-oss-process"] = "json/select"
+ }
+ selectReq.encodeBase64()
+ bs, err := xml.Marshal(selectReq)
+ if err != nil {
+ return nil, err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+ resp, err := bucket.DoPostSelectObject(key, params, buffer, options...)
+ if err != nil {
+ return nil, err
+ }
+ if selectReq.OutputSerializationSelect.EnablePayloadCrc != nil && *selectReq.OutputSerializationSelect.EnablePayloadCrc == true {
+ resp.Frame.EnablePayloadCrc = true
+ }
+ resp.Frame.OutputRawData = strings.ToUpper(resp.Headers.Get("x-oss-select-output-raw")) == "TRUE"
+
+ return resp, err
+}
+
+// DoPostSelectObject is the SelectObject/CreateMeta api, approve csv and json file.
+//
+// key the object key.
+// params the resource of oss approve csv/meta, json/meta, csv/select, json/select.
+// buf the request data trans to buffer.
+// options the options for select file of the object.
+//
+// SelectObjectResponse the response of select object.
+// error it's nil if no error, otherwise it's an error object.
+//
+func (bucket Bucket) DoPostSelectObject(key string, params map[string]interface{}, buf *bytes.Buffer, options ...Option) (*SelectObjectResponse, error) {
+ resp, err := bucket.do("POST", key, params, options, buf, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ result := &SelectObjectResponse{
+ Body: resp.Body,
+ StatusCode: resp.StatusCode,
+ Frame: SelectObjectResult{},
+ }
+ result.Headers = resp.Headers
+ // result.Frame = SelectObjectResult{}
+ result.ReadTimeOut = bucket.GetConfig().Timeout
+
+ // Progress
+ listener := GetProgressListener(options)
+
+ // CRC32
+ crcCalc := crc32.NewIEEE()
+ result.WriterForCheckCrc32 = crcCalc
+ result.Body = TeeReader(resp.Body, nil, 0, listener, nil)
+
+ err = CheckRespCode(resp.StatusCode, []int{http.StatusPartialContent, http.StatusOK})
+
+ return result, err
+}
+
+// SelectObjectIntoFile is the selectObject to file api
+//
+// key the object key.
+// fileName saving file's name to localstation.
+// selectReq the request data for select object
+// options the options for select file of the object.
+//
+// error it's nil if no error, otherwise it's an error object.
+//
+func (bucket Bucket) SelectObjectIntoFile(key, fileName string, selectReq SelectRequest, options ...Option) error {
+ tempFilePath := fileName + TempFileSuffix
+
+ params := map[string]interface{}{}
+ if selectReq.InputSerializationSelect.JsonBodyInput.JsonIsEmpty() {
+ params["x-oss-process"] = "csv/select" // default select csv file
+ } else {
+ params["x-oss-process"] = "json/select"
+ }
+ selectReq.encodeBase64()
+ bs, err := xml.Marshal(selectReq)
+ if err != nil {
+ return err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+ resp, err := bucket.DoPostSelectObject(key, params, buffer, options...)
+ if err != nil {
+ return err
+ }
+ defer resp.Close()
+
+ // If the local file does not exist, create a new one. If it exists, overwrite it.
+ fd, err := os.OpenFile(tempFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, FilePermMode)
+ if err != nil {
+ return err
+ }
+
+ // Copy the data to the local file path.
+ _, err = io.Copy(fd, resp)
+ fd.Close()
+ if err != nil {
+ return err
+ }
+
+ return os.Rename(tempFilePath, fileName)
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/select_object_type.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/select_object_type.go
new file mode 100644
index 00000000000..75a454555d1
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/select_object_type.go
@@ -0,0 +1,365 @@
+package oss
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "hash"
+ "hash/crc32"
+ "io"
+ "net/http"
+ "time"
+)
+
+// The adapter class for Select object's response.
+// The response consists of frames. Each frame has the following format:
+
+// Type | Payload Length | Header Checksum | Payload | Payload Checksum
+
+// |<4-->| <--4 bytes------><---4 bytes-------><-n/a-----><--4 bytes--------->
+// And we have three kind of frames.
+// Data Frame:
+// Type:8388609
+// Payload: Offset | Data
+// <-8 bytes>
+
+// Continuous Frame
+// Type:8388612
+// Payload: Offset (8-bytes)
+
+// End Frame
+// Type:8388613
+// Payload: Offset | total scanned bytes | http status code | error message
+// <-- 8bytes--><-----8 bytes--------><---4 bytes-------><---variabe--->
+
+// SelectObjectResponse defines HTTP response from OSS SelectObject
+type SelectObjectResponse struct {
+ StatusCode int
+ Headers http.Header
+ Body io.ReadCloser
+ Frame SelectObjectResult
+ ReadTimeOut uint
+ ClientCRC32 uint32
+ ServerCRC32 uint32
+ WriterForCheckCrc32 hash.Hash32
+ Finish bool
+}
+
+func (sr *SelectObjectResponse) Read(p []byte) (n int, err error) {
+ n, err = sr.readFrames(p)
+ return
+}
+
+// Close http reponse body
+func (sr *SelectObjectResponse) Close() error {
+ return sr.Body.Close()
+}
+
+// PostSelectResult is the request of SelectObject
+type PostSelectResult struct {
+ Response *SelectObjectResponse
+}
+
+// readFrames is read Frame
+func (sr *SelectObjectResponse) readFrames(p []byte) (int, error) {
+ var nn int
+ var err error
+ var checkValid bool
+ if sr.Frame.OutputRawData == true {
+ nn, err = sr.Body.Read(p)
+ return nn, err
+ }
+
+ if sr.Finish {
+ return 0, io.EOF
+ }
+
+ for {
+ // if this Frame is Readed, then not reading Header
+ if sr.Frame.OpenLine != true {
+ err = sr.analysisHeader()
+ if err != nil {
+ return nn, err
+ }
+ }
+
+ if sr.Frame.FrameType == DataFrameType {
+ n, err := sr.analysisData(p[nn:])
+ if err != nil {
+ return nn, err
+ }
+ nn += n
+
+ // if this Frame is readed all data, then empty the Frame to read it with next frame
+ if sr.Frame.ConsumedBytesLength == sr.Frame.PayloadLength-8 {
+ checkValid, err = sr.checkPayloadSum()
+ if err != nil || !checkValid {
+ return nn, fmt.Errorf("%s", err.Error())
+ }
+ sr.emptyFrame()
+ }
+
+ if nn == len(p) {
+ return nn, nil
+ }
+ } else if sr.Frame.FrameType == ContinuousFrameType {
+ checkValid, err = sr.checkPayloadSum()
+ if err != nil || !checkValid {
+ return nn, fmt.Errorf("%s", err.Error())
+ }
+ sr.Frame.OpenLine = false
+ } else if sr.Frame.FrameType == EndFrameType {
+ err = sr.analysisEndFrame()
+ if err != nil {
+ return nn, err
+ }
+ checkValid, err = sr.checkPayloadSum()
+ if checkValid {
+ sr.Finish = true
+ }
+ return nn, err
+ } else if sr.Frame.FrameType == MetaEndFrameCSVType {
+ err = sr.analysisMetaEndFrameCSV()
+ if err != nil {
+ return nn, err
+ }
+ checkValid, err = sr.checkPayloadSum()
+ if checkValid {
+ sr.Finish = true
+ }
+ return nn, err
+ } else if sr.Frame.FrameType == MetaEndFrameJSONType {
+ err = sr.analysisMetaEndFrameJSON()
+ if err != nil {
+ return nn, err
+ }
+ checkValid, err = sr.checkPayloadSum()
+ if checkValid {
+ sr.Finish = true
+ }
+ return nn, err
+ }
+ }
+ return nn, nil
+}
+
+type chanReadIO struct {
+ readLen int
+ err error
+}
+
+func (sr *SelectObjectResponse) readLen(p []byte, timeOut time.Duration) (int, error) {
+ r := sr.Body
+ ch := make(chan chanReadIO, 1)
+ defer close(ch)
+ go func(p []byte) {
+ var needReadLength int
+ readChan := chanReadIO{}
+ needReadLength = len(p)
+ for {
+ n, err := r.Read(p[readChan.readLen:needReadLength])
+ readChan.readLen += n
+ if err != nil {
+ readChan.err = err
+ ch <- readChan
+ return
+ }
+
+ if readChan.readLen == needReadLength {
+ break
+ }
+ }
+ ch <- readChan
+ }(p)
+
+ select {
+ case <-time.After(time.Second * timeOut):
+ return 0, fmt.Errorf("requestId: %s, readLen timeout, timeout is %d(second),need read:%d", sr.Headers.Get(HTTPHeaderOssRequestID), timeOut, len(p))
+ case result := <-ch:
+ return result.readLen, result.err
+ }
+}
+
+// analysisHeader is reading selectObject response body's header
+func (sr *SelectObjectResponse) analysisHeader() error {
+ headFrameByte := make([]byte, 20)
+ _, err := sr.readLen(headFrameByte, time.Duration(sr.ReadTimeOut))
+ if err != nil {
+ return fmt.Errorf("requestId: %s, Read response frame header failure,err:%s", sr.Headers.Get(HTTPHeaderOssRequestID), err.Error())
+ }
+
+ frameTypeByte := headFrameByte[0:4]
+ sr.Frame.Version = frameTypeByte[0]
+ frameTypeByte[0] = 0
+ bytesToInt(frameTypeByte, &sr.Frame.FrameType)
+
+ if sr.Frame.FrameType != DataFrameType && sr.Frame.FrameType != ContinuousFrameType &&
+ sr.Frame.FrameType != EndFrameType && sr.Frame.FrameType != MetaEndFrameCSVType && sr.Frame.FrameType != MetaEndFrameJSONType {
+ return fmt.Errorf("requestId: %s, Unexpected frame type: %d", sr.Headers.Get(HTTPHeaderOssRequestID), sr.Frame.FrameType)
+ }
+
+ payloadLengthByte := headFrameByte[4:8]
+ bytesToInt(payloadLengthByte, &sr.Frame.PayloadLength)
+ headCheckSumByte := headFrameByte[8:12]
+ bytesToInt(headCheckSumByte, &sr.Frame.HeaderCheckSum)
+ byteOffset := headFrameByte[12:20]
+ bytesToInt(byteOffset, &sr.Frame.Offset)
+ sr.Frame.OpenLine = true
+
+ err = sr.writerCheckCrc32(byteOffset)
+ return err
+}
+
+// analysisData is reading the DataFrameType data of selectObject response body
+func (sr *SelectObjectResponse) analysisData(p []byte) (int, error) {
+ var needReadLength int32
+ lenP := int32(len(p))
+ restByteLength := sr.Frame.PayloadLength - 8 - sr.Frame.ConsumedBytesLength
+ if lenP <= restByteLength {
+ needReadLength = lenP
+ } else {
+ needReadLength = restByteLength
+ }
+ n, err := sr.readLen(p[:needReadLength], time.Duration(sr.ReadTimeOut))
+ if err != nil {
+ return n, fmt.Errorf("read frame data error,%s", err.Error())
+ }
+ sr.Frame.ConsumedBytesLength += int32(n)
+ err = sr.writerCheckCrc32(p[:n])
+ return n, err
+}
+
+// analysisEndFrame is reading the EndFrameType data of selectObject response body
+func (sr *SelectObjectResponse) analysisEndFrame() error {
+ var eF EndFrame
+ payLoadBytes := make([]byte, sr.Frame.PayloadLength-8)
+ _, err := sr.readLen(payLoadBytes, time.Duration(sr.ReadTimeOut))
+ if err != nil {
+ return fmt.Errorf("read end frame error:%s", err.Error())
+ }
+ bytesToInt(payLoadBytes[0:8], &eF.TotalScanned)
+ bytesToInt(payLoadBytes[8:12], &eF.HTTPStatusCode)
+ errMsgLength := sr.Frame.PayloadLength - 20
+ eF.ErrorMsg = string(payLoadBytes[12 : errMsgLength+12])
+ sr.Frame.EndFrame.TotalScanned = eF.TotalScanned
+ sr.Frame.EndFrame.HTTPStatusCode = eF.HTTPStatusCode
+ sr.Frame.EndFrame.ErrorMsg = eF.ErrorMsg
+ err = sr.writerCheckCrc32(payLoadBytes)
+ return err
+}
+
+// analysisMetaEndFrameCSV is reading the MetaEndFrameCSVType data of selectObject response body
+func (sr *SelectObjectResponse) analysisMetaEndFrameCSV() error {
+ var mCF MetaEndFrameCSV
+ payLoadBytes := make([]byte, sr.Frame.PayloadLength-8)
+ _, err := sr.readLen(payLoadBytes, time.Duration(sr.ReadTimeOut))
+ if err != nil {
+ return fmt.Errorf("read meta end csv frame error:%s", err.Error())
+ }
+
+ bytesToInt(payLoadBytes[0:8], &mCF.TotalScanned)
+ bytesToInt(payLoadBytes[8:12], &mCF.Status)
+ bytesToInt(payLoadBytes[12:16], &mCF.SplitsCount)
+ bytesToInt(payLoadBytes[16:24], &mCF.RowsCount)
+ bytesToInt(payLoadBytes[24:28], &mCF.ColumnsCount)
+ errMsgLength := sr.Frame.PayloadLength - 36
+ mCF.ErrorMsg = string(payLoadBytes[28 : errMsgLength+28])
+ sr.Frame.MetaEndFrameCSV.ErrorMsg = mCF.ErrorMsg
+ sr.Frame.MetaEndFrameCSV.TotalScanned = mCF.TotalScanned
+ sr.Frame.MetaEndFrameCSV.Status = mCF.Status
+ sr.Frame.MetaEndFrameCSV.SplitsCount = mCF.SplitsCount
+ sr.Frame.MetaEndFrameCSV.RowsCount = mCF.RowsCount
+ sr.Frame.MetaEndFrameCSV.ColumnsCount = mCF.ColumnsCount
+ err = sr.writerCheckCrc32(payLoadBytes)
+ return err
+}
+
+// analysisMetaEndFrameJSON is reading the MetaEndFrameJSONType data of selectObject response body
+func (sr *SelectObjectResponse) analysisMetaEndFrameJSON() error {
+ var mJF MetaEndFrameJSON
+ payLoadBytes := make([]byte, sr.Frame.PayloadLength-8)
+ _, err := sr.readLen(payLoadBytes, time.Duration(sr.ReadTimeOut))
+ if err != nil {
+ return fmt.Errorf("read meta end json frame error:%s", err.Error())
+ }
+
+ bytesToInt(payLoadBytes[0:8], &mJF.TotalScanned)
+ bytesToInt(payLoadBytes[8:12], &mJF.Status)
+ bytesToInt(payLoadBytes[12:16], &mJF.SplitsCount)
+ bytesToInt(payLoadBytes[16:24], &mJF.RowsCount)
+ errMsgLength := sr.Frame.PayloadLength - 32
+ mJF.ErrorMsg = string(payLoadBytes[24 : errMsgLength+24])
+ sr.Frame.MetaEndFrameJSON.ErrorMsg = mJF.ErrorMsg
+ sr.Frame.MetaEndFrameJSON.TotalScanned = mJF.TotalScanned
+ sr.Frame.MetaEndFrameJSON.Status = mJF.Status
+ sr.Frame.MetaEndFrameJSON.SplitsCount = mJF.SplitsCount
+ sr.Frame.MetaEndFrameJSON.RowsCount = mJF.RowsCount
+
+ err = sr.writerCheckCrc32(payLoadBytes)
+ return err
+}
+
+func (sr *SelectObjectResponse) checkPayloadSum() (bool, error) {
+ payLoadChecksumByte := make([]byte, 4)
+ n, err := sr.readLen(payLoadChecksumByte, time.Duration(sr.ReadTimeOut))
+ if n == 4 {
+ bytesToInt(payLoadChecksumByte, &sr.Frame.PayloadChecksum)
+ sr.ServerCRC32 = sr.Frame.PayloadChecksum
+ sr.ClientCRC32 = sr.WriterForCheckCrc32.Sum32()
+ if sr.Frame.EnablePayloadCrc == true && sr.ServerCRC32 != 0 && sr.ServerCRC32 != sr.ClientCRC32 {
+ return false, fmt.Errorf("RequestId: %s, Unexpected frame type: %d, client %d but server %d",
+ sr.Headers.Get(HTTPHeaderOssRequestID), sr.Frame.FrameType, sr.ClientCRC32, sr.ServerCRC32)
+ }
+ return true, err
+ }
+ return false, fmt.Errorf("RequestId:%s, read checksum error:%s", sr.Headers.Get(HTTPHeaderOssRequestID), err.Error())
+}
+
+func (sr *SelectObjectResponse) writerCheckCrc32(p []byte) (err error) {
+ err = nil
+ if sr.Frame.EnablePayloadCrc == true {
+ _, err = sr.WriterForCheckCrc32.Write(p)
+ }
+ return err
+}
+
+// emptyFrame is emptying SelectObjectResponse Frame information
+func (sr *SelectObjectResponse) emptyFrame() {
+ crcCalc := crc32.NewIEEE()
+ sr.WriterForCheckCrc32 = crcCalc
+ sr.Finish = false
+
+ sr.Frame.ConsumedBytesLength = 0
+ sr.Frame.OpenLine = false
+ sr.Frame.Version = byte(0)
+ sr.Frame.FrameType = 0
+ sr.Frame.PayloadLength = 0
+ sr.Frame.HeaderCheckSum = 0
+ sr.Frame.Offset = 0
+ sr.Frame.Data = ""
+
+ sr.Frame.EndFrame.TotalScanned = 0
+ sr.Frame.EndFrame.HTTPStatusCode = 0
+ sr.Frame.EndFrame.ErrorMsg = ""
+
+ sr.Frame.MetaEndFrameCSV.TotalScanned = 0
+ sr.Frame.MetaEndFrameCSV.Status = 0
+ sr.Frame.MetaEndFrameCSV.SplitsCount = 0
+ sr.Frame.MetaEndFrameCSV.RowsCount = 0
+ sr.Frame.MetaEndFrameCSV.ColumnsCount = 0
+ sr.Frame.MetaEndFrameCSV.ErrorMsg = ""
+
+ sr.Frame.MetaEndFrameJSON.TotalScanned = 0
+ sr.Frame.MetaEndFrameJSON.Status = 0
+ sr.Frame.MetaEndFrameJSON.SplitsCount = 0
+ sr.Frame.MetaEndFrameJSON.RowsCount = 0
+ sr.Frame.MetaEndFrameJSON.ErrorMsg = ""
+
+ sr.Frame.PayloadChecksum = 0
+}
+
+// bytesToInt byte's array trans to int
+func bytesToInt(b []byte, ret interface{}) {
+ binBuf := bytes.NewBuffer(b)
+ binary.Read(binBuf, binary.BigEndian, ret)
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/transport_1_6.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/transport_1_6.go
new file mode 100644
index 00000000000..08a83a00178
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/transport_1_6.go
@@ -0,0 +1,42 @@
+//go:build !go1.7
+// +build !go1.7
+
+package oss
+
+import (
+ "crypto/tls"
+ "net"
+ "net/http"
+ "time"
+)
+
+func newTransport(conn *Conn, config *Config) *http.Transport {
+ httpTimeOut := conn.config.HTTPTimeout
+ httpMaxConns := conn.config.HTTPMaxConns
+ // New Transport
+ transport := &http.Transport{
+ Dial: func(netw, addr string) (net.Conn, error) {
+ d := net.Dialer{
+ Timeout: httpTimeOut.ConnectTimeout,
+ KeepAlive: 30 * time.Second,
+ }
+ if config.LocalAddr != nil {
+ d.LocalAddr = config.LocalAddr
+ }
+ conn, err := d.Dial(netw, addr)
+ if err != nil {
+ return nil, err
+ }
+ return newTimeoutConn(conn, httpTimeOut.ReadWriteTimeout, httpTimeOut.LongTimeout), nil
+ },
+ MaxIdleConnsPerHost: httpMaxConns.MaxIdleConnsPerHost,
+ ResponseHeaderTimeout: httpTimeOut.HeaderTimeout,
+ }
+
+ if config.InsecureSkipVerify {
+ transport.TLSClientConfig = &tls.Config{
+ InsecureSkipVerify: true,
+ }
+ }
+ return transport
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/transport_1_7.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/transport_1_7.go
new file mode 100644
index 00000000000..1acb84154d7
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/transport_1_7.go
@@ -0,0 +1,45 @@
+//go:build go1.7
+// +build go1.7
+
+package oss
+
+import (
+ "crypto/tls"
+ "net"
+ "net/http"
+ "time"
+)
+
+func newTransport(conn *Conn, config *Config) *http.Transport {
+ httpTimeOut := conn.config.HTTPTimeout
+ httpMaxConns := conn.config.HTTPMaxConns
+ // New Transport
+ transport := &http.Transport{
+ Dial: func(netw, addr string) (net.Conn, error) {
+ d := net.Dialer{
+ Timeout: httpTimeOut.ConnectTimeout,
+ KeepAlive: 30 * time.Second,
+ }
+ if config.LocalAddr != nil {
+ d.LocalAddr = config.LocalAddr
+ }
+ conn, err := d.Dial(netw, addr)
+ if err != nil {
+ return nil, err
+ }
+ return newTimeoutConn(conn, httpTimeOut.ReadWriteTimeout, httpTimeOut.LongTimeout), nil
+ },
+ MaxIdleConns: httpMaxConns.MaxIdleConns,
+ MaxIdleConnsPerHost: httpMaxConns.MaxIdleConnsPerHost,
+ MaxConnsPerHost: httpMaxConns.MaxConnsPerHost,
+ IdleConnTimeout: httpTimeOut.IdleConnTimeout,
+ ResponseHeaderTimeout: httpTimeOut.HeaderTimeout,
+ }
+
+ if config.InsecureSkipVerify {
+ transport.TLSClientConfig = &tls.Config{
+ InsecureSkipVerify: true,
+ }
+ }
+ return transport
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/type.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/type.go
new file mode 100644
index 00000000000..a704e3ceff8
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/type.go
@@ -0,0 +1,1695 @@
+package oss
+
+import (
+ "encoding/base64"
+ "encoding/xml"
+ "fmt"
+ "net/url"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// ListBucketsResult defines the result object from ListBuckets request
+type ListBucketsResult struct {
+ XMLName xml.Name `xml:"ListAllMyBucketsResult"`
+ Prefix string `xml:"Prefix"` // The prefix in this query
+ Marker string `xml:"Marker"` // The marker filter
+ MaxKeys int `xml:"MaxKeys"` // The max entry count to return. This information is returned when IsTruncated is true.
+ IsTruncated bool `xml:"IsTruncated"` // Flag true means there's remaining buckets to return.
+ NextMarker string `xml:"NextMarker"` // The marker filter for the next list call
+ Owner Owner `xml:"Owner"` // The owner information
+ Buckets []BucketProperties `xml:"Buckets>Bucket"` // The bucket list
+}
+
+// BucketProperties defines bucket properties
+type BucketProperties struct {
+ XMLName xml.Name `xml:"Bucket"`
+ Name string `xml:"Name"` // Bucket name
+ Location string `xml:"Location"` // Bucket datacenter
+ CreationDate time.Time `xml:"CreationDate"` // Bucket create time
+ StorageClass string `xml:"StorageClass"` // Bucket storage class
+ Region string `xml:"Region"` // Bucket region
+}
+
+// ListCloudBoxResult defines the result object from ListBuckets request
+type ListCloudBoxResult struct {
+ XMLName xml.Name `xml:"ListCloudBoxResult"`
+ Prefix string `xml:"Prefix"` // The prefix in this query
+ Marker string `xml:"Marker"` // The marker filter
+ MaxKeys int `xml:"MaxKeys"` // The max entry count to return. This information is returned when IsTruncated is true.
+ IsTruncated bool `xml:"IsTruncated"` // Flag true means there's remaining cloudboxes to return.
+ NextMarker string `xml:"NextMarker"` // The marker filter for the next list call
+ Owner string `xml:"Owner>DisplayName"` // The owner information
+ CloudBoxes []CloudBoxProperties `xml:"CloudBoxes>CloudBox"` // The cloudbox list
+}
+
+// CloudBoxProperties defines cloudbox properties
+type CloudBoxProperties struct {
+ XMLName xml.Name `xml:"CloudBox"`
+ ID string `xml:"ID"`
+ Name string `xml:"Name"`
+ Region string `xml:"Region"`
+ ControlEndpoint string `xml:"ControlEndpoint"`
+ DataEndpoint string `xml:"DataEndpoint"`
+}
+
+// GetBucketACLResult defines GetBucketACL request's result
+type GetBucketACLResult struct {
+ XMLName xml.Name `xml:"AccessControlPolicy"`
+ ACL string `xml:"AccessControlList>Grant"` // Bucket ACL
+ Owner Owner `xml:"Owner"` // Bucket owner
+}
+
+// LifecycleConfiguration is the Bucket Lifecycle configuration
+type LifecycleConfiguration struct {
+ XMLName xml.Name `xml:"LifecycleConfiguration"`
+ Rules []LifecycleRule `xml:"Rule"`
+}
+
+// LifecycleRule defines Lifecycle rules
+type LifecycleRule struct {
+ XMLName xml.Name `xml:"Rule"`
+ ID string `xml:"ID,omitempty"` // The rule ID
+ Prefix string `xml:"Prefix"` // The object key prefix
+ Status string `xml:"Status"` // The rule status (enabled or not)
+ Tags []Tag `xml:"Tag,omitempty"` // the tags property
+ Expiration *LifecycleExpiration `xml:"Expiration,omitempty"` // The expiration property
+ Transitions []LifecycleTransition `xml:"Transition,omitempty"` // The transition property
+ AbortMultipartUpload *LifecycleAbortMultipartUpload `xml:"AbortMultipartUpload,omitempty"` // The AbortMultipartUpload property
+ NonVersionExpiration *LifecycleVersionExpiration `xml:"NoncurrentVersionExpiration,omitempty"`
+ // Deprecated: Use NonVersionTransitions instead.
+ NonVersionTransition *LifecycleVersionTransition `xml:"-"` // NonVersionTransition is not suggested to use
+ NonVersionTransitions []LifecycleVersionTransition `xml:"NoncurrentVersionTransition,omitempty"`
+ Filter *LifecycleFilter `xml:Filter,omitempty` //condition parameter container of this exclusion rule
+}
+
+// LifecycleExpiration defines the rule's expiration property
+type LifecycleExpiration struct {
+ XMLName xml.Name `xml:"Expiration"`
+ Days int `xml:"Days,omitempty"` // Relative expiration time: The expiration time in days after the last modified time
+ Date string `xml:"Date,omitempty"` // Absolute expiration time: The expiration time in date, not recommended
+ CreatedBeforeDate string `xml:"CreatedBeforeDate,omitempty"` // objects created before the date will be expired
+ ExpiredObjectDeleteMarker *bool `xml:"ExpiredObjectDeleteMarker,omitempty"` // Specifies whether the expired delete tag is automatically deleted
+}
+
+// LifecycleTransition defines the rule's transition propery
+type LifecycleTransition struct {
+ XMLName xml.Name `xml:"Transition"`
+ Days int `xml:"Days,omitempty"` // Relative transition time: The transition time in days after the last modified time
+ CreatedBeforeDate string `xml:"CreatedBeforeDate,omitempty"` // objects created before the date will be expired
+ StorageClass StorageClassType `xml:"StorageClass,omitempty"` // Specifies the target storage type
+ IsAccessTime *bool `xml:"IsAccessTime,omitempty"` // access time
+ ReturnToStdWhenVisit *bool `xml:"ReturnToStdWhenVisit,omitempty"` // Return To Std When Visit
+ AllowSmallFile *bool `xml:AllowSmallFile,omitempty`
+}
+
+// LifecycleAbortMultipartUpload defines the rule's abort multipart upload propery
+type LifecycleAbortMultipartUpload struct {
+ XMLName xml.Name `xml:"AbortMultipartUpload"`
+ Days int `xml:"Days,omitempty"` // Relative expiration time: The expiration time in days after the last modified time
+ CreatedBeforeDate string `xml:"CreatedBeforeDate,omitempty"` // objects created before the date will be expired
+}
+
+// LifecycleVersionExpiration defines the rule's NoncurrentVersionExpiration propery
+type LifecycleVersionExpiration struct {
+ XMLName xml.Name `xml:"NoncurrentVersionExpiration"`
+ NoncurrentDays int `xml:"NoncurrentDays,omitempty"` // How many days after the Object becomes a non-current version
+}
+
+// LifecycleVersionTransition defines the rule's NoncurrentVersionTransition propery
+type LifecycleVersionTransition struct {
+ XMLName xml.Name `xml:"NoncurrentVersionTransition"`
+ NoncurrentDays int `xml:"NoncurrentDays,omitempty"` // How many days after the Object becomes a non-current version
+ StorageClass StorageClassType `xml:"StorageClass,omitempty"`
+ IsAccessTime *bool `xml:"IsAccessTime,omitempty"` // access time
+ ReturnToStdWhenVisit *bool `xml:"ReturnToStdWhenVisit,omitempty"` // Return To Std When Visit
+ AllowSmallFile *bool `xml:AllowSmallFile,omitempty`
+}
+
+// LifecycleFilter defines the rule's Filter propery
+type LifecycleFilter struct {
+ XMLName xml.Name `xml:"Filter"`
+ Not []LifecycleFilterNot `xml:"Not,omitempty"`
+ ObjectSizeGreaterThan *int64 `xml:"ObjectSizeGreaterThan,omitempty"`
+ ObjectSizeLessThan *int64 `xml:"ObjectSizeLessThan,omitempty"`
+}
+
+// LifecycleFilterNot defines the rule's Filter Not propery
+type LifecycleFilterNot struct {
+ XMLName xml.Name `xml:"Not"`
+ Prefix string `xml:"Prefix"` //Object prefix applicable to this exclusion rule
+ Tag *Tag `xml:"Tag,omitempty"` //the tags applicable to this exclusion rule
+}
+
+const iso8601DateFormat = "2006-01-02T15:04:05.000Z"
+const iso8601DateFormatSecond = "2006-01-02T15:04:05Z"
+
+// BuildLifecycleRuleByDays builds a lifecycle rule objects will expiration in days after the last modified time
+func BuildLifecycleRuleByDays(id, prefix string, status bool, days int) LifecycleRule {
+ var statusStr = "Enabled"
+ if !status {
+ statusStr = "Disabled"
+ }
+ return LifecycleRule{ID: id, Prefix: prefix, Status: statusStr,
+ Expiration: &LifecycleExpiration{Days: days}}
+}
+
+// BuildLifecycleRuleByDate builds a lifecycle rule objects will expiration in specified date
+func BuildLifecycleRuleByDate(id, prefix string, status bool, year, month, day int) LifecycleRule {
+ var statusStr = "Enabled"
+ if !status {
+ statusStr = "Disabled"
+ }
+ date := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC).Format(iso8601DateFormat)
+ return LifecycleRule{ID: id, Prefix: prefix, Status: statusStr,
+ Expiration: &LifecycleExpiration{Date: date}}
+}
+
+// ValidateLifecycleRule Determine if a lifecycle rule is valid, if it is invalid, it will return an error.
+func verifyLifecycleRules(rules []LifecycleRule) error {
+ if len(rules) == 0 {
+ return fmt.Errorf("invalid rules, the length of rules is zero")
+ }
+ for k, rule := range rules {
+ if rule.Status != "Enabled" && rule.Status != "Disabled" {
+ return fmt.Errorf("invalid rule, the value of status must be Enabled or Disabled")
+ }
+
+ abortMPU := rule.AbortMultipartUpload
+ if abortMPU != nil {
+ if (abortMPU.Days != 0 && abortMPU.CreatedBeforeDate != "") || (abortMPU.Days == 0 && abortMPU.CreatedBeforeDate == "") {
+ return fmt.Errorf("invalid abort multipart upload lifecycle, must be set one of CreatedBeforeDate and Days")
+ }
+ }
+
+ transitions := rule.Transitions
+ if len(transitions) > 0 {
+ for _, transition := range transitions {
+ if (transition.Days != 0 && transition.CreatedBeforeDate != "") || (transition.Days == 0 && transition.CreatedBeforeDate == "") {
+ return fmt.Errorf("invalid transition lifecycle, must be set one of CreatedBeforeDate and Days")
+ }
+ }
+ }
+
+ // NonVersionTransition is not suggested to use
+ // to keep compatible
+ if rule.NonVersionTransition != nil && len(rule.NonVersionTransitions) > 0 {
+ return fmt.Errorf("NonVersionTransition and NonVersionTransitions cannot both have values")
+ } else if rule.NonVersionTransition != nil {
+ rules[k].NonVersionTransitions = append(rules[k].NonVersionTransitions, *rule.NonVersionTransition)
+ }
+ }
+
+ return nil
+}
+
+// GetBucketLifecycleResult defines GetBucketLifecycle's result object
+type GetBucketLifecycleResult LifecycleConfiguration
+
+// RefererXML defines Referer configuration
+type RefererXML struct {
+ XMLName xml.Name `xml:"RefererConfiguration"`
+ AllowEmptyReferer bool `xml:"AllowEmptyReferer"` // Allow empty referrer
+ AllowTruncateQueryString *bool `xml:"AllowTruncateQueryString,omitempty"`
+ RefererList []string `xml:"RefererList>Referer"` // Referer whitelist
+ RefererBlacklist *RefererBlacklist `xml:"RefererBlacklist,omitempty"` // Referer blacklist
+}
+
+// GetBucketRefererResult defines result object for GetBucketReferer request
+type GetBucketRefererResult RefererXML
+
+type RefererBlacklist struct {
+ Referer []string `xml:"Referer,omitempty"`
+}
+
+// LoggingXML defines logging configuration
+type LoggingXML struct {
+ XMLName xml.Name `xml:"BucketLoggingStatus"`
+ LoggingEnabled LoggingEnabled `xml:"LoggingEnabled"` // The logging configuration information
+}
+
+type loggingXMLEmpty struct {
+ XMLName xml.Name `xml:"BucketLoggingStatus"`
+}
+
+// LoggingEnabled defines the logging configuration information
+type LoggingEnabled struct {
+ XMLName xml.Name `xml:"LoggingEnabled"`
+ TargetBucket string `xml:"TargetBucket"` // The bucket name for storing the log files
+ TargetPrefix string `xml:"TargetPrefix"` // The log file prefix
+}
+
+// GetBucketLoggingResult defines the result from GetBucketLogging request
+type GetBucketLoggingResult LoggingXML
+
+// WebsiteXML defines Website configuration
+type WebsiteXML struct {
+ XMLName xml.Name `xml:"WebsiteConfiguration"`
+ IndexDocument IndexDocument `xml:"IndexDocument,omitempty"` // The index page
+ ErrorDocument ErrorDocument `xml:"ErrorDocument,omitempty"` // The error page
+ RoutingRules []RoutingRule `xml:"RoutingRules>RoutingRule,omitempty"` // The routing Rule list
+}
+
+// IndexDocument defines the index page info
+type IndexDocument struct {
+ XMLName xml.Name `xml:"IndexDocument"`
+ Suffix string `xml:"Suffix"` // The file name for the index page
+}
+
+// ErrorDocument defines the 404 error page info
+type ErrorDocument struct {
+ XMLName xml.Name `xml:"ErrorDocument"`
+ Key string `xml:"Key"` // 404 error file name
+}
+
+// RoutingRule defines the routing rules
+type RoutingRule struct {
+ XMLName xml.Name `xml:"RoutingRule"`
+ RuleNumber int `xml:"RuleNumber,omitempty"` // The routing number
+ Condition Condition `xml:"Condition,omitempty"` // The routing condition
+ Redirect Redirect `xml:"Redirect,omitempty"` // The routing redirect
+
+}
+
+// Condition defines codition in the RoutingRule
+type Condition struct {
+ XMLName xml.Name `xml:"Condition"`
+ KeyPrefixEquals string `xml:"KeyPrefixEquals,omitempty"` // Matching objcet prefix
+ HTTPErrorCodeReturnedEquals int `xml:"HttpErrorCodeReturnedEquals,omitempty"` // The rule is for Accessing to the specified object
+ IncludeHeader []IncludeHeader `xml:"IncludeHeader"` // The rule is for request which include header
+}
+
+// IncludeHeader defines includeHeader in the RoutingRule's Condition
+type IncludeHeader struct {
+ XMLName xml.Name `xml:"IncludeHeader"`
+ Key string `xml:"Key,omitempty"` // The Include header key
+ Equals string `xml:"Equals,omitempty"` // The Include header value
+}
+
+// Redirect defines redirect in the RoutingRule
+type Redirect struct {
+ XMLName xml.Name `xml:"Redirect"`
+ RedirectType string `xml:"RedirectType,omitempty"` // The redirect type, it have Mirror,External,Internal,AliCDN
+ PassQueryString *bool `xml:"PassQueryString"` // Whether to send the specified request's parameters, true or false
+ MirrorURL string `xml:"MirrorURL,omitempty"` // Mirror of the website address back to the source.
+ MirrorPassQueryString *bool `xml:"MirrorPassQueryString"` // To Mirror of the website Whether to send the specified request's parameters, true or false
+ MirrorFollowRedirect *bool `xml:"MirrorFollowRedirect"` // Redirect the location, if the mirror return 3XX
+ MirrorCheckMd5 *bool `xml:"MirrorCheckMd5"` // Check the mirror is MD5.
+ MirrorHeaders MirrorHeaders `xml:"MirrorHeaders,omitempty"` // Mirror headers
+ Protocol string `xml:"Protocol,omitempty"` // The redirect Protocol
+ HostName string `xml:"HostName,omitempty"` // The redirect HostName
+ ReplaceKeyPrefixWith string `xml:"ReplaceKeyPrefixWith,omitempty"` // object name'Prefix replace the value
+ HttpRedirectCode int `xml:"HttpRedirectCode,omitempty"` // THe redirect http code
+ ReplaceKeyWith string `xml:"ReplaceKeyWith,omitempty"` // object name replace the value
+}
+
+// MirrorHeaders defines MirrorHeaders in the Redirect
+type MirrorHeaders struct {
+ XMLName xml.Name `xml:"MirrorHeaders"`
+ PassAll *bool `xml:"PassAll"` // Penetrating all of headers to source website.
+ Pass []string `xml:"Pass"` // Penetrating some of headers to source website.
+ Remove []string `xml:"Remove"` // Prohibit passthrough some of headers to source website
+ Set []MirrorHeaderSet `xml:"Set"` // Setting some of headers send to source website
+}
+
+// MirrorHeaderSet defines Set for Redirect's MirrorHeaders
+type MirrorHeaderSet struct {
+ XMLName xml.Name `xml:"Set"`
+ Key string `xml:"Key,omitempty"` // The mirror header key
+ Value string `xml:"Value,omitempty"` // The mirror header value
+}
+
+// GetBucketWebsiteResult defines the result from GetBucketWebsite request.
+type GetBucketWebsiteResult WebsiteXML
+
+// CORSXML defines CORS configuration
+type CORSXML struct {
+ XMLName xml.Name `xml:"CORSConfiguration"`
+ CORSRules []CORSRule `xml:"CORSRule"` // CORS rules
+ ResponseVary *bool `xml:"ResponseVary,omitempty"` // return Vary or not
+}
+
+// CORSRule defines CORS rules
+type CORSRule struct {
+ XMLName xml.Name `xml:"CORSRule"`
+ AllowedOrigin []string `xml:"AllowedOrigin"` // Allowed origins. By default it's wildcard '*'
+ AllowedMethod []string `xml:"AllowedMethod"` // Allowed methods
+ AllowedHeader []string `xml:"AllowedHeader"` // Allowed headers
+ ExposeHeader []string `xml:"ExposeHeader"` // Allowed response headers
+ MaxAgeSeconds int `xml:"MaxAgeSeconds"` // Max cache ages in seconds
+}
+
+// GetBucketCORSResult defines the result from GetBucketCORS request.
+type GetBucketCORSResult CORSXML
+
+// PutBucketCORS defines the PutBucketCORS config xml.
+type PutBucketCORS CORSXML
+
+// GetBucketInfoResult defines the result from GetBucketInfo request.
+type GetBucketInfoResult struct {
+ XMLName xml.Name `xml:"BucketInfo"`
+ BucketInfo BucketInfo `xml:"Bucket"`
+}
+
+// BucketInfo defines Bucket information
+type BucketInfo struct {
+ XMLName xml.Name `xml:"Bucket"`
+ Name string `xml:"Name"` // Bucket name
+ AccessMonitor string `xml:"AccessMonitor"` // Bucket Access Monitor
+ Location string `xml:"Location"` // Bucket datacenter
+ CreationDate time.Time `xml:"CreationDate"` // Bucket creation time
+ ExtranetEndpoint string `xml:"ExtranetEndpoint"` // Bucket external endpoint
+ IntranetEndpoint string `xml:"IntranetEndpoint"` // Bucket internal endpoint
+ ACL string `xml:"AccessControlList>Grant"` // Bucket ACL
+ RedundancyType string `xml:"DataRedundancyType"` // Bucket DataRedundancyType
+ Owner Owner `xml:"Owner"` // Bucket owner
+ StorageClass string `xml:"StorageClass"` // Bucket storage class
+ SseRule SSERule `xml:"ServerSideEncryptionRule"` // Bucket ServerSideEncryptionRule
+ Versioning string `xml:"Versioning"` // Bucket Versioning
+ TransferAcceleration string `xml:"TransferAcceleration"` // bucket TransferAcceleration
+ CrossRegionReplication string `xml:"CrossRegionReplication"` // bucket CrossRegionReplication
+}
+
+type SSERule struct {
+ XMLName xml.Name `xml:"ServerSideEncryptionRule"` // Bucket ServerSideEncryptionRule
+ KMSMasterKeyID string `xml:"KMSMasterKeyID,omitempty"` // Bucket KMSMasterKeyID
+ SSEAlgorithm string `xml:"SSEAlgorithm,omitempty"` // Bucket SSEAlgorithm
+ KMSDataEncryption string `xml:"KMSDataEncryption,omitempty"` //Bucket KMSDataEncryption
+}
+
+// ListObjectsResult defines the result from ListObjects request
+type ListObjectsResult struct {
+ XMLName xml.Name `xml:"ListBucketResult"`
+ Prefix string `xml:"Prefix"` // The object prefix
+ Marker string `xml:"Marker"` // The marker filter.
+ MaxKeys int `xml:"MaxKeys"` // Max keys to return
+ Delimiter string `xml:"Delimiter"` // The delimiter for grouping objects' name
+ IsTruncated bool `xml:"IsTruncated"` // Flag indicates if all results are returned (when it's false)
+ NextMarker string `xml:"NextMarker"` // The start point of the next query
+ Objects []ObjectProperties `xml:"Contents"` // Object list
+ CommonPrefixes []string `xml:"CommonPrefixes>Prefix"` // You can think of commonprefixes as "folders" whose names end with the delimiter
+}
+
+// ObjectProperties defines Objecct properties
+type ObjectProperties struct {
+ XMLName xml.Name `xml:"Contents"`
+ Key string `xml:"Key"` // Object key
+ Type string `xml:"Type"` // Object type
+ Size int64 `xml:"Size"` // Object size
+ ETag string `xml:"ETag"` // Object ETag
+ Owner Owner `xml:"Owner"` // Object owner information
+ LastModified time.Time `xml:"LastModified"` // Object last modified time
+ StorageClass string `xml:"StorageClass"` // Object storage class (Standard, IA, Archive)
+ RestoreInfo string `xml:"RestoreInfo,omitempty"` // Object restoreInfo
+}
+
+// ListObjectsResultV2 defines the result from ListObjectsV2 request
+type ListObjectsResultV2 struct {
+ XMLName xml.Name `xml:"ListBucketResult"`
+ Prefix string `xml:"Prefix"` // The object prefix
+ StartAfter string `xml:"StartAfter"` // the input StartAfter
+ ContinuationToken string `xml:"ContinuationToken"` // the input ContinuationToken
+ MaxKeys int `xml:"MaxKeys"` // Max keys to return
+ Delimiter string `xml:"Delimiter"` // The delimiter for grouping objects' name
+ IsTruncated bool `xml:"IsTruncated"` // Flag indicates if all results are returned (when it's false)
+ NextContinuationToken string `xml:"NextContinuationToken"` // The start point of the next NextContinuationToken
+ Objects []ObjectProperties `xml:"Contents"` // Object list
+ CommonPrefixes []string `xml:"CommonPrefixes>Prefix"` // You can think of commonprefixes as "folders" whose names end with the delimiter
+}
+
+// ListObjectVersionsResult defines the result from ListObjectVersions request
+type ListObjectVersionsResult struct {
+ XMLName xml.Name `xml:"ListVersionsResult"`
+ Name string `xml:"Name"` // The Bucket Name
+ Owner Owner `xml:"Owner"` // The owner of bucket
+ Prefix string `xml:"Prefix"` // The object prefix
+ KeyMarker string `xml:"KeyMarker"` // The start marker filter.
+ VersionIdMarker string `xml:"VersionIdMarker"` // The start VersionIdMarker filter.
+ MaxKeys int `xml:"MaxKeys"` // Max keys to return
+ Delimiter string `xml:"Delimiter"` // The delimiter for grouping objects' name
+ IsTruncated bool `xml:"IsTruncated"` // Flag indicates if all results are returned (when it's false)
+ NextKeyMarker string `xml:"NextKeyMarker"` // The start point of the next query
+ NextVersionIdMarker string `xml:"NextVersionIdMarker"` // The start point of the next query
+ CommonPrefixes []string `xml:"CommonPrefixes>Prefix"` // You can think of commonprefixes as "folders" whose names end with the delimiter
+ ObjectDeleteMarkers []ObjectDeleteMarkerProperties `xml:"DeleteMarker"` // DeleteMarker list
+ ObjectVersions []ObjectVersionProperties `xml:"Version"` // version list
+}
+
+type ObjectDeleteMarkerProperties struct {
+ XMLName xml.Name `xml:"DeleteMarker"`
+ Key string `xml:"Key"` // The Object Key
+ VersionId string `xml:"VersionId"` // The Object VersionId
+ IsLatest bool `xml:"IsLatest"` // is current version or not
+ LastModified time.Time `xml:"LastModified"` // Object last modified time
+ Owner Owner `xml:"Owner"` // bucket owner element
+}
+
+type ObjectVersionProperties struct {
+ XMLName xml.Name `xml:"Version"`
+ Key string `xml:"Key"` // The Object Key
+ VersionId string `xml:"VersionId"` // The Object VersionId
+ IsLatest bool `xml:"IsLatest"` // is latest version or not
+ LastModified time.Time `xml:"LastModified"` // Object last modified time
+ Type string `xml:"Type"` // Object type
+ Size int64 `xml:"Size"` // Object size
+ ETag string `xml:"ETag"` // Object ETag
+ StorageClass string `xml:"StorageClass"` // Object storage class (Standard, IA, Archive)
+ Owner Owner `xml:"Owner"` // bucket owner element
+ RestoreInfo string `xml:"RestoreInfo,omitempty"` // Object restoreInfo
+}
+
+// Owner defines Bucket/Object's owner
+type Owner struct {
+ XMLName xml.Name `xml:"Owner"`
+ ID string `xml:"ID"` // Owner ID
+ DisplayName string `xml:"DisplayName"` // Owner's display name
+}
+
+// CopyObjectResult defines result object of CopyObject
+type CopyObjectResult struct {
+ XMLName xml.Name `xml:"CopyObjectResult"`
+ LastModified time.Time `xml:"LastModified"` // New object's last modified time.
+ ETag string `xml:"ETag"` // New object's ETag
+}
+
+// GetObjectACLResult defines result of GetObjectACL request
+type GetObjectACLResult GetBucketACLResult
+
+type deleteXML struct {
+ XMLName xml.Name `xml:"Delete"`
+ Objects []DeleteObject `xml:"Object"` // Objects to delete
+ Quiet bool `xml:"Quiet"` // Flag of quiet mode.
+}
+
+// DeleteObject defines the struct for deleting object
+type DeleteObject struct {
+ XMLName xml.Name `xml:"Object"`
+ Key string `xml:"Key"` // Object name
+ VersionId string `xml:"VersionId,omitempty"` // Object VersionId
+}
+
+// DeleteObjectsResult defines result of DeleteObjects request
+type DeleteObjectsResult struct {
+ XMLName xml.Name
+ DeletedObjects []string // Deleted object key list
+}
+
+// DeleteObjectVersionsResult defines result of DeleteObjects request
+type DeleteObjectVersionsResult struct {
+ XMLName xml.Name `xml:"DeleteResult"`
+ DeletedObjectsDetail []DeletedKeyInfo `xml:"Deleted"` // Deleted object detail info
+}
+
+// DeletedKeyInfo defines object delete info
+type DeletedKeyInfo struct {
+ XMLName xml.Name `xml:"Deleted"`
+ Key string `xml:"Key"` // Object key
+ VersionId string `xml:"VersionId"` // VersionId
+ DeleteMarker bool `xml:"DeleteMarker"` // Object DeleteMarker
+ DeleteMarkerVersionId string `xml:"DeleteMarkerVersionId"` // Object DeleteMarkerVersionId
+}
+
+// InitiateMultipartUploadResult defines result of InitiateMultipartUpload request
+type InitiateMultipartUploadResult struct {
+ XMLName xml.Name `xml:"InitiateMultipartUploadResult"`
+ Bucket string `xml:"Bucket"` // Bucket name
+ Key string `xml:"Key"` // Object name to upload
+ UploadID string `xml:"UploadId"` // Generated UploadId
+}
+
+// UploadPart defines the upload/copy part
+type UploadPart struct {
+ XMLName xml.Name `xml:"Part"`
+ PartNumber int `xml:"PartNumber"` // Part number
+ ETag string `xml:"ETag"` // ETag value of the part's data
+}
+
+type UploadParts []UploadPart
+
+func (slice UploadParts) Len() int {
+ return len(slice)
+}
+
+func (slice UploadParts) Less(i, j int) bool {
+ return slice[i].PartNumber < slice[j].PartNumber
+}
+
+func (slice UploadParts) Swap(i, j int) {
+ slice[i], slice[j] = slice[j], slice[i]
+}
+
+// UploadPartCopyResult defines result object of multipart copy request.
+type UploadPartCopyResult struct {
+ XMLName xml.Name `xml:"CopyPartResult"`
+ LastModified time.Time `xml:"LastModified"` // Last modified time
+ ETag string `xml:"ETag"` // ETag
+}
+
+type completeMultipartUploadXML struct {
+ XMLName xml.Name `xml:"CompleteMultipartUpload"`
+ Part []UploadPart `xml:"Part"`
+}
+
+// CompleteMultipartUploadResult defines result object of CompleteMultipartUploadRequest
+type CompleteMultipartUploadResult struct {
+ XMLName xml.Name `xml:"CompleteMultipartUploadResult"`
+ Location string `xml:"Location"` // Object URL
+ Bucket string `xml:"Bucket"` // Bucket name
+ ETag string `xml:"ETag"` // Object ETag
+ Key string `xml:"Key"` // Object name
+}
+
+// ListUploadedPartsResult defines result object of ListUploadedParts
+type ListUploadedPartsResult struct {
+ XMLName xml.Name `xml:"ListPartsResult"`
+ Bucket string `xml:"Bucket"` // Bucket name
+ Key string `xml:"Key"` // Object name
+ UploadID string `xml:"UploadId"` // Upload ID
+ NextPartNumberMarker string `xml:"NextPartNumberMarker"` // Next part number
+ MaxParts int `xml:"MaxParts"` // Max parts count
+ IsTruncated bool `xml:"IsTruncated"` // Flag indicates all entries returned.false: all entries returned.
+ UploadedParts []UploadedPart `xml:"Part"` // Uploaded parts
+}
+
+// UploadedPart defines uploaded part
+type UploadedPart struct {
+ XMLName xml.Name `xml:"Part"`
+ PartNumber int `xml:"PartNumber"` // Part number
+ LastModified time.Time `xml:"LastModified"` // Last modified time
+ ETag string `xml:"ETag"` // ETag cache
+ Size int `xml:"Size"` // Part size
+}
+
+// ListMultipartUploadResult defines result object of ListMultipartUpload
+type ListMultipartUploadResult struct {
+ XMLName xml.Name `xml:"ListMultipartUploadsResult"`
+ Bucket string `xml:"Bucket"` // Bucket name
+ Delimiter string `xml:"Delimiter"` // Delimiter for grouping object.
+ Prefix string `xml:"Prefix"` // Object prefix
+ KeyMarker string `xml:"KeyMarker"` // Object key marker
+ UploadIDMarker string `xml:"UploadIdMarker"` // UploadId marker
+ NextKeyMarker string `xml:"NextKeyMarker"` // Next key marker, if not all entries returned.
+ NextUploadIDMarker string `xml:"NextUploadIdMarker"` // Next uploadId marker, if not all entries returned.
+ MaxUploads int `xml:"MaxUploads"` // Max uploads to return
+ IsTruncated bool `xml:"IsTruncated"` // Flag indicates all entries are returned.
+ Uploads []UncompletedUpload `xml:"Upload"` // Ongoing uploads (not completed, not aborted)
+ CommonPrefixes []string `xml:"CommonPrefixes>Prefix"` // Common prefixes list.
+}
+
+// UncompletedUpload structure wraps an uncompleted upload task
+type UncompletedUpload struct {
+ XMLName xml.Name `xml:"Upload"`
+ Key string `xml:"Key"` // Object name
+ UploadID string `xml:"UploadId"` // The UploadId
+ Initiated time.Time `xml:"Initiated"` // Initialization time in the format such as 2012-02-23T04:18:23.000Z
+}
+
+// ProcessObjectResult defines result object of ProcessObject
+type ProcessObjectResult struct {
+ Bucket string `json:"bucket"`
+ FileSize int `json:"fileSize"`
+ Object string `json:"object"`
+ Status string `json:"status"`
+}
+
+// AsyncProcessObjectResult defines result object of AsyncProcessObject
+type AsyncProcessObjectResult struct {
+ EventId string `json:"EventId"`
+ RequestId string `json:"RequestId"`
+ TaskId string `json:"TaskId"`
+}
+
+// decodeDeleteObjectsResult decodes deleting objects result in URL encoding
+func decodeDeleteObjectsResult(result *DeleteObjectVersionsResult) error {
+ var err error
+ for i := 0; i < len(result.DeletedObjectsDetail); i++ {
+ result.DeletedObjectsDetail[i].Key, err = url.QueryUnescape(result.DeletedObjectsDetail[i].Key)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// decodeListObjectsResult decodes list objects result in URL encoding
+func decodeListObjectsResult(result *ListObjectsResult) error {
+ var err error
+ result.Prefix, err = url.QueryUnescape(result.Prefix)
+ if err != nil {
+ return err
+ }
+ result.Marker, err = url.QueryUnescape(result.Marker)
+ if err != nil {
+ return err
+ }
+ result.Delimiter, err = url.QueryUnescape(result.Delimiter)
+ if err != nil {
+ return err
+ }
+ result.NextMarker, err = url.QueryUnescape(result.NextMarker)
+ if err != nil {
+ return err
+ }
+ for i := 0; i < len(result.Objects); i++ {
+ result.Objects[i].Key, err = url.QueryUnescape(result.Objects[i].Key)
+ if err != nil {
+ return err
+ }
+ }
+ for i := 0; i < len(result.CommonPrefixes); i++ {
+ result.CommonPrefixes[i], err = url.QueryUnescape(result.CommonPrefixes[i])
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// decodeListObjectsResult decodes list objects result in URL encoding
+func decodeListObjectsResultV2(result *ListObjectsResultV2) error {
+ var err error
+ result.Prefix, err = url.QueryUnescape(result.Prefix)
+ if err != nil {
+ return err
+ }
+ result.StartAfter, err = url.QueryUnescape(result.StartAfter)
+ if err != nil {
+ return err
+ }
+ result.Delimiter, err = url.QueryUnescape(result.Delimiter)
+ if err != nil {
+ return err
+ }
+ result.NextContinuationToken, err = url.QueryUnescape(result.NextContinuationToken)
+ if err != nil {
+ return err
+ }
+ for i := 0; i < len(result.Objects); i++ {
+ result.Objects[i].Key, err = url.QueryUnescape(result.Objects[i].Key)
+ if err != nil {
+ return err
+ }
+ }
+ for i := 0; i < len(result.CommonPrefixes); i++ {
+ result.CommonPrefixes[i], err = url.QueryUnescape(result.CommonPrefixes[i])
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// decodeListObjectVersionsResult decodes list version objects result in URL encoding
+func decodeListObjectVersionsResult(result *ListObjectVersionsResult) error {
+ var err error
+
+ // decode:Delimiter
+ result.Delimiter, err = url.QueryUnescape(result.Delimiter)
+ if err != nil {
+ return err
+ }
+
+ // decode Prefix
+ result.Prefix, err = url.QueryUnescape(result.Prefix)
+ if err != nil {
+ return err
+ }
+
+ // decode KeyMarker
+ result.KeyMarker, err = url.QueryUnescape(result.KeyMarker)
+ if err != nil {
+ return err
+ }
+
+ // decode VersionIdMarker
+ result.VersionIdMarker, err = url.QueryUnescape(result.VersionIdMarker)
+ if err != nil {
+ return err
+ }
+
+ // decode NextKeyMarker
+ result.NextKeyMarker, err = url.QueryUnescape(result.NextKeyMarker)
+ if err != nil {
+ return err
+ }
+
+ // decode NextVersionIdMarker
+ result.NextVersionIdMarker, err = url.QueryUnescape(result.NextVersionIdMarker)
+ if err != nil {
+ return err
+ }
+
+ // decode CommonPrefixes
+ for i := 0; i < len(result.CommonPrefixes); i++ {
+ result.CommonPrefixes[i], err = url.QueryUnescape(result.CommonPrefixes[i])
+ if err != nil {
+ return err
+ }
+ }
+
+ // decode deleteMarker
+ for i := 0; i < len(result.ObjectDeleteMarkers); i++ {
+ result.ObjectDeleteMarkers[i].Key, err = url.QueryUnescape(result.ObjectDeleteMarkers[i].Key)
+ if err != nil {
+ return err
+ }
+ }
+
+ // decode ObjectVersions
+ for i := 0; i < len(result.ObjectVersions); i++ {
+ result.ObjectVersions[i].Key, err = url.QueryUnescape(result.ObjectVersions[i].Key)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// decodeListUploadedPartsResult decodes
+func decodeListUploadedPartsResult(result *ListUploadedPartsResult) error {
+ var err error
+ result.Key, err = url.QueryUnescape(result.Key)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// decodeListMultipartUploadResult decodes list multipart upload result in URL encoding
+func decodeListMultipartUploadResult(result *ListMultipartUploadResult) error {
+ var err error
+ result.Prefix, err = url.QueryUnescape(result.Prefix)
+ if err != nil {
+ return err
+ }
+ result.Delimiter, err = url.QueryUnescape(result.Delimiter)
+ if err != nil {
+ return err
+ }
+ result.KeyMarker, err = url.QueryUnescape(result.KeyMarker)
+ if err != nil {
+ return err
+ }
+ result.NextKeyMarker, err = url.QueryUnescape(result.NextKeyMarker)
+ if err != nil {
+ return err
+ }
+ for i := 0; i < len(result.Uploads); i++ {
+ result.Uploads[i].Key, err = url.QueryUnescape(result.Uploads[i].Key)
+ if err != nil {
+ return err
+ }
+ }
+ for i := 0; i < len(result.CommonPrefixes); i++ {
+ result.CommonPrefixes[i], err = url.QueryUnescape(result.CommonPrefixes[i])
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// marshalDeleteObjectToXml deleteXML struct to xml
+func marshalDeleteObjectToXml(dxml deleteXML) string {
+ var builder strings.Builder
+ builder.WriteString("")
+ builder.WriteString("")
+ builder.WriteString(strconv.FormatBool(dxml.Quiet))
+ builder.WriteString("")
+ if len(dxml.Objects) > 0 {
+ for _, object := range dxml.Objects {
+ builder.WriteString("")
+ }
+ }
+ builder.WriteString("")
+ return builder.String()
+}
+
+// createBucketConfiguration defines the configuration for creating a bucket.
+type createBucketConfiguration struct {
+ XMLName xml.Name `xml:"CreateBucketConfiguration"`
+ StorageClass StorageClassType `xml:"StorageClass,omitempty"`
+ DataRedundancyType DataRedundancyType `xml:"DataRedundancyType,omitempty"`
+ ObjectHashFunction ObjecthashFuncType `xml:"ObjectHashFunction,omitempty"`
+}
+
+// LiveChannelConfiguration defines the configuration for live-channel
+type LiveChannelConfiguration struct {
+ XMLName xml.Name `xml:"LiveChannelConfiguration"`
+ Description string `xml:"Description,omitempty"` //Description of live-channel, up to 128 bytes
+ Status string `xml:"Status,omitempty"` //Specify the status of livechannel
+ Target LiveChannelTarget `xml:"Target"` //target configuration of live-channel
+ // use point instead of struct to avoid omit empty snapshot
+ Snapshot *LiveChannelSnapshot `xml:"Snapshot,omitempty"` //snapshot configuration of live-channel
+}
+
+// LiveChannelTarget target configuration of live-channel
+type LiveChannelTarget struct {
+ XMLName xml.Name `xml:"Target"`
+ Type string `xml:"Type"` //the type of object, only supports HLS
+ FragDuration int `xml:"FragDuration,omitempty"` //the length of each ts object (in seconds), in the range [1,100]
+ FragCount int `xml:"FragCount,omitempty"` //the number of ts objects in the m3u8 object, in the range of [1,100]
+ PlaylistName string `xml:"PlaylistName,omitempty"` //the name of m3u8 object, which must end with ".m3u8" and the length range is [6,128]
+}
+
+// LiveChannelSnapshot snapshot configuration of live-channel
+type LiveChannelSnapshot struct {
+ XMLName xml.Name `xml:"Snapshot"`
+ RoleName string `xml:"RoleName,omitempty"` //The role of snapshot operations, it sholud has write permission of DestBucket and the permission to send messages to the NotifyTopic.
+ DestBucket string `xml:"DestBucket,omitempty"` //Bucket the snapshots will be written to. should be the same owner as the source bucket.
+ NotifyTopic string `xml:"NotifyTopic,omitempty"` //Topics of MNS for notifying users of high frequency screenshot operation results
+ Interval int `xml:"Interval,omitempty"` //interval of snapshots, threre is no snapshot if no I-frame during the interval time
+}
+
+// CreateLiveChannelResult the result of crete live-channel
+type CreateLiveChannelResult struct {
+ XMLName xml.Name `xml:"CreateLiveChannelResult"`
+ PublishUrls []string `xml:"PublishUrls>Url"` //push urls list
+ PlayUrls []string `xml:"PlayUrls>Url"` //play urls list
+}
+
+// LiveChannelStat the result of get live-channel state
+type LiveChannelStat struct {
+ XMLName xml.Name `xml:"LiveChannelStat"`
+ Status string `xml:"Status"` //Current push status of live-channel: Disabled,Live,Idle
+ ConnectedTime time.Time `xml:"ConnectedTime"` //The time when the client starts pushing, format: ISO8601
+ RemoteAddr string `xml:"RemoteAddr"` //The ip address of the client
+ Video LiveChannelVideo `xml:"Video"` //Video stream information
+ Audio LiveChannelAudio `xml:"Audio"` //Audio stream information
+}
+
+// LiveChannelVideo video stream information
+type LiveChannelVideo struct {
+ XMLName xml.Name `xml:"Video"`
+ Width int `xml:"Width"` //Width (unit: pixels)
+ Height int `xml:"Height"` //Height (unit: pixels)
+ FrameRate int `xml:"FrameRate"` //FramRate
+ Bandwidth int `xml:"Bandwidth"` //Bandwidth (unit: B/s)
+}
+
+// LiveChannelAudio audio stream information
+type LiveChannelAudio struct {
+ XMLName xml.Name `xml:"Audio"`
+ SampleRate int `xml:"SampleRate"` //SampleRate
+ Bandwidth int `xml:"Bandwidth"` //Bandwidth (unit: B/s)
+ Codec string `xml:"Codec"` //Encoding forma
+}
+
+// LiveChannelHistory the result of GetLiveChannelHistory, at most return up to lastest 10 push records
+type LiveChannelHistory struct {
+ XMLName xml.Name `xml:"LiveChannelHistory"`
+ Record []LiveRecord `xml:"LiveRecord"` //push records list
+}
+
+// LiveRecord push recode
+type LiveRecord struct {
+ XMLName xml.Name `xml:"LiveRecord"`
+ StartTime time.Time `xml:"StartTime"` //StartTime, format: ISO8601
+ EndTime time.Time `xml:"EndTime"` //EndTime, format: ISO8601
+ RemoteAddr string `xml:"RemoteAddr"` //The ip address of remote client
+}
+
+// ListLiveChannelResult the result of ListLiveChannel
+type ListLiveChannelResult struct {
+ XMLName xml.Name `xml:"ListLiveChannelResult"`
+ Prefix string `xml:"Prefix"` //Filter by the name start with the value of "Prefix"
+ Marker string `xml:"Marker"` //cursor from which starting list
+ MaxKeys int `xml:"MaxKeys"` //The maximum count returned. the default value is 100. it cannot be greater than 1000.
+ IsTruncated bool `xml:"IsTruncated"` //Indicates whether all results have been returned, "true" indicates partial results returned while "false" indicates all results have been returned
+ NextMarker string `xml:"NextMarker"` //NextMarker indicate the Marker value of the next request
+ LiveChannel []LiveChannelInfo `xml:"LiveChannel"` //The infomation of live-channel
+}
+
+// LiveChannelInfo the infomation of live-channel
+type LiveChannelInfo struct {
+ XMLName xml.Name `xml:"LiveChannel"`
+ Name string `xml:"Name"` //The name of live-channel
+ Description string `xml:"Description"` //Description of live-channel
+ Status string `xml:"Status"` //Status: disabled or enabled
+ LastModified time.Time `xml:"LastModified"` //Last modification time, format: ISO8601
+ PublishUrls []string `xml:"PublishUrls>Url"` //push urls list
+ PlayUrls []string `xml:"PlayUrls>Url"` //play urls list
+}
+
+// Tag a tag for the object
+type Tag struct {
+ XMLName xml.Name `xml:"Tag"`
+ Key string `xml:"Key"`
+ Value string `xml:"Value"`
+}
+
+// Tagging tag set for the object
+type Tagging struct {
+ XMLName xml.Name `xml:"Tagging"`
+ Tags []Tag `xml:"TagSet>Tag,omitempty"`
+}
+
+// GetObjectTaggingResult for GetObjectTagging return value
+type GetObjectTaggingResult Tagging
+
+// VersioningConfig for the bucket
+type VersioningConfig struct {
+ XMLName xml.Name `xml:"VersioningConfiguration"`
+ Status string `xml:"Status"`
+}
+
+type GetBucketVersioningResult VersioningConfig
+
+// ServerEncryptionRule Server Encryption rule for the bucket
+type ServerEncryptionRule struct {
+ XMLName xml.Name `xml:"ServerSideEncryptionRule"`
+ SSEDefault SSEDefaultRule `xml:"ApplyServerSideEncryptionByDefault"`
+}
+
+// SSEDefaultRule Server Encryption deafult rule for the bucket
+type SSEDefaultRule struct {
+ XMLName xml.Name `xml:"ApplyServerSideEncryptionByDefault"`
+ SSEAlgorithm string `xml:"SSEAlgorithm,omitempty"`
+ KMSMasterKeyID string `xml:"KMSMasterKeyID,omitempty"`
+ KMSDataEncryption string `xml:"KMSDataEncryption,,omitempty"`
+}
+
+type GetBucketEncryptionResult ServerEncryptionRule
+type GetBucketTaggingResult Tagging
+
+type BucketStat struct {
+ XMLName xml.Name `xml:"BucketStat"`
+ Storage int64 `xml:"Storage"`
+ ObjectCount int64 `xml:"ObjectCount"`
+ MultipartUploadCount int64 `xml:"MultipartUploadCount"`
+ LiveChannelCount int64 `xml:"LiveChannelCount"`
+ LastModifiedTime int64 `xml:"LastModifiedTime"`
+ StandardStorage int64 `xml:"StandardStorage"`
+ StandardObjectCount int64 `xml:"StandardObjectCount"`
+ InfrequentAccessStorage int64 `xml:"InfrequentAccessStorage"`
+ InfrequentAccessRealStorage int64 `xml:"InfrequentAccessRealStorage"`
+ InfrequentAccessObjectCount int64 `xml:"InfrequentAccessObjectCount"`
+ ArchiveStorage int64 `xml:"ArchiveStorage"`
+ ArchiveRealStorage int64 `xml:"ArchiveRealStorage"`
+ ArchiveObjectCount int64 `xml:"ArchiveObjectCount"`
+ ColdArchiveStorage int64 `xml:"ColdArchiveStorage"`
+ ColdArchiveRealStorage int64 `xml:"ColdArchiveRealStorage"`
+ ColdArchiveObjectCount int64 `xml:"ColdArchiveObjectCount"`
+}
+type GetBucketStatResult BucketStat
+
+// RequestPaymentConfiguration define the request payment configuration
+type RequestPaymentConfiguration struct {
+ XMLName xml.Name `xml:"RequestPaymentConfiguration"`
+ Payer string `xml:"Payer,omitempty"`
+}
+
+// BucketQoSConfiguration define QoS configuration
+type BucketQoSConfiguration struct {
+ XMLName xml.Name `xml:"QoSConfiguration"`
+ TotalUploadBandwidth *int `xml:"TotalUploadBandwidth"` // Total upload bandwidth
+ IntranetUploadBandwidth *int `xml:"IntranetUploadBandwidth"` // Intranet upload bandwidth
+ ExtranetUploadBandwidth *int `xml:"ExtranetUploadBandwidth"` // Extranet upload bandwidth
+ TotalDownloadBandwidth *int `xml:"TotalDownloadBandwidth"` // Total download bandwidth
+ IntranetDownloadBandwidth *int `xml:"IntranetDownloadBandwidth"` // Intranet download bandwidth
+ ExtranetDownloadBandwidth *int `xml:"ExtranetDownloadBandwidth"` // Extranet download bandwidth
+ TotalQPS *int `xml:"TotalQps"` // Total Qps
+ IntranetQPS *int `xml:"IntranetQps"` // Intranet Qps
+ ExtranetQPS *int `xml:"ExtranetQps"` // Extranet Qps
+}
+
+// UserQoSConfiguration define QoS and Range configuration
+type UserQoSConfiguration struct {
+ XMLName xml.Name `xml:"QoSConfiguration"`
+ Region string `xml:"Region,omitempty"` // Effective area of Qos configuration
+ BucketQoSConfiguration
+}
+
+//////////////////////////////////////////////////////////////
+/////////////////// Select OBject ////////////////////////////
+//////////////////////////////////////////////////////////////
+
+type CsvMetaRequest struct {
+ XMLName xml.Name `xml:"CsvMetaRequest"`
+ InputSerialization InputSerialization `xml:"InputSerialization"`
+ OverwriteIfExists *bool `xml:"OverwriteIfExists,omitempty"`
+}
+
+// encodeBase64 encode base64 of the CreateSelectObjectMeta api request params
+func (meta *CsvMetaRequest) encodeBase64() {
+ meta.InputSerialization.CSV.RecordDelimiter =
+ base64.StdEncoding.EncodeToString([]byte(meta.InputSerialization.CSV.RecordDelimiter))
+ meta.InputSerialization.CSV.FieldDelimiter =
+ base64.StdEncoding.EncodeToString([]byte(meta.InputSerialization.CSV.FieldDelimiter))
+ meta.InputSerialization.CSV.QuoteCharacter =
+ base64.StdEncoding.EncodeToString([]byte(meta.InputSerialization.CSV.QuoteCharacter))
+}
+
+type JsonMetaRequest struct {
+ XMLName xml.Name `xml:"JsonMetaRequest"`
+ InputSerialization InputSerialization `xml:"InputSerialization"`
+ OverwriteIfExists *bool `xml:"OverwriteIfExists,omitempty"`
+}
+
+type InputSerialization struct {
+ XMLName xml.Name `xml:"InputSerialization"`
+ CSV CSV `xml:CSV,omitempty`
+ JSON JSON `xml:JSON,omitempty`
+ CompressionType string `xml:"CompressionType,omitempty"`
+}
+type CSV struct {
+ XMLName xml.Name `xml:"CSV"`
+ RecordDelimiter string `xml:"RecordDelimiter,omitempty"`
+ FieldDelimiter string `xml:"FieldDelimiter,omitempty"`
+ QuoteCharacter string `xml:"QuoteCharacter,omitempty"`
+}
+
+type JSON struct {
+ XMLName xml.Name `xml:"JSON"`
+ JSONType string `xml:"Type,omitempty"`
+}
+
+// SelectRequest is for the SelectObject request params of json file
+type SelectRequest struct {
+ XMLName xml.Name `xml:"SelectRequest"`
+ Expression string `xml:"Expression"`
+ InputSerializationSelect InputSerializationSelect `xml:"InputSerialization"`
+ OutputSerializationSelect OutputSerializationSelect `xml:"OutputSerialization"`
+ SelectOptions SelectOptions `xml:"Options,omitempty"`
+}
+type InputSerializationSelect struct {
+ XMLName xml.Name `xml:"InputSerialization"`
+ CsvBodyInput CSVSelectInput `xml:CSV,omitempty`
+ JsonBodyInput JSONSelectInput `xml:JSON,omitempty`
+ CompressionType string `xml:"CompressionType,omitempty"`
+}
+type CSVSelectInput struct {
+ XMLName xml.Name `xml:"CSV"`
+ FileHeaderInfo string `xml:"FileHeaderInfo,omitempty"`
+ RecordDelimiter string `xml:"RecordDelimiter,omitempty"`
+ FieldDelimiter string `xml:"FieldDelimiter,omitempty"`
+ QuoteCharacter string `xml:"QuoteCharacter,omitempty"`
+ CommentCharacter string `xml:"CommentCharacter,omitempty"`
+ Range string `xml:"Range,omitempty"`
+ SplitRange string
+}
+type JSONSelectInput struct {
+ XMLName xml.Name `xml:"JSON"`
+ JSONType string `xml:"Type,omitempty"`
+ Range string `xml:"Range,omitempty"`
+ ParseJSONNumberAsString *bool `xml:"ParseJsonNumberAsString"`
+ SplitRange string
+}
+
+func (jsonInput *JSONSelectInput) JsonIsEmpty() bool {
+ if jsonInput.JSONType != "" {
+ return false
+ }
+ return true
+}
+
+type OutputSerializationSelect struct {
+ XMLName xml.Name `xml:"OutputSerialization"`
+ CsvBodyOutput CSVSelectOutput `xml:CSV,omitempty`
+ JsonBodyOutput JSONSelectOutput `xml:JSON,omitempty`
+ OutputRawData *bool `xml:"OutputRawData,omitempty"`
+ KeepAllColumns *bool `xml:"KeepAllColumns,omitempty"`
+ EnablePayloadCrc *bool `xml:"EnablePayloadCrc,omitempty"`
+ OutputHeader *bool `xml:"OutputHeader,omitempty"`
+}
+type CSVSelectOutput struct {
+ XMLName xml.Name `xml:"CSV"`
+ RecordDelimiter string `xml:"RecordDelimiter,omitempty"`
+ FieldDelimiter string `xml:"FieldDelimiter,omitempty"`
+}
+type JSONSelectOutput struct {
+ XMLName xml.Name `xml:"JSON"`
+ RecordDelimiter string `xml:"RecordDelimiter,omitempty"`
+}
+
+func (selectReq *SelectRequest) encodeBase64() {
+ if selectReq.InputSerializationSelect.JsonBodyInput.JsonIsEmpty() {
+ selectReq.csvEncodeBase64()
+ } else {
+ selectReq.jsonEncodeBase64()
+ }
+}
+
+// csvEncodeBase64 encode base64 of the SelectObject api request params
+func (selectReq *SelectRequest) csvEncodeBase64() {
+ selectReq.Expression = base64.StdEncoding.EncodeToString([]byte(selectReq.Expression))
+ selectReq.InputSerializationSelect.CsvBodyInput.RecordDelimiter =
+ base64.StdEncoding.EncodeToString([]byte(selectReq.InputSerializationSelect.CsvBodyInput.RecordDelimiter))
+ selectReq.InputSerializationSelect.CsvBodyInput.FieldDelimiter =
+ base64.StdEncoding.EncodeToString([]byte(selectReq.InputSerializationSelect.CsvBodyInput.FieldDelimiter))
+ selectReq.InputSerializationSelect.CsvBodyInput.QuoteCharacter =
+ base64.StdEncoding.EncodeToString([]byte(selectReq.InputSerializationSelect.CsvBodyInput.QuoteCharacter))
+ selectReq.InputSerializationSelect.CsvBodyInput.CommentCharacter =
+ base64.StdEncoding.EncodeToString([]byte(selectReq.InputSerializationSelect.CsvBodyInput.CommentCharacter))
+ selectReq.OutputSerializationSelect.CsvBodyOutput.FieldDelimiter =
+ base64.StdEncoding.EncodeToString([]byte(selectReq.OutputSerializationSelect.CsvBodyOutput.FieldDelimiter))
+ selectReq.OutputSerializationSelect.CsvBodyOutput.RecordDelimiter =
+ base64.StdEncoding.EncodeToString([]byte(selectReq.OutputSerializationSelect.CsvBodyOutput.RecordDelimiter))
+
+ // handle Range
+ if selectReq.InputSerializationSelect.CsvBodyInput.Range != "" {
+ selectReq.InputSerializationSelect.CsvBodyInput.Range = "line-range=" + selectReq.InputSerializationSelect.CsvBodyInput.Range
+ }
+
+ if selectReq.InputSerializationSelect.CsvBodyInput.SplitRange != "" {
+ selectReq.InputSerializationSelect.CsvBodyInput.Range = "split-range=" + selectReq.InputSerializationSelect.CsvBodyInput.SplitRange
+ }
+}
+
+// jsonEncodeBase64 encode base64 of the SelectObject api request params
+func (selectReq *SelectRequest) jsonEncodeBase64() {
+ selectReq.Expression = base64.StdEncoding.EncodeToString([]byte(selectReq.Expression))
+ selectReq.OutputSerializationSelect.JsonBodyOutput.RecordDelimiter =
+ base64.StdEncoding.EncodeToString([]byte(selectReq.OutputSerializationSelect.JsonBodyOutput.RecordDelimiter))
+
+ // handle Range
+ if selectReq.InputSerializationSelect.JsonBodyInput.Range != "" {
+ selectReq.InputSerializationSelect.JsonBodyInput.Range = "line-range=" + selectReq.InputSerializationSelect.JsonBodyInput.Range
+ }
+
+ if selectReq.InputSerializationSelect.JsonBodyInput.SplitRange != "" {
+ selectReq.InputSerializationSelect.JsonBodyInput.Range = "split-range=" + selectReq.InputSerializationSelect.JsonBodyInput.SplitRange
+ }
+}
+
+// SelectOptions is a element in the SelectObject api request's params
+type SelectOptions struct {
+ XMLName xml.Name `xml:"Options"`
+ SkipPartialDataRecord *bool `xml:"SkipPartialDataRecord,omitempty"`
+ MaxSkippedRecordsAllowed string `xml:"MaxSkippedRecordsAllowed,omitempty"`
+}
+
+// SelectObjectResult is the SelectObject api's return
+type SelectObjectResult struct {
+ Version byte
+ FrameType int32
+ PayloadLength int32
+ HeaderCheckSum uint32
+ Offset uint64
+ Data string // DataFrame
+ EndFrame EndFrame // EndFrame
+ MetaEndFrameCSV MetaEndFrameCSV // MetaEndFrameCSV
+ MetaEndFrameJSON MetaEndFrameJSON // MetaEndFrameJSON
+ PayloadChecksum uint32
+ ReadFlagInfo
+}
+
+// ReadFlagInfo if reading the frame data, recode the reading status
+type ReadFlagInfo struct {
+ OpenLine bool
+ ConsumedBytesLength int32
+ EnablePayloadCrc bool
+ OutputRawData bool
+}
+
+// EndFrame is EndFrameType of SelectObject api
+type EndFrame struct {
+ TotalScanned int64
+ HTTPStatusCode int32
+ ErrorMsg string
+}
+
+// MetaEndFrameCSV is MetaEndFrameCSVType of CreateSelectObjectMeta
+type MetaEndFrameCSV struct {
+ TotalScanned int64
+ Status int32
+ SplitsCount int32
+ RowsCount int64
+ ColumnsCount int32
+ ErrorMsg string
+}
+
+// MetaEndFrameJSON is MetaEndFrameJSON of CreateSelectObjectMeta
+type MetaEndFrameJSON struct {
+ TotalScanned int64
+ Status int32
+ SplitsCount int32
+ RowsCount int64
+ ErrorMsg string
+}
+
+// InventoryConfiguration is Inventory config
+type InventoryConfiguration struct {
+ XMLName xml.Name `xml:"InventoryConfiguration"`
+ Id string `xml:"Id,omitempty"`
+ IsEnabled *bool `xml:"IsEnabled,omitempty"`
+ Prefix string `xml:"Filter>Prefix,omitempty"`
+ OSSBucketDestination OSSBucketDestination `xml:"Destination>OSSBucketDestination,omitempty"`
+ Frequency string `xml:"Schedule>Frequency,omitempty"`
+ IncludedObjectVersions string `xml:"IncludedObjectVersions,omitempty"`
+ OptionalFields OptionalFields `xml:OptionalFields,omitempty`
+}
+
+type OptionalFields struct {
+ XMLName xml.Name `xml:"OptionalFields,omitempty`
+ Field []string `xml:"Field,omitempty`
+}
+
+type OSSBucketDestination struct {
+ XMLName xml.Name `xml:"OSSBucketDestination"`
+ Format string `xml:"Format,omitempty"`
+ AccountId string `xml:"AccountId,omitempty"`
+ RoleArn string `xml:"RoleArn,omitempty"`
+ Bucket string `xml:"Bucket,omitempty"`
+ Prefix string `xml:"Prefix,omitempty"`
+ Encryption *InvEncryption `xml:"Encryption,omitempty"`
+}
+
+type InvEncryption struct {
+ XMLName xml.Name `xml:"Encryption"`
+ SseOss *InvSseOss `xml:"SSE-OSS"`
+ SseKms *InvSseKms `xml:"SSE-KMS"`
+}
+
+type InvSseOss struct {
+ XMLName xml.Name `xml:"SSE-OSS"`
+}
+
+type InvSseKms struct {
+ XMLName xml.Name `xml:"SSE-KMS"`
+ KmsId string `xml:"KeyId,omitempty"`
+}
+
+type ListInventoryConfigurationsResult struct {
+ XMLName xml.Name `xml:"ListInventoryConfigurationsResult"`
+ InventoryConfiguration []InventoryConfiguration `xml:"InventoryConfiguration,omitempty`
+ IsTruncated *bool `xml:"IsTruncated,omitempty"`
+ NextContinuationToken string `xml:"NextContinuationToken,omitempty"`
+}
+
+// RestoreConfiguration for RestoreObject
+type RestoreConfiguration struct {
+ XMLName xml.Name `xml:"RestoreRequest"`
+ Days int32 `xml:"Days,omitempty"`
+ Tier string `xml:"JobParameters>Tier,omitempty"`
+}
+
+// AsyncFetchTaskConfiguration for SetBucketAsyncFetchTask
+type AsyncFetchTaskConfiguration struct {
+ XMLName xml.Name `xml:"AsyncFetchTaskConfiguration"`
+ Url string `xml:"Url,omitempty"`
+ Object string `xml:"Object,omitempty"`
+ Host string `xml:"Host,omitempty"`
+ ContentMD5 string `xml:"ContentMD5,omitempty"`
+ Callback string `xml:"Callback,omitempty"`
+ StorageClass string `xml:"StorageClass,omitempty"`
+ IgnoreSameKey bool `xml:"IgnoreSameKey"`
+}
+
+// AsyncFetchTaskResult for SetBucketAsyncFetchTask result
+type AsyncFetchTaskResult struct {
+ XMLName xml.Name `xml:"AsyncFetchTaskResult"`
+ TaskId string `xml:"TaskId,omitempty"`
+}
+
+// AsynFetchTaskInfo for GetBucketAsyncFetchTask result
+type AsynFetchTaskInfo struct {
+ XMLName xml.Name `xml:"AsyncFetchTaskInfo"`
+ TaskId string `xml:"TaskId,omitempty"`
+ State string `xml:"State,omitempty"`
+ ErrorMsg string `xml:"ErrorMsg,omitempty"`
+ TaskInfo AsyncTaskInfo `xml:"TaskInfo,omitempty"`
+}
+
+// AsyncTaskInfo for async task information
+type AsyncTaskInfo struct {
+ XMLName xml.Name `xml:"TaskInfo"`
+ Url string `xml:"Url,omitempty"`
+ Object string `xml:"Object,omitempty"`
+ Host string `xml:"Host,omitempty"`
+ ContentMD5 string `xml:"ContentMD5,omitempty"`
+ Callback string `xml:"Callback,omitempty"`
+ StorageClass string `xml:"StorageClass,omitempty"`
+ IgnoreSameKey bool `xml:"IgnoreSameKey"`
+}
+
+// InitiateWormConfiguration define InitiateBucketWorm configuration
+type InitiateWormConfiguration struct {
+ XMLName xml.Name `xml:"InitiateWormConfiguration"`
+ RetentionPeriodInDays int `xml:"RetentionPeriodInDays"` // specify retention days
+}
+
+// ExtendWormConfiguration define ExtendWormConfiguration configuration
+type ExtendWormConfiguration struct {
+ XMLName xml.Name `xml:"ExtendWormConfiguration"`
+ RetentionPeriodInDays int `xml:"RetentionPeriodInDays"` // specify retention days
+}
+
+// WormConfiguration define WormConfiguration
+type WormConfiguration struct {
+ XMLName xml.Name `xml:"WormConfiguration"`
+ WormId string `xml:"WormId,omitempty"`
+ State string `xml:"State,omitempty"`
+ RetentionPeriodInDays int `xml:"RetentionPeriodInDays"` // specify retention days
+ CreationDate string `xml:"CreationDate,omitempty"`
+}
+
+// TransferAccConfiguration define transfer acceleration configuration
+type TransferAccConfiguration struct {
+ XMLName xml.Name `xml:"TransferAccelerationConfiguration"`
+ Enabled bool `xml:"Enabled"`
+}
+
+// ReplicationXML defines simple replication xml, and ReplicationXML is used for "DeleteBucketReplication" in client.go
+type ReplicationXML struct {
+ XMLName xml.Name `xml:"ReplicationRules"`
+ ID string `xml:"ID,omitempty"`
+}
+
+// PutBucketReplication define the bucket replication config
+type PutBucketReplication BucketReplicationXml
+
+// GetBucketReplicationResult define get bucket's replication config
+type GetBucketReplicationResult BucketReplicationXml
+
+// GetBucketReplicationLocationResult define get bucket's replication location
+type GetBucketReplicationLocationResult BucketReplicationLocationXml
+
+// GetBucketReplicationProgressResult define get bucket's replication progress
+type GetBucketReplicationProgressResult BucketReplicationProgressXml
+
+// PutBucketRTC define the bucket rtc config
+type PutBucketRTC BucketRTCXml
+
+// BucketReplicationXml define the xml of bucket replication config
+type BucketReplicationXml struct {
+ XMLName xml.Name `xml:"ReplicationConfiguration"`
+ Rule []ReplicationRule `xml:"Rule,omitempty"`
+}
+
+// BucketReplicationProgressXml define the xml of bucket replication config
+type BucketReplicationProgressXml struct {
+ XMLName xml.Name `xml:"ReplicationProgress"`
+ Rule []ReplicationRule `xml:"Rule,omitempty"`
+}
+
+// BucketRTCXml define the xml of bucket rtc config
+type BucketRTCXml struct {
+ XMLName xml.Name `xml:"ReplicationRule"`
+ RTC *string `xml:"RTC>Status,omitempty"`
+ ID string `xml:"ID,omitempty"`
+}
+
+// ReplicationRule define the xml of bucket replication config rule
+type ReplicationRule struct {
+ ID string `xml:"ID,omitempty"`
+ RTC *string `xml:"RTC>Status,omitempty"`
+ PrefixSet *ReplicationRulePrefix `xml:"PrefixSet,omitempty"`
+ Action string `xml:"Action,omitempty"`
+ Destination *ReplicationRuleDestination `xml:"Destination,omitempty"`
+ HistoricalObjectReplication string `xml:"HistoricalObjectReplication,omitempty"`
+ Status string `xml:"Status,omitempty"`
+ SyncRole string `xml:"SyncRole,omitempty"`
+ SourceSelectionCriteria *string `xml:"SourceSelectionCriteria>SseKmsEncryptedObjects>Status,omitempty"`
+ EncryptionConfiguration *string `xml:"EncryptionConfiguration>ReplicaKmsKeyID,omitempty"`
+ Progress *ReplicationRuleProgress `xml:"Progress,omitempty"`
+ HistoricalObject string `xml:"HistoricalObject,omitempty"`
+}
+
+type ReplicationRulePrefix struct {
+ Prefix []*string `xml:"Prefix,omitempty"`
+}
+
+type ReplicationRuleDestination struct {
+ Bucket string `xml:"Bucket,omitempty"`
+ Location string `xml:"Location,omitempty"`
+ TransferType string `xml:"TransferType,omitempty"`
+}
+
+// BucketReplicationLocationXml define the xml of bucket replication location info
+type BucketReplicationLocationXml struct {
+ XMLName xml.Name `xml:"ReplicationLocation"`
+ Location []string `xml:"Location,omitempty"`
+ LocationTransferType []ReplicationLocationTransferType `xml:"LocationTransferTypeConstraint>LocationTransferType,omitempty"`
+ RTCLocation []string `xml:"LocationRTCConstraint>Location,omitempty"`
+}
+
+type ReplicationLocation struct {
+ Location string `xml:"Location,omitempty"`
+}
+
+type ReplicationLocationTransferType struct {
+ Location string `xml:"Location,omitempty"`
+ TransferTypes string `xml:"TransferTypes>Type,omitempty"`
+}
+
+type ReplicationRuleProgress struct {
+ HistoricalObject string `xml:"HistoricalObject,omitempty"`
+ NewObject string `xml:"NewObject,omitempty"`
+}
+
+// CnameConfigurationXML define cname configuration
+type CnameConfigurationXML struct {
+ XMLName xml.Name `xml:"BucketCnameConfiguration"`
+ Domain string `xml:"Cname>Domain"`
+}
+
+type PutBucketCname PutBucketCnameXml
+
+// PutBucketCnameXml define cname configuration
+type PutBucketCnameXml struct {
+ XMLName xml.Name `xml:"BucketCnameConfiguration"`
+ Cname string `xml:"Cname>Domain"`
+ CertificateConfiguration *CertificateConfiguration `xml:"Cname>CertificateConfiguration"`
+}
+
+type CertificateConfiguration struct {
+ CertId string `xml:"CertId,omitempty"`
+ Certificate string `xml:"Certificate,omitempty"`
+ PrivateKey string `xml:"PrivateKey,omitempty"`
+ PreviousCertId string `xml:"PreviousCertId,omitempty"`
+ Force bool `xml:"Force,omitempty"`
+ DeleteCertificate bool `xml:"DeleteCertificate,omitempty"`
+}
+
+// CnameTokenXML define cname token information
+type CnameTokenXML struct {
+ XMLName xml.Name `xml:"CnameToken"`
+ Bucket string `xml:"Bucket,omitempty"`
+ Cname string `xml:"Cname,omitempty"`
+ Token string `xml:"Token,omitempty"`
+ ExpireTime string `xml:"ExpireTime,omitempty"`
+}
+
+// CreateBucketCnameTokenResult defines result object for CreateBucketCnameToken request
+type CreateBucketCnameTokenResult CnameTokenXML
+
+// GetBucketCnameTokenResult defines result object for GetBucketCnameToken request
+type GetBucketCnameTokenResult CnameTokenXML
+
+// GetMetaQueryStatusResult defines result for GetMetaQueryStatus result
+type GetMetaQueryStatusResult GetMetaQueryStatusResultXml
+
+// GetMetaQueryStatusResultXml define get meta query status information
+type GetMetaQueryStatusResultXml struct {
+ XMLName xml.Name `xml:"MetaQueryStatus"`
+ State string `xml:"State"`
+ Phase string `xml:"Phase"`
+ CreateTime string `xml:"CreateTime"`
+ UpdateTime string `xml:"UpdateTime"`
+}
+
+// MetaQuery defines meta query struct
+type MetaQuery struct {
+ XMLName xml.Name `xml:"MetaQuery"`
+ NextToken string `xml:"NextToken,omitempty"`
+ MaxResults int64 `xml:"MaxResults,omitempty"`
+ Query string `xml:"Query"`
+ Sort string `xml:"Sort,omitempty"`
+ Order string `xml:"Order,omitempty"`
+ Aggregations []MetaQueryAggregationRequest `xml:"Aggregations>Aggregation,omitempty"`
+}
+
+// MetaQueryAggregationRequest defines meta query aggregation request
+type MetaQueryAggregationRequest struct {
+ XMLName xml.Name `xml:"Aggregation"`
+ Field string `xml:"Field,omitempty"`
+ Operation string `xml:"Operation,omitempty"`
+}
+
+// MetaQueryAggregationResponse defines meta query aggregation response
+type MetaQueryAggregationResponse struct {
+ XMLName xml.Name `xml:"Aggregation"`
+ Field string `xml:"Field,omitempty"`
+ Operation string `xml:"Operation,omitempty"`
+ Value float64 `xml:"Value,omitempty"`
+ Groups []MetaQueryGroup `xml:"Groups>Group,omitempty"`
+}
+
+// DoMetaQueryResult defines result for DoMetaQuery result
+type DoMetaQueryResult DoMetaQueryResultXml
+
+// DoMetaQueryResultXml defines do meta query information
+type DoMetaQueryResultXml struct {
+ XMLName xml.Name `xml:"MetaQuery"`
+ NextToken string `xml:"NextToken,omitempty"` // next token
+ Files []MetaQueryFile `xml:"Files>File,omitempty"` // file
+ Aggregations []MetaQueryAggregationResponse `xml:"Aggregations>Aggregation,omitempty"'` // Aggregation
+}
+
+// MetaQueryFile defines do meta query result file information
+type MetaQueryFile struct {
+ XMLName xml.Name `xml:"File"`
+ Filename string `xml:"Filename"` //file name
+ Size int64 `xml:"Size"` // file size
+ FileModifiedTime string `xml:"FileModifiedTime"` // file Modified Time
+ OssObjectType string `xml:"OSSObjectType"` // Oss Object Type
+ OssStorageClass string `xml:"OSSStorageClass"` // Oss Storage Class
+ ObjectACL string `xml:"ObjectACL"` // Object Acl
+ ETag string `xml:"ETag"` // ETag
+ OssCRC64 string `xml:"OSSCRC64"` // Oss CRC64
+ OssTaggingCount int64 `xml:"OSSTaggingCount,omitempty"` // Oss Tagging Count
+ OssTagging []MetaQueryTagging `xml:"OSSTagging>Tagging,omitempty"` // Tagging
+ OssUserMeta []MetaQueryUserMeta `xml:"OSSUserMeta>UserMeta,omitempty"` // UserMeta
+ ServerSideEncryption string `xml:"ServerSideEncryption,omitempty"` //Server Side Encryption
+ ServerSideEncryptionCustomerAlgorithm string `xml:"ServerSideEncryptionCustomerAlgorithm,omitempty"` // Server Side Encryption Customer Algorithm
+}
+
+// MetaQueryTagging defines do meta query result tagging information
+type MetaQueryTagging struct {
+ XMLName xml.Name `xml:"Tagging"`
+ Key string `xml:"Key"`
+ Value string `xml:"Value"`
+}
+
+// MetaQueryUserMeta defines do meta query result user meta information
+type MetaQueryUserMeta struct {
+ XMLName xml.Name `xml:"UserMeta"`
+ Key string `xml:"Key"`
+ Value string `xml:"Value"`
+}
+
+// MetaQueryGroup defines do meta query result group information
+type MetaQueryGroup struct {
+ XMLName xml.Name `xml:"Group"`
+ Value string `xml:"Value"`
+ Count int64 `xml:"Count"`
+}
+
+// GetBucketAccessMonitorResult define config for get bucket access monitor
+type GetBucketAccessMonitorResult BucketAccessMonitorXml
+
+// PutBucketAccessMonitor define the xml of bucket access monitor config
+type PutBucketAccessMonitor BucketAccessMonitorXml
+
+// BucketAccessMonitorXml define get bucket access monitor information
+type BucketAccessMonitorXml struct {
+ XMLName xml.Name `xml:"AccessMonitorConfiguration"`
+ Status string `xml:"Status"` // access monitor status
+}
+
+// ListBucketCnameResult define the cname list of the bucket
+type ListBucketCnameResult BucketCnameXml
+
+// BucketCnameXml define get the bucket cname information
+type BucketCnameXml struct {
+ XMLName xml.Name `xml:"ListCnameResult"`
+ Bucket string `xml:"Bucket"`
+ Owner string `xml:"Owner"`
+ Cname []Cname `xml:"Cname"`
+}
+
+// Cname define the cname information
+type Cname struct {
+ Domain string `xml:"Domain"`
+ LastModified string `xml:"LastModified"`
+ Status string `xml:"Status"`
+ Certificate Certificate `xml:"Certificate"`
+}
+
+// Certificate define Details of domain name certificate
+type Certificate struct {
+ Type string `xml:"Type"`
+ CertId string `xml:"CertId"`
+ Status string `xml:"Status"`
+ CreationDate string `xml:"CreationDate"`
+ Fingerprint string `xml:"Fingerprint"`
+ ValidStartDate string `xml:"ValidStartDate"`
+ ValidEndDate string `xml:"ValidEndDate"`
+}
+
+// GetBucketResourceGroupResult define resource group for the bucket
+type GetBucketResourceGroupResult BucketResourceGroupXml
+
+// PutBucketResourceGroup define the xml of bucket's resource group config
+type PutBucketResourceGroup BucketResourceGroupXml
+
+// BucketResourceGroupXml define the information of the bucket's resource group
+type BucketResourceGroupXml struct {
+ XMLName xml.Name `xml:"BucketResourceGroupConfiguration"`
+ ResourceGroupId string `xml:"ResourceGroupId"` // resource groupId
+}
+
+// GetBucketStyleResult define style for the bucket
+type GetBucketStyleResult BucketStyleXml
+
+// GetBucketListStyleResult define the list style for the bucket
+type GetBucketListStyleResult BucketListStyleXml
+
+// BucketListStyleXml define the list style of the bucket
+type BucketListStyleXml struct {
+ XMLName xml.Name `xml:"StyleList"`
+ Style []BucketStyleXml `xml:"Style,omitempty"` // style
+}
+
+// BucketStyleXml define the information of the bucket's style
+type BucketStyleXml struct {
+ XMLName xml.Name `xml:"Style"`
+ Name string `xml:"Name,omitempty"` // style name
+ Content string `xml:"Content"` // style content
+ CreateTime string `xml:"CreateTime,omitempty"` // style create time
+ LastModifyTime string `xml:"LastModifyTime,omitempty"` // style last modify time
+}
+
+// DescribeRegionsResult define get the describe regions result
+type DescribeRegionsResult RegionInfoList
+
+type RegionInfo struct {
+ Region string `xml:"Region"`
+ InternetEndpoint string `xml:"InternetEndpoint"`
+ InternalEndpoint string `xml:"InternalEndpoint"`
+ AccelerateEndpoint string `xml:"AccelerateEndpoint"`
+}
+
+type RegionInfoList struct {
+ XMLName xml.Name `xml:"RegionInfoList"`
+ Regions []RegionInfo `xml:"RegionInfo"`
+}
+
+//PutBucketResponseHeader define the xml of bucket's response header config
+type PutBucketResponseHeader ResponseHeaderXml
+
+//GetBucketResponseHeaderResult define the xml of bucket's response header result
+type GetBucketResponseHeaderResult ResponseHeaderXml
+
+type ResponseHeaderXml struct {
+ XMLName xml.Name `xml:"ResponseHeaderConfiguration"`
+ Rule []ResponseHeaderRule `xml:Rule,omitempty"` // rule
+}
+
+type ResponseHeaderRule struct {
+ Name string `xml:"Name"` // rule name
+ Filters ResponseHeaderRuleFilters `xml:"Filters,omitempty"` // rule filters Operation
+ HideHeaders ResponseHeaderRuleHeaders `xml:"HideHeaders,omitempty"` // rule hide header
+}
+
+type ResponseHeaderRuleFilters struct {
+ Operation []string `xml:"Operation,omitempty"`
+}
+
+type ResponseHeaderRuleHeaders struct {
+ Header []string `xml:"Header,omitempty"`
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/upload.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/upload.go
new file mode 100644
index 00000000000..becc6433dcf
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/upload.go
@@ -0,0 +1,578 @@
+package oss
+
+import (
+ "bytes"
+ "crypto/md5"
+ "encoding/base64"
+ "encoding/hex"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path/filepath"
+ "time"
+)
+
+// UploadFile is multipart file upload.
+//
+// objectKey the object name.
+// filePath the local file path to upload.
+// partSize the part size in byte.
+// options the options for uploading object.
+//
+// error it's nil if the operation succeeds, otherwise it's an error object.
+//
+func (bucket Bucket) UploadFile(objectKey, filePath string, partSize int64, options ...Option) error {
+ if partSize < MinPartSize || partSize > MaxPartSize {
+ return errors.New("oss: part size invalid range (100KB, 5GB]")
+ }
+
+ cpConf := getCpConfig(options)
+ routines := getRoutines(options)
+
+ if cpConf != nil && cpConf.IsEnable {
+ cpFilePath := getUploadCpFilePath(cpConf, filePath, bucket.BucketName, objectKey)
+ if cpFilePath != "" {
+ return bucket.uploadFileWithCp(objectKey, filePath, partSize, options, cpFilePath, routines)
+ }
+ }
+
+ return bucket.uploadFile(objectKey, filePath, partSize, options, routines)
+}
+
+func getUploadCpFilePath(cpConf *cpConfig, srcFile, destBucket, destObject string) string {
+ if cpConf.FilePath == "" && cpConf.DirPath != "" {
+ dest := fmt.Sprintf("oss://%v/%v", destBucket, destObject)
+ absPath, _ := filepath.Abs(srcFile)
+ cpFileName := getCpFileName(absPath, dest, "")
+ cpConf.FilePath = cpConf.DirPath + string(os.PathSeparator) + cpFileName
+ }
+ return cpConf.FilePath
+}
+
+// ----- concurrent upload without checkpoint -----
+
+// getCpConfig gets checkpoint configuration
+func getCpConfig(options []Option) *cpConfig {
+ cpcOpt, err := FindOption(options, checkpointConfig, nil)
+ if err != nil || cpcOpt == nil {
+ return nil
+ }
+
+ return cpcOpt.(*cpConfig)
+}
+
+// getCpFileName return the name of the checkpoint file
+func getCpFileName(src, dest, versionId string) string {
+ md5Ctx := md5.New()
+ md5Ctx.Write([]byte(src))
+ srcCheckSum := hex.EncodeToString(md5Ctx.Sum(nil))
+
+ md5Ctx.Reset()
+ md5Ctx.Write([]byte(dest))
+ destCheckSum := hex.EncodeToString(md5Ctx.Sum(nil))
+
+ if versionId == "" {
+ return fmt.Sprintf("%v-%v.cp", srcCheckSum, destCheckSum)
+ }
+
+ md5Ctx.Reset()
+ md5Ctx.Write([]byte(versionId))
+ versionCheckSum := hex.EncodeToString(md5Ctx.Sum(nil))
+ return fmt.Sprintf("%v-%v-%v.cp", srcCheckSum, destCheckSum, versionCheckSum)
+}
+
+// getRoutines gets the routine count. by default it's 1.
+func getRoutines(options []Option) int {
+ rtnOpt, err := FindOption(options, routineNum, nil)
+ if err != nil || rtnOpt == nil {
+ return 1
+ }
+
+ rs := rtnOpt.(int)
+ if rs < 1 {
+ rs = 1
+ } else if rs > 100 {
+ rs = 100
+ }
+
+ return rs
+}
+
+// getPayer return the payer of the request
+func getPayer(options []Option) string {
+ payerOpt, err := FindOption(options, HTTPHeaderOssRequester, nil)
+ if err != nil || payerOpt == nil {
+ return ""
+ }
+ return payerOpt.(string)
+}
+
+// GetProgressListener gets the progress callback
+func GetProgressListener(options []Option) ProgressListener {
+ isSet, listener, _ := IsOptionSet(options, progressListener)
+ if !isSet {
+ return nil
+ }
+ return listener.(ProgressListener)
+}
+
+// uploadPartHook is for testing usage
+type uploadPartHook func(id int, chunk FileChunk) error
+
+var uploadPartHooker uploadPartHook = defaultUploadPart
+
+func defaultUploadPart(id int, chunk FileChunk) error {
+ return nil
+}
+
+// workerArg defines worker argument structure
+type workerArg struct {
+ bucket *Bucket
+ filePath string
+ imur InitiateMultipartUploadResult
+ options []Option
+ hook uploadPartHook
+}
+
+// worker is the worker coroutine function
+type defaultUploadProgressListener struct {
+}
+
+// ProgressChanged no-ops
+func (listener *defaultUploadProgressListener) ProgressChanged(event *ProgressEvent) {
+}
+
+func worker(id int, arg workerArg, jobs <-chan FileChunk, results chan<- UploadPart, failed chan<- error, die <-chan bool) {
+ for chunk := range jobs {
+ if err := arg.hook(id, chunk); err != nil {
+ failed <- err
+ break
+ }
+ var respHeader http.Header
+ p := Progress(&defaultUploadProgressListener{})
+ opts := make([]Option, len(arg.options)+2)
+ opts = append(opts, arg.options...)
+
+ // use defaultUploadProgressListener
+ opts = append(opts, p, GetResponseHeader(&respHeader))
+
+ startT := time.Now().UnixNano() / 1000 / 1000 / 1000
+ part, err := arg.bucket.UploadPartFromFile(arg.imur, arg.filePath, chunk.Offset, chunk.Size, chunk.Number, opts...)
+ endT := time.Now().UnixNano() / 1000 / 1000 / 1000
+ if err != nil {
+ arg.bucket.Client.Config.WriteLog(Debug, "upload part error,cost:%d second,part number:%d,request id:%s,error:%s\n", endT-startT, chunk.Number, GetRequestId(respHeader), err.Error())
+ failed <- err
+ break
+ }
+ select {
+ case <-die:
+ return
+ default:
+ }
+ results <- part
+ }
+}
+
+// scheduler function
+func scheduler(jobs chan FileChunk, chunks []FileChunk) {
+ for _, chunk := range chunks {
+ jobs <- chunk
+ }
+ close(jobs)
+}
+
+func getTotalBytes(chunks []FileChunk) int64 {
+ var tb int64
+ for _, chunk := range chunks {
+ tb += chunk.Size
+ }
+ return tb
+}
+
+// uploadFile is a concurrent upload, without checkpoint
+func (bucket Bucket) uploadFile(objectKey, filePath string, partSize int64, options []Option, routines int) error {
+ listener := GetProgressListener(options)
+
+ chunks, err := SplitFileByPartSize(filePath, partSize)
+ if err != nil {
+ return err
+ }
+
+ partOptions := ChoiceTransferPartOption(options)
+ completeOptions := ChoiceCompletePartOption(options)
+ abortOptions := ChoiceAbortPartOption(options)
+
+ // Initialize the multipart upload
+ imur, err := bucket.InitiateMultipartUpload(objectKey, options...)
+ if err != nil {
+ return err
+ }
+
+ jobs := make(chan FileChunk, len(chunks))
+ results := make(chan UploadPart, len(chunks))
+ failed := make(chan error)
+ die := make(chan bool)
+
+ var completedBytes int64
+ totalBytes := getTotalBytes(chunks)
+ event := newProgressEvent(TransferStartedEvent, 0, totalBytes, 0)
+ publishProgress(listener, event)
+
+ // Start the worker coroutine
+ arg := workerArg{&bucket, filePath, imur, partOptions, uploadPartHooker}
+ for w := 1; w <= routines; w++ {
+ go worker(w, arg, jobs, results, failed, die)
+ }
+
+ // Schedule the jobs
+ go scheduler(jobs, chunks)
+
+ // Waiting for the upload finished
+ completed := 0
+ parts := make([]UploadPart, len(chunks))
+ for completed < len(chunks) {
+ select {
+ case part := <-results:
+ completed++
+ parts[part.PartNumber-1] = part
+ completedBytes += chunks[part.PartNumber-1].Size
+
+ // why RwBytes in ProgressEvent is 0 ?
+ // because read or write event has been notified in teeReader.Read()
+ event = newProgressEvent(TransferDataEvent, completedBytes, totalBytes, chunks[part.PartNumber-1].Size)
+ publishProgress(listener, event)
+ case err := <-failed:
+ close(die)
+ event = newProgressEvent(TransferFailedEvent, completedBytes, totalBytes, 0)
+ publishProgress(listener, event)
+ bucket.AbortMultipartUpload(imur, abortOptions...)
+ return err
+ }
+
+ if completed >= len(chunks) {
+ break
+ }
+ }
+
+ event = newProgressEvent(TransferCompletedEvent, completedBytes, totalBytes, 0)
+ publishProgress(listener, event)
+
+ // Complete the multpart upload
+ _, err = bucket.CompleteMultipartUpload(imur, parts, completeOptions...)
+ if err != nil {
+ bucket.AbortMultipartUpload(imur, abortOptions...)
+ return err
+ }
+ return nil
+}
+
+// ----- concurrent upload with checkpoint -----
+const uploadCpMagic = "FE8BB4EA-B593-4FAC-AD7A-2459A36E2E62"
+
+type uploadCheckpoint struct {
+ Magic string // Magic
+ MD5 string // Checkpoint file content's MD5
+ FilePath string // Local file path
+ FileStat cpStat // File state
+ ObjectKey string // Key
+ UploadID string // Upload ID
+ Parts []cpPart // All parts of the local file
+ CallbackVal string
+ CallbackBody *[]byte
+}
+
+type cpStat struct {
+ Size int64 // File size
+ LastModified time.Time // File's last modified time
+ MD5 string // Local file's MD5
+}
+
+type cpPart struct {
+ Chunk FileChunk // File chunk
+ Part UploadPart // Uploaded part
+ IsCompleted bool // Upload complete flag
+}
+
+// isValid checks if the uploaded data is valid---it's valid when the file is not updated and the checkpoint data is valid.
+func (cp uploadCheckpoint) isValid(filePath string,options []Option) (bool, error) {
+
+ callbackVal, _ := FindOption(options, HTTPHeaderOssCallback, "")
+ if callbackVal != "" && cp.CallbackVal != callbackVal {
+ return false, nil
+ }
+ callbackBody, _ := FindOption(options, responseBody, nil)
+ if callbackBody != nil{
+ body, _ := json.Marshal(callbackBody)
+ if bytes.Equal(*cp.CallbackBody, body) {
+ return false, nil
+ }
+ }
+ // Compare the CP's magic number and MD5.
+ cpb := cp
+ cpb.MD5 = ""
+ js, _ := json.Marshal(cpb)
+ sum := md5.Sum(js)
+ b64 := base64.StdEncoding.EncodeToString(sum[:])
+
+ if cp.Magic != uploadCpMagic || b64 != cp.MD5 {
+ return false, nil
+ }
+
+ // Make sure if the local file is updated.
+ fd, err := os.Open(filePath)
+ if err != nil {
+ return false, err
+ }
+ defer fd.Close()
+
+ st, err := fd.Stat()
+ if err != nil {
+ return false, err
+ }
+
+ md, err := calcFileMD5(filePath)
+ if err != nil {
+ return false, err
+ }
+
+ // Compare the file size, file's last modified time and file's MD5
+ if cp.FileStat.Size != st.Size() ||
+ !cp.FileStat.LastModified.Equal(st.ModTime()) ||
+ cp.FileStat.MD5 != md {
+ return false, nil
+ }
+
+ return true, nil
+}
+
+// load loads from the file
+func (cp *uploadCheckpoint) load(filePath string) error {
+ contents, err := ioutil.ReadFile(filePath)
+ if err != nil {
+ return err
+ }
+
+ err = json.Unmarshal(contents, cp)
+ return err
+}
+
+// dump dumps to the local file
+func (cp *uploadCheckpoint) dump(filePath string) error {
+ bcp := *cp
+
+ // Calculate MD5
+ bcp.MD5 = ""
+ js, err := json.Marshal(bcp)
+ if err != nil {
+ return err
+ }
+ sum := md5.Sum(js)
+ b64 := base64.StdEncoding.EncodeToString(sum[:])
+ bcp.MD5 = b64
+
+ // Serialization
+ js, err = json.Marshal(bcp)
+ if err != nil {
+ return err
+ }
+
+ // Dump
+ return ioutil.WriteFile(filePath, js, FilePermMode)
+}
+
+// updatePart updates the part status
+func (cp *uploadCheckpoint) updatePart(part UploadPart) {
+ cp.Parts[part.PartNumber-1].Part = part
+ cp.Parts[part.PartNumber-1].IsCompleted = true
+}
+
+// todoParts returns unfinished parts
+func (cp *uploadCheckpoint) todoParts() []FileChunk {
+ fcs := []FileChunk{}
+ for _, part := range cp.Parts {
+ if !part.IsCompleted {
+ fcs = append(fcs, part.Chunk)
+ }
+ }
+ return fcs
+}
+
+// allParts returns all parts
+func (cp *uploadCheckpoint) allParts() []UploadPart {
+ ps := []UploadPart{}
+ for _, part := range cp.Parts {
+ ps = append(ps, part.Part)
+ }
+ return ps
+}
+
+// getCompletedBytes returns completed bytes count
+func (cp *uploadCheckpoint) getCompletedBytes() int64 {
+ var completedBytes int64
+ for _, part := range cp.Parts {
+ if part.IsCompleted {
+ completedBytes += part.Chunk.Size
+ }
+ }
+ return completedBytes
+}
+
+// calcFileMD5 calculates the MD5 for the specified local file
+func calcFileMD5(filePath string) (string, error) {
+ return "", nil
+}
+
+// prepare initializes the multipart upload
+func prepare(cp *uploadCheckpoint, objectKey, filePath string, partSize int64, bucket *Bucket, options []Option) error {
+ // CP
+ cp.Magic = uploadCpMagic
+ cp.FilePath = filePath
+ cp.ObjectKey = objectKey
+
+ // Local file
+ fd, err := os.Open(filePath)
+ if err != nil {
+ return err
+ }
+ defer fd.Close()
+
+ st, err := fd.Stat()
+ if err != nil {
+ return err
+ }
+ cp.FileStat.Size = st.Size()
+ cp.FileStat.LastModified = st.ModTime()
+ callbackVal, _ := FindOption(options, HTTPHeaderOssCallback, "")
+ cp.CallbackVal = callbackVal.(string)
+ callbackBody, _ := FindOption(options, responseBody, nil)
+ if callbackBody != nil {
+ body, _ := json.Marshal(callbackBody)
+ cp.CallbackBody = &body
+ }
+ md, err := calcFileMD5(filePath)
+ if err != nil {
+ return err
+ }
+ cp.FileStat.MD5 = md
+
+ // Chunks
+ parts, err := SplitFileByPartSize(filePath, partSize)
+ if err != nil {
+ return err
+ }
+
+ cp.Parts = make([]cpPart, len(parts))
+ for i, part := range parts {
+ cp.Parts[i].Chunk = part
+ cp.Parts[i].IsCompleted = false
+ }
+
+ // Init load
+ imur, err := bucket.InitiateMultipartUpload(objectKey, options...)
+ if err != nil {
+ return err
+ }
+ cp.UploadID = imur.UploadID
+
+ return nil
+}
+
+// complete completes the multipart upload and deletes the local CP files
+func complete(cp *uploadCheckpoint, bucket *Bucket, parts []UploadPart, cpFilePath string, options []Option) error {
+ imur := InitiateMultipartUploadResult{Bucket: bucket.BucketName,
+ Key: cp.ObjectKey, UploadID: cp.UploadID}
+
+ _, err := bucket.CompleteMultipartUpload(imur, parts, options...)
+ if err != nil {
+ if e, ok := err.(ServiceError);ok && (e.StatusCode == 203 || e.StatusCode == 404) {
+ os.Remove(cpFilePath)
+ }
+ return err
+ }
+ os.Remove(cpFilePath)
+ return err
+}
+
+// uploadFileWithCp handles concurrent upload with checkpoint
+func (bucket Bucket) uploadFileWithCp(objectKey, filePath string, partSize int64, options []Option, cpFilePath string, routines int) error {
+ listener := GetProgressListener(options)
+
+ partOptions := ChoiceTransferPartOption(options)
+ completeOptions := ChoiceCompletePartOption(options)
+
+ // Load CP data
+ ucp := uploadCheckpoint{}
+ err := ucp.load(cpFilePath)
+ if err != nil {
+ os.Remove(cpFilePath)
+ }
+
+ // Load error or the CP data is invalid.
+ valid, err := ucp.isValid(filePath,options)
+ if err != nil || !valid {
+ if err = prepare(&ucp, objectKey, filePath, partSize, &bucket, options); err != nil {
+ return err
+ }
+ os.Remove(cpFilePath)
+ }
+
+ chunks := ucp.todoParts()
+ imur := InitiateMultipartUploadResult{
+ Bucket: bucket.BucketName,
+ Key: objectKey,
+ UploadID: ucp.UploadID}
+
+ jobs := make(chan FileChunk, len(chunks))
+ results := make(chan UploadPart, len(chunks))
+ failed := make(chan error)
+ die := make(chan bool)
+
+ completedBytes := ucp.getCompletedBytes()
+
+ // why RwBytes in ProgressEvent is 0 ?
+ // because read or write event has been notified in teeReader.Read()
+ event := newProgressEvent(TransferStartedEvent, completedBytes, ucp.FileStat.Size, 0)
+ publishProgress(listener, event)
+
+ // Start the workers
+ arg := workerArg{&bucket, filePath, imur, partOptions, uploadPartHooker}
+ for w := 1; w <= routines; w++ {
+ go worker(w, arg, jobs, results, failed, die)
+ }
+
+ // Schedule jobs
+ go scheduler(jobs, chunks)
+
+ // Waiting for the job finished
+ completed := 0
+ for completed < len(chunks) {
+ select {
+ case part := <-results:
+ completed++
+ ucp.updatePart(part)
+ ucp.dump(cpFilePath)
+ completedBytes += ucp.Parts[part.PartNumber-1].Chunk.Size
+ event = newProgressEvent(TransferDataEvent, completedBytes, ucp.FileStat.Size, ucp.Parts[part.PartNumber-1].Chunk.Size)
+ publishProgress(listener, event)
+ case err := <-failed:
+ close(die)
+ event = newProgressEvent(TransferFailedEvent, completedBytes, ucp.FileStat.Size, 0)
+ publishProgress(listener, event)
+ return err
+ }
+
+ if completed >= len(chunks) {
+ break
+ }
+ }
+
+ event = newProgressEvent(TransferCompletedEvent, completedBytes, ucp.FileStat.Size, 0)
+ publishProgress(listener, event)
+
+ // Complete the multipart upload
+ err = complete(&ucp, &bucket, ucp.allParts(), cpFilePath, completeOptions)
+ return err
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/utils.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/utils.go
new file mode 100644
index 00000000000..8f3f035003a
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/utils.go
@@ -0,0 +1,674 @@
+package oss
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "hash/crc32"
+ "hash/crc64"
+ "io"
+ "net/http"
+ "os"
+ "runtime"
+ "strconv"
+ "strings"
+ "time"
+ "unicode/utf8"
+)
+
+var sys_name string
+var sys_release string
+var sys_machine string
+
+var (
+ escQuot = []byte(""") // shorter than """
+ escApos = []byte("'") // shorter than "'"
+ escAmp = []byte("&")
+ escLT = []byte("<")
+ escGT = []byte(">")
+ escTab = []byte(" ")
+ escNL = []byte("
")
+ escCR = []byte("
")
+ escFFFD = []byte("\uFFFD") // Unicode replacement character
+)
+
+func init() {
+ sys_name = runtime.GOOS
+ sys_release = "-"
+ sys_machine = runtime.GOARCH
+}
+
+// userAgent gets user agent
+// It has the SDK version information, OS information and GO version
+func userAgent() string {
+ sys := getSysInfo()
+ return fmt.Sprintf("aliyun-sdk-go/%s (%s/%s/%s;%s)", Version, sys.name,
+ sys.release, sys.machine, runtime.Version())
+}
+
+type sysInfo struct {
+ name string // OS name such as windows/Linux
+ release string // OS version 2.6.32-220.23.2.ali1089.el5.x86_64 etc
+ machine string // CPU type amd64/x86_64
+}
+
+// getSysInfo gets system info
+// gets the OS information and CPU type
+func getSysInfo() sysInfo {
+ return sysInfo{name: sys_name, release: sys_release, machine: sys_machine}
+}
+
+// GetRangeConfig gets the download range from the options.
+func GetRangeConfig(options []Option) (*UnpackedRange, error) {
+ rangeOpt, err := FindOption(options, HTTPHeaderRange, nil)
+ if err != nil || rangeOpt == nil {
+ return nil, err
+ }
+ return ParseRange(rangeOpt.(string))
+}
+
+// UnpackedRange
+type UnpackedRange struct {
+ HasStart bool // Flag indicates if the start point is specified
+ HasEnd bool // Flag indicates if the end point is specified
+ Start int64 // Start point
+ End int64 // End point
+}
+
+// InvalidRangeError returns invalid range error
+func InvalidRangeError(r string) error {
+ return fmt.Errorf("InvalidRange %s", r)
+}
+
+func GetRangeString(unpackRange UnpackedRange) string {
+ var strRange string
+ if unpackRange.HasStart && unpackRange.HasEnd {
+ strRange = fmt.Sprintf("%d-%d", unpackRange.Start, unpackRange.End)
+ } else if unpackRange.HasStart {
+ strRange = fmt.Sprintf("%d-", unpackRange.Start)
+ } else if unpackRange.HasEnd {
+ strRange = fmt.Sprintf("-%d", unpackRange.End)
+ }
+ return strRange
+}
+
+// ParseRange parse various styles of range such as bytes=M-N
+func ParseRange(normalizedRange string) (*UnpackedRange, error) {
+ var err error
+ hasStart := false
+ hasEnd := false
+ var start int64
+ var end int64
+
+ // Bytes==M-N or ranges=M-N
+ nrSlice := strings.Split(normalizedRange, "=")
+ if len(nrSlice) != 2 || nrSlice[0] != "bytes" {
+ return nil, InvalidRangeError(normalizedRange)
+ }
+
+ // Bytes=M-N,X-Y
+ rSlice := strings.Split(nrSlice[1], ",")
+ rStr := rSlice[0]
+
+ if strings.HasSuffix(rStr, "-") { // M-
+ startStr := rStr[:len(rStr)-1]
+ start, err = strconv.ParseInt(startStr, 10, 64)
+ if err != nil {
+ return nil, InvalidRangeError(normalizedRange)
+ }
+ hasStart = true
+ } else if strings.HasPrefix(rStr, "-") { // -N
+ len := rStr[1:]
+ end, err = strconv.ParseInt(len, 10, 64)
+ if err != nil {
+ return nil, InvalidRangeError(normalizedRange)
+ }
+ if end == 0 { // -0
+ return nil, InvalidRangeError(normalizedRange)
+ }
+ hasEnd = true
+ } else { // M-N
+ valSlice := strings.Split(rStr, "-")
+ if len(valSlice) != 2 {
+ return nil, InvalidRangeError(normalizedRange)
+ }
+ start, err = strconv.ParseInt(valSlice[0], 10, 64)
+ if err != nil {
+ return nil, InvalidRangeError(normalizedRange)
+ }
+ hasStart = true
+ end, err = strconv.ParseInt(valSlice[1], 10, 64)
+ if err != nil {
+ return nil, InvalidRangeError(normalizedRange)
+ }
+ hasEnd = true
+ }
+
+ return &UnpackedRange{hasStart, hasEnd, start, end}, nil
+}
+
+// AdjustRange returns adjusted range, adjust the range according to the length of the file
+func AdjustRange(ur *UnpackedRange, size int64) (start, end int64) {
+ if ur == nil {
+ return 0, size
+ }
+
+ if ur.HasStart && ur.HasEnd {
+ start = ur.Start
+ end = ur.End + 1
+ if ur.Start < 0 || ur.Start >= size || ur.End > size || ur.Start > ur.End {
+ start = 0
+ end = size
+ }
+ } else if ur.HasStart {
+ start = ur.Start
+ end = size
+ if ur.Start < 0 || ur.Start >= size {
+ start = 0
+ }
+ } else if ur.HasEnd {
+ start = size - ur.End
+ end = size
+ if ur.End < 0 || ur.End > size {
+ start = 0
+ end = size
+ }
+ }
+ return
+}
+
+// GetNowSec returns Unix time, the number of seconds elapsed since January 1, 1970 UTC.
+// gets the current time in Unix time, in seconds.
+func GetNowSec() int64 {
+ return time.Now().Unix()
+}
+
+// GetNowNanoSec returns t as a Unix time, the number of nanoseconds elapsed
+// since January 1, 1970 UTC. The result is undefined if the Unix time
+// in nanoseconds cannot be represented by an int64. Note that this
+// means the result of calling UnixNano on the zero Time is undefined.
+// gets the current time in Unix time, in nanoseconds.
+func GetNowNanoSec() int64 {
+ return time.Now().UnixNano()
+}
+
+// GetNowGMT gets the current time in GMT format.
+func GetNowGMT() string {
+ return time.Now().UTC().Format(http.TimeFormat)
+}
+
+// FileChunk is the file chunk definition
+type FileChunk struct {
+ Number int // Chunk number
+ Offset int64 // Chunk offset
+ Size int64 // Chunk size.
+}
+
+// SplitFileByPartNum splits big file into parts by the num of parts.
+// Split the file with specified parts count, returns the split result when error is nil.
+func SplitFileByPartNum(fileName string, chunkNum int) ([]FileChunk, error) {
+ if chunkNum <= 0 || chunkNum > 10000 {
+ return nil, errors.New("chunkNum invalid")
+ }
+
+ file, err := os.Open(fileName)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+
+ stat, err := file.Stat()
+ if err != nil {
+ return nil, err
+ }
+
+ if int64(chunkNum) > stat.Size() {
+ return nil, errors.New("oss: chunkNum invalid")
+ }
+
+ var chunks []FileChunk
+ var chunk = FileChunk{}
+ var chunkN = (int64)(chunkNum)
+ for i := int64(0); i < chunkN; i++ {
+ chunk.Number = int(i + 1)
+ chunk.Offset = i * (stat.Size() / chunkN)
+ if i == chunkN-1 {
+ chunk.Size = stat.Size()/chunkN + stat.Size()%chunkN
+ } else {
+ chunk.Size = stat.Size() / chunkN
+ }
+ chunks = append(chunks, chunk)
+ }
+
+ return chunks, nil
+}
+
+// SplitFileByPartSize splits big file into parts by the size of parts.
+// Splits the file by the part size. Returns the FileChunk when error is nil.
+func SplitFileByPartSize(fileName string, chunkSize int64) ([]FileChunk, error) {
+ if chunkSize <= 0 {
+ return nil, errors.New("chunkSize invalid")
+ }
+
+ file, err := os.Open(fileName)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+
+ stat, err := file.Stat()
+ if err != nil {
+ return nil, err
+ }
+ var chunkN = stat.Size() / chunkSize
+ if chunkN >= 10000 {
+ return nil, errors.New("Too many parts, please increase part size")
+ }
+
+ var chunks []FileChunk
+ var chunk = FileChunk{}
+ for i := int64(0); i < chunkN; i++ {
+ chunk.Number = int(i + 1)
+ chunk.Offset = i * chunkSize
+ chunk.Size = chunkSize
+ chunks = append(chunks, chunk)
+ }
+
+ if stat.Size()%chunkSize > 0 {
+ chunk.Number = len(chunks) + 1
+ chunk.Offset = int64(len(chunks)) * chunkSize
+ chunk.Size = stat.Size() % chunkSize
+ chunks = append(chunks, chunk)
+ }
+
+ return chunks, nil
+}
+
+// GetPartEnd calculates the end position
+func GetPartEnd(begin int64, total int64, per int64) int64 {
+ if begin+per > total {
+ return total - 1
+ }
+ return begin + per - 1
+}
+
+// CrcTable returns the table constructed from the specified polynomial
+var CrcTable = func() *crc64.Table {
+ return crc64.MakeTable(crc64.ECMA)
+}
+
+// CrcTable returns the table constructed from the specified polynomial
+var crc32Table = func() *crc32.Table {
+ return crc32.MakeTable(crc32.IEEE)
+}
+
+// choiceTransferPartOption choices valid option supported by Uploadpart or DownloadPart
+func ChoiceTransferPartOption(options []Option) []Option {
+ var outOption []Option
+
+ listener, _ := FindOption(options, progressListener, nil)
+ if listener != nil {
+ outOption = append(outOption, Progress(listener.(ProgressListener)))
+ }
+
+ payer, _ := FindOption(options, HTTPHeaderOssRequester, nil)
+ if payer != nil {
+ outOption = append(outOption, RequestPayer(PayerType(payer.(string))))
+ }
+
+ versionId, _ := FindOption(options, "versionId", nil)
+ if versionId != nil {
+ outOption = append(outOption, VersionId(versionId.(string)))
+ }
+
+ trafficLimit, _ := FindOption(options, HTTPHeaderOssTrafficLimit, nil)
+ if trafficLimit != nil {
+ speed, _ := strconv.ParseInt(trafficLimit.(string), 10, 64)
+ outOption = append(outOption, TrafficLimitHeader(speed))
+ }
+
+ respHeader, _ := FindOption(options, responseHeader, nil)
+ if respHeader != nil {
+ outOption = append(outOption, GetResponseHeader(respHeader.(*http.Header)))
+ }
+
+ return outOption
+}
+
+// ChoiceCompletePartOption choices valid option supported by CompleteMulitiPart
+func ChoiceCompletePartOption(options []Option) []Option {
+ var outOption []Option
+
+ listener, _ := FindOption(options, progressListener, nil)
+ if listener != nil {
+ outOption = append(outOption, Progress(listener.(ProgressListener)))
+ }
+
+ payer, _ := FindOption(options, HTTPHeaderOssRequester, nil)
+ if payer != nil {
+ outOption = append(outOption, RequestPayer(PayerType(payer.(string))))
+ }
+
+ acl, _ := FindOption(options, HTTPHeaderOssObjectACL, nil)
+ if acl != nil {
+ outOption = append(outOption, ObjectACL(ACLType(acl.(string))))
+ }
+
+ callback, _ := FindOption(options, HTTPHeaderOssCallback, nil)
+ if callback != nil {
+ outOption = append(outOption, Callback(callback.(string)))
+ }
+
+ callbackVar, _ := FindOption(options, HTTPHeaderOssCallbackVar, nil)
+ if callbackVar != nil {
+ outOption = append(outOption, CallbackVar(callbackVar.(string)))
+ }
+
+ respHeader, _ := FindOption(options, responseHeader, nil)
+ if respHeader != nil {
+ outOption = append(outOption, GetResponseHeader(respHeader.(*http.Header)))
+ }
+
+ forbidOverWrite, _ := FindOption(options, HTTPHeaderOssForbidOverWrite, nil)
+ if forbidOverWrite != nil {
+ if forbidOverWrite.(string) == "true" {
+ outOption = append(outOption, ForbidOverWrite(true))
+ } else {
+ outOption = append(outOption, ForbidOverWrite(false))
+ }
+ }
+
+ notification, _ := FindOption(options, HttpHeaderOssNotification, nil)
+ if notification != nil {
+ outOption = append(outOption, SetHeader(HttpHeaderOssNotification, notification))
+ }
+
+ return outOption
+}
+
+// ChoiceAbortPartOption choices valid option supported by AbortMultipartUpload
+func ChoiceAbortPartOption(options []Option) []Option {
+ var outOption []Option
+ payer, _ := FindOption(options, HTTPHeaderOssRequester, nil)
+ if payer != nil {
+ outOption = append(outOption, RequestPayer(PayerType(payer.(string))))
+ }
+
+ respHeader, _ := FindOption(options, responseHeader, nil)
+ if respHeader != nil {
+ outOption = append(outOption, GetResponseHeader(respHeader.(*http.Header)))
+ }
+
+ return outOption
+}
+
+// ChoiceHeadObjectOption choices valid option supported by HeadObject
+func ChoiceHeadObjectOption(options []Option) []Option {
+ var outOption []Option
+
+ // not select HTTPHeaderRange to get whole object length
+ payer, _ := FindOption(options, HTTPHeaderOssRequester, nil)
+ if payer != nil {
+ outOption = append(outOption, RequestPayer(PayerType(payer.(string))))
+ }
+
+ versionId, _ := FindOption(options, "versionId", nil)
+ if versionId != nil {
+ outOption = append(outOption, VersionId(versionId.(string)))
+ }
+
+ respHeader, _ := FindOption(options, responseHeader, nil)
+ if respHeader != nil {
+ outOption = append(outOption, GetResponseHeader(respHeader.(*http.Header)))
+ }
+
+ return outOption
+}
+
+func CheckBucketName(bucketName string) error {
+ nameLen := len(bucketName)
+ if nameLen < 3 || nameLen > 63 {
+ return fmt.Errorf("bucket name %s len is between [3-63],now is %d", bucketName, nameLen)
+ }
+
+ for _, v := range bucketName {
+ if !(('a' <= v && v <= 'z') || ('0' <= v && v <= '9') || v == '-') {
+ return fmt.Errorf("bucket name %s can only include lowercase letters, numbers, and -", bucketName)
+ }
+ }
+ if bucketName[0] == '-' || bucketName[nameLen-1] == '-' {
+ return fmt.Errorf("bucket name %s must start and end with a lowercase letter or number", bucketName)
+ }
+ return nil
+}
+
+func CheckObjectName(objectName string) error {
+ if len(objectName) == 0 {
+ return fmt.Errorf("object name is empty")
+ }
+ return nil
+}
+
+func CheckObjectNameEx(objectName string, strict bool) error {
+ if err := CheckObjectName(objectName); err != nil {
+ return err
+ }
+
+ if strict && strings.HasPrefix(objectName, "?") {
+ return fmt.Errorf("object name is invalid, can't start with '?'")
+ }
+
+ return nil
+}
+
+/*
+ func GetReaderLen(reader io.Reader) (int64, error) {
+ var contentLength int64
+ var err error
+ switch v := reader.(type) {
+ case *bytes.Buffer:
+ contentLength = int64(v.Len())
+ case *bytes.Reader:
+ contentLength = int64(v.Len())
+ case *strings.Reader:
+ contentLength = int64(v.Len())
+ case *os.File:
+ fInfo, fError := v.Stat()
+ if fError != nil {
+ err = fmt.Errorf("can't get reader content length,%s", fError.Error())
+ } else {
+ contentLength = fInfo.Size()
+ }
+ case *io.LimitedReader:
+ contentLength = int64(v.N)
+ case *LimitedReadCloser:
+ contentLength = int64(v.N)
+ default:
+ err = fmt.Errorf("can't get reader content length,unkown reader type")
+ }
+ return contentLength, err
+ }
+*/
+
+func GetReaderLen(reader io.Reader) (int64, error) {
+ var contentLength int64
+ var err error
+ switch v := reader.(type) {
+ case *io.LimitedReader:
+ contentLength = int64(v.N)
+ case *LimitedReadCloser:
+ contentLength = int64(v.N)
+ default:
+ // Len
+ type lenner interface {
+ Len() int
+ }
+ if lr, ok := reader.(lenner); ok {
+ return int64(lr.Len()), nil
+ }
+ // seeker len
+ if s, ok := reader.(io.Seeker); ok {
+ curOffset, err := s.Seek(0, io.SeekCurrent)
+ if err != nil {
+ return 0, err
+ }
+ endOffset, err := s.Seek(0, io.SeekEnd)
+ if err != nil {
+ return 0, err
+ }
+ _, err = s.Seek(curOffset, io.SeekStart)
+ if err != nil {
+ return 0, err
+ }
+ n := endOffset - curOffset
+ if n >= 0 {
+ return n, nil
+ }
+ }
+ //
+ err = fmt.Errorf("can't get reader content length,unkown reader type")
+ }
+ return contentLength, err
+}
+
+func LimitReadCloser(r io.Reader, n int64) io.Reader {
+ var lc LimitedReadCloser
+ lc.R = r
+ lc.N = n
+ return &lc
+}
+
+// LimitedRC support Close()
+type LimitedReadCloser struct {
+ io.LimitedReader
+}
+
+func (lc *LimitedReadCloser) Close() error {
+ if closer, ok := lc.R.(io.ReadCloser); ok {
+ return closer.Close()
+ }
+ return nil
+}
+
+type DiscardReadCloser struct {
+ RC io.ReadCloser
+ Discard int
+}
+
+func (drc *DiscardReadCloser) Read(b []byte) (int, error) {
+ n, err := drc.RC.Read(b)
+ if drc.Discard == 0 || n <= 0 {
+ return n, err
+ }
+
+ if n <= drc.Discard {
+ drc.Discard -= n
+ return 0, err
+ }
+
+ realLen := n - drc.Discard
+ copy(b[0:realLen], b[drc.Discard:n])
+ drc.Discard = 0
+ return realLen, err
+}
+
+func (drc *DiscardReadCloser) Close() error {
+ closer, ok := drc.RC.(io.ReadCloser)
+ if ok {
+ return closer.Close()
+ }
+ return nil
+}
+
+func ConvertEmptyValueToNil(params map[string]interface{}, keys []string) {
+ for _, key := range keys {
+ value, ok := params[key]
+ if ok && value == "" {
+ // convert "" to nil
+ params[key] = nil
+ }
+ }
+}
+
+func EscapeLFString(str string) string {
+ var log bytes.Buffer
+ for i := 0; i < len(str); i++ {
+ if str[i] != '\n' {
+ log.WriteByte(str[i])
+ } else {
+ log.WriteString("\\n")
+ }
+ }
+ return log.String()
+}
+
+// EscapeString writes to p the properly escaped XML equivalent
+// of the plain text data s.
+func EscapeXml(s string) string {
+ var p strings.Builder
+ var esc []byte
+ hextable := "0123456789ABCDEF"
+ escPattern := []byte("")
+ last := 0
+ for i := 0; i < len(s); {
+ r, width := utf8.DecodeRuneInString(s[i:])
+ i += width
+ switch r {
+ case '"':
+ esc = escQuot
+ case '\'':
+ esc = escApos
+ case '&':
+ esc = escAmp
+ case '<':
+ esc = escLT
+ case '>':
+ esc = escGT
+ case '\t':
+ esc = escTab
+ case '\n':
+ esc = escNL
+ case '\r':
+ esc = escCR
+ default:
+ if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) {
+ if r >= 0x00 && r < 0x20 {
+ escPattern[3] = hextable[r>>4]
+ escPattern[4] = hextable[r&0x0f]
+ esc = escPattern
+ } else {
+ esc = escFFFD
+ }
+ break
+ }
+ continue
+ }
+ p.WriteString(s[last : i-width])
+ p.Write(esc)
+ last = i
+ }
+ p.WriteString(s[last:])
+ return p.String()
+}
+
+// Decide whether the given rune is in the XML Character Range, per
+// the Char production of https://www.xml.com/axml/testaxml.htm,
+// Section 2.2 Characters.
+func isInCharacterRange(r rune) (inrange bool) {
+ return r == 0x09 ||
+ r == 0x0A ||
+ r == 0x0D ||
+ r >= 0x20 && r <= 0xD7FF ||
+ r >= 0xE000 && r <= 0xFFFD ||
+ r >= 0x10000 && r <= 0x10FFFF
+}
+
+func isVerifyObjectStrict(config *Config) bool {
+ if config != nil {
+ if config.AuthVersion == AuthV2 || config.AuthVersion == AuthV4 {
+ return false
+ }
+ return config.VerifyObjectStrict
+ }
+ return true
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/LICENSE b/vendor/github.com/baidubce/bce-sdk-go/LICENSE
new file mode 100644
index 00000000000..f433b1a53f5
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/LICENSE
@@ -0,0 +1,177 @@
+
+ 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
diff --git a/vendor/github.com/baidubce/bce-sdk-go/auth/credentials.go b/vendor/github.com/baidubce/bce-sdk-go/auth/credentials.go
new file mode 100644
index 00000000000..fb1e415fd5c
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/auth/credentials.go
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// credentials.go - the credentials data structure definition
+
+// Package auth implements the authorization functionality for BCE.
+// It use the BCE access key ID and secret access key with the specific sign algorithm to generate
+// the authorization string. It also supports the temporary authorization by the STS token.
+package auth
+
+import "errors"
+
+// BceCredentials define the data structure for authorization
+type BceCredentials struct {
+ AccessKeyId string // access key id to the service
+ SecretAccessKey string // secret access key to the service
+ SessionToken string // session token generate by the STS service
+}
+
+func (b *BceCredentials) String() string {
+ str := "ak: " + b.AccessKeyId + ", sk: " + b.SecretAccessKey
+ if len(b.SessionToken) != 0 {
+ return str + ", sessionToken: " + b.SessionToken
+ }
+ return str
+}
+
+func NewBceCredentials(ak, sk string) (*BceCredentials, error) {
+ if len(ak) == 0 {
+ return nil, errors.New("accessKeyId should not be empty")
+ }
+ if len(sk) == 0 {
+ return nil, errors.New("secretKey should not be empty")
+ }
+
+ return &BceCredentials{ak, sk, ""}, nil
+}
+
+func NewSessionBceCredentials(ak, sk, token string) (*BceCredentials, error) {
+ if len(token) == 0 {
+ return nil, errors.New("sessionToken should not be empty")
+ }
+
+ result, err := NewBceCredentials(ak, sk)
+ if err != nil {
+ return nil, err
+ }
+ result.SessionToken = token
+
+ return result, nil
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/auth/signer.go b/vendor/github.com/baidubce/bce-sdk-go/auth/signer.go
new file mode 100644
index 00000000000..c5c01d1a5b1
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/auth/signer.go
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// signer.go - implement the specific sign algorithm of BCE V1 protocol
+
+package auth
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+
+ "github.com/baidubce/bce-sdk-go/http"
+ "github.com/baidubce/bce-sdk-go/util"
+ "github.com/baidubce/bce-sdk-go/util/log"
+)
+
+var (
+ BCE_AUTH_VERSION = "bce-auth-v1"
+ SIGN_JOINER = "\n"
+ SIGN_HEADER_JOINER = ";"
+ DEFAULT_EXPIRE_SECONDS = 1800
+ DEFAULT_HEADERS_TO_SIGN = map[string]struct{}{
+ strings.ToLower(http.HOST): {},
+ strings.ToLower(http.CONTENT_LENGTH): {},
+ strings.ToLower(http.CONTENT_TYPE): {},
+ strings.ToLower(http.CONTENT_MD5): {},
+ }
+)
+
+// Signer abstracts the entity that implements the `Sign` method
+type Signer interface {
+ // Sign the given Request with the Credentials and SignOptions
+ Sign(*http.Request, *BceCredentials, *SignOptions)
+}
+
+// SignOptions defines the data structure used by Signer
+type SignOptions struct {
+ HeadersToSign map[string]struct{}
+ Timestamp int64
+ ExpireSeconds int
+}
+
+func (opt *SignOptions) String() string {
+ return fmt.Sprintf(`SignOptions [
+ HeadersToSign=%s;
+ Timestamp=%d;
+ ExpireSeconds=%d
+ ]`, opt.HeadersToSign, opt.Timestamp, opt.ExpireSeconds)
+}
+
+// BceV1Signer implements the v1 sign algorithm
+type BceV1Signer struct{}
+
+// Sign - generate the authorization string from the BceCredentials and SignOptions
+//
+// PARAMS:
+// - req: *http.Request for this sign
+// - cred: *BceCredentials to access the serice
+// - opt: *SignOptions for this sign algorithm
+func (b *BceV1Signer) Sign(req *http.Request, cred *BceCredentials, opt *SignOptions) {
+ if req == nil {
+ log.Fatal("request should not be null for sign")
+ return
+ }
+ if cred == nil {
+ log.Fatal("credentials should not be null for sign")
+ return
+ }
+
+ // Prepare parameters
+ accessKeyId := cred.AccessKeyId
+ secretAccessKey := cred.SecretAccessKey
+ signDate := util.FormatISO8601Date(util.NowUTCSeconds())
+ // Modify the sign time if it is not the default value but specified by client
+ if opt.Timestamp != 0 {
+ signDate = util.FormatISO8601Date(opt.Timestamp)
+ }
+
+ // Set security token if using session credentials and session token not in param
+ if len(cred.SessionToken) != 0 && req.Param(http.BCE_SECURITY_TOKEN) == "" {
+ req.SetHeader(http.BCE_SECURITY_TOKEN, cred.SessionToken)
+ }
+
+ // Prepare the canonical request components
+ signKeyInfo := fmt.Sprintf("%s/%s/%s/%d",
+ BCE_AUTH_VERSION,
+ accessKeyId,
+ signDate,
+ opt.ExpireSeconds)
+ signKey := util.HmacSha256Hex(secretAccessKey, signKeyInfo)
+ canonicalUri := getCanonicalURIPath(req.Uri())
+ canonicalQueryString := getCanonicalQueryString(req.Params())
+ canonicalHeaders, signedHeadersArr := getCanonicalHeaders(req.Headers(), opt.HeadersToSign)
+
+ // Generate signed headers string
+ signedHeaders := ""
+ if len(signedHeadersArr) > 0 {
+ sort.Strings(signedHeadersArr)
+ signedHeaders = strings.Join(signedHeadersArr, SIGN_HEADER_JOINER)
+ }
+
+ // Generate signature
+ canonicalParts := []string{req.Method(), canonicalUri, canonicalQueryString, canonicalHeaders}
+ canonicalReq := strings.Join(canonicalParts, SIGN_JOINER)
+ log.Debug("CanonicalRequest data:\n" + canonicalReq)
+ signature := util.HmacSha256Hex(signKey, canonicalReq)
+
+ // Generate auth string and add to the reqeust header
+ authStr := signKeyInfo + "/" + signedHeaders + "/" + signature
+ log.Info("Authorization=" + authStr)
+
+ req.SetHeader(http.AUTHORIZATION, authStr)
+}
+
+func getCanonicalURIPath(path string) string {
+ if len(path) == 0 {
+ return "/"
+ }
+ canonical_path := path
+ if strings.HasPrefix(path, "/") {
+ canonical_path = path[1:]
+ }
+ canonical_path = util.UriEncode(canonical_path, false)
+ return "/" + canonical_path
+}
+
+func getCanonicalQueryString(params map[string]string) string {
+ if len(params) == 0 {
+ return ""
+ }
+
+ result := make([]string, 0, len(params))
+ for k, v := range params {
+ if strings.ToLower(k) == strings.ToLower(http.AUTHORIZATION) {
+ continue
+ }
+ item := ""
+ if len(v) == 0 {
+ item = fmt.Sprintf("%s=", util.UriEncode(k, true))
+ } else {
+ item = fmt.Sprintf("%s=%s", util.UriEncode(k, true), util.UriEncode(v, true))
+ }
+ result = append(result, item)
+ }
+ sort.Strings(result)
+ return strings.Join(result, "&")
+}
+
+func getCanonicalHeaders(headers map[string]string,
+ headersToSign map[string]struct{}) (string, []string) {
+ canonicalHeaders := make([]string, 0, len(headers))
+ signHeaders := make([]string, 0, len(headersToSign))
+ for k, v := range headers {
+ headKey := strings.ToLower(k)
+ if headKey == strings.ToLower(http.AUTHORIZATION) {
+ continue
+ }
+ _, headExists := headersToSign[headKey]
+ if headExists ||
+ (strings.HasPrefix(headKey, http.BCE_PREFIX) &&
+ (headKey != http.BCE_REQUEST_ID)) {
+
+ headVal := strings.TrimSpace(v)
+ encoded := util.UriEncode(headKey, true) + ":" + util.UriEncode(headVal, true)
+ canonicalHeaders = append(canonicalHeaders, encoded)
+ signHeaders = append(signHeaders, headKey)
+ }
+ }
+ sort.Strings(canonicalHeaders)
+ sort.Strings(signHeaders)
+ return strings.Join(canonicalHeaders, SIGN_JOINER), signHeaders
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/bce/builder.go b/vendor/github.com/baidubce/bce-sdk-go/bce/builder.go
new file mode 100644
index 00000000000..1b25504cb4e
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/bce/builder.go
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// builder.go - defines the RequestBuilder structure for BCE servies
+
+package bce
+
+import (
+ "encoding/json"
+ "fmt"
+)
+
+// RequestBuilder holds config data for bce request.
+// Some of fields are required and the others are optional.
+// The builder pattern can simplify the execution of requests.
+type RequestBuilder struct {
+ client Client
+
+ url string // required
+ method string // required
+ queryParams map[string]string // optional
+ headers map[string]string // optional
+ body interface{} // optional
+ result interface{} // optional
+}
+
+// create RequestBuilder with the client.
+func NewRequestBuilder(client Client) *RequestBuilder {
+ return &RequestBuilder{
+ client: client,
+ }
+}
+
+func (b *RequestBuilder) WithURL(url string) *RequestBuilder {
+ b.url = url
+ return b
+}
+
+func (b *RequestBuilder) WithMethod(method string) *RequestBuilder {
+ b.method = method
+ return b
+}
+
+// set query param with the key/value directly.
+func (b *RequestBuilder) WithQueryParam(key, value string) *RequestBuilder {
+ if b.queryParams == nil {
+ b.queryParams = make(map[string]string)
+ }
+ b.queryParams[key] = value
+ return b
+}
+
+// set query param with the key/value only when the value is not blank.
+func (b *RequestBuilder) WithQueryParamFilter(key, value string) *RequestBuilder {
+ if len(value) == 0 {
+ return b
+ }
+ return b.WithQueryParam(key, value)
+}
+
+func (b *RequestBuilder) WithQueryParams(params map[string]string) *RequestBuilder {
+ if b.queryParams == nil {
+ b.queryParams = params
+ } else {
+ for key, value := range params {
+ b.queryParams[key] = value
+ }
+ }
+ return b
+}
+
+func (b *RequestBuilder) WithHeader(key, value string) *RequestBuilder {
+ if b.headers == nil {
+ b.headers = make(map[string]string)
+ }
+ b.headers[key] = value
+ return b
+}
+
+func (b *RequestBuilder) WithHeaders(headers map[string]string) *RequestBuilder {
+ if b.headers == nil {
+ b.headers = headers
+ } else {
+ for key, value := range headers {
+ b.headers[key] = value
+ }
+ }
+ return b
+}
+
+func (b *RequestBuilder) WithBody(body interface{}) *RequestBuilder {
+ b.body = body
+ return b
+}
+
+func (b *RequestBuilder) WithResult(result interface{}) *RequestBuilder {
+ b.result = result
+ return b
+}
+
+// Do will send request to bce and get result with the builder's parameters.
+func (b *RequestBuilder) Do() error {
+ if err := b.validate(); err != nil {
+ return err
+ }
+
+ // build BceRequest
+ req, err := b.buildBceRequest()
+ if err != nil {
+ return err
+ }
+
+ // get result from BceResponse
+ if err := b.buildBceResponse(req); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// Validate if the required fields are providered.
+func (b *RequestBuilder) validate() error {
+ if len(b.url) == 0 {
+ return fmt.Errorf("The url can't be null.")
+ }
+ if len(b.method) == 0 {
+ return fmt.Errorf("The method can't be null.")
+ }
+ if b.client == nil {
+ return fmt.Errorf("The client can't be null.")
+ }
+ return nil
+}
+
+func (b *RequestBuilder) buildBceRequest() (*BceRequest, error) {
+ // Build the request
+ req := &BceRequest{}
+ req.SetUri(b.url)
+ req.SetMethod(b.method)
+
+ if b.headers != nil {
+ req.SetHeaders(b.headers)
+ }
+ if b.queryParams != nil {
+ req.SetParams(b.queryParams)
+ }
+ if b.body != nil {
+ bodyBytes, err := json.Marshal(b.body)
+ if err != nil {
+ return nil, err
+ }
+ body, err := NewBodyFromBytes(bodyBytes)
+ if err != nil {
+ return nil, err
+ }
+ req.SetBody(body)
+ }
+
+ return req, nil
+}
+
+func (b *RequestBuilder) buildBceResponse(req *BceRequest) error {
+ // Send request and get response
+ resp := &BceResponse{}
+ if err := b.client.SendRequest(req, resp); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer resp.Body().Close()
+
+ if b.result == nil {
+ return nil
+ }
+
+ return resp.ParseJsonBody(b.result)
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/bce/client.go b/vendor/github.com/baidubce/bce-sdk-go/bce/client.go
new file mode 100644
index 00000000000..d8ea23dbe5f
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/bce/client.go
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// client.go - definiton the BceClientConfiguration and BceClient structure
+
+// Package bce implements the infrastructure to access BCE services.
+//
+// - BceClient:
+// It is the general client of BCE to access all services. It builds http request to access the
+// services based on the given client configuration.
+//
+// - BceClientConfiguration:
+// The client configuration data structure which contains endpoint, region, credentials, retry
+// policy, sign options and so on. It supports most of the default value and user can also
+// access or change the default with its public fields' name.
+//
+// - Error types:
+// The error types when making request or receiving response to the BCE services contains two
+// types: the BceClientError when making request to BCE services and the BceServiceError when
+// recieving response from them.
+//
+// - BceRequest:
+// The request instance stands for an request to access the BCE services.
+//
+// - BceResponse:
+// The response instance stands for an response from the BCE services.
+package bce
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "time"
+
+ "github.com/baidubce/bce-sdk-go/auth"
+ "github.com/baidubce/bce-sdk-go/http"
+ "github.com/baidubce/bce-sdk-go/util"
+ "github.com/baidubce/bce-sdk-go/util/log"
+)
+
+// Client is the general interface which can perform sending request. Different service
+// will define its own client in case of specific extension.
+type Client interface {
+ SendRequest(*BceRequest, *BceResponse) error
+ SendRequestFromBytes(*BceRequest, *BceResponse, []byte) error
+ GetBceClientConfig() *BceClientConfiguration
+}
+
+// BceClient defines the general client to access the BCE services.
+type BceClient struct {
+ Config *BceClientConfiguration
+ Signer auth.Signer // the sign algorithm
+}
+
+// BuildHttpRequest - the helper method for the client to build http request
+//
+// PARAMS:
+// - request: the input request object to be built
+func (c *BceClient) buildHttpRequest(request *BceRequest) {
+ // Construct the http request instance for the special fields
+ request.BuildHttpRequest()
+
+ // Set the client specific configurations
+ if request.Endpoint() == "" {
+ request.SetEndpoint(c.Config.Endpoint)
+ }
+ if request.Protocol() == "" {
+ request.SetProtocol(DEFAULT_PROTOCOL)
+ }
+ if len(c.Config.ProxyUrl) != 0 {
+ request.SetProxyUrl(c.Config.ProxyUrl)
+ }
+ request.SetTimeout(c.Config.ConnectionTimeoutInMillis / 1000)
+
+ // Set the BCE request headers
+ request.SetHeader(http.HOST, request.Host())
+ request.SetHeader(http.USER_AGENT, c.Config.UserAgent)
+ request.SetHeader(http.BCE_DATE, util.FormatISO8601Date(util.NowUTCSeconds()))
+
+ //set default content-type if null
+ if request.Header(http.CONTENT_TYPE) == "" {
+ request.SetHeader(http.CONTENT_TYPE, DEFAULT_CONTENT_TYPE)
+ }
+
+ // Generate the auth string if needed
+ if c.Config.Credentials != nil {
+ c.Signer.Sign(&request.Request, c.Config.Credentials, c.Config.SignOption)
+ }
+}
+
+// SendRequest - the client performs sending the http request with retry policy and receive the
+// response from the BCE services.
+//
+// PARAMS:
+// - req: the request object to be sent to the BCE service
+// - resp: the response object to receive the content from BCE service
+//
+// RETURNS:
+// - error: nil if ok otherwise the specific error
+func (c *BceClient) SendRequest(req *BceRequest, resp *BceResponse) error {
+ // Return client error if it is not nil
+ if req.ClientError() != nil {
+ return req.ClientError()
+ }
+
+ // Build the http request and prepare to send
+ c.buildHttpRequest(req)
+ log.Infof("send http request: %v", req)
+
+ // Send request with the given retry policy
+ retries := 0
+ if req.Body() != nil {
+ defer req.Body().Close() // Manually close the ReadCloser body for retry
+ }
+ for {
+ // The request body should be temporarily saved if retry to send the http request
+ var retryBuf bytes.Buffer
+ var teeReader io.Reader
+ if c.Config.Retry.ShouldRetry(nil, 0) && req.Body() != nil {
+ teeReader = io.TeeReader(req.Body(), &retryBuf)
+ req.Request.SetBody(ioutil.NopCloser(teeReader))
+ }
+ httpResp, err := http.Execute(&req.Request)
+
+ if err != nil {
+ if c.Config.Retry.ShouldRetry(err, retries) {
+ delay_in_mills := c.Config.Retry.GetDelayBeforeNextRetryInMillis(err, retries)
+ time.Sleep(delay_in_mills)
+ } else {
+ return &BceClientError{
+ fmt.Sprintf("execute http request failed! Retried %d times, error: %v",
+ retries, err)}
+ }
+ retries++
+ log.Warnf("send request failed: %v, retry for %d time(s)", err, retries)
+ if req.Body() != nil {
+ ioutil.ReadAll(teeReader)
+ req.Request.SetBody(ioutil.NopCloser(&retryBuf))
+ }
+ continue
+ }
+ resp.SetHttpResponse(httpResp)
+ resp.ParseResponse()
+
+ log.Infof("receive http response: status: %s, debugId: %s, requestId: %s, elapsed: %v",
+ resp.StatusText(), resp.DebugId(), resp.RequestId(), resp.ElapsedTime())
+
+ if resp.ElapsedTime().Milliseconds() > DEFAULT_WARN_LOG_TIMEOUT_IN_MILLS {
+ log.Warnf("request time more than 5 second, debugId: %s, requestId: %s, elapsed: %v",
+ resp.DebugId(), resp.RequestId(), resp.ElapsedTime())
+ }
+ for k, v := range resp.Headers() {
+ log.Debugf("%s=%s", k, v)
+ }
+ if resp.IsFail() {
+ err := resp.ServiceError()
+ if c.Config.Retry.ShouldRetry(err, retries) {
+ delay_in_mills := c.Config.Retry.GetDelayBeforeNextRetryInMillis(err, retries)
+ time.Sleep(delay_in_mills)
+ } else {
+ return err
+ }
+ retries++
+ log.Warnf("send request failed, retry for %d time(s)", retries)
+ if req.Body() != nil {
+ ioutil.ReadAll(teeReader)
+ req.Request.SetBody(ioutil.NopCloser(&retryBuf))
+ }
+ continue
+ }
+ return nil
+ }
+}
+
+// SendRequestFromBytes - the client performs sending the http request with retry policy and receive the
+// response from the BCE services.
+//
+// PARAMS:
+// - req: the request object to be sent to the BCE service
+// - resp: the response object to receive the content from BCE service
+// - content: the content of body
+//
+// RETURNS:
+// - error: nil if ok otherwise the specific error
+func (c *BceClient) SendRequestFromBytes(req *BceRequest, resp *BceResponse, content []byte) error {
+ // Return client error if it is not nil
+ if req.ClientError() != nil {
+ return req.ClientError()
+ }
+ // Build the http request and prepare to send
+ c.buildHttpRequest(req)
+ log.Infof("send http request: %v", req)
+ // Send request with the given retry policy
+ retries := 0
+ for {
+ // The request body should be temporarily saved if retry to send the http request
+ buf := bytes.NewBuffer(content)
+ req.Request.SetBody(ioutil.NopCloser(buf))
+ defer req.Request.Body().Close() // Manually close the ReadCloser body for retry
+ httpResp, err := http.Execute(&req.Request)
+ if err != nil {
+ if c.Config.Retry.ShouldRetry(err, retries) {
+ delay_in_mills := c.Config.Retry.GetDelayBeforeNextRetryInMillis(err, retries)
+ time.Sleep(delay_in_mills)
+ } else {
+ return &BceClientError{
+ fmt.Sprintf("execute http request failed! Retried %d times, error: %v",
+ retries, err)}
+ }
+ retries++
+ log.Warnf("send request failed: %v, retry for %d time(s)", err, retries)
+ continue
+ }
+ resp.SetHttpResponse(httpResp)
+ resp.ParseResponse()
+ log.Infof("receive http response: status: %s, debugId: %s, requestId: %s, elapsed: %v",
+ resp.StatusText(), resp.DebugId(), resp.RequestId(), resp.ElapsedTime())
+ for k, v := range resp.Headers() {
+ log.Debugf("%s=%s", k, v)
+ }
+ if resp.IsFail() {
+ err := resp.ServiceError()
+ if c.Config.Retry.ShouldRetry(err, retries) {
+ delay_in_mills := c.Config.Retry.GetDelayBeforeNextRetryInMillis(err, retries)
+ time.Sleep(delay_in_mills)
+ } else {
+ return err
+ }
+ retries++
+ log.Warnf("send request failed, retry for %d time(s)", retries)
+ continue
+ }
+ return nil
+ }
+}
+
+func (c *BceClient) GetBceClientConfig() *BceClientConfiguration {
+ return c.Config
+}
+
+func NewBceClient(conf *BceClientConfiguration, sign auth.Signer) *BceClient {
+ clientConfig := http.ClientConfig{RedirectDisabled: conf.RedirectDisabled}
+ http.InitClient(clientConfig)
+ return &BceClient{conf, sign}
+}
+
+func NewBceClientWithAkSk(ak, sk, endPoint string) (*BceClient, error) {
+ credentials, err := auth.NewBceCredentials(ak, sk)
+ if err != nil {
+ return nil, err
+ }
+ defaultSignOptions := &auth.SignOptions{
+ HeadersToSign: auth.DEFAULT_HEADERS_TO_SIGN,
+ ExpireSeconds: auth.DEFAULT_EXPIRE_SECONDS}
+ defaultConf := &BceClientConfiguration{
+ Endpoint: endPoint,
+ Region: DEFAULT_REGION,
+ UserAgent: DEFAULT_USER_AGENT,
+ Credentials: credentials,
+ SignOption: defaultSignOptions,
+ Retry: DEFAULT_RETRY_POLICY,
+ ConnectionTimeoutInMillis: DEFAULT_CONNECTION_TIMEOUT_IN_MILLIS,
+ RedirectDisabled: false}
+ v1Signer := &auth.BceV1Signer{}
+
+ return NewBceClient(defaultConf, v1Signer), nil
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/bce/config.go b/vendor/github.com/baidubce/bce-sdk-go/bce/config.go
new file mode 100644
index 00000000000..c9e57cd8303
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/bce/config.go
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// config.go - define the client configuration for BCE
+
+package bce
+
+import (
+ "fmt"
+ "reflect"
+ "runtime"
+
+ "github.com/baidubce/bce-sdk-go/auth"
+)
+
+// Constants and default values for the package bce
+const (
+ SDK_VERSION = "0.9.230"
+ URI_PREFIX = "/" // now support uri without prefix "v1" so just set root path
+ DEFAULT_DOMAIN = "baidubce.com"
+ DEFAULT_PROTOCOL = "http"
+ HTTPS_PROTOCAL = "https"
+ DEFAULT_REGION = "bj"
+ DEFAULT_CONTENT_TYPE = "application/json;charset=utf-8"
+ DEFAULT_CONNECTION_TIMEOUT_IN_MILLIS = 1200 * 1000
+ DEFAULT_WARN_LOG_TIMEOUT_IN_MILLS = 5 * 1000
+)
+
+var (
+ DEFAULT_USER_AGENT string
+ DEFAULT_RETRY_POLICY = NewBackOffRetryPolicy(3, 20000, 300)
+)
+
+func init() {
+ DEFAULT_USER_AGENT = "bce-sdk-go"
+ DEFAULT_USER_AGENT += "/" + SDK_VERSION
+ DEFAULT_USER_AGENT += "/" + runtime.Version()
+ DEFAULT_USER_AGENT += "/" + runtime.GOOS
+ DEFAULT_USER_AGENT += "/" + runtime.GOARCH
+}
+
+// BceClientConfiguration defines the config components structure.
+type BceClientConfiguration struct {
+ Endpoint string
+ ProxyUrl string
+ Region string
+ UserAgent string
+ Credentials *auth.BceCredentials
+ SignOption *auth.SignOptions
+ Retry RetryPolicy
+ ConnectionTimeoutInMillis int
+ // CnameEnabled should be true when use custom domain as endpoint to visit bos resource
+ CnameEnabled bool
+ BackupEndpoint string
+ RedirectDisabled bool
+}
+
+func (c *BceClientConfiguration) String() string {
+ return fmt.Sprintf(`BceClientConfiguration [
+ Endpoint=%s;
+ ProxyUrl=%s;
+ Region=%s;
+ UserAgent=%s;
+ Credentials=%v;
+ SignOption=%v;
+ RetryPolicy=%v;
+ ConnectionTimeoutInMillis=%v;
+ RedirectDisabled=%v
+ ]`, c.Endpoint, c.ProxyUrl, c.Region, c.UserAgent, c.Credentials,
+ c.SignOption, reflect.TypeOf(c.Retry).Name(), c.ConnectionTimeoutInMillis, c.RedirectDisabled)
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/bce/error.go b/vendor/github.com/baidubce/bce-sdk-go/bce/error.go
new file mode 100644
index 00000000000..e6256512981
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/bce/error.go
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// error.go - define the error types for BCE
+
+package bce
+
+const (
+ EACCESS_DENIED = "AccessDenied"
+ EINAPPROPRIATE_JSON = "InappropriateJSON"
+ EINTERNAL_ERROR = "InternalError"
+ EINVALID_ACCESS_KEY_ID = "InvalidAccessKeyId"
+ EINVALID_HTTP_AUTH_HEADER = "InvalidHTTPAuthHeader"
+ EINVALID_HTTP_REQUEST = "InvalidHTTPRequest"
+ EINVALID_URI = "InvalidURI"
+ EMALFORMED_JSON = "MalformedJSON"
+ EINVALID_VERSION = "InvalidVersion"
+ EOPT_IN_REQUIRED = "OptInRequired"
+ EPRECONDITION_FAILED = "PreconditionFailed"
+ EREQUEST_EXPIRED = "RequestExpired"
+ ESIGNATURE_DOES_NOT_MATCH = "SignatureDoesNotMatch"
+)
+
+// BceError abstracts the error for BCE
+type BceError interface {
+ error
+}
+
+// BceClientError defines the error struct for the client when making request
+type BceClientError struct{ Message string }
+
+func (b *BceClientError) Error() string { return b.Message }
+
+func NewBceClientError(msg string) *BceClientError { return &BceClientError{msg} }
+
+// BceServiceError defines the error struct for the BCE service when receiving response
+type BceServiceError struct {
+ Code string
+ Message string
+ RequestId string
+ StatusCode int
+}
+
+func (b *BceServiceError) Error() string {
+ ret := "[Code: " + b.Code
+ ret += "; Message: " + b.Message
+ ret += "; RequestId: " + b.RequestId + "]"
+ return ret
+}
+
+func NewBceServiceError(code, msg, reqId string, status int) *BceServiceError {
+ return &BceServiceError{code, msg, reqId, status}
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/bce/request.go b/vendor/github.com/baidubce/bce-sdk-go/bce/request.go
new file mode 100644
index 00000000000..49eecc2ffa5
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/bce/request.go
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// request.go - defines the BCE servies request
+
+package bce
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+
+ "github.com/baidubce/bce-sdk-go/http"
+ "github.com/baidubce/bce-sdk-go/util"
+)
+
+// Body defines the data structure used in BCE request.
+// Every BCE request that sets the body field must set its content-length and content-md5 headers
+// to ensure the correctness of the body content forcely, and users can also set the content-sha256
+// header to strengthen the correctness with the "SetHeader" method.
+type Body struct {
+ stream io.ReadCloser
+ size int64
+ contentMD5 string
+}
+
+func (b *Body) Stream() io.ReadCloser { return b.stream }
+
+func (b *Body) SetStream(stream io.ReadCloser) { b.stream = stream }
+
+func (b *Body) Size() int64 { return b.size }
+
+func (b *Body) ContentMD5() string { return b.contentMD5 }
+
+// NewBodyFromBytes - build a Body object from the byte stream to be used in the http request, it
+// calculates the content-md5 of the byte stream and store the size as well as the stream.
+//
+// PARAMS:
+// - stream: byte stream
+//
+// RETURNS:
+// - *Body: the return Body object
+// - error: error if any specific error occurs
+func NewBodyFromBytes(stream []byte) (*Body, error) {
+ buf := bytes.NewBuffer(stream)
+ size := int64(buf.Len())
+ contentMD5, err := util.CalculateContentMD5(buf, size)
+ if err != nil {
+ return nil, err
+ }
+ buf = bytes.NewBuffer(stream)
+ return &Body{ioutil.NopCloser(buf), size, contentMD5}, nil
+}
+
+// NewBodyFromString - build a Body object from the string to be used in the http request, it
+// calculates the content-md5 of the byte stream and store the size as well as the stream.
+//
+// PARAMS:
+// - str: the input string
+//
+// RETURNS:
+// - *Body: the return Body object
+// - error: error if any specific error occurs
+func NewBodyFromString(str string) (*Body, error) {
+ buf := bytes.NewBufferString(str)
+ size := int64(len(str))
+ contentMD5, err := util.CalculateContentMD5(buf, size)
+ if err != nil {
+ return nil, err
+ }
+ buf = bytes.NewBufferString(str)
+ return &Body{ioutil.NopCloser(buf), size, contentMD5}, nil
+}
+
+// NewBodyFromFile - build a Body object from the given file name to be used in the http request,
+// it calculates the content-md5 of the byte stream and store the size as well as the stream.
+//
+// PARAMS:
+// - fname: the given file name
+//
+// RETURNS:
+// - *Body: the return Body object
+// - error: error if any specific error occurs
+func NewBodyFromFile(fname string) (*Body, error) {
+ file, err := os.Open(fname)
+ if err != nil {
+ return nil, err
+ }
+ fileInfo, infoErr := file.Stat()
+ if infoErr != nil {
+ return nil, infoErr
+ }
+ contentMD5, md5Err := util.CalculateContentMD5(file, fileInfo.Size())
+ if md5Err != nil {
+ return nil, md5Err
+ }
+ if _, err = file.Seek(0, 0); err != nil {
+ return nil, err
+ }
+ return &Body{file, fileInfo.Size(), contentMD5}, nil
+}
+
+// NewBodyFromSectionFile - build a Body object from the given file pointer with offset and size.
+// It calculates the content-md5 of the given content and store the size as well as the stream.
+//
+// PARAMS:
+// - file: the input file pointer
+// - off: offset of current section body
+// - size: current section body size
+//
+// RETURNS:
+// - *Body: the return Body object
+// - error: error if any specific error occurs
+func NewBodyFromSectionFile(file *os.File, off, size int64) (*Body, error) {
+ if _, err := file.Seek(off, 0); err != nil {
+ return nil, err
+ }
+ contentMD5, md5Err := util.CalculateContentMD5(file, size)
+ if md5Err != nil {
+ return nil, md5Err
+ }
+ if _, err := file.Seek(0, 0); err != nil {
+ return nil, err
+ }
+ section := io.NewSectionReader(file, off, size)
+ return &Body{ioutil.NopCloser(section), size, contentMD5}, nil
+}
+
+// NewBodyFromSizedReader - build a Body object from the given reader with size.
+// It calculates the content-md5 of the given content and store the size as well as the stream.
+//
+// PARAMS:
+// - r: the input reader
+// - size: the size to be read, -1 is read all
+//
+// RETURNS:
+// - *Body: the return Body object
+// - error: error if any specific error occurs
+func NewBodyFromSizedReader(r io.Reader, size int64) (*Body, error) {
+ var buffer bytes.Buffer
+ var rlen int64
+ var err error
+ if size >= 0 {
+ rlen, err = io.CopyN(&buffer, r, size)
+ } else {
+ rlen, err = io.Copy(&buffer, r)
+ }
+ if err != nil {
+ return nil, err
+ }
+ if rlen != int64(buffer.Len()) { // must be equal
+ return nil, NewBceClientError("unexpected reader")
+ }
+ if size >= 0 {
+ if rlen < size {
+ return nil, NewBceClientError("size is great than reader actual size")
+ }
+ }
+ contentMD5, err := util.CalculateContentMD5(bytes.NewBuffer(buffer.Bytes()), rlen)
+ if err != nil {
+ return nil, err
+ }
+ body := &Body{
+ stream: ioutil.NopCloser(&buffer),
+ size: rlen,
+ contentMD5: contentMD5,
+ }
+ return body, nil
+}
+
+// BceRequest defines the request structure for accessing BCE services
+type BceRequest struct {
+ http.Request
+ requestId string
+ clientError *BceClientError
+}
+
+func (b *BceRequest) RequestId() string { return b.requestId }
+
+func (b *BceRequest) SetRequestId(val string) { b.requestId = val }
+
+func (b *BceRequest) ClientError() *BceClientError { return b.clientError }
+
+func (b *BceRequest) SetClientError(err *BceClientError) { b.clientError = err }
+
+func (b *BceRequest) SetBody(body *Body) { // override SetBody derived from http.Request
+ b.Request.SetBody(body.Stream())
+ b.SetLength(body.Size()) // set field of "net/http.Request.ContentLength"
+ if body.Size() > 0 {
+ b.SetHeader(http.CONTENT_MD5, body.ContentMD5())
+ b.SetHeader(http.CONTENT_LENGTH, fmt.Sprintf("%d", body.Size()))
+ }
+}
+
+func (b *BceRequest) BuildHttpRequest() {
+ // Only need to build the specific `requestId` field for BCE, other fields are same as the
+ // `http.Request` as well as its methods.
+ if len(b.requestId) == 0 {
+ // Construct the request ID with UUID
+ b.requestId = util.NewRequestId()
+ }
+ b.SetHeader(http.BCE_REQUEST_ID, b.requestId)
+}
+
+func (b *BceRequest) String() string {
+ requestIdStr := "requestId=" + b.requestId
+ if b.clientError != nil {
+ return requestIdStr + ", client error: " + b.ClientError().Error()
+ }
+ return requestIdStr + "\n" + b.Request.String()
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/bce/response.go b/vendor/github.com/baidubce/bce-sdk-go/bce/response.go
new file mode 100644
index 00000000000..94f2ae4b8c0
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/bce/response.go
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// response.go - defines the common BCE services response
+
+package bce
+
+import (
+ "bytes"
+ "encoding/json"
+ "io"
+ "io/ioutil"
+ "strings"
+ "time"
+
+ "github.com/baidubce/bce-sdk-go/http"
+)
+
+// BceResponse defines the response structure for receiving BCE services response.
+type BceResponse struct {
+ statusCode int
+ statusText string
+ requestId string
+ debugId string
+ response *http.Response
+ serviceError *BceServiceError
+}
+
+func (r *BceResponse) IsFail() bool {
+ return r.response.StatusCode() >= 400
+}
+
+func (r *BceResponse) StatusCode() int {
+ return r.statusCode
+}
+
+func (r *BceResponse) StatusText() string {
+ return r.statusText
+}
+
+func (r *BceResponse) RequestId() string {
+ return r.requestId
+}
+
+func (r *BceResponse) DebugId() string {
+ return r.debugId
+}
+
+func (r *BceResponse) Header(key string) string {
+ return r.response.GetHeader(key)
+}
+
+func (r *BceResponse) Headers() map[string]string {
+ return r.response.GetHeaders()
+}
+
+func (r *BceResponse) Body() io.ReadCloser {
+ return r.response.Body()
+}
+
+func (r *BceResponse) SetHttpResponse(response *http.Response) {
+ r.response = response
+}
+
+func (r *BceResponse) ElapsedTime() time.Duration {
+ return r.response.ElapsedTime()
+}
+
+func (r *BceResponse) ServiceError() *BceServiceError {
+ return r.serviceError
+}
+
+func (r *BceResponse) ParseResponse() {
+ r.statusCode = r.response.StatusCode()
+ r.statusText = r.response.StatusText()
+ r.requestId = r.response.GetHeader(http.BCE_REQUEST_ID)
+ r.debugId = r.response.GetHeader(http.BCE_DEBUG_ID)
+ if r.IsFail() {
+ r.serviceError = NewBceServiceError("", r.statusText, r.requestId, r.statusCode)
+
+ // First try to read the error `Code' and `Message' from body
+ rawBody, _ := ioutil.ReadAll(r.Body())
+ defer r.Body().Close()
+ if len(rawBody) != 0 {
+ jsonDecoder := json.NewDecoder(bytes.NewBuffer(rawBody))
+ if err := jsonDecoder.Decode(r.serviceError); err != nil {
+ r.serviceError = NewBceServiceError(
+ EMALFORMED_JSON,
+ "Service json error message decode failed",
+ r.requestId,
+ r.statusCode)
+ }
+ return
+ }
+
+ // Then guess the `Message' from by the return status code
+ switch r.statusCode {
+ case 400:
+ r.serviceError.Code = EINVALID_HTTP_REQUEST
+ case 403:
+ r.serviceError.Code = EACCESS_DENIED
+ case 412:
+ r.serviceError.Code = EPRECONDITION_FAILED
+ case 500:
+ r.serviceError.Code = EINTERNAL_ERROR
+ default:
+ words := strings.Split(r.statusText, " ")
+ r.serviceError.Code = strings.Join(words[1:], "")
+ }
+ }
+}
+
+func (r *BceResponse) ParseJsonBody(result interface{}) error {
+ defer r.Body().Close()
+ jsonDecoder := json.NewDecoder(r.Body())
+ return jsonDecoder.Decode(result)
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/bce/retry.go b/vendor/github.com/baidubce/bce-sdk-go/bce/retry.go
new file mode 100644
index 00000000000..3e05e740e73
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/bce/retry.go
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// retry.go - define the retry policy when making requests to BCE services
+
+package bce
+
+import (
+ "net"
+ "net/http"
+ "strings"
+ "time"
+
+ "github.com/baidubce/bce-sdk-go/util/log"
+)
+
+// RetryPolicy defines the two methods to retry for sending request.
+type RetryPolicy interface {
+ ShouldRetry(BceError, int) bool
+ GetDelayBeforeNextRetryInMillis(BceError, int) time.Duration
+}
+
+// NoRetryPolicy just does not retry.
+type NoRetryPolicy struct{}
+
+func (_ *NoRetryPolicy) ShouldRetry(err BceError, attempts int) bool {
+ return false
+}
+
+func (_ *NoRetryPolicy) GetDelayBeforeNextRetryInMillis(
+ err BceError, attempts int) time.Duration {
+ return 0 * time.Millisecond
+}
+
+func NewNoRetryPolicy() *NoRetryPolicy {
+ return &NoRetryPolicy{}
+}
+
+// BackOffRetryPolicy implements a policy that retries with exponential back-off strategy.
+// This policy will keep retrying until the maximum number of retries is reached. The delay time
+// will be a fixed interval for the first time then 2 * interval for the second, 4 * internal for
+// the third, and so on.
+// In general, the delay time will be 2^number_of_retries_attempted*interval. When a maximum of
+// delay time is specified, the delay time will never exceed this limit.
+type BackOffRetryPolicy struct {
+ maxErrorRetry int
+ maxDelayInMillis int64
+ baseIntervalInMillis int64
+}
+
+func (b *BackOffRetryPolicy) ShouldRetry(err BceError, attempts int) bool {
+ // Do not retry any more when retry the max times
+ if attempts >= b.maxErrorRetry {
+ return false
+ }
+
+ if err == nil {
+ return true
+ }
+
+ // Always retry on IO error
+ if _, ok := err.(net.Error); ok {
+ // context canceled should not retry
+ if strings.Contains(err.Error(), "context deadline exceeded") ||
+ strings.Contains(err.Error(), "context canceled") {
+ return false
+ }
+ return true
+ }
+
+ // Only retry on a service error
+ if realErr, ok := err.(*BceServiceError); ok {
+ switch realErr.StatusCode {
+ case http.StatusInternalServerError:
+ log.Warn("retry for internal server error(500)")
+ return true
+ case http.StatusBadGateway:
+ log.Warn("retry for bad gateway(502)")
+ return true
+ case http.StatusServiceUnavailable:
+ log.Warn("retry for service unavailable(503)")
+ return true
+ case http.StatusBadRequest:
+ if realErr.Code != "Http400" {
+ return false
+ }
+ log.Warn("retry for bad request(400)")
+ return true
+ }
+
+ if realErr.Code == EREQUEST_EXPIRED {
+ log.Warn("retry for request expired")
+ return true
+ }
+ }
+ return false
+}
+
+func (b *BackOffRetryPolicy) GetDelayBeforeNextRetryInMillis(
+ err BceError, attempts int) time.Duration {
+ if attempts < 0 {
+ return 0 * time.Millisecond
+ }
+ delayInMillis := (1 << uint64(attempts)) * b.baseIntervalInMillis
+ if delayInMillis > b.maxDelayInMillis {
+ return time.Duration(b.maxDelayInMillis) * time.Millisecond
+ }
+ return time.Duration(delayInMillis) * time.Millisecond
+}
+
+func NewBackOffRetryPolicy(maxRetry int, maxDelay, base int64) *BackOffRetryPolicy {
+ return &BackOffRetryPolicy{maxRetry, maxDelay, base}
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/http/client.go b/vendor/github.com/baidubce/bce-sdk-go/http/client.go
new file mode 100644
index 00000000000..76b5501004e
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/http/client.go
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// client.go - define the execute function to send http request and get response
+
+// Package http defines the structure of request and response which used to access the BCE services
+// as well as the http constant headers and and methods. And finally implement the `Execute` funct-
+// ion to do the work.
+package http
+
+import (
+ "net"
+ "net/http"
+ "net/url"
+ "sync"
+ "time"
+)
+
+const (
+ defaultMaxIdleConnsPerHost = 500
+ defaultResponseHeaderTimeout = 60 * time.Second
+ defaultDialTimeout = 30 * time.Second
+ defaultSmallInterval = 600 * time.Second
+ defaultLargeInterval = 1200 * time.Second
+)
+
+// The httpClient is the global variable to send the request and get response
+// for reuse and the Client provided by the Go standard library is thread safe.
+var (
+ httpClient *http.Client
+ transport *http.Transport
+)
+
+type timeoutConn struct {
+ conn net.Conn
+ smallInterval time.Duration
+ largeInterval time.Duration
+}
+
+func (c *timeoutConn) Read(b []byte) (n int, err error) {
+ c.SetReadDeadline(time.Now().Add(c.smallInterval))
+ n, err = c.conn.Read(b)
+ c.SetReadDeadline(time.Now().Add(c.largeInterval))
+ return n, err
+}
+func (c *timeoutConn) Write(b []byte) (n int, err error) {
+ c.SetWriteDeadline(time.Now().Add(c.smallInterval))
+ n, err = c.conn.Write(b)
+ c.SetWriteDeadline(time.Now().Add(c.largeInterval))
+ return n, err
+}
+func (c *timeoutConn) Close() error { return c.conn.Close() }
+func (c *timeoutConn) LocalAddr() net.Addr { return c.conn.LocalAddr() }
+func (c *timeoutConn) RemoteAddr() net.Addr { return c.conn.RemoteAddr() }
+func (c *timeoutConn) SetDeadline(t time.Time) error { return c.conn.SetDeadline(t) }
+func (c *timeoutConn) SetReadDeadline(t time.Time) error { return c.conn.SetReadDeadline(t) }
+func (c *timeoutConn) SetWriteDeadline(t time.Time) error { return c.conn.SetWriteDeadline(t) }
+
+type ClientConfig struct {
+ RedirectDisabled bool
+}
+
+var customizeInit sync.Once
+
+func InitClient(config ClientConfig) {
+ customizeInit.Do(func() {
+ httpClient = &http.Client{}
+ transport = &http.Transport{
+ MaxIdleConnsPerHost: defaultMaxIdleConnsPerHost,
+ ResponseHeaderTimeout: defaultResponseHeaderTimeout,
+ Dial: func(network, address string) (net.Conn, error) {
+ conn, err := net.DialTimeout(network, address, defaultDialTimeout)
+ if err != nil {
+ return nil, err
+ }
+ tc := &timeoutConn{conn, defaultSmallInterval, defaultLargeInterval}
+ tc.SetReadDeadline(time.Now().Add(defaultLargeInterval))
+ return tc, nil
+ },
+ }
+ httpClient.Transport = transport
+ if config.RedirectDisabled {
+ httpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
+ return http.ErrUseLastResponse
+ }
+ }
+ })
+}
+
+// Execute - do the http requset and get the response
+//
+// PARAMS:
+// - request: the http request instance to be sent
+//
+// RETURNS:
+// - response: the http response returned from the server
+// - error: nil if ok otherwise the specific error
+func Execute(request *Request) (*Response, error) {
+ // Build the request object for the current requesting
+ httpRequest := &http.Request{
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ }
+
+ if request.Context() != nil {
+ httpRequest = httpRequest.WithContext(request.Context())
+ }
+
+ // Set the connection timeout for current request
+ httpClient.Timeout = time.Duration(request.Timeout()) * time.Second
+
+ // Set the request method
+ httpRequest.Method = request.Method()
+
+ // Set the request url
+ internalUrl := &url.URL{
+ Scheme: request.Protocol(),
+ Host: request.Host(),
+ Path: request.Uri(),
+ RawQuery: request.QueryString()}
+ httpRequest.URL = internalUrl
+
+ // Set the request headers
+ internalHeader := make(http.Header)
+ for k, v := range request.Headers() {
+ val := make([]string, 0, 1)
+ val = append(val, v)
+ internalHeader[k] = val
+ }
+ httpRequest.Header = internalHeader
+
+ if request.Body() != nil {
+ if request.Length() > 0 {
+ httpRequest.ContentLength = request.Length()
+ httpRequest.Body = request.Body()
+ } else if request.Length() < 0 {
+ // if set body and ContentLength <= 0, will be chunked
+ httpRequest.Body = request.Body()
+ } // else {} body == nil and ContentLength == 0
+ }
+
+ // Set the proxy setting if needed
+ if len(request.ProxyUrl()) != 0 {
+ transport.Proxy = func(_ *http.Request) (*url.URL, error) {
+ return url.Parse(request.ProxyUrl())
+ }
+ }
+
+ // Perform the http request and get response
+ // It needs to explicitly close the keep-alive connections when error occurs for the request
+ // that may continue sending request's data subsequently.
+ start := time.Now()
+
+ httpResponse, err := httpClient.Do(httpRequest)
+
+ end := time.Now()
+ if err != nil {
+ transport.CloseIdleConnections()
+ return nil, err
+ }
+ if httpResponse.StatusCode >= 400 &&
+ (httpRequest.Method == PUT || httpRequest.Method == POST) {
+ transport.CloseIdleConnections()
+ }
+ response := &Response{httpResponse, end.Sub(start)}
+ return response, nil
+}
+func SetResponseHeaderTimeout(t int) {
+ transport = &http.Transport{
+ MaxIdleConnsPerHost: defaultMaxIdleConnsPerHost,
+ ResponseHeaderTimeout: time.Duration(t) * time.Second,
+ Dial: func(network, address string) (net.Conn, error) {
+ conn, err := net.DialTimeout(network, address, defaultDialTimeout)
+ if err != nil {
+ return nil, err
+ }
+ tc := &timeoutConn{conn, defaultSmallInterval, defaultLargeInterval}
+ err = tc.SetReadDeadline(time.Now().Add(defaultLargeInterval))
+ if err != nil {
+ return nil, err
+ }
+ return tc, nil
+ },
+ }
+ httpClient.Transport = transport
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/http/constants.go b/vendor/github.com/baidubce/bce-sdk-go/http/constants.go
new file mode 100644
index 00000000000..27ae864f321
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/http/constants.go
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// constants.go - defines constants of the BCE http package including headers and methods
+
+package http
+
+// Constants of the supported HTTP methods for BCE
+const (
+ GET = "GET"
+ PUT = "PUT"
+ POST = "POST"
+ DELETE = "DELETE"
+ HEAD = "HEAD"
+ OPTIONS = "OPTIONS"
+ PATCH = "PATCH"
+)
+
+// Constants of the HTTP headers for BCE
+const (
+ // Standard HTTP Headers
+ AUTHORIZATION = "Authorization"
+ CACHE_CONTROL = "Cache-Control"
+ CONTENT_DISPOSITION = "Content-Disposition"
+ CONTENT_ENCODING = "Content-Encoding"
+ CONTENT_LANGUAGE = "Content-Language"
+ CONTENT_LENGTH = "Content-Length"
+ CONTENT_MD5 = "Content-Md5"
+ CONTENT_RANGE = "Content-Range"
+ CONTENT_TYPE = "Content-Type"
+ DATE = "Date"
+ ETAG = "Etag"
+ EXPIRES = "Expires"
+ HOST = "Host"
+ LAST_MODIFIED = "Last-Modified"
+ LOCATION = "Location"
+ RANGE = "Range"
+ SERVER = "Server"
+ TRANSFER_ENCODING = "Transfer-Encoding"
+ USER_AGENT = "User-Agent"
+
+ // BCE Common HTTP Headers
+ BCE_PREFIX = "x-bce-"
+ BCE_ACL = "x-bce-acl"
+ BCE_GRANT_READ = "x-bce-grant-read"
+ BCE_GRANT_FULL_CONTROL = "x-bce-grant-full-control"
+ BCE_CONTENT_SHA256 = "x-bce-content-sha256"
+ BCE_CONTENT_CRC32 = "x-bce-content-crc32"
+ BCE_CONTENT_CRC32C = "x-bce-content-crc32c"
+ BCE_CONTENT_CRC32C_FLAG = "x-bce-content-crc32c-flag"
+ BCE_REQUEST_ID = "x-bce-request-id"
+ BCE_USER_METADATA_PREFIX = "x-bce-meta-"
+ BCE_SECURITY_TOKEN = "x-bce-security-token"
+ BCE_DATE = "x-bce-date"
+ BCE_TAG = "x-bce-tag-list"
+
+ // BOS HTTP Headers
+ BCE_COPY_METADATA_DIRECTIVE = "x-bce-metadata-directive"
+ BCE_COPY_TAGGING_DIRECTIVE = "x-bce-tagging-directive"
+ BCE_COPY_SOURCE = "x-bce-copy-source"
+ BCE_COPY_SOURCE_IF_MATCH = "x-bce-copy-source-if-match"
+ BCE_COPY_SOURCE_IF_MODIFIED_SINCE = "x-bce-copy-source-if-modified-since"
+ BCE_COPY_SOURCE_IF_NONE_MATCH = "x-bce-copy-source-if-none-match"
+ BCE_COPY_SOURCE_IF_UNMODIFIED_SINCE = "x-bce-copy-source-if-unmodified-since"
+ BCE_COPY_SOURCE_RANGE = "x-bce-copy-source-range"
+ BCE_DEBUG_ID = "x-bce-debug-id"
+ BCE_OBJECT_TYPE = "x-bce-object-type"
+ BCE_NEXT_APPEND_OFFSET = "x-bce-next-append-offset"
+ BCE_STORAGE_CLASS = "x-bce-storage-class"
+ BCE_PROCESS = "x-bce-process"
+ BCE_RESTORE_TIER = "x-bce-restore-tier"
+ BCE_RESTORE_DAYS = "x-bce-restore-days"
+ BCE_RESTORE = "x-bce-restore"
+ BCE_FORBID_OVERWRITE = "x-bce-forbid-overwrite"
+ BCE_SYMLINK_TARGET = "x-bce-symlink-target"
+ BCE_SYMLINK_BUCKET = "x-bce-symlink-bucket"
+ BCE_TRAFFIC_LIMIT = "x-bce-traffic-limit"
+ BCE_BUCKET_TYPE = "x-bce-bucket-type"
+ BCE_OBJECT_TAGGING = "x-bce-tagging"
+ BCE_FETCH_CALLBACK_ADDRESS = "x-bce-callback-address"
+ BCE_VERSION_ID = "x-bce-version-id"
+)
diff --git a/vendor/github.com/baidubce/bce-sdk-go/http/request.go b/vendor/github.com/baidubce/bce-sdk-go/http/request.go
new file mode 100644
index 00000000000..21b5eb0517a
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/http/request.go
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// request.go - the custom HTTP request for BCE
+
+package http
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+
+ "github.com/baidubce/bce-sdk-go/util"
+)
+
+// Reauest stands for the general http request structure to make request to the BCE services.
+type Request struct {
+ protocol string
+ host string
+ port int
+ method string
+ uri string
+ proxyUrl string
+ timeout int
+ headers map[string]string
+ params map[string]string
+
+ // Optional body and length fields to set the body stream and content length
+ body io.ReadCloser
+ length int64
+ ctx context.Context
+}
+
+func (r *Request) Context() context.Context {
+ return r.ctx
+}
+
+func (r *Request) SetContext(ctx context.Context) {
+ if ctx != nil {
+ r.ctx = ctx
+ }
+}
+
+func (r *Request) Protocol() string {
+ return r.protocol
+}
+
+func (r *Request) SetProtocol(protocol string) {
+ r.protocol = protocol
+}
+
+func (r *Request) Endpoint() string {
+ if r.host == "" {
+ return ""
+ }
+ return r.protocol + "://" + r.host
+}
+
+func (r *Request) SetEndpoint(endpoint string) {
+ pos := strings.Index(endpoint, "://")
+ rest := endpoint
+ if pos != -1 {
+ r.protocol = endpoint[0:pos]
+ rest = endpoint[pos+3:]
+ } else {
+ r.protocol = "http"
+ }
+
+ r.SetHost(rest)
+}
+
+func (r *Request) Host() string {
+ return r.host
+}
+
+func (r *Request) SetHost(host string) {
+ r.host = host
+ pos := strings.Index(host, ":")
+ if pos != -1 {
+ p, e := strconv.Atoi(host[pos+1:])
+ if e == nil {
+ r.port = p
+ }
+ }
+
+ if r.port == 0 {
+ if r.protocol == "http" {
+ r.port = 80
+ } else if r.protocol == "https" {
+ r.port = 443
+ }
+ }
+}
+
+func (r *Request) Port() int {
+ return r.port
+}
+
+func (r *Request) SetPort(port int) {
+ // Port can be set by the endpoint or host, this method is rarely used.
+ r.port = port
+}
+
+func (r *Request) Headers() map[string]string {
+ return r.headers
+}
+
+func (r *Request) SetHeaders(headers map[string]string) {
+ r.headers = headers
+}
+
+func (r *Request) Header(key string) string {
+ if v, ok := r.headers[key]; ok {
+ return v
+ }
+ return ""
+}
+
+func (r *Request) SetHeader(key, value string) {
+ if r.headers == nil {
+ r.headers = make(map[string]string)
+ }
+ r.headers[key] = value
+}
+
+func (r *Request) Params() map[string]string {
+ return r.params
+}
+
+func (r *Request) SetParams(params map[string]string) {
+ r.params = params
+}
+
+func (r *Request) Param(key string) string {
+ if v, ok := r.params[key]; ok {
+ return v
+ }
+ return ""
+}
+
+func (r *Request) SetParam(key, value string) {
+ if r.params == nil {
+ r.params = make(map[string]string)
+ }
+ r.params[key] = value
+}
+
+func (r *Request) QueryString() string {
+ buf := make([]string, 0, len(r.params))
+ for k, v := range r.params {
+ if len(v) == 0 {
+ buf = append(buf, util.UriEncode(k, true))
+ } else {
+ buf = append(buf, util.UriEncode(k, true)+"="+util.UriEncode(v, true))
+ }
+ }
+ return strings.Join(buf, "&")
+}
+
+func (r *Request) Method() string {
+ return r.method
+}
+
+func (r *Request) SetMethod(method string) {
+ r.method = method
+}
+
+func (r *Request) Uri() string {
+ return r.uri
+}
+
+func (r *Request) SetUri(uri string) {
+ r.uri = uri
+}
+
+func (r *Request) ProxyUrl() string {
+ return r.proxyUrl
+}
+
+func (r *Request) SetProxyUrl(url string) {
+ r.proxyUrl = url
+}
+
+func (r *Request) Timeout() int {
+ return r.timeout
+}
+
+func (r *Request) SetTimeout(timeout int) {
+ r.timeout = timeout
+}
+
+func (r *Request) Body() io.ReadCloser {
+ return r.body
+}
+
+func (r *Request) SetBody(stream io.ReadCloser) {
+ r.body = stream
+}
+
+func (r *Request) Length() int64 {
+ return r.length
+}
+
+func (r *Request) SetLength(l int64) {
+ r.length = l
+}
+
+func (r *Request) GenerateUrl(addPort bool) string {
+ if addPort {
+ return fmt.Sprintf("%s://%s:%d%s?%s",
+ r.protocol, r.host, r.port, r.uri, r.QueryString())
+ } else {
+ return fmt.Sprintf("%s://%s%s?%s", r.protocol, r.host, r.uri, r.QueryString())
+ }
+}
+
+func (r *Request) String() string {
+ header := make([]string, 0, len(r.headers))
+ for k, v := range r.headers {
+ header = append(header, "\t"+k+"="+v)
+ }
+ return fmt.Sprintf("\t%s %s\n%v",
+ r.method, r.GenerateUrl(false), strings.Join(header, "\n"))
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/http/response.go b/vendor/github.com/baidubce/bce-sdk-go/http/response.go
new file mode 100644
index 00000000000..98632fc9fd0
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/http/response.go
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// response.go - the custom HTTP response for BCE
+
+package http
+
+import (
+ "io"
+ "net/http"
+ "time"
+)
+
+// Response defines the general http response structure for accessing the BCE services.
+type Response struct {
+ httpResponse *http.Response // the standard libaray http.Response object
+ elapsedTime time.Duration // elapsed time just from sending request to receiving response
+}
+
+func (r *Response) StatusText() string {
+ return r.httpResponse.Status
+}
+
+func (r *Response) StatusCode() int {
+ return r.httpResponse.StatusCode
+}
+
+func (r *Response) Protocol() string {
+ return r.httpResponse.Proto
+}
+
+func (r *Response) HttpResponse() *http.Response {
+ return r.httpResponse
+}
+
+func (r *Response) SetHttpResponse(response *http.Response) {
+ r.httpResponse = response
+}
+
+func (r *Response) ElapsedTime() time.Duration {
+ return r.elapsedTime
+}
+
+func (r *Response) GetHeader(name string) string {
+ return r.httpResponse.Header.Get(name)
+}
+
+func (r *Response) GetHeaders() map[string]string {
+ header := r.httpResponse.Header
+ ret := make(map[string]string, len(header))
+ for k, v := range header {
+ ret[k] = v[0]
+ }
+ return ret
+}
+
+func (r *Response) ContentLength() int64 {
+ return r.httpResponse.ContentLength
+}
+
+func (r *Response) Body() io.ReadCloser {
+ return r.httpResponse.Body
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/services/bos/api/bucket.go b/vendor/github.com/baidubce/bce-sdk-go/services/bos/api/bucket.go
new file mode 100644
index 00000000000..486025b958f
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/services/bos/api/bucket.go
@@ -0,0 +1,1387 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// bucket.go - the bucket APIs definition supported by the BOS service
+
+// Package api defines all APIs supported by the BOS service of BCE.
+package api
+
+import (
+ "encoding/json"
+ "fmt"
+ "strconv"
+
+ "github.com/baidubce/bce-sdk-go/bce"
+ "github.com/baidubce/bce-sdk-go/http"
+)
+
+// ListBuckets - list all buckets of the account
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// RETURNS:
+// - *ListBucketsResult: the result bucket list structure
+// - error: nil if ok otherwise the specific error
+func ListBuckets(cli bce.Client, ctx *BosContext) (*ListBucketsResult, error) {
+ req := &bce.BceRequest{}
+ req.SetMethod(http.GET)
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &ListBucketsResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+// ListObjects - list all objects of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - args: the optional arguments to list objects
+// RETURNS:
+// - *ListObjectsResult: the result object list structure
+// - error: nil if ok otherwise the specific error
+func ListObjects(cli bce.Client, bucket string,
+ args *ListObjectsArgs, ctx *BosContext) (*ListObjectsResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ ctx.Bucket = bucket
+ // Optional arguments settings
+ if args != nil {
+ if len(args.Delimiter) != 0 {
+ req.SetParam("delimiter", args.Delimiter)
+ }
+ if len(args.Marker) != 0 {
+ req.SetParam("marker", args.Marker)
+ }
+ if args.MaxKeys != 0 {
+ req.SetParam("maxKeys", strconv.Itoa(args.MaxKeys))
+ }
+ if len(args.Prefix) != 0 {
+ req.SetParam("prefix", args.Prefix)
+ }
+ }
+ if args == nil || args.MaxKeys == 0 {
+ req.SetParam("maxKeys", "1000")
+ }
+
+ // Send the request and get result
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &ListObjectsResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ defer func() { resp.Body().Close() }()
+ return result, nil
+}
+
+func ListObjectsVersions(cli bce.Client, bucket string,
+ args *ListObjectsArgs, ctx *BosContext) (*ListObjectsResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("versions", "")
+ ctx.Bucket = bucket
+ // Optional arguments settings
+ if args != nil {
+ if len(args.Delimiter) != 0 {
+ req.SetParam("delimiter", args.Delimiter)
+ }
+ if len(args.Marker) != 0 {
+ req.SetParam("marker", args.Marker)
+ }
+ if args.MaxKeys != 0 {
+ req.SetParam("maxKeys", strconv.Itoa(args.MaxKeys))
+ }
+ if len(args.Prefix) != 0 {
+ req.SetParam("prefix", args.Prefix)
+ }
+ if len(args.VersionIdMarker) != 0 {
+ req.SetParam("versionIdMarker", args.VersionIdMarker)
+ }
+ }
+ if args == nil || args.MaxKeys == 0 {
+ req.SetParam("maxKeys", "1000")
+ }
+
+ // Send the request and get result
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &ListObjectsResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ defer func() { resp.Body().Close() }()
+ return result, nil
+}
+
+// HeadBucket - test the given bucket existed and access authority
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// RETURNS:
+// - error: nil if exists and have authority otherwise the specific error
+func HeadBucket(cli bce.Client, bucket string, ctx *BosContext) (error, *bce.BceResponse) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.HEAD)
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err, resp
+ }
+ if resp.IsFail() {
+ return resp.ServiceError(), resp
+ }
+ defer func() { resp.Body().Close() }()
+ return nil, resp
+}
+
+// PutBucket - create a new bucket with the given name
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the new bucket name
+// RETURNS:
+// - string: the location of the new bucket if create success
+// - error: nil if create success otherwise the specific error
+func PutBucket(cli bce.Client, bucket string, args *PutBucketArgs, ctx *BosContext) (string, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.PUT)
+ ctx.Bucket = bucket
+ if args != nil {
+ if len(args.TagList) != 0 {
+ req.SetHeader(http.BCE_TAG, args.TagList)
+ }
+ jsonBytes, jsonErr := json.Marshal(args)
+ if jsonErr != nil {
+ return "", jsonErr
+ }
+ body, err := bce.NewBodyFromBytes(jsonBytes)
+ if err != nil {
+ return "", err
+ }
+ req.SetBody(body)
+ }
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return "", err
+ }
+ if resp.IsFail() {
+ return "", resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return resp.Header(http.LOCATION), nil
+}
+
+// DeleteBucket - delete an empty bucket by given name
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name to be deleted
+// RETURNS:
+// - error: nil if delete success otherwise the specific error
+func DeleteBucket(cli bce.Client, bucket string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.DELETE)
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// GetBucketLocation - get the location of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// RETURNS:
+// - string: the location of the bucket
+// - error: nil if delete success otherwise the specific error
+func GetBucketLocation(cli bce.Client, bucket string, ctx *BosContext) (string, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("location", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return "", err
+ }
+ if resp.IsFail() {
+ return "", resp.ServiceError()
+ }
+ result := &LocationType{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return "", err
+ }
+ defer func() { resp.Body().Close() }()
+ return result.LocationConstraint, nil
+}
+
+// PutBucketAcl - set the acl of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - cannedAcl: support private, public-read, public-read-write
+// - aclBody: the acl file body
+// RETURNS:
+// - error: nil if delete success otherwise the specific error
+func PutBucketAcl(cli bce.Client, bucket, cannedAcl string, aclBody *bce.Body, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.PUT)
+ req.SetParam("acl", "")
+ ctx.Bucket = bucket
+ // The acl setting
+ if len(cannedAcl) != 0 && aclBody != nil {
+ return bce.NewBceClientError("BOS does not support cannedAcl and acl file at the same time")
+ }
+ if validCannedAcl(cannedAcl) {
+ req.SetHeader(http.BCE_ACL, cannedAcl)
+ }
+ if aclBody != nil {
+ req.SetHeader(http.CONTENT_TYPE, bce.DEFAULT_CONTENT_TYPE)
+ req.SetBody(aclBody)
+ }
+
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// GetBucketAcl - get the acl of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// RETURNS:
+// - *GetBucketAclResult: the result of the bucket acl
+// - error: nil if success otherwise the specific error
+func GetBucketAcl(cli bce.Client, bucket string, ctx *BosContext) (*GetBucketAclResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("acl", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &GetBucketAclResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ defer func() { resp.Body().Close() }()
+ return result, nil
+}
+
+// PutBucketLogging - set the logging prefix of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - logging: the logging prefix json string body
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func PutBucketLogging(cli bce.Client, bucket string, logging *bce.Body, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.PUT)
+ req.SetParam("logging", "")
+ req.SetBody(logging)
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// GetBucketLogging - get the logging config of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// RETURNS:
+// - *GetBucketLoggingResult: the logging setting of the bucket
+// - error: nil if success otherwise the specific error
+func GetBucketLogging(cli bce.Client, bucket string, ctx *BosContext) (*GetBucketLoggingResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("logging", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &GetBucketLoggingResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+// DeleteBucketLogging - delete the logging setting of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func DeleteBucketLogging(cli bce.Client, bucket string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.DELETE)
+ req.SetParam("logging", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// PutBucketLifecycle - set the lifecycle rule of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - lifecycle: the lifecycle rule json string body
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func PutBucketLifecycle(cli bce.Client, bucket string, lifecycle *bce.Body, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.PUT)
+ req.SetParam("lifecycle", "")
+ req.SetBody(lifecycle)
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// GetBucketLifecycle - get the lifecycle rule of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// RETURNS:
+// - *GetBucketLifecycleResult: the lifecycle rule of the bucket
+// - error: nil if success otherwise the specific error
+func GetBucketLifecycle(cli bce.Client, bucket string, ctx *BosContext) (*GetBucketLifecycleResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("lifecycle", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &GetBucketLifecycleResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+// DeleteBucketLifecycle - delete the lifecycle rule of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func DeleteBucketLifecycle(cli bce.Client, bucket string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.DELETE)
+ req.SetParam("lifecycle", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// PutBucketStorageclass - set the storage class of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - storageClass: the storage class string
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func PutBucketStorageclass(cli bce.Client, bucket, storageClass string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.PUT)
+ req.SetParam("storageClass", "")
+ ctx.Bucket = bucket
+ obj := &StorageClassType{storageClass}
+ jsonBytes, jsonErr := json.Marshal(obj)
+ if jsonErr != nil {
+ return jsonErr
+ }
+ body, err := bce.NewBodyFromBytes(jsonBytes)
+ if err != nil {
+ return err
+ }
+ req.SetBody(body)
+
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// GetBucketStorageclass - get the storage class of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// RETURNS:
+// - string: the storage class of the bucket
+// - error: nil if success otherwise the specific error
+func GetBucketStorageclass(cli bce.Client, bucket string, ctx *BosContext) (string, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("storageClass", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return "", err
+ }
+ if resp.IsFail() {
+ return "", resp.ServiceError()
+ }
+ result := &StorageClassType{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return "", err
+ }
+ return result.StorageClass, nil
+}
+
+// PutBucketReplication - set the bucket replication of different region
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - replicationConf: the replication config body stream
+// - replicationRuleId: the replication rule id composed of [0-9 A-Z a-z _ -]
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func PutBucketReplication(cli bce.Client, bucket string, replicationConf *bce.Body, replicationRuleId string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.PUT)
+ req.SetParam("replication", "")
+ ctx.Bucket = bucket
+ if len(replicationRuleId) > 0 {
+ req.SetParam("id", replicationRuleId)
+ }
+
+ if replicationConf != nil {
+ req.SetHeader(http.CONTENT_TYPE, bce.DEFAULT_CONTENT_TYPE)
+ req.SetBody(replicationConf)
+ }
+
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// GetBucketReplication - get the bucket replication config of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - replicationRuleId: the replication rule id composed of [0-9 A-Z a-z _ -]
+// RETURNS:
+// - *GetBucketReplicationResult: the result of the bucket replication config
+// - error: nil if success otherwise the specific error
+func GetBucketReplication(cli bce.Client, bucket string, replicationRuleId string, ctx *BosContext) (*GetBucketReplicationResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("replication", "")
+ ctx.Bucket = bucket
+ if len(replicationRuleId) > 0 {
+ req.SetParam("id", replicationRuleId)
+ }
+
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &GetBucketReplicationResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+// ListBucketReplication - list all replication config of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func ListBucketReplication(cli bce.Client, bucket string, ctx *BosContext) (*ListBucketReplicationResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("replication", "")
+ req.SetParam("list", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := cli.SendRequest(req, resp); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &ListBucketReplicationResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+// DeleteBucketReplication - delete the bucket replication config of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - replicationRuleId: the replication rule id composed of [0-9 A-Z a-z _ -]
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func DeleteBucketReplication(cli bce.Client, bucket string, replicationRuleId string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.DELETE)
+ req.SetParam("replication", "")
+ ctx.Bucket = bucket
+ if len(replicationRuleId) > 0 {
+ req.SetParam("id", replicationRuleId)
+ }
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// GetBucketReplicationProgress - get the bucket replication process of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - replicationRuleId: the replication rule id composed of [0-9 A-Z a-z _ -]
+// RETURNS:
+// - *GetBucketReplicationProgressResult: the result of the bucket replication process
+// - error: nil if success otherwise the specific error
+func GetBucketReplicationProgress(cli bce.Client, bucket string, replicationRuleId string, ctx *BosContext) (
+ *GetBucketReplicationProgressResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("replicationProgress", "")
+ ctx.Bucket = bucket
+ if len(replicationRuleId) > 0 {
+ req.SetParam("id", replicationRuleId)
+ }
+
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &GetBucketReplicationProgressResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+// PutBucketEncryption - set the bucket encrpytion config
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - algorithm: the encryption algorithm
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func PutBucketEncryption(cli bce.Client, bucket, algorithm string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.PUT)
+ req.SetParam("encryption", "")
+ ctx.Bucket = bucket
+ obj := &BucketEncryptionType{algorithm}
+ jsonBytes, jsonErr := json.Marshal(obj)
+ if jsonErr != nil {
+ return jsonErr
+ }
+ body, err := bce.NewBodyFromBytes(jsonBytes)
+ if err != nil {
+ return err
+ }
+ req.SetHeader(http.CONTENT_TYPE, bce.DEFAULT_CONTENT_TYPE)
+ req.SetBody(body)
+
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// GetBucketEncryption - get the encryption config of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// RETURNS:
+// - algorithm: the bucket encryption algorithm
+// - error: nil if success otherwise the specific error
+func GetBucketEncryption(cli bce.Client, bucket string, ctx *BosContext) (string, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("encryption", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return "", err
+ }
+ if resp.IsFail() {
+ return "", resp.ServiceError()
+ }
+ result := &BucketEncryptionType{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return "", err
+ }
+ return result.EncryptionAlgorithm, nil
+}
+
+// DeleteBucketEncryption - delete the encryption config of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func DeleteBucketEncryption(cli bce.Client, bucket string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.DELETE)
+ req.SetParam("encryption", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// PutBucketStaticWebsite - set the bucket static website config
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - confBody: the static website config body stream
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func PutBucketStaticWebsite(cli bce.Client, bucket string, confBody *bce.Body, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.PUT)
+ req.SetParam("website", "")
+ ctx.Bucket = bucket
+ if confBody != nil {
+ req.SetHeader(http.CONTENT_TYPE, bce.DEFAULT_CONTENT_TYPE)
+ req.SetBody(confBody)
+ }
+
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// GetBucketStaticWebsite - get the static website config of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// RETURNS:
+// - result: the bucket static website config result object
+// - error: nil if success otherwise the specific error
+func GetBucketStaticWebsite(cli bce.Client, bucket string, ctx *BosContext) (
+ *GetBucketStaticWebsiteResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("website", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &GetBucketStaticWebsiteResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+// DeleteBucketStaticWebsite - delete the static website config of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func DeleteBucketStaticWebsite(cli bce.Client, bucket string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.DELETE)
+ req.SetParam("website", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// PutBucketCors - set the bucket CORS config
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - confBody: the CORS config body stream
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func PutBucketCors(cli bce.Client, bucket string, confBody *bce.Body, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.PUT)
+ req.SetParam("cors", "")
+ ctx.Bucket = bucket
+ if confBody != nil {
+ req.SetHeader(http.CONTENT_TYPE, bce.DEFAULT_CONTENT_TYPE)
+ req.SetBody(confBody)
+ }
+
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// GetBucketCors - get the CORS config of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// RETURNS:
+// - result: the bucket CORS config result object
+// - error: nil if success otherwise the specific error
+func GetBucketCors(cli bce.Client, bucket string, ctx *BosContext) (
+ *GetBucketCorsResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("cors", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &GetBucketCorsResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+// DeleteBucketCors - delete the CORS config of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func DeleteBucketCors(cli bce.Client, bucket string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.DELETE)
+ req.SetParam("cors", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// PutBucketCopyrightProtection - set the copyright protection config of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - resources: the resource items in the bucket to be protected
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func PutBucketCopyrightProtection(cli bce.Client, ctx *BosContext, bucket string, resources ...string) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.PUT)
+ req.SetParam("copyrightProtection", "")
+ ctx.Bucket = bucket
+ if len(resources) == 0 {
+ return bce.NewBceClientError("the resource to set copyright protection is empty")
+ }
+ arg := &CopyrightProtectionType{resources}
+ jsonBytes, jsonErr := json.Marshal(arg)
+ if jsonErr != nil {
+ return jsonErr
+ }
+ body, err := bce.NewBodyFromBytes(jsonBytes)
+ if err != nil {
+ return err
+ }
+ req.SetHeader(http.CONTENT_TYPE, bce.DEFAULT_CONTENT_TYPE)
+ req.SetBody(body)
+
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// GetBucketCopyrightProtection - get the copyright protection config of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// RETURNS:
+// - result: the bucket copyright protection resources array
+// - error: nil if success otherwise the specific error
+func GetBucketCopyrightProtection(cli bce.Client, bucket string, ctx *BosContext) ([]string, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("copyrightProtection", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &CopyrightProtectionType{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result.Resource, nil
+}
+
+// DeleteBucketCopyrightProtection - delete the copyright protection config of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func DeleteBucketCopyrightProtection(cli bce.Client, bucket string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.DELETE)
+ req.SetParam("copyrightProtection", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// PutBucketTrash - put the trash setting of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - trashDir: the trash dir name
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func PutBucketTrash(cli bce.Client, bucket string, trashReq PutBucketTrashReq, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.PUT)
+ req.SetParam("trash", "")
+ ctx.Bucket = bucket
+ reqByte, _ := json.Marshal(trashReq)
+ body, err := bce.NewBodyFromString(string(reqByte))
+ if err != nil {
+ return err
+ }
+ req.SetBody(body)
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+func GetBucketTrash(cli bce.Client, bucket string, ctx *BosContext) (*GetBucketTrashResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("trash", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &GetBucketTrashResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+func DeleteBucketTrash(cli bce.Client, bucket string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.DELETE)
+ req.SetParam("trash", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+func PutBucketNotification(cli bce.Client, bucket string, putBucketNotificationReq PutBucketNotificationReq, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.PUT)
+ req.SetParam("notification", "")
+ ctx.Bucket = bucket
+ reqByte, _ := json.Marshal(putBucketNotificationReq)
+ body, err := bce.NewBodyFromString(string(reqByte))
+ if err != nil {
+ return err
+ }
+ req.SetBody(body)
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+func GetBucketNotification(cli bce.Client, bucket string, ctx *BosContext) (*PutBucketNotificationReq, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("notification", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &PutBucketNotificationReq{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+func DeleteBucketNotification(cli bce.Client, bucket string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.DELETE)
+ req.SetParam("notification", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+func PutBucketMirror(cli bce.Client, bucket string, putBucketMirrorArgs *PutBucketMirrorArgs, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.PUT)
+ req.SetParam("mirroring", "")
+ ctx.Bucket = bucket
+ reqByte, _ := json.Marshal(putBucketMirrorArgs)
+ body, err := bce.NewBodyFromString(string(reqByte))
+ if err != nil {
+ return err
+ }
+ req.SetBody(body)
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+func GetBucketMirror(cli bce.Client, bucket string, ctx *BosContext) (*PutBucketMirrorArgs, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("mirroring", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &PutBucketMirrorArgs{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+func DeleteBucketMirror(cli bce.Client, bucket string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.DELETE)
+ req.SetParam("mirroring", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+func PutBucketTag(cli bce.Client, bucket string, putBucketTagArgs *PutBucketTagArgs, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.PUT)
+ req.SetParam("tagging", "")
+ ctx.Bucket = bucket
+ reqByte, _ := json.Marshal(putBucketTagArgs)
+ body, err := bce.NewBodyFromString(string(reqByte))
+ if err != nil {
+ return err
+ }
+ req.SetBody(body)
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+func GetBucketTag(cli bce.Client, bucket string, ctx *BosContext) (*GetBucketTagResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("tagging", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &GetBucketTagResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+func DeleteBucketTag(cli bce.Client, bucket string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.DELETE)
+ req.SetParam("tagging", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+func GetBosShareLink(cli bce.Client, bucket, prefix, shareCode string, duration int) (string, error) {
+ req := &bce.BceRequest{}
+ req.SetEndpoint(BOS_SHARE_ENDPOINT)
+ req.SetParam("action", "")
+ req.SetMethod(http.POST)
+ if len(shareCode) != 0 && len(shareCode) != 6 {
+ return "", fmt.Errorf("shareCode length must be 0 or 6")
+ }
+ if duration < 60 || duration > 64800 {
+ return "", fmt.Errorf("duration must between 1 minute and 18 hours")
+ }
+ bosShareReqBody := &BosShareLinkArgs{
+ Bucket: bucket,
+ Endpoint: cli.GetBceClientConfig().Endpoint,
+ Prefix: prefix,
+ ShareCode: shareCode,
+ DurationSeconds: int64(duration),
+ }
+ jsonBytes, jsonErr := json.Marshal(bosShareReqBody)
+ if jsonErr != nil {
+ return "", jsonErr
+ }
+ body, err := bce.NewBodyFromBytes(jsonBytes)
+ if err != nil {
+ return "", err
+ }
+ req.SetBody(body)
+ resp := &bce.BceResponse{}
+ if err = cli.SendRequest(req, resp); err != nil {
+ return "", err
+ }
+ if resp.IsFail() {
+ return "", resp.ServiceError()
+ }
+ bosShareResBody := &BosShareResBody{}
+ if err := resp.ParseJsonBody(bosShareResBody); err != nil {
+ return "", err
+ }
+ jsonData, err := json.Marshal(bosShareResBody)
+ if err != nil {
+ return "", err
+ }
+ return string(jsonData), nil
+}
+
+func PutBucketVersioning(cli bce.Client, bucket string, putBucketVersioningArgs *BucketVersioningArgs, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.PUT)
+ req.SetParam("versioning", "")
+ ctx.Bucket = bucket
+ reqByte, _ := json.Marshal(putBucketVersioningArgs)
+ body, err := bce.NewBodyFromString(string(reqByte))
+ if err != nil {
+ return err
+ }
+ req.SetBody(body)
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+func GetBucketVersioning(cli bce.Client, bucket string, ctx *BosContext) (*BucketVersioningArgs, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("versioning", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &BucketVersioningArgs{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/services/bos/api/model.go b/vendor/github.com/baidubce/bce-sdk-go/services/bos/api/model.go
new file mode 100644
index 00000000000..f896fd27d40
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/services/bos/api/model.go
@@ -0,0 +1,722 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// model.go - definitions of the request arguments and results data structure model
+
+package api
+
+import (
+ "context"
+ "io"
+)
+
+type OwnerType struct {
+ Id string `json:"id"`
+ DisplayName string `json:"displayName"`
+}
+
+type BucketSummaryType struct {
+ Name string `json:"name"`
+ Location string `json:"location"`
+ CreationDate string `json:"creationDate"`
+ LccLocation string `json:"lccLocation,omitempty"`
+ EnableDedicated bool `json:"enableDedicated,omitempty"`
+}
+
+// ListBucketsResult defines the result structure of ListBuckets api.
+type ListBucketsResult struct {
+ Owner OwnerType `json:"owner"`
+ Buckets []BucketSummaryType `json:"buckets"`
+}
+
+// ListObjectsArgs defines the optional arguments for ListObjects api.
+type ListObjectsArgs struct {
+ Delimiter string `json:"delimiter"`
+ Marker string `json:"marker"`
+ MaxKeys int `json:"maxKeys"`
+ Prefix string `json:"prefix"`
+ VersionIdMarker string `json:"versionIdMarker,omitempty"`
+}
+
+type ObjectSummaryType struct {
+ Key string `json:"key"`
+ LastModified string `json:"lastModified"`
+ ETag string `json:"eTag"`
+ Size int `json:"size"`
+ StorageClass string `json:"storageClass"`
+ Owner OwnerType `json:"owner"`
+ VersionId string `json:"versionId,omitempty"`
+ IsLatest int `json:"isLatest,omitempty"`
+}
+
+type PrefixType struct {
+ Prefix string `json:"prefix"`
+}
+
+type PutBucketArgs struct {
+ TagList string `json:"-"`
+ EnableMultiAz bool `json:"enableMultiAz"`
+ LccLocation string `json:"lccLocation,omitempty"`
+ EnableDedicated bool `json:"enableDedicated,omitempty"`
+}
+
+// ListObjectsResult defines the result structure of ListObjects api.
+type ListObjectsResult struct {
+ Name string `json:"name"`
+ Prefix string `json:"prefix"`
+ Delimiter string `json:"delimiter"`
+ Marker string `json:"marker"`
+ NextMarker string `json:"nextMarker,omitempty"`
+ NextVersionidMarker string `json:"nextVersionidMarker,omitempty"`
+ MaxKeys int `json:"maxKeys"`
+ IsTruncated bool `json:"isTruncated"`
+ Contents []ObjectSummaryType `json:"contents"`
+ CommonPrefixes []PrefixType `json:"commonPrefixes"`
+}
+
+type LocationType struct {
+ LocationConstraint string `json:"locationConstraint"`
+}
+
+// AclOwnerType defines the owner struct in ACL setting
+type AclOwnerType struct {
+ Id string `json:"id"`
+}
+
+// GranteeType defines the grantee struct in ACL setting
+type GranteeType struct {
+ Id string `json:"id"`
+}
+
+type AclRefererType struct {
+ StringLike []string `json:"stringLike"`
+ StringEquals []string `json:"stringEquals"`
+}
+
+type AclCondType struct {
+ IpAddress []string `json:"ipAddress"`
+ Referer AclRefererType `json:"referer"`
+ VpcId []string `json:"vpcId"`
+}
+
+// GrantType defines the grant struct in ACL setting
+type GrantType struct {
+ Grantee []GranteeType `json:"grantee"`
+ Permission []string `json:"permission"`
+ Resource []string `json:"resource,omitempty"`
+ NotResource []string `json:"notResource,omitempty"`
+ Condition AclCondType `json:"condition,omitempty"`
+ Effect string `json:"effect,omitempty"`
+}
+
+// PutBucketAclArgs defines the input args structure for putting bucket acl.
+type PutBucketAclArgs struct {
+ AccessControlList []GrantType `json:"accessControlList"`
+}
+
+// GetBucketAclResult defines the result structure of getting bucket acl.
+type GetBucketAclResult struct {
+ AccessControlList []GrantType `json:"accessControlList"`
+ Owner AclOwnerType `json:"owner"`
+}
+
+// PutBucketLoggingArgs defines the input args structure for putting bucket logging.
+type PutBucketLoggingArgs struct {
+ TargetBucket string `json:"targetBucket"`
+ TargetPrefix string `json:"targetPrefix"`
+}
+
+// GetBucketLoggingResult defines the result structure for getting bucket logging.
+type GetBucketLoggingResult struct {
+ Status string `json:"status"`
+ TargetBucket string `json:"targetBucket,omitempty"`
+ TargetPrefix string `json:"targetPrefix,omitempty"`
+}
+
+// LifecycleConditionTimeType defines the structure of time condition
+type LifecycleConditionTimeType struct {
+ DateGreaterThan string `json:"dateGreaterThan"`
+}
+
+// LifecycleConditionType defines the structure of condition
+type LifecycleConditionType struct {
+ Time LifecycleConditionTimeType `json:"time"`
+}
+
+// LifecycleActionType defines the structure of lifecycle action
+type LifecycleActionType struct {
+ Name string `json:"name"`
+ StorageClass string `json:"storageClass,omitempty"`
+}
+
+// LifecycleRuleType defines the structure of a single lifecycle rule
+type LifecycleRuleType struct {
+ Id string `json:"id"`
+ Status string `json:"status"`
+ Resource []string `json:"resource"`
+ Condition LifecycleConditionType `json:"condition"`
+ Action LifecycleActionType `json:"action"`
+}
+
+// GetBucketLifecycleResult defines the lifecycle argument structure for putting
+type PutBucketLifecycleArgs struct {
+ Rule []LifecycleRuleType `json:"rule"`
+}
+
+// GetBucketLifecycleResult defines the lifecycle result structure for getting
+type GetBucketLifecycleResult struct {
+ Rule []LifecycleRuleType `json:"rule"`
+}
+
+type StorageClassType struct {
+ StorageClass string `json:"storageClass"`
+}
+
+// BucketReplicationDescriptor defines the description data structure
+type BucketReplicationDescriptor struct {
+ Bucket string `json:"bucket,omitempty"`
+ StorageClass string `json:"storageClass,omitempty"`
+ Prefix string `json:"prefix,omitempty"`
+}
+
+// BucketReplicationType defines the data structure for Put and Get of bucket replication
+type BucketReplicationType struct {
+ Id string `json:"id"`
+ Status string `json:"status"`
+ Resource []string `json:"resource"`
+ NotIncludeResource []string `json:"notIncludeResource,omitempty"`
+ ReplicateDeletes string `json:"replicateDeletes"`
+ Destination *BucketReplicationDescriptor `json:"destination,omitempty"`
+ ReplicateHistory *BucketReplicationDescriptor `json:"replicateHistory,omitempty"`
+ CreateTime int64 `json:"createTime"`
+ DestRegion string `json:"destRegion"`
+}
+
+type PutBucketReplicationArgs BucketReplicationType
+type GetBucketReplicationResult BucketReplicationType
+
+// ListBucketReplicationResult defines output result for replication conf list
+type ListBucketReplicationResult struct {
+ Rules []BucketReplicationType `json:"rules"`
+}
+
+// GetBucketReplicationProgressResult defines output result for replication process
+type GetBucketReplicationProgressResult struct {
+ Status string `json:"status"`
+ HistoryReplicationPercent float64 `json:"historyReplicationPercent"`
+ LatestReplicationTime string `json:"latestReplicationTime"`
+}
+
+// BucketEncryptionType defines the data structure for Put and Get of bucket encryption
+type BucketEncryptionType struct {
+ EncryptionAlgorithm string `json:"encryptionAlgorithm"`
+}
+
+// BucketStaticWebsiteType defines the data structure for Put and Get of bucket static website
+type BucketStaticWebsiteType struct {
+ Index string `json:"index"`
+ NotFound string `json:"notFound"`
+}
+
+type PutBucketStaticWebsiteArgs BucketStaticWebsiteType
+type GetBucketStaticWebsiteResult BucketStaticWebsiteType
+
+type BucketCORSType struct {
+ AllowedOrigins []string `json:"allowedOrigins"`
+ AllowedMethods []string `json:"allowedMethods"`
+ AllowedHeaders []string `json:"allowedHeaders,omitempty"`
+ AllowedExposeHeaders []string `json:"allowedExposeHeaders,omitempty"`
+ MaxAgeSeconds int64 `json:"maxAgeSeconds,omitempty"`
+}
+
+// PutBucketCorsArgs defines the request argument for bucket CORS setting
+type PutBucketCorsArgs struct {
+ CorsConfiguration []BucketCORSType `json:"corsConfiguration"`
+}
+
+// GetBucketCorsResult defines the data structure of getting bucket CORS result
+type GetBucketCorsResult struct {
+ CorsConfiguration []BucketCORSType `json:"corsConfiguration"`
+}
+
+// CopyrightProtectionType defines the data structure for Put and Get copyright protection API
+type CopyrightProtectionType struct {
+ Resource []string `json:"resource"`
+}
+
+// ObjectAclType defines the data structure for Put and Get object acl API
+type ObjectAclType struct {
+ AccessControlList []GrantType `json:"accessControlList"`
+}
+
+type PutObjectAclArgs ObjectAclType
+type GetObjectAclResult ObjectAclType
+
+// PutObjectArgs defines the optional args structure for the put object api.
+type PutObjectArgs struct {
+ CacheControl string
+ ContentDisposition string
+ ContentMD5 string
+ ContentType string
+ ContentLength int64
+ Expires string
+ UserMeta map[string]string
+ ContentSha256 string
+ ContentCrc32 string
+ StorageClass string
+ Process string
+ CannedAcl string
+ ObjectTagging string
+ TrafficLimit int64
+ ContentCrc32c string
+ ContentCrc32cFlag bool
+}
+
+// CopyObjectArgs defines the optional args structure for the copy object api.
+type CopyObjectArgs struct {
+ ObjectMeta
+ MetadataDirective string
+ IfMatch string
+ IfNoneMatch string
+ IfModifiedSince string
+ IfUnmodifiedSince string
+ TrafficLimit int64
+ CannedAcl string
+ TaggingDirective string
+ ObjectTagging string
+ ContentCrc32c string
+ ContentCrc32cFlag bool
+}
+
+type MultiCopyObjectArgs struct {
+ StorageClass string
+ ObjectTagging string
+ TaggingDirective string
+}
+
+type CallbackResult struct {
+ Result string `json:"result"`
+}
+
+type PutObjectResult struct {
+ Callback CallbackResult `json:"callback"`
+ ContentCrc32 string `json:"-"`
+ ContentCrc32c string `json:"-"`
+}
+
+// CopyObjectResult defines the result json structure for the copy object api.
+type CopyObjectResult struct {
+ LastModified string `json:"lastModified"`
+ ETag string `json:"eTag"`
+ VersionId string `json:"versionId"`
+}
+
+type ObjectMeta struct {
+ CacheControl string
+ ContentDisposition string
+ ContentEncoding string
+ ContentLength int64
+ ContentRange string
+ ContentType string
+ ContentMD5 string
+ ContentSha256 string
+ ContentCrc32 string
+ Expires string
+ LastModified string
+ ETag string
+ UserMeta map[string]string
+ StorageClass string
+ NextAppendOffset string
+ ObjectType string
+ BceRestore string
+ BceObjectType string
+ VersionId string
+ ContentCrc32c string
+}
+
+// GetObjectResult defines the result data of the get object api.
+type GetObjectResult struct {
+ ObjectMeta
+ ContentLanguage string
+ Body io.ReadCloser
+}
+
+// GetObjectMetaResult defines the result data of the get object meta api.
+type GetObjectMetaResult struct {
+ ObjectMeta
+}
+
+// SelectObjectResult defines the result data of the select object api.
+type SelectObjectResult struct {
+ Body io.ReadCloser
+}
+
+// selectObject request args
+type SelectObjectArgs struct {
+ SelectType string `json:"-"`
+ SelectRequest *SelectObjectRequest `json:"selectRequest"`
+}
+
+type SelectObjectRequest struct {
+ Expression string `json:"expression"`
+ ExpressionType string `json:"expressionType"` // SQL
+ InputSerialization *SelectObjectInput `json:"inputSerialization"`
+ OutputSerialization *SelectObjectOutput `json:"outputSerialization"`
+ RequestProgress *SelectObjectProgress `json:"requestProgress"`
+}
+
+type SelectObjectInput struct {
+ CompressionType string `json:"compressionType"`
+ CsvParams map[string]string `json:"csv"`
+ JsonParams map[string]string `json:"json"`
+}
+type SelectObjectOutput struct {
+ OutputHeader bool `json:"outputHeader"`
+ CsvParams map[string]string `json:"csv"`
+ JsonParams map[string]string `json:"json"`
+}
+type SelectObjectProgress struct {
+ Enabled bool `json:"enabled"`
+}
+
+type Prelude struct {
+ TotalLen uint32
+ HeadersLen uint32
+}
+
+// selectObject response msg
+type CommonMessage struct {
+ Prelude
+ Headers map[string]string // message-type/content-type……
+ Crc32 uint32 // crc32 of RecordsMessage
+}
+type RecordsMessage struct {
+ CommonMessage
+ Records []string // csv/json seleted data, one or more records
+}
+type ContinuationMessage struct {
+ CommonMessage
+ BytesScanned uint64
+ BytesReturned uint64
+}
+type EndMessage struct {
+ CommonMessage
+}
+
+// FetchObjectArgs defines the optional arguments structure for the fetch object api.
+type FetchObjectArgs struct {
+ FetchMode string
+ StorageClass string
+ FetchCallBackAddress string
+}
+
+// FetchObjectResult defines the result json structure for the fetch object api.
+type FetchObjectResult struct {
+ Code string `json:"code"`
+ Message string `json:"message"`
+ RequestId string `json:"requestId"`
+ JobId string `json:"jobId"`
+}
+
+// AppendObjectArgs defines the optional arguments structure for appending object.
+type AppendObjectArgs struct {
+ Offset int64
+ CacheControl string
+ ContentDisposition string
+ ContentMD5 string
+ ContentType string
+ Expires string
+ UserMeta map[string]string
+ ContentSha256 string
+ ContentCrc32 string
+ StorageClass string
+ TrafficLimit int64
+ ContentCrc32c string
+ ContentCrc32cFlag bool
+}
+
+// AppendObjectResult defines the result data structure for appending object.
+type AppendObjectResult struct {
+ ContentMD5 string
+ NextAppendOffset int64
+ ContentCrc32 string
+ ETag string
+ ContentCrc32c string
+}
+
+// DeleteObjectArgs defines the input args structure for a single object.
+type DeleteObjectArgs struct {
+ Key string `json:"key"`
+}
+
+// DeleteMultipleObjectsResult defines the input args structure for deleting multiple objects.
+type DeleteMultipleObjectsArgs struct {
+ Objects []DeleteObjectArgs `json:"objects"`
+}
+
+// DeleteObjectResult defines the result structure for deleting a single object.
+type DeleteObjectResult struct {
+ Key string `json:"key"`
+ Code string `json:"code"`
+ Message string `json:"message"`
+}
+
+// DeleteMultipleObjectsResult defines the result structure for deleting multiple objects.
+type DeleteMultipleObjectsResult struct {
+ Errors []DeleteObjectResult `json:"errors"`
+}
+
+// InitiateMultipartUploadArgs defines the input arguments to initiate a multipart upload.
+type InitiateMultipartUploadArgs struct {
+ CacheControl string
+ ContentDisposition string
+ Expires string
+ StorageClass string
+ ObjectTagging string
+ TaggingDirective string
+ CannedAcl string
+}
+
+// InitiateMultipartUploadResult defines the result structure to initiate a multipart upload.
+type InitiateMultipartUploadResult struct {
+ Bucket string `json:"bucket"`
+ Key string `json:"key"`
+ UploadId string `json:"uploadId"`
+}
+
+// UploadPartArgs defines the optinoal argumets for uploading part.
+type UploadPartArgs struct {
+ ContentMD5 string
+ ContentSha256 string
+ ContentCrc32 string
+ TrafficLimit int64
+ ContentCrc32c string
+ ContentCrc32cFlag bool
+}
+
+// UploadPartCopyArgs defines the optional arguments of UploadPartCopy.
+type UploadPartCopyArgs struct {
+ SourceRange string
+ IfMatch string
+ IfNoneMatch string
+ IfModifiedSince string
+ IfUnmodifiedSince string
+ TrafficLimit int64
+ ContentCrc32c string
+ ContentCrc32cFlag bool
+}
+
+type PutSymlinkArgs struct {
+ ForbidOverwrite string
+ StorageClass string
+ UserMeta map[string]string
+ SymlinkBucket string
+}
+
+// UploadInfoType defines an uploaded part info structure.
+type UploadInfoType struct {
+ PartNumber int `json:"partNumber"`
+ ETag string `json:"eTag"`
+}
+
+// CompleteMultipartUploadArgs defines the input arguments structure of CompleteMultipartUpload.
+type CompleteMultipartUploadArgs struct {
+ Parts []UploadInfoType `json:"parts"`
+ UserMeta map[string]string `json:"-"`
+ Process string `json:"-"`
+ ContentCrc32 string `json:"-"`
+ ContentCrc32c string `json:"-"`
+ ContentCrc32cFlag bool `json:"-"`
+}
+
+// CompleteMultipartUploadResult defines the result structure of CompleteMultipartUpload.
+type CompleteMultipartUploadResult struct {
+ Location string `json:"location"`
+ Bucket string `json:"bucket"`
+ Key string `json:"key"`
+ ETag string `json:"eTag"`
+ ContentCrc32 string `json:"-"`
+ ContentCrc32c string `json:"-"`
+}
+
+// ListPartsArgs defines the input optional arguments of listing parts information.
+type ListPartsArgs struct {
+ MaxParts int
+ PartNumberMarker string
+}
+
+type ListPartType struct {
+ PartNumber int `json:"partNumber"`
+ LastModified string `json:"lastModified"`
+ ETag string `json:"eTag"`
+ Size int `json:"size"`
+}
+
+// ListPartsResult defines the parts info result from ListParts.
+type ListPartsResult struct {
+ Bucket string `json:"bucket"`
+ Key string `json:"key"`
+ UploadId string `json:"uploadId"`
+ Initiated string `json:"initiated"`
+ Owner OwnerType `json:"owner"`
+ StorageClass string `json:"storageClass"`
+ PartNumberMarker int `json:"partNumberMarker"`
+ NextPartNumberMarker int `json:"nextPartNumberMarker"`
+ MaxParts int `json:"maxParts"`
+ IsTruncated bool `json:"isTruncated"`
+ Parts []ListPartType `json:"parts"`
+}
+
+// ListMultipartUploadsArgs defines the optional arguments for ListMultipartUploads.
+type ListMultipartUploadsArgs struct {
+ Delimiter string
+ KeyMarker string
+ MaxUploads int
+ Prefix string
+}
+
+type ListMultipartUploadsType struct {
+ Key string `json:"key"`
+ UploadId string `json:"uploadId"`
+ Owner OwnerType `json:"owner"`
+ Initiated string `json:"initiated"`
+ StorageClass string `json:"storageClass,omitempty"`
+}
+
+// ListMultipartUploadsResult defines the multipart uploads result structure.
+type ListMultipartUploadsResult struct {
+ Bucket string `json:"bucket"`
+ CommonPrefixes []PrefixType `json:"commonPrefixes"`
+ Delimiter string `json:"delimiter"`
+ Prefix string `json:"prefix"`
+ IsTruncated bool `json:"isTruncated"`
+ KeyMarker string `json:"keyMarker"`
+ MaxUploads int `json:"maxUploads"`
+ NextKeyMarker string `json:"nextKeyMarker"`
+ Uploads []ListMultipartUploadsType `json:"uploads"`
+}
+
+type ArchiveRestoreArgs struct {
+ RestoreTier string
+ RestoreDays int
+}
+
+type GetBucketTrashResult struct {
+ TrashDir string `json:"trashDir"`
+}
+
+type PutBucketTrashReq struct {
+ TrashDir string `json:"trashDir"`
+}
+
+type PutBucketNotificationReq struct {
+ Notifications []PutBucketNotificationSt `json:"notifications"`
+}
+
+type PutBucketNotificationSt struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+ AppId string `json:"appId"`
+ Status string `json:"status"`
+ Resources []string `json:"resources"`
+ Events []string `json:"events"`
+ Apps []PutBucketNotificationAppsSt `json:"apps"`
+}
+
+type PutBucketNotificationAppsSt struct {
+ Id string `json:"id"`
+ EventUrl string `json:"eventUrl"`
+ XVars string `json:"xVars"`
+}
+
+type MirrorConfigurationRule struct {
+ Prefix string `json:"prefix,omitempty"`
+ SourceUrl string `json:"sourceUrl"`
+ PassQueryString bool `json:"passQuerystring"`
+ Mode string `json:"mode"`
+ StorageClass string `json:"storageClass"`
+ PassHeaders []string `json:"passHeaders"`
+ IgnoreHeaders []string `json:"ignoreHeaders"`
+ CustomHeaders []HeaderPair `json:"customHeaders"`
+ BackSourceUrl string `json:"backSourceUrl"`
+ Resource string `json:"resource"`
+ Suffix string `json:"suffix"`
+ FixedKey string `json:"fixedKey"`
+ PrefixReplace string `json:"prefixReplace"`
+ Version string `json:"version"`
+}
+
+type HeaderPair struct {
+ HeaderName string `json:"headerName"`
+ HeaderValue string `json:"headerValue"`
+}
+
+type PutBucketMirrorArgs struct {
+ BucketMirroringConfiguration []MirrorConfigurationRule `json:"bucketMirroringConfiguration"`
+}
+
+type PutBucketTagArgs struct {
+ Tags []Tag `json:"tags"`
+}
+
+type Tag struct {
+ TagKey string `json:"tagKey"`
+ TagValue string `json:"tagValue"`
+}
+
+type GetBucketTagResult struct {
+ Tags []BucketTag `json:"tag"`
+}
+
+type BucketTag struct {
+ TagKey string `json:"tag_key"`
+ TagValue string `json:"tag_value"`
+}
+
+type BosContext struct {
+ Bucket string
+ PathStyleEnable bool
+ Ctx context.Context
+}
+
+type PutObjectTagArgs struct {
+ ObjectTags []ObjectTags `json:"tagSet"`
+}
+
+type ObjectTags struct {
+ TagInfo []ObjectTag `json:"tagInfo"`
+}
+
+type ObjectTag struct {
+ Key string `json:"key"`
+ Value string `json:"value"`
+}
+
+type BosShareLinkArgs struct {
+ Bucket string `json:"bucket"`
+ Endpoint string `json:"endpoint"`
+ Prefix string `json:"prefix"`
+ ShareCode string `json:"shareCode"`
+ DurationSeconds int64 `json:"durationSeconds"`
+}
+
+type BosShareResBody struct {
+ ShareUrl string `json:"shareUrl"`
+ LinkExpireTime int64 `json:"linkExpireTime"`
+ ShareCode string `json:"shareCode"`
+}
+
+type BucketVersioningArgs struct {
+ Status string `json:"status"`
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/services/bos/api/multipart.go b/vendor/github.com/baidubce/bce-sdk-go/services/bos/api/multipart.go
new file mode 100644
index 00000000000..4d13a7b29cf
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/services/bos/api/multipart.go
@@ -0,0 +1,491 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// multipart.go - the multipart-related APIs definition supported by the BOS service
+
+package api
+
+import (
+ "bytes"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/baidubce/bce-sdk-go/bce"
+ "github.com/baidubce/bce-sdk-go/http"
+ "github.com/baidubce/bce-sdk-go/util"
+)
+
+// InitiateMultipartUpload - initiate a multipart upload to get a upload ID
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - object: the object name
+// - contentType: the content type of the object to be uploaded which should be specified,
+// otherwise use the default(application/octet-stream)
+// - args: the optional arguments
+//
+// RETURNS:
+// - *InitiateMultipartUploadResult: the result data structure
+// - error: nil if ok otherwise the specific error
+func InitiateMultipartUpload(cli bce.Client, bucket, object, contentType string,
+ args *InitiateMultipartUploadArgs, ctx *BosContext) (*InitiateMultipartUploadResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.POST)
+ req.SetParam("uploads", "")
+ ctx.Bucket = bucket
+ if len(contentType) == 0 {
+ contentType = RAW_CONTENT_TYPE
+ }
+ req.SetHeader(http.CONTENT_TYPE, contentType)
+
+ // Optional arguments settings
+ if args != nil {
+ setOptionalNullHeaders(req, map[string]string{
+ http.CACHE_CONTROL: args.CacheControl,
+ http.CONTENT_DISPOSITION: args.ContentDisposition,
+ http.EXPIRES: args.Expires,
+ })
+
+ if validStorageClass(args.StorageClass) {
+ req.SetHeader(http.BCE_STORAGE_CLASS, args.StorageClass)
+ } else {
+ if len(args.StorageClass) != 0 {
+ return nil, bce.NewBceClientError("invalid storage class value: " +
+ args.StorageClass)
+ }
+ }
+ if len(args.ObjectTagging) != 0 {
+ if ok, encodeTagging := validObjectTagging(args.ObjectTagging); ok {
+ req.SetHeader(http.BCE_OBJECT_TAGGING, encodeTagging)
+ }
+ }
+ if validMetadataDirective(args.TaggingDirective) {
+ req.SetHeader(http.BCE_COPY_TAGGING_DIRECTIVE, args.TaggingDirective)
+ }
+ if validCannedAcl(args.CannedAcl) {
+ req.SetHeader(http.BCE_ACL, args.CannedAcl)
+ } else {
+ if len(args.CannedAcl) != 0 {
+ return nil, bce.NewBceClientError("invalid canned acl value: " +
+ args.CannedAcl)
+ }
+ }
+
+ }
+
+ // Send request and get the result
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &InitiateMultipartUploadResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ defer func() { resp.Body().Close() }()
+ return result, nil
+}
+
+// UploadPart - upload the single part in the multipart upload process
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - object: the object name
+// - uploadId: the multipart upload id
+// - partNumber: the current part number
+// - content: the uploaded part content
+// - args: the optional arguments
+//
+// RETURNS:
+// - string: the etag of the uploaded part
+// - error: nil if ok otherwise the specific error
+func UploadPart(cli bce.Client, bucket, object, uploadId string, partNumber int,
+ content *bce.Body, args *UploadPartArgs, ctx *BosContext) (string, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.PUT)
+ req.SetParam("uploadId", uploadId)
+ req.SetParam("partNumber", fmt.Sprintf("%d", partNumber))
+ ctx.Bucket = bucket
+ if content == nil {
+ return "", bce.NewBceClientError("upload part content should not be empty")
+ }
+ if content.Size() >= THRESHOLD_100_CONTINUE {
+ req.SetHeader("Expect", "100-continue")
+ }
+ req.SetBody(content)
+
+ // Optional arguments settings
+ if args != nil {
+ setOptionalNullHeaders(req, map[string]string{
+ http.CONTENT_MD5: args.ContentMD5,
+ http.BCE_CONTENT_SHA256: args.ContentSha256,
+ http.BCE_CONTENT_CRC32: args.ContentCrc32,
+ http.BCE_CONTENT_CRC32C: args.ContentCrc32c,
+ http.BCE_CONTENT_CRC32C_FLAG: strconv.FormatBool(args.ContentCrc32cFlag),
+ })
+ //set traffic-limit
+ if args.TrafficLimit > 0 {
+ if args.TrafficLimit > TRAFFIC_LIMIT_MAX || args.TrafficLimit < TRAFFIC_LIMIT_MIN {
+ return "", bce.NewBceClientError(fmt.Sprintf("TrafficLimit must between %d ~ %d, current value:%d", TRAFFIC_LIMIT_MIN, TRAFFIC_LIMIT_MAX, args.TrafficLimit))
+ }
+ req.SetHeader(http.BCE_TRAFFIC_LIMIT, fmt.Sprintf("%d", args.TrafficLimit))
+ }
+
+ }
+
+ // Send request and get the result
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return "", err
+ }
+ if resp.IsFail() {
+ return "", resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return strings.Trim(resp.Header(http.ETAG), "\""), nil
+}
+
+// UploadPartFromBytes - upload the single part in the multipart upload process
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - object: the object name
+// - uploadId: the multipart upload id
+// - partNumber: the current part number
+// - content: the uploaded part content
+// - args: the optional arguments
+//
+// RETURNS:
+// - string: the etag of the uploaded part
+// - error: nil if ok otherwise the specific error
+func UploadPartFromBytes(cli bce.Client, bucket, object, uploadId string, partNumber int,
+ content []byte, args *UploadPartArgs, ctx *BosContext) (string, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.PUT)
+ req.SetParam("uploadId", uploadId)
+ req.SetParam("partNumber", fmt.Sprintf("%d", partNumber))
+ ctx.Bucket = bucket
+ if content == nil {
+ return "", bce.NewBceClientError("upload part content should not be empty")
+ }
+ size := len(content)
+ if size >= THRESHOLD_100_CONTINUE {
+ req.SetHeader("Expect", "100-continue")
+ }
+ // set md5 and content-length
+ req.SetLength(int64(size))
+ if size > 0 {
+ // calc md5
+ if args == nil || args.ContentMD5 == "" {
+ buf := bytes.NewBuffer(content)
+ contentMD5, err := util.CalculateContentMD5(buf, int64(size))
+ if err != nil {
+ return "", err
+ }
+ req.SetHeader(http.CONTENT_MD5, contentMD5)
+ }
+ req.SetHeader(http.CONTENT_LENGTH, fmt.Sprintf("%d", size))
+ }
+ // Optional arguments settings
+ if args != nil {
+ setOptionalNullHeaders(req, map[string]string{
+ http.CONTENT_MD5: args.ContentMD5,
+ http.BCE_CONTENT_SHA256: args.ContentSha256,
+ http.BCE_CONTENT_CRC32: args.ContentCrc32,
+ http.BCE_CONTENT_CRC32C: args.ContentCrc32c,
+ http.BCE_CONTENT_CRC32C_FLAG: strconv.FormatBool(args.ContentCrc32cFlag),
+ })
+ //set traffic-limit
+ if args.TrafficLimit > 0 {
+ if args.TrafficLimit > TRAFFIC_LIMIT_MAX || args.TrafficLimit < TRAFFIC_LIMIT_MIN {
+ return "", bce.NewBceClientError(fmt.Sprintf("TrafficLimit must between %d ~ %d, current value:%d",
+ TRAFFIC_LIMIT_MIN, TRAFFIC_LIMIT_MAX, args.TrafficLimit))
+ }
+ req.SetHeader(http.BCE_TRAFFIC_LIMIT, fmt.Sprintf("%d", args.TrafficLimit))
+ }
+ }
+ // Send request and get the result
+ resp := &bce.BceResponse{}
+ if err := SendRequestFromBytes(cli, req, resp, ctx, content); err != nil {
+ return "", err
+ }
+ if resp.IsFail() {
+ return "", resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return strings.Trim(resp.Header(http.ETAG), "\""), nil
+}
+
+// UploadPartCopy - copy the multipart data
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the destination bucket name
+// - object: the destination object name
+// - source: the copy source uri
+// - uploadId: the multipart upload id
+// - partNumber: the current part number
+// - args: the optional arguments
+//
+// RETURNS:
+// - *CopyObjectResult: the lastModified and eTag of the part
+// - error: nil if ok otherwise the specific error
+func UploadPartCopy(cli bce.Client, bucket, object, source, uploadId string, partNumber int,
+ args *UploadPartCopyArgs, ctx *BosContext) (*CopyObjectResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.PUT)
+ req.SetParam("uploadId", uploadId)
+ req.SetParam("partNumber", fmt.Sprintf("%d", partNumber))
+ ctx.Bucket = bucket
+ if len(source) == 0 {
+ return nil, bce.NewBceClientError("upload part copy source should not be empty")
+ }
+ req.SetHeader(http.BCE_COPY_SOURCE, util.UriEncode(source, false))
+
+ // Optional arguments settings
+ if args != nil {
+ setOptionalNullHeaders(req, map[string]string{
+ http.BCE_COPY_SOURCE_RANGE: args.SourceRange,
+ http.BCE_COPY_SOURCE_IF_MATCH: args.IfMatch,
+ http.BCE_COPY_SOURCE_IF_NONE_MATCH: args.IfNoneMatch,
+ http.BCE_COPY_SOURCE_IF_MODIFIED_SINCE: args.IfModifiedSince,
+ http.BCE_COPY_SOURCE_IF_UNMODIFIED_SINCE: args.IfUnmodifiedSince,
+ http.BCE_CONTENT_CRC32C: args.ContentCrc32c,
+ http.BCE_CONTENT_CRC32C_FLAG: strconv.FormatBool(args.ContentCrc32cFlag),
+ })
+ //set traffic-limit
+ if args.TrafficLimit > 0 {
+ if args.TrafficLimit > TRAFFIC_LIMIT_MAX || args.TrafficLimit < TRAFFIC_LIMIT_MIN {
+ return nil, bce.NewBceClientError(fmt.Sprintf("TrafficLimit must between %d ~ %d, current value:%d", TRAFFIC_LIMIT_MIN, TRAFFIC_LIMIT_MAX, args.TrafficLimit))
+ }
+ req.SetHeader(http.BCE_TRAFFIC_LIMIT, fmt.Sprintf("%d", args.TrafficLimit))
+ }
+ }
+
+ // Send request and get the result
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &CopyObjectResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+// CompleteMultipartUpload - finish a multipart upload operation
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the destination bucket name
+// - object: the destination object name
+// - uploadId: the multipart upload id
+// - parts: all parts info stream
+// - meta: user defined meta data
+//
+// RETURNS:
+// - *CompleteMultipartUploadResult: the result data
+// - error: nil if ok otherwise the specific error
+func CompleteMultipartUpload(cli bce.Client, bucket, object, uploadId string,
+ body *bce.Body, args *CompleteMultipartUploadArgs, ctx *BosContext) (*CompleteMultipartUploadResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.POST)
+ req.SetParam("uploadId", uploadId)
+ ctx.Bucket = bucket
+ if body == nil {
+ return nil, bce.NewBceClientError("upload body info should not be emtpy")
+ }
+ if body.Size() >= THRESHOLD_100_CONTINUE {
+ req.SetHeader("Expect", "100-continue")
+ }
+ req.SetBody(body)
+
+ // Optional arguments settings
+ if args.UserMeta != nil {
+ if err := setUserMetadata(req, args.UserMeta); err != nil {
+ return nil, err
+ }
+ }
+ if len(args.Process) != 0 {
+ req.SetHeader(http.BCE_PROCESS, args.Process)
+ }
+ if len(args.ContentCrc32) != 0 {
+ req.SetHeader(http.BCE_CONTENT_CRC32, args.ContentCrc32)
+ }
+ if len(args.ContentCrc32c) != 0 {
+ req.SetHeader(http.BCE_CONTENT_CRC32C, args.ContentCrc32c)
+ }
+ if args.ContentCrc32cFlag {
+ req.SetHeader(http.BCE_CONTENT_CRC32C_FLAG, strconv.FormatBool(args.ContentCrc32cFlag))
+ }
+
+ // Send request and get the result
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &CompleteMultipartUploadResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ headers := resp.Headers()
+ if val, ok := headers[toHttpHeaderKey(http.BCE_CONTENT_CRC32)]; ok {
+ result.ContentCrc32 = val
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_CONTENT_CRC32C)]; ok {
+ result.ContentCrc32c = val
+ }
+ return result, nil
+}
+
+// AbortMultipartUpload - abort a multipart upload operation
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the destination bucket name
+// - object: the destination object name
+// - uploadId: the multipart upload id
+//
+// RETURNS:
+// - error: nil if ok otherwise the specific error
+func AbortMultipartUpload(cli bce.Client, bucket, object, uploadId string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.DELETE)
+ req.SetParam("uploadId", uploadId)
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// ListParts - list the successfully uploaded parts info by upload id
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the destination bucket name
+// - object: the destination object name
+// - uploadId: the multipart upload id
+// - args: the optional arguments
+// partNumberMarker: return parts after this marker
+// maxParts: the max number of return parts, default and maximum is 1000
+//
+// RETURNS:
+// - *ListPartsResult: the uploaded parts info result
+// - error: nil if ok otherwise the specific error
+func ListParts(cli bce.Client, bucket, object, uploadId string,
+ args *ListPartsArgs, ctx *BosContext) (*ListPartsResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.GET)
+ req.SetParam("uploadId", uploadId)
+ ctx.Bucket = bucket
+ // Optional arguments settings
+ if args != nil {
+ if len(args.PartNumberMarker) > 0 {
+ req.SetParam("partNumberMarker", args.PartNumberMarker)
+ }
+ if args.MaxParts > 0 {
+ req.SetParam("maxParts", fmt.Sprintf("%d", args.MaxParts))
+ }
+ }
+
+ // Send request and get the result
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &ListPartsResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+// ListMultipartUploads - list the unfinished uploaded parts of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the destination bucket name
+// - args: the optional arguments
+//
+// RETURNS:
+// - *ListMultipartUploadsResult: the unfinished uploaded parts info result
+// - error: nil if ok otherwise the specific error
+func ListMultipartUploads(cli bce.Client, bucket string,
+ args *ListMultipartUploadsArgs, ctx *BosContext) (*ListMultipartUploadsResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.GET)
+ req.SetParam("uploads", "")
+ ctx.Bucket = bucket
+
+ // Optional arguments settings
+ if args != nil {
+ if len(args.Delimiter) > 0 {
+ req.SetParam("delimiter", args.Delimiter)
+ }
+ if len(args.KeyMarker) > 0 {
+ req.SetParam("keyMarker", args.KeyMarker)
+ }
+ if args.MaxUploads > 0 {
+ req.SetParam("maxUploads", fmt.Sprintf("%d", args.MaxUploads))
+ }
+ if len(args.Prefix) > 0 {
+ req.SetParam("prefix", args.Prefix)
+ }
+ }
+
+ // Send request and get the result
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &ListMultipartUploadsResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/services/bos/api/object.go b/vendor/github.com/baidubce/bce-sdk-go/services/bos/api/object.go
new file mode 100644
index 00000000000..b64196473c0
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/services/bos/api/object.go
@@ -0,0 +1,1164 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// object.go - the object APIs definition supported by the BOS service
+
+package api
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "strconv"
+ "strings"
+
+ "github.com/baidubce/bce-sdk-go/auth"
+ "github.com/baidubce/bce-sdk-go/bce"
+ "github.com/baidubce/bce-sdk-go/http"
+ "github.com/baidubce/bce-sdk-go/util"
+)
+
+// PutObject - put the object from the string or the stream
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name of the object
+// - object: the name of the object
+// - body: the input content of the object
+// - args: the optional arguments of this api
+//
+// RETURNS:
+// - string: the etag of the object
+// - error: nil if ok otherwise the specific error
+func PutObject(cli bce.Client, bucket, object string, body *bce.Body,
+ args *PutObjectArgs, ctx *BosContext) (string, *PutObjectResult, error) {
+ req := &bce.BceRequest{}
+ NeedReturnCallback := false
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.PUT)
+ ctx.Bucket = bucket
+ if body == nil {
+ return "", nil, bce.NewBceClientError("PutObject body should not be emtpy")
+ }
+ if body.Size() >= THRESHOLD_100_CONTINUE {
+ req.SetHeader("Expect", "100-continue")
+ }
+ req.SetBody(body)
+
+ // Optional arguments settings
+ if args != nil {
+ setOptionalNullHeaders(req, map[string]string{
+ http.CACHE_CONTROL: args.CacheControl,
+ http.CONTENT_DISPOSITION: args.ContentDisposition,
+ http.CONTENT_TYPE: args.ContentType,
+ http.EXPIRES: args.Expires,
+ http.BCE_CONTENT_SHA256: args.ContentSha256,
+ http.BCE_CONTENT_CRC32: args.ContentCrc32,
+ http.BCE_CONTENT_CRC32C: args.ContentCrc32c,
+ http.BCE_CONTENT_CRC32C_FLAG: strconv.FormatBool(args.ContentCrc32cFlag),
+ })
+ if args.ContentLength > 0 {
+ // User specified Content-Length can be smaller than the body size, so the body should
+ // be reset. The `net/http.Client' does not support the Content-Length bigger than the
+ // body size.
+ if args.ContentLength > body.Size() {
+ return "", nil, bce.NewBceClientError(fmt.Sprintf("ContentLength %d is bigger than body size %d", args.ContentLength, body.Size()))
+ }
+ body, err := bce.NewBodyFromSizedReader(body.Stream(), args.ContentLength)
+ if err != nil {
+ return "", nil, bce.NewBceClientError(err.Error())
+ }
+ req.SetHeader(http.CONTENT_LENGTH, fmt.Sprintf("%d", args.ContentLength))
+ req.SetBody(body) // re-assign body
+ }
+
+ //set traffic-limit
+ if args.TrafficLimit > 0 {
+ if args.TrafficLimit > TRAFFIC_LIMIT_MAX || args.TrafficLimit < TRAFFIC_LIMIT_MIN {
+ return "", nil, bce.NewBceClientError(fmt.Sprintf("TrafficLimit must between %d ~ %d, current value:%d", TRAFFIC_LIMIT_MIN, TRAFFIC_LIMIT_MAX, args.TrafficLimit))
+ }
+ req.SetHeader(http.BCE_TRAFFIC_LIMIT, fmt.Sprintf("%d", args.TrafficLimit))
+ }
+
+ // Reset the contentMD5 if set by user
+ if len(args.ContentMD5) != 0 {
+ req.SetHeader(http.CONTENT_MD5, args.ContentMD5)
+ }
+
+ if validStorageClass(args.StorageClass) {
+ req.SetHeader(http.BCE_STORAGE_CLASS, args.StorageClass)
+ } else {
+ if len(args.StorageClass) != 0 {
+ return "", nil, bce.NewBceClientError("invalid storage class value: " +
+ args.StorageClass)
+ }
+ }
+
+ if err := setUserMetadata(req, args.UserMeta); err != nil {
+ return "", nil, err
+ }
+
+ if len(args.Process) != 0 {
+ req.SetHeader(http.BCE_PROCESS, args.Process)
+ if strings.HasPrefix(args.Process, "callback") {
+ NeedReturnCallback = true
+ }
+ }
+ if len(args.CannedAcl) != 0 {
+ if validCannedAcl(args.CannedAcl) {
+ req.SetHeader(http.BCE_ACL, args.CannedAcl)
+ }
+ }
+ if len(args.ObjectTagging) != 0 {
+ if ok, encodeTagging := validObjectTagging(args.ObjectTagging); ok {
+ req.SetHeader(http.BCE_OBJECT_TAGGING, encodeTagging)
+ }
+ }
+ }
+ // add content-type if not assigned by user
+ if req.Header(http.CONTENT_TYPE) == "" {
+ req.SetHeader(http.CONTENT_TYPE, getDefaultContentType(object))
+ }
+
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return "", nil, err
+ }
+ if resp.IsFail() {
+ return "", nil, resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ headers := resp.Headers()
+ jsonBody := &PutObjectResult{}
+ if val, ok := headers[toHttpHeaderKey(http.BCE_CONTENT_CRC32)]; ok {
+ jsonBody.ContentCrc32 = val
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_CONTENT_CRC32C)]; ok {
+ jsonBody.ContentCrc32c = val
+ }
+ if NeedReturnCallback {
+ if err := resp.ParseJsonBody(jsonBody); err != nil {
+ return "", nil, err
+ }
+ return strings.Trim(resp.Header(http.ETAG), "\""), jsonBody, nil
+ }
+ return strings.Trim(resp.Header(http.ETAG), "\""), jsonBody, nil
+}
+
+// CopyObject - copy one object to a new object with new bucket and/or name. It can alse set the
+// metadata of the object with the same source and target.
+//
+// PARAMS:
+// - cli: the client object which can perform sending request
+// - bucket: the bucket name of the target object
+// - object: the name of the target object
+// - source: the source object uri
+// - *CopyObjectArgs: the optional input args for copying object
+//
+// RETURNS:
+// - *CopyObjectResult: the result object which contains etag and lastmodified
+// - error: nil if ok otherwise the specific error
+func CopyObject(cli bce.Client, bucket, object, source string,
+ args *CopyObjectArgs, ctx *BosContext) (*CopyObjectResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.PUT)
+ ctx.Bucket = bucket
+ if len(source) == 0 {
+ return nil, bce.NewBceClientError("copy source should not be null")
+ }
+ req.SetHeader(http.BCE_COPY_SOURCE, util.UriEncode(source, false))
+
+ // Optional arguments settings
+ if args != nil {
+ setOptionalNullHeaders(req, map[string]string{
+ http.CACHE_CONTROL: args.CacheControl,
+ http.CONTENT_DISPOSITION: args.ContentDisposition,
+ http.CONTENT_ENCODING: args.ContentEncoding,
+ http.CONTENT_RANGE: args.ContentRange,
+ http.CONTENT_TYPE: args.ContentType,
+ http.EXPIRES: args.Expires,
+ http.LAST_MODIFIED: args.LastModified,
+ http.ETAG: args.ETag,
+ http.CONTENT_MD5: args.ContentMD5,
+ http.BCE_CONTENT_SHA256: args.ContentSha256,
+ http.BCE_OBJECT_TYPE: args.ObjectType,
+ http.BCE_NEXT_APPEND_OFFSET: args.NextAppendOffset,
+ http.BCE_COPY_SOURCE_IF_MATCH: args.IfMatch,
+ http.BCE_COPY_SOURCE_IF_NONE_MATCH: args.IfNoneMatch,
+ http.BCE_COPY_SOURCE_IF_MODIFIED_SINCE: args.IfModifiedSince,
+ http.BCE_COPY_SOURCE_IF_UNMODIFIED_SINCE: args.IfUnmodifiedSince,
+ http.BCE_CONTENT_CRC32C: args.ContentCrc32c,
+ http.BCE_CONTENT_CRC32C_FLAG: strconv.FormatBool(args.ContentCrc32cFlag),
+ })
+ if validMetadataDirective(args.MetadataDirective) {
+ req.SetHeader(http.BCE_COPY_METADATA_DIRECTIVE, args.MetadataDirective)
+ } else {
+ if len(args.MetadataDirective) != 0 {
+ return nil, bce.NewBceClientError(
+ "invalid metadata directive value: " + args.MetadataDirective)
+ }
+ }
+ if validMetadataDirective(args.TaggingDirective) {
+ req.SetHeader(http.BCE_COPY_TAGGING_DIRECTIVE, args.TaggingDirective)
+ }
+ if len(args.ObjectTagging) != 0 {
+ if ok, encodeTagging := validObjectTagging(args.ObjectTagging); ok {
+ req.SetHeader(http.BCE_OBJECT_TAGGING, encodeTagging)
+ }
+ }
+ if validStorageClass(args.StorageClass) {
+ req.SetHeader(http.BCE_STORAGE_CLASS, args.StorageClass)
+ } else {
+ if len(args.StorageClass) != 0 {
+ return nil, bce.NewBceClientError("invalid storage class value: " +
+ args.StorageClass)
+ }
+ }
+
+ //set traffic-limit
+ if args.TrafficLimit > 0 {
+ if args.TrafficLimit > TRAFFIC_LIMIT_MAX || args.TrafficLimit < TRAFFIC_LIMIT_MIN {
+ return nil, bce.NewBceClientError(fmt.Sprintf("TrafficLimit must between %d ~ %d, current value:%d", TRAFFIC_LIMIT_MIN, TRAFFIC_LIMIT_MAX, args.TrafficLimit))
+ }
+ req.SetHeader(http.BCE_TRAFFIC_LIMIT, fmt.Sprintf("%d", args.TrafficLimit))
+ }
+
+ if validCannedAcl(args.CannedAcl) {
+ req.SetHeader(http.BCE_ACL, args.CannedAcl)
+ }
+
+ if err := setUserMetadata(req, args.UserMeta); err != nil {
+ return nil, err
+ }
+ }
+
+ // Send request and get the result
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ jsonBody := &CopyObjectResult{}
+ if err := resp.ParseJsonBody(jsonBody); err != nil {
+ return nil, err
+ }
+ if resp.Header(http.BCE_VERSION_ID) != "" {
+ jsonBody.VersionId = resp.Header(http.BCE_VERSION_ID)
+ }
+ return jsonBody, nil
+}
+
+// GetObject - get the object content with range and response-headers-specified support
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name of the object
+// - object: the name of the object
+// - args: the optional args in querysring
+// - ranges: the optional range start and end to get the given object
+//
+// RETURNS:
+// - *GetObjectResult: the output content result of the object
+// - error: nil if ok otherwise the specific error
+func GetObject(cli bce.Client, bucket, object string, ctx *BosContext, args map[string]string, // nolint:gocyclo
+ ranges ...int64) (*GetObjectResult, error) {
+
+ if object == "" {
+ err := fmt.Errorf("Get Object don't accept \"\" as a parameter")
+ return nil, err
+ }
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.GET)
+ ctx.Bucket = bucket
+ // Optional arguments settings
+ if args != nil {
+ for k, v := range args {
+ if _, ok := GET_OBJECT_ALLOWED_RESPONSE_HEADERS[k]; ok {
+ req.SetParam("response"+k, v)
+ }
+ if strings.HasPrefix(k, http.BCE_PREFIX) {
+ req.SetParam(k, v)
+ }
+ }
+ }
+ if len(ranges) != 0 {
+ rangeStr := "bytes="
+ if len(ranges) == 1 {
+ rangeStr += fmt.Sprintf("%d", ranges[0]) + "-"
+ } else {
+ rangeStr += fmt.Sprintf("%d", ranges[0]) + "-" + fmt.Sprintf("%d", ranges[1])
+ }
+ req.SetHeader("Range", rangeStr)
+ }
+
+ // Send request and get the result
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ headers := resp.Headers()
+ result := &GetObjectResult{}
+ if val, ok := headers[http.CACHE_CONTROL]; ok {
+ result.CacheControl = val
+ }
+ if val, ok := headers[http.CONTENT_DISPOSITION]; ok {
+ result.ContentDisposition = val
+ }
+ if val, ok := headers[http.CONTENT_LENGTH]; ok {
+ if length, err := strconv.ParseInt(val, 10, 64); err == nil {
+ result.ContentLength = length
+ }
+ }
+ if val, ok := headers[http.CONTENT_RANGE]; ok {
+ result.ContentRange = val
+ }
+ if val, ok := headers[http.CONTENT_TYPE]; ok {
+ result.ContentType = val
+ }
+ if val, ok := headers[http.CONTENT_MD5]; ok {
+ result.ContentMD5 = val
+ }
+ if val, ok := headers[http.EXPIRES]; ok {
+ result.Expires = val
+ }
+ if val, ok := headers[http.LAST_MODIFIED]; ok {
+ result.LastModified = val
+ }
+ if val, ok := headers[http.ETAG]; ok {
+ result.ETag = strings.Trim(val, "\"")
+ }
+ if val, ok := headers[http.CONTENT_LANGUAGE]; ok {
+ result.ContentLanguage = val
+ }
+ if val, ok := headers[http.CONTENT_ENCODING]; ok {
+ result.ContentEncoding = val
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_CONTENT_SHA256)]; ok {
+ result.ContentSha256 = val
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_CONTENT_CRC32)]; ok {
+ result.ContentCrc32 = val
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_STORAGE_CLASS)]; ok {
+ result.StorageClass = val
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_VERSION_ID)]; ok {
+ result.VersionId = val
+ }
+ bcePrefix := toHttpHeaderKey(http.BCE_USER_METADATA_PREFIX)
+ for k, v := range headers {
+ if strings.Index(k, bcePrefix) == 0 {
+ if result.UserMeta == nil {
+ result.UserMeta = make(map[string]string)
+ }
+ result.UserMeta[k[len(bcePrefix):]] = v
+ }
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_OBJECT_TYPE)]; ok {
+ result.ObjectType = val
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_NEXT_APPEND_OFFSET)]; ok {
+ result.NextAppendOffset = val
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_CONTENT_CRC32C)]; ok {
+ result.ContentCrc32c = val
+ }
+ result.Body = resp.Body()
+ return result, nil
+}
+
+// GetObjectMeta - get the meta data of the given object
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name of the object
+// - object: the name of the object
+//
+// RETURNS:
+// - *GetObjectMetaResult: the result of this api
+// - error: nil if ok otherwise the specific error
+func GetObjectMeta(cli bce.Client, bucket, object string, ctx *BosContext) (*GetObjectMetaResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.HEAD)
+ ctx.Bucket = bucket
+ // Send request and get the result
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ headers := resp.Headers()
+ result := &GetObjectMetaResult{}
+ if val, ok := headers[http.CACHE_CONTROL]; ok {
+ result.CacheControl = val
+ }
+ if val, ok := headers[http.CONTENT_DISPOSITION]; ok {
+ result.ContentDisposition = val
+ }
+ if val, ok := headers[http.CONTENT_LENGTH]; ok {
+ if length, err := strconv.ParseInt(val, 10, 64); err == nil {
+ result.ContentLength = length
+ }
+ }
+ if val, ok := headers[http.CONTENT_RANGE]; ok {
+ result.ContentRange = val
+ }
+ if val, ok := headers[http.CONTENT_TYPE]; ok {
+ result.ContentType = val
+ }
+ if val, ok := headers[http.CONTENT_MD5]; ok {
+ result.ContentMD5 = val
+ }
+ if val, ok := headers[http.EXPIRES]; ok {
+ result.Expires = val
+ }
+ if val, ok := headers[http.LAST_MODIFIED]; ok {
+ result.LastModified = val
+ }
+ if val, ok := headers[http.ETAG]; ok {
+ result.ETag = strings.Trim(val, "\"")
+ }
+ if val, ok := headers[http.CONTENT_ENCODING]; ok {
+ result.ContentEncoding = val
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_CONTENT_SHA256)]; ok {
+ result.ContentSha256 = val
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_CONTENT_CRC32)]; ok {
+ result.ContentCrc32 = val
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_STORAGE_CLASS)]; ok {
+ result.StorageClass = val
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_RESTORE)]; ok {
+ result.BceRestore = val
+ }
+ if val, ok := headers[http.BCE_OBJECT_TYPE]; ok {
+ result.BceObjectType = val
+ }
+ if val, ok := headers[http.BCE_VERSION_ID]; ok {
+ result.VersionId = val
+ }
+ bcePrefix := toHttpHeaderKey(http.BCE_USER_METADATA_PREFIX)
+ for k, v := range headers {
+ if strings.Index(k, bcePrefix) == 0 {
+ if result.UserMeta == nil {
+ result.UserMeta = make(map[string]string)
+ }
+ result.UserMeta[k[len(bcePrefix):]] = v
+ }
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_OBJECT_TYPE)]; ok {
+ result.ObjectType = val
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_NEXT_APPEND_OFFSET)]; ok {
+ result.NextAppendOffset = val
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_CONTENT_CRC32C)]; ok {
+ result.ContentCrc32c = val
+ }
+ defer func() { resp.Body().Close() }()
+ return result, nil
+}
+
+// SelectObject - select the object content
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name of the object
+// - object: the name of the object
+// - args: the optional arguments to perform the select operation
+//
+// RETURNS:
+// - *SelectObjectResult: the output select content result of the object
+// - error: nil if ok otherwise the specific error
+func SelectObject(cli bce.Client, bucket, object string, args *SelectObjectArgs, ctx *BosContext) (*SelectObjectResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.POST)
+ req.SetParam("select", "")
+ req.SetParam("type", args.SelectType)
+ ctx.Bucket = bucket
+ jsonBytes, jsonErr := json.Marshal(args)
+ if jsonErr != nil {
+ return nil, jsonErr
+ }
+ body, err := bce.NewBodyFromBytes(jsonBytes)
+ if err != nil {
+ return nil, err
+ }
+ req.SetBody(body)
+
+ // Send request and get the result
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+
+ result := &SelectObjectResult{}
+
+ result.Body = resp.Body()
+ return result, nil
+}
+
+// FetchObject - fetch the object by the given url and store it to a bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name to store the object
+// - object: the name of the object to be stored
+// - source: the source url to fetch
+// - args: the optional arguments to perform the fetch operation
+//
+// RETURNS:
+// - *FetchObjectArgs: the result of this api
+// - error: nil if ok otherwise the specific error
+func FetchObject(cli bce.Client, bucket, object, source string,
+ args *FetchObjectArgs, ctx *BosContext) (*FetchObjectResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.POST)
+ req.SetParam("fetch", "")
+ ctx.Bucket = bucket
+ if len(source) == 0 {
+ return nil, bce.NewBceClientError("invalid fetch source value: " + source)
+ }
+ req.SetHeader(http.BCE_PREFIX+"fetch-source", source)
+
+ // Optional arguments settings
+ if args != nil {
+ if validFetchMode(args.FetchMode) {
+ req.SetHeader(http.BCE_PREFIX+"fetch-mode", args.FetchMode)
+ } else {
+ if len(args.FetchMode) != 0 {
+ return nil, bce.NewBceClientError("invalid fetch mode value: " + args.FetchMode)
+ }
+ }
+ if validStorageClass(args.StorageClass) {
+ req.SetHeader(http.BCE_STORAGE_CLASS, args.StorageClass)
+ } else {
+ if len(args.StorageClass) != 0 {
+ return nil, bce.NewBceClientError("invalid storage class value: " +
+ args.StorageClass)
+ }
+ }
+ if len(args.FetchCallBackAddress) != 0 {
+ req.SetHeader(http.BCE_FETCH_CALLBACK_ADDRESS, args.FetchCallBackAddress)
+ }
+ }
+
+ // Send request and get the result
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ jsonBody := &FetchObjectResult{}
+ if err := resp.ParseJsonBody(jsonBody); err != nil {
+ return nil, err
+ }
+ return jsonBody, nil
+}
+
+// AppendObject - append the given content to a new or existed object which is appendable
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name of the object
+// - object: the name of the object
+// - content: the content to be appended
+// - args: the optional arguments to perform the append operation
+//
+// RETURNS:
+// - *AppendObjectResult: the result status for this api
+// - error: nil if ok otherwise the specific error
+func AppendObject(cli bce.Client, bucket, object string, content *bce.Body,
+ args *AppendObjectArgs, ctx *BosContext) (*AppendObjectResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.POST)
+ req.SetParam("append", "")
+ ctx.Bucket = bucket
+ if content == nil {
+ return nil, bce.NewBceClientError("AppendObject body should not be emtpy")
+ }
+ if content.Size() >= THRESHOLD_100_CONTINUE {
+ req.SetHeader("Expect", "100-continue")
+ }
+ req.SetBody(content)
+
+ // Optional arguments settings
+ if args != nil {
+ if args.Offset < 0 {
+ return nil, bce.NewBceClientError(
+ fmt.Sprintf("invalid append offset value: %d", args.Offset))
+ }
+ if args.Offset > 0 {
+ req.SetParam("offset", fmt.Sprintf("%d", args.Offset))
+ }
+ setOptionalNullHeaders(req, map[string]string{
+ http.CACHE_CONTROL: args.CacheControl,
+ http.CONTENT_DISPOSITION: args.ContentDisposition,
+ http.CONTENT_MD5: args.ContentMD5,
+ http.CONTENT_TYPE: args.ContentType,
+ http.EXPIRES: args.Expires,
+ http.BCE_CONTENT_SHA256: args.ContentSha256,
+ http.BCE_CONTENT_CRC32: args.ContentCrc32,
+ http.BCE_CONTENT_CRC32C: args.ContentCrc32c,
+ http.BCE_CONTENT_CRC32C_FLAG: strconv.FormatBool(args.ContentCrc32cFlag),
+ })
+
+ if validStorageClass(args.StorageClass) {
+ req.SetHeader(http.BCE_STORAGE_CLASS, args.StorageClass)
+ } else {
+ if len(args.StorageClass) != 0 {
+ return nil, bce.NewBceClientError("invalid storage class value: " +
+ args.StorageClass)
+ }
+ }
+ if err := setUserMetadata(req, args.UserMeta); err != nil {
+ return nil, err
+ }
+ //set traffic-limit
+ if args.TrafficLimit > 0 {
+ if args.TrafficLimit > TRAFFIC_LIMIT_MAX || args.TrafficLimit < TRAFFIC_LIMIT_MIN {
+ return nil, bce.NewBceClientError(fmt.Sprintf("TrafficLimit must between %d ~ %d, current value:%d", TRAFFIC_LIMIT_MIN, TRAFFIC_LIMIT_MAX, args.TrafficLimit))
+ }
+ req.SetHeader(http.BCE_TRAFFIC_LIMIT, fmt.Sprintf("%d", args.TrafficLimit))
+ }
+ }
+
+ // Send request and get the result
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ headers := resp.Headers()
+ result := &AppendObjectResult{}
+ if val, ok := headers[http.CONTENT_MD5]; ok {
+ result.ContentMD5 = val
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_NEXT_APPEND_OFFSET)]; ok {
+ nextOffset, offsetErr := strconv.ParseInt(val, 10, 64)
+ if offsetErr != nil {
+ nextOffset = content.Size()
+ }
+ result.NextAppendOffset = nextOffset
+ } else {
+ result.NextAppendOffset = content.Size()
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_CONTENT_CRC32)]; ok {
+ result.ContentCrc32 = val
+ }
+ if val, ok := headers[toHttpHeaderKey(http.BCE_CONTENT_CRC32C)]; ok {
+ result.ContentCrc32c = val
+ }
+ if val, ok := headers[http.ETAG]; ok {
+ result.ETag = strings.Trim(val, "\"")
+ }
+ defer func() { resp.Body().Close() }()
+ return result, nil
+}
+
+// DeleteObject - delete the given object
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name of the object to be deleted
+// - object: the name of the object
+//
+// RETURNS:
+// - error: nil if ok otherwise the specific error
+func DeleteObject(cli bce.Client, bucket, object, versionId string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.DELETE)
+ if versionId != "" {
+ req.SetParam("versionId", versionId)
+ }
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// DeleteMultipleObjects - delete the given objects within a single http request
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name of the objects to be deleted
+// - objectListStream: the objects list to be delete with json format
+//
+// RETURNS:
+// - *DeleteMultipleObjectsResult: the objects failed to delete
+// - error: nil if ok otherwise the specific error
+func DeleteMultipleObjects(cli bce.Client, bucket string,
+ objectListStream *bce.Body, ctx *BosContext) (*DeleteMultipleObjectsResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getBucketUri(bucket))
+ req.SetMethod(http.POST)
+ req.SetParam("delete", "")
+ req.SetHeader(http.CONTENT_TYPE, "application/json; charset=utf-8")
+ ctx.Bucket = bucket
+ if objectListStream == nil {
+ return nil, bce.NewBceClientError("DeleteMultipleObjects body should not be emtpy")
+ }
+ if objectListStream.Size() >= THRESHOLD_100_CONTINUE {
+ req.SetHeader("Expect", "100-continue")
+ }
+ req.SetBody(objectListStream)
+
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ jsonBody := &DeleteMultipleObjectsResult{}
+
+ if resp.Header(http.CONTENT_LENGTH) == "0" {
+ resp.Body().Close()
+ return jsonBody, nil
+ }
+ if err := resp.ParseJsonBody(jsonBody); err != nil {
+ return nil, err
+ }
+ return jsonBody, nil
+}
+
+// GeneratePresignedUrl - generate an authorization url with expire time and optional arguments
+//
+// PARAMS:
+// - conf: the client configuration
+// - signer: the client signer object to generate the authorization string
+// - bucket: the target bucket name
+// - object: the target object name
+// - expire: expire time in seconds
+// - method: optional sign method, default is GET
+// - headers: optional sign headers, default just set the Host
+// - params: optional sign params, default is empty
+// RETURNS:
+// - string: the presigned url with authorization string
+
+func GeneratePresignedUrl(conf *bce.BceClientConfiguration, signer auth.Signer, bucket,
+ object string, expire int, method string, headers, params map[string]string) string {
+ return GeneratePresignedUrlInternal(conf, signer, bucket, object, expire, method, headers, params, false)
+}
+func GeneratePresignedUrlPathStyle(conf *bce.BceClientConfiguration, signer auth.Signer, bucket,
+ object string, expire int, method string, headers, params map[string]string) string {
+ return GeneratePresignedUrlInternal(conf, signer, bucket, object, expire, method, headers, params, true)
+}
+
+func GeneratePresignedUrlInternal(conf *bce.BceClientConfiguration, signer auth.Signer, bucket,
+ object string, expire int, method string, headers, params map[string]string, path_style bool) string {
+ req := &bce.BceRequest{}
+ // Set basic arguments
+ if len(method) == 0 {
+ method = http.GET
+ }
+ req.SetMethod(method)
+ req.SetEndpoint(conf.Endpoint)
+ if req.Protocol() == "" {
+ req.SetProtocol(bce.DEFAULT_PROTOCOL)
+ }
+ domain := req.Host()
+ if pos := strings.Index(domain, ":"); pos != -1 {
+ domain = domain[:pos]
+ }
+ if path_style {
+ req.SetUri(getObjectUri(bucket, object))
+ if conf.CnameEnabled || isCnameLikeHost(conf.Endpoint) {
+ req.SetUri(getCnameUri(req.Uri()))
+ }
+ } else {
+ if len(bucket) != 0 && net.ParseIP(domain) == nil { // not use an IP as the endpoint by client
+ req.SetUri(bce.URI_PREFIX + object)
+ if !conf.CnameEnabled && !isCnameLikeHost(conf.Endpoint) {
+ req.SetHost(bucket + "." + req.Host())
+ }
+ } else {
+ req.SetUri(getObjectUri(bucket, object))
+ if conf.CnameEnabled || isCnameLikeHost(conf.Endpoint) {
+ req.SetUri(getCnameUri(req.Uri()))
+ }
+ }
+ }
+ // Set headers and params if given.
+ req.SetHeader(http.HOST, req.Host())
+ if headers != nil {
+ for k, v := range headers {
+ req.SetHeader(k, v)
+ }
+ }
+ if params != nil {
+ for k, v := range params {
+ req.SetParam(k, v)
+ }
+ }
+ // Copy one SignOptions object to rewrite it.
+ option := *conf.SignOption
+ if expire != 0 {
+ option.ExpireSeconds = expire
+ }
+
+ if conf.Credentials.SessionToken != "" {
+ req.SetParam(http.BCE_SECURITY_TOKEN, conf.Credentials.SessionToken)
+ }
+
+ // Generate the authorization string and return the signed url.
+ signer.Sign(&req.Request, conf.Credentials, &option)
+ req.SetParam("authorization", req.Header(http.AUTHORIZATION))
+ return fmt.Sprintf("%s://%s%s?%s", req.Protocol(), req.Host(),
+ util.UriEncode(req.Uri(), false), req.QueryString())
+}
+
+// PutObjectAcl - set the ACL of the given object
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - object: the object name
+// - cannedAcl: support private and public-read
+// - grantRead: user id list
+// - grantFullControl: user id list
+// - aclBody: the acl file body
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func PutObjectAcl(cli bce.Client, bucket, object, cannedAcl string,
+ grantRead, grantFullControl []string, aclBody *bce.Body, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.PUT)
+ req.SetParam("acl", "")
+ ctx.Bucket = bucket
+ // Joiner for generate the user id list string for grant acl header
+ joiner := func(ids []string) string {
+ for i := range ids {
+ ids[i] = "id=\"" + ids[i] + "\""
+ }
+ return strings.Join(ids, ",")
+ }
+
+ // Choose a acl setting method
+ methods := 0
+ if len(cannedAcl) != 0 {
+ methods += 1
+ if validCannedAcl(cannedAcl) {
+ req.SetHeader(http.BCE_ACL, cannedAcl)
+ }
+ }
+ if len(grantRead) != 0 {
+ methods += 1
+ req.SetHeader(http.BCE_GRANT_READ, joiner(grantRead))
+ }
+ if len(grantFullControl) != 0 {
+ methods += 1
+ req.SetHeader(http.BCE_GRANT_FULL_CONTROL, joiner(grantFullControl))
+ }
+ if aclBody != nil {
+ methods += 1
+ req.SetHeader(http.CONTENT_TYPE, bce.DEFAULT_CONTENT_TYPE)
+ req.SetBody(aclBody)
+ }
+ if methods != 1 {
+ return bce.NewBceClientError("BOS only support one acl setting method at the same time")
+ }
+
+ // Do sending request
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// GetObjectAcl - get the ACL of the given object
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - object: the object name
+//
+// RETURNS:
+// - result: the object acl result object
+// - error: nil if success otherwise the specific error
+func GetObjectAcl(cli bce.Client, bucket, object string, ctx *BosContext) (*GetObjectAclResult, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.GET)
+ req.SetParam("acl", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ result := &GetObjectAclResult{}
+ if err := resp.ParseJsonBody(result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+// DeleteObjectAcl - delete the ACL of the given object
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - object: the object name
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func DeleteObjectAcl(cli bce.Client, bucket, object string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.DELETE)
+ req.SetParam("acl", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// RestoreObject - restore the archive object
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - object: the object name
+// - args: the restore args
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func RestoreObject(cli bce.Client, bucket string, object string, args ArchiveRestoreArgs, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetParam("restore", "")
+ req.SetMethod(http.POST)
+ req.SetHeader(http.BCE_RESTORE_DAYS, strconv.Itoa(args.RestoreDays))
+ req.SetHeader(http.BCE_RESTORE_TIER, args.RestoreTier)
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+
+ return nil
+}
+
+// PutObjectSymlink - put the object from the string or the stream
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name of the object
+// - object: the name of the object
+// - symlinkKey: the name of the symlink
+// - symlinkArgs: the optional arguments of this api
+//
+// RETURNS:
+// - error: nil if ok otherwise the specific error
+func PutObjectSymlink(cli bce.Client, bucket string, object string, symlinkKey string, symlinkArgs *PutSymlinkArgs, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, symlinkKey))
+ req.SetParam("symlink", "")
+ req.SetMethod(http.PUT)
+ ctx.Bucket = bucket
+ if symlinkArgs != nil {
+ if len(symlinkArgs.ForbidOverwrite) != 0 {
+ if !validForbidOverwrite(symlinkArgs.ForbidOverwrite) {
+ return bce.NewBceClientError("invalid forbid overwrite val," + symlinkArgs.ForbidOverwrite)
+ }
+ req.SetHeader(http.BCE_FORBID_OVERWRITE, symlinkArgs.ForbidOverwrite)
+ }
+
+ if len(symlinkArgs.StorageClass) != 0 {
+ if !validStorageClass(symlinkArgs.StorageClass) {
+ return bce.NewBceClientError("invalid storage class val," + symlinkArgs.StorageClass)
+ }
+ if symlinkArgs.StorageClass == STORAGE_CLASS_ARCHIVE {
+ return bce.NewBceClientError("archive storage class not support")
+ }
+ req.SetHeader(http.BCE_STORAGE_CLASS, symlinkArgs.StorageClass)
+ }
+
+ if len(symlinkArgs.UserMeta) != 0 {
+ if err := setUserMetadata(req, symlinkArgs.UserMeta); err != nil {
+ return err
+ }
+ }
+ if len(symlinkArgs.SymlinkBucket) != 0 {
+ req.SetHeader(http.BCE_SYMLINK_BUCKET, symlinkArgs.SymlinkBucket)
+ }
+ }
+ req.SetHeader(http.BCE_SYMLINK_TARGET, object)
+
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+// PutObjectSymlink - put the object from the string or the stream
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name of the object
+// - symlinkKey: the name of the symlink
+//
+// RETURNS:
+// - string: the name of the target object
+// - error: nil if ok otherwise the specific error
+func GetObjectSymlink(cli bce.Client, bucket string, symlinkKey string, ctx *BosContext) (string, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, symlinkKey))
+ req.SetParam("symlink", "")
+ req.SetMethod(http.GET)
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return "", err
+ }
+ if resp.IsFail() {
+ return "", resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ if resp.Header(http.BCE_SYMLINK_BUCKET) != "" {
+ result := BOS_CONFIG_PREFIX + resp.Header(http.BCE_SYMLINK_BUCKET) + "/" + resp.Header(http.BCE_SYMLINK_TARGET)
+ return result, nil
+ }
+ return resp.Header(http.BCE_SYMLINK_TARGET), nil
+}
+
+// PutObjectTag - set tag for given object
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name of the object
+// - object: the name of the object
+// - putObjectTagArgs: the arguments of object tag
+// RETURNS:
+// - error: nil if ok otherwise the specific error
+
+func PutObjectTag(cli bce.Client, bucket, object string, putObjectTagArgs *PutObjectTagArgs, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.PUT)
+ req.SetParam("tagging", "")
+ ctx.Bucket = bucket
+ reqByte, _ := json.Marshal(putObjectTagArgs)
+ body, err := bce.NewBodyFromString(string(reqByte))
+ if err != nil {
+ return err
+ }
+ req.SetBody(body)
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
+
+func GetObjectTag(cli bce.Client, bucket, object string, ctx *BosContext) (map[string]interface{}, error) {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.GET)
+ req.SetParam("tagging", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ bodyBytes, err := ioutil.ReadAll(resp.Body())
+ if err != nil {
+ return nil, err
+ }
+
+ result, err := ParseObjectTagResult(bodyBytes)
+ if err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+func DeleteObjectTag(cli bce.Client, bucket, object string, ctx *BosContext) error {
+ req := &bce.BceRequest{}
+ req.SetUri(getObjectUri(bucket, object))
+ req.SetMethod(http.DELETE)
+ req.SetParam("tagging", "")
+ ctx.Bucket = bucket
+ resp := &bce.BceResponse{}
+ if err := SendRequest(cli, req, resp, ctx); err != nil {
+ return err
+ }
+ if resp.IsFail() {
+ return resp.ServiceError()
+ }
+ defer func() { resp.Body().Close() }()
+ return nil
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/services/bos/api/util.go b/vendor/github.com/baidubce/bce-sdk-go/services/bos/api/util.go
new file mode 100644
index 00000000000..bd426e60ac1
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/services/bos/api/util.go
@@ -0,0 +1,481 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// util.go - define the utilities for api package of BOS service
+
+package api
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "net"
+ net_http "net/http"
+ "net/url"
+ "strings"
+
+ "github.com/baidubce/bce-sdk-go/bce"
+ "github.com/baidubce/bce-sdk-go/http"
+ "github.com/baidubce/bce-sdk-go/util"
+)
+
+const (
+ METADATA_DIRECTIVE_COPY = "copy"
+ METADATA_DIRECTIVE_REPLACE = "replace"
+
+ STORAGE_CLASS_STANDARD = "STANDARD"
+ STORAGE_CLASS_STANDARD_IA = "STANDARD_IA"
+ STORAGE_CLASS_COLD = "COLD"
+ STORAGE_CLASS_ARCHIVE = "ARCHIVE"
+ STORAGE_CLASS_MAZ_STANDARD = "MAZ_STANDARD"
+ STORAGE_CLASS_MAZ_STANDARD_IA = "MAZ_STANDARD_IA"
+
+ FETCH_MODE_SYNC = "sync"
+ FETCH_MODE_ASYNC = "async"
+
+ CANNED_ACL_PRIVATE = "private"
+ CANNED_ACL_PUBLIC_READ = "public-read"
+ CANNED_ACL_PUBLIC_READ_WRITE = "public-read-write"
+
+ RAW_CONTENT_TYPE = "application/octet-stream"
+
+ THRESHOLD_100_CONTINUE = 1 << 20 // add 100 continue header if body size bigger than 1MB
+
+ TRAFFIC_LIMIT_MAX = 8 * (100 << 20) // 100M bit = 838860800
+ TRAFFIC_LIMIT_MIN = 8 * (100 << 10) // 100K bit = 819200
+
+ STATUS_ENABLED = "enabled"
+ STATUS_DISABLED = "disabled"
+
+ ENCRYPTION_AES256 = "AES256"
+
+ RESTORE_TIER_STANDARD = "Standard" //标准取回对象
+ RESTORE_TIER_EXPEDITED = "Expedited" //快速取回对象
+
+ FORBID_OVERWRITE_FALSE = "false"
+ FORBID_OVERWRITE_TRUE = "true"
+
+ NAMESPACE_BUCKET = "namespace"
+ BOS_CONFIG_PREFIX = "bos://"
+ BOS_SHARE_ENDPOINT = "bos-share.baidubce.com"
+)
+
+var DEFAULT_CNAME_LIKE_LIST = []string{
+ ".cdn.bcebos.com",
+}
+
+var VALID_STORAGE_CLASS_TYPE = map[string]int{
+ STORAGE_CLASS_STANDARD: 0,
+ STORAGE_CLASS_STANDARD_IA: 1,
+ STORAGE_CLASS_COLD: 2,
+ STORAGE_CLASS_ARCHIVE: 3,
+ STORAGE_CLASS_MAZ_STANDARD: 4,
+ STORAGE_CLASS_MAZ_STANDARD_IA: 5,
+}
+
+var VALID_RESTORE_TIER = map[string]int{
+ RESTORE_TIER_STANDARD: 1,
+ RESTORE_TIER_EXPEDITED: 1,
+}
+
+var VALID_FORBID_OVERWRITE = map[string]int{
+ FORBID_OVERWRITE_FALSE: 1,
+ FORBID_OVERWRITE_TRUE: 1,
+}
+
+var (
+ GET_OBJECT_ALLOWED_RESPONSE_HEADERS = map[string]struct{}{
+ "ContentDisposition": {},
+ "ContentType": {},
+ "ContentLanguage": {},
+ "Expires": {},
+ "CacheControl": {},
+ "ContentEncoding": {},
+ }
+)
+
+func getBucketUri(bucketName string) string {
+ return bce.URI_PREFIX + bucketName
+}
+
+func getObjectUri(bucketName, objectName string) string {
+ return bce.URI_PREFIX + bucketName + "/" + objectName
+}
+
+func getCnameUri(uri string) string {
+ if len(uri) <= 0 {
+ return uri
+ }
+ slash_index := strings.Index(uri[1:], "/")
+ if slash_index == -1 {
+ return bce.URI_PREFIX
+ } else {
+ return uri[slash_index+1:]
+ }
+}
+
+func validMetadataDirective(val string) bool {
+ if val == METADATA_DIRECTIVE_COPY || val == METADATA_DIRECTIVE_REPLACE {
+ return true
+ }
+ return false
+}
+
+func validForbidOverwrite(val string) bool {
+ if _, ok := VALID_FORBID_OVERWRITE[val]; ok {
+ return true
+ }
+ return false
+}
+
+func validStorageClass(val string) bool {
+ if _, ok := VALID_STORAGE_CLASS_TYPE[val]; ok {
+ return true
+ }
+ return false
+}
+
+func validFetchMode(val string) bool {
+ if val == FETCH_MODE_SYNC || val == FETCH_MODE_ASYNC {
+ return true
+ }
+ return false
+}
+
+func validCannedAcl(val string) bool {
+ if val == CANNED_ACL_PRIVATE ||
+ val == CANNED_ACL_PUBLIC_READ ||
+ val == CANNED_ACL_PUBLIC_READ_WRITE {
+ return true
+ }
+ return false
+}
+
+func validObjectTagging(tagging string) (bool, string) {
+ if len(tagging) > 4000 {
+ return false, ""
+ }
+ encodeTagging := []string{}
+ pair := strings.Split(tagging, "&")
+ for _, p := range pair {
+ kv := strings.Split(p, "=")
+ if len(kv) != 2 {
+ return false, ""
+ }
+ key := kv[0]
+ value := kv[1]
+ encodeKey := url.QueryEscape(key)
+ encodeValue := url.QueryEscape(value)
+ if len(encodeKey) > 128 || len(encodeValue) > 256 {
+ return false, ""
+ }
+ encodeTagging = append(encodeTagging, encodeKey+"="+encodeValue)
+ }
+ return true, strings.Join(encodeTagging, "&")
+}
+
+func toHttpHeaderKey(key string) string {
+ var result bytes.Buffer
+ needToUpper := true
+ for _, c := range []byte(key) {
+ if needToUpper && (c >= 'a' && c <= 'z') {
+ result.WriteByte(c - 32)
+ needToUpper = false
+ } else if c == '-' {
+ result.WriteByte(c)
+ needToUpper = true
+ } else {
+ result.WriteByte(c)
+ }
+ }
+ return result.String()
+}
+
+func setOptionalNullHeaders(req *bce.BceRequest, args map[string]string) {
+ for k, v := range args {
+ if len(v) == 0 {
+ continue
+ }
+ switch k {
+ case http.CACHE_CONTROL:
+ fallthrough
+ case http.CONTENT_DISPOSITION:
+ fallthrough
+ case http.CONTENT_ENCODING:
+ fallthrough
+ case http.CONTENT_RANGE:
+ fallthrough
+ case http.CONTENT_MD5:
+ fallthrough
+ case http.CONTENT_TYPE:
+ fallthrough
+ case http.EXPIRES:
+ fallthrough
+ case http.LAST_MODIFIED:
+ fallthrough
+ case http.ETAG:
+ fallthrough
+ case http.BCE_OBJECT_TYPE:
+ fallthrough
+ case http.BCE_NEXT_APPEND_OFFSET:
+ fallthrough
+ case http.BCE_CONTENT_SHA256:
+ fallthrough
+ case http.BCE_CONTENT_CRC32:
+ fallthrough
+ case http.BCE_CONTENT_CRC32C:
+ fallthrough
+ case http.BCE_CONTENT_CRC32C_FLAG:
+ if v == "false" {
+ continue
+ }
+ fallthrough
+ case http.BCE_COPY_SOURCE_RANGE:
+ fallthrough
+ case http.BCE_COPY_SOURCE_IF_MATCH:
+ fallthrough
+ case http.BCE_COPY_SOURCE_IF_NONE_MATCH:
+ fallthrough
+ case http.BCE_COPY_SOURCE_IF_MODIFIED_SINCE:
+ fallthrough
+ case http.BCE_COPY_SOURCE_IF_UNMODIFIED_SINCE:
+ req.SetHeader(k, v)
+ }
+ }
+}
+
+func setUserMetadata(req *bce.BceRequest, meta map[string]string) error {
+ if meta == nil {
+ return nil
+ }
+ for k, v := range meta {
+ if len(k) == 0 {
+ continue
+ }
+ if len(k)+len(v) > 32*1024 {
+ return bce.NewBceClientError("MetadataTooLarge")
+ }
+ req.SetHeader(http.BCE_USER_METADATA_PREFIX+k, v)
+ }
+ return nil
+}
+
+func isCnameLikeHost(host string) bool {
+ for _, suffix := range DEFAULT_CNAME_LIKE_LIST {
+ if strings.HasSuffix(strings.ToLower(host), suffix) {
+ return true
+ }
+ }
+ if isVirtualHost(host) {
+ return true
+ }
+ return false
+}
+
+func SendRequest(cli bce.Client, req *bce.BceRequest, resp *bce.BceResponse, ctx *BosContext) error {
+ var (
+ err error
+ need_retry bool
+ )
+ setUriAndEndpoint(cli, req, ctx, cli.GetBceClientConfig().Endpoint)
+ req.SetContext(ctx.Ctx)
+ if err = cli.SendRequest(req, resp); err != nil {
+ if serviceErr, isServiceErr := err.(*bce.BceServiceError); isServiceErr {
+ if serviceErr.StatusCode == net_http.StatusInternalServerError ||
+ serviceErr.StatusCode == net_http.StatusBadGateway ||
+ serviceErr.StatusCode == net_http.StatusServiceUnavailable ||
+ (serviceErr.StatusCode == net_http.StatusBadRequest && serviceErr.Code == "Http400") {
+ need_retry = true
+ }
+ }
+ if _, isClientErr := err.(*bce.BceClientError); isClientErr {
+ need_retry = true
+ }
+ // retry backup endpoint
+ if need_retry && cli.GetBceClientConfig().BackupEndpoint != "" {
+ setUriAndEndpoint(cli, req, ctx, cli.GetBceClientConfig().BackupEndpoint)
+ if err = cli.SendRequest(req, resp); err != nil {
+ return err
+ }
+ }
+ }
+ return err
+}
+
+func SendRequestFromBytes(cli bce.Client, req *bce.BceRequest, resp *bce.BceResponse, ctx *BosContext, content []byte) error {
+ var (
+ err error
+ need_retry bool
+ )
+ setUriAndEndpoint(cli, req, ctx, cli.GetBceClientConfig().Endpoint)
+ if err = cli.SendRequestFromBytes(req, resp, content); err != nil {
+ if serviceErr, isServiceErr := err.(*bce.BceServiceError); isServiceErr {
+ if serviceErr.StatusCode == net_http.StatusInternalServerError ||
+ serviceErr.StatusCode == net_http.StatusBadGateway ||
+ serviceErr.StatusCode == net_http.StatusServiceUnavailable ||
+ (serviceErr.StatusCode == net_http.StatusBadRequest && serviceErr.Code == "Http400") {
+ need_retry = true
+ }
+ }
+ if _, isClientErr := err.(*bce.BceClientError); isClientErr {
+ need_retry = true
+ }
+ // retry backup endpoint
+ if need_retry && cli.GetBceClientConfig().BackupEndpoint != "" {
+ setUriAndEndpoint(cli, req, ctx, cli.GetBceClientConfig().BackupEndpoint)
+ if err = cli.SendRequestFromBytes(req, resp, content); err != nil {
+ return err
+ }
+ }
+ }
+ return err
+}
+
+func isVirtualHost(host string) bool {
+ domain := getDomainWithoutPort(host)
+ arr := strings.Split(domain, ".")
+ if len(arr) != 4 {
+ return false
+ }
+ // bucket max length is 64
+ if len(arr[0]) == 0 || len(arr[0]) > 64 {
+ return false
+ }
+ if arr[2] != "bcebos" || arr[3] != "com" {
+ return false
+ }
+ return true
+}
+
+func isIpHost(host string) bool {
+ domain := getDomainWithoutPort(host)
+ validIp := net.ParseIP(domain)
+ return validIp != nil
+}
+
+func isBosHost(host string) bool {
+ domain := getDomainWithoutPort(host)
+ arr := strings.Split(domain, ".")
+ if len(arr) != 3 {
+ return false
+ }
+ if arr[1] != "bcebos" || arr[2] != "com" {
+ return false
+ }
+ return true
+}
+
+func getDomainWithoutPort(host string) string {
+ end := 0
+ if end = strings.Index(host, ":"); end == -1 {
+ end = len(host)
+ }
+ return host[:end]
+}
+
+func needCompatibleBucketAndEndpoint(bucket, endpoint string) bool {
+ if bucket == "" {
+ return false
+ }
+ if !isVirtualHost(endpoint) {
+ return false
+ }
+ if strings.Split(endpoint, ".")[0] == bucket {
+ return false
+ }
+ // bucket from sdk and from endpoint is different
+ return true
+}
+
+// replace endpoint by bucket, only effective when two bucket are in same region, otherwise server return NoSuchBucket error
+func replaceEndpointByBucket(bucket, endpoint string) string {
+ arr := strings.Split(endpoint, ".")
+ arr[0] = bucket
+ return strings.Join(arr, ".")
+}
+
+func setUriAndEndpoint(cli bce.Client, req *bce.BceRequest, ctx *BosContext, endpoint string) {
+ origin_uri := req.Uri()
+ bucket := ctx.Bucket
+ protocol := bce.DEFAULT_PROTOCOL
+ // deal with protocal
+ if strings.HasPrefix(endpoint, "https://") {
+ protocol = bce.HTTPS_PROTOCAL
+ endpoint = strings.TrimPrefix(endpoint, "https://")
+ } else if strings.HasPrefix(endpoint, "http://") {
+ endpoint = strings.TrimPrefix(endpoint, "http://")
+ }
+ // set uri, endpoint for cname, cdn, virtual host
+ if cli.GetBceClientConfig().CnameEnabled || isCnameLikeHost(endpoint) {
+ req.SetEndpoint(endpoint)
+ // if virtual host endpoint and bucket is not empty, compatible bucket and endpoint
+ if needCompatibleBucketAndEndpoint(bucket, endpoint) {
+ req.SetEndpoint(replaceEndpointByBucket(bucket, endpoint))
+ }
+ req.SetUri(getCnameUri(origin_uri))
+ } else if isIpHost(endpoint) {
+ // set endpoint for ip host
+ req.SetEndpoint(endpoint)
+ } else if isBosHost(endpoint) {
+ // endpoint is xx.bcebos.com, set endpoint depends on PathStyleEnable
+ if bucket != "" && !ctx.PathStyleEnable {
+ req.SetEndpoint(bucket + "." + endpoint)
+ req.SetUri(getCnameUri(origin_uri))
+ } else {
+ req.SetEndpoint(endpoint)
+ }
+ } else {
+ // user define custom endpoint
+ req.SetEndpoint(endpoint)
+ }
+ req.SetProtocol(protocol)
+}
+
+func getDefaultContentType(object string) string {
+ dot := strings.LastIndex(object, ".")
+ if dot == -1 {
+ return "application/octet-stream"
+ }
+ ext := object[dot:]
+ mimeMap := util.GetMimeMap()
+ if contentType, ok := mimeMap[ext]; ok {
+ return contentType
+ }
+ return "application/octet-stream"
+
+}
+
+func ParseObjectTagResult(rawData []byte) (map[string]interface{}, error) {
+ var data map[string]interface{}
+ err := json.Unmarshal(rawData, &data)
+ if err != nil {
+ return nil, err
+ }
+
+ tagSet, ok := data["tagSet"].([]interface{})
+ if !ok || len(tagSet) == 0 {
+ return nil, fmt.Errorf("decode tagSet error")
+ }
+
+ tagInfoMap, ok := tagSet[0].(map[string]interface{})
+ if !ok {
+ return nil, fmt.Errorf("decode tagInfo error")
+ }
+
+ tags, ok := tagInfoMap["tagInfo"].(map[string]interface{})
+ if !ok {
+ return nil, fmt.Errorf("decode tags error")
+ }
+ return tags, nil
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/services/bos/client.go b/vendor/github.com/baidubce/bce-sdk-go/services/bos/client.go
new file mode 100644
index 00000000000..02ed8805109
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/services/bos/client.go
@@ -0,0 +1,2637 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// client.go - define the client for BOS service
+
+// Package bos defines the BOS services of BCE. The supported APIs are all defined in sub-package
+// model with three types: 16 bucket APIs, 9 object APIs and 7 multipart APIs.
+package bos
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "math"
+ "net/http"
+ "os"
+
+ "github.com/baidubce/bce-sdk-go/auth"
+ "github.com/baidubce/bce-sdk-go/bce"
+ sdk_http "github.com/baidubce/bce-sdk-go/http"
+ "github.com/baidubce/bce-sdk-go/services/bos/api"
+ "github.com/baidubce/bce-sdk-go/services/sts"
+ "github.com/baidubce/bce-sdk-go/util/log"
+)
+
+const (
+ DEFAULT_SERVICE_DOMAIN = bce.DEFAULT_REGION + ".bcebos.com"
+ DEFAULT_MAX_PARALLEL = 10
+ MULTIPART_ALIGN = 1 << 20 // 1MB
+ MIN_MULTIPART_SIZE = 100 * (1 << 10) // 100 KB
+ DEFAULT_MULTIPART_SIZE = 12 * (1 << 20) // 12MB
+
+ MAX_PART_NUMBER = 10000
+ MAX_SINGLE_PART_SIZE = 5 * (1 << 30) // 5GB
+ MAX_SINGLE_OBJECT_SIZE = 48.8 * (1 << 40) // 48.8TB
+)
+
+// Client of BOS service is a kind of BceClient, so derived from BceClient
+type Client struct {
+ *bce.BceClient
+
+ // Fileds that used in parallel operation for BOS service
+ MaxParallel int64
+ MultipartSize int64
+ BosContext *api.BosContext
+}
+
+// BosClientConfiguration defines the config components structure by user.
+type BosClientConfiguration struct {
+ Ak string
+ Sk string
+ Endpoint string
+ RedirectDisabled bool
+ PathStyleEnable bool
+}
+
+// NewClient make the BOS service client with default configuration.
+// Use `cli.Config.xxx` to access the config or change it to non-default value.
+func NewClient(ak, sk, endpoint string) (*Client, error) {
+ return NewClientWithConfig(&BosClientConfiguration{
+ Ak: ak,
+ Sk: sk,
+ Endpoint: endpoint,
+ RedirectDisabled: false,
+ PathStyleEnable: false,
+ })
+}
+
+// NewStsClient make the BOS service client with STS configuration, it will first apply stsAK,stsSK, sessionToken, then return bosClient using temporary sts Credential
+func NewStsClient(ak, sk, endpoint string, expiration int) (*Client, error) {
+ stsClient, err := sts.NewClient(ak, sk)
+ if err != nil {
+ fmt.Println("create sts client object :", err)
+ return nil, err
+ }
+ sts, err := stsClient.GetSessionToken(expiration, "")
+ if err != nil {
+ fmt.Println("get session token failed:", err)
+ return nil, err
+ }
+
+ bosClient, err := NewClient(sts.AccessKeyId, sts.SecretAccessKey, endpoint)
+ if err != nil {
+ fmt.Println("create bos client failed:", err)
+ return nil, err
+ }
+ stsCredential, err := auth.NewSessionBceCredentials(
+ sts.AccessKeyId,
+ sts.SecretAccessKey,
+ sts.SessionToken)
+ if err != nil {
+ fmt.Println("create sts credential object failed:", err)
+ return nil, err
+ }
+ bosClient.Config.Credentials = stsCredential
+ return bosClient, nil
+}
+
+func NewClientWithConfig(config *BosClientConfiguration) (*Client, error) {
+ var credentials *auth.BceCredentials
+ var err error
+ ak, sk, endpoint := config.Ak, config.Sk, config.Endpoint
+ if len(ak) == 0 && len(sk) == 0 { // to support public-read-write request
+ credentials, err = nil, nil
+ } else {
+ credentials, err = auth.NewBceCredentials(ak, sk)
+ if err != nil {
+ return nil, err
+ }
+ }
+ if len(endpoint) == 0 {
+ endpoint = DEFAULT_SERVICE_DOMAIN
+ }
+ defaultSignOptions := &auth.SignOptions{
+ HeadersToSign: auth.DEFAULT_HEADERS_TO_SIGN,
+ ExpireSeconds: auth.DEFAULT_EXPIRE_SECONDS}
+ defaultConf := &bce.BceClientConfiguration{
+ Endpoint: endpoint,
+ Region: bce.DEFAULT_REGION,
+ UserAgent: bce.DEFAULT_USER_AGENT,
+ Credentials: credentials,
+ SignOption: defaultSignOptions,
+ Retry: bce.DEFAULT_RETRY_POLICY,
+ ConnectionTimeoutInMillis: bce.DEFAULT_CONNECTION_TIMEOUT_IN_MILLIS,
+ RedirectDisabled: config.RedirectDisabled}
+ v1Signer := &auth.BceV1Signer{}
+ defaultContext := &api.BosContext{
+ PathStyleEnable: config.PathStyleEnable,
+ }
+ client := &Client{bce.NewBceClient(defaultConf, v1Signer),
+ DEFAULT_MAX_PARALLEL, DEFAULT_MULTIPART_SIZE, defaultContext}
+ return client, nil
+}
+
+// ListBuckets - list all buckets
+//
+// RETURNS:
+// - *api.ListBucketsResult: the all buckets
+// - error: the return error if any occurs
+func (c *Client) ListBuckets() (*api.ListBucketsResult, error) {
+ return api.ListBuckets(c, c.BosContext)
+}
+
+// ListBucketsWithContext - support to cancel request by context.Context
+func (c *Client) ListBucketsWithContext(ctx context.Context) (*api.ListBucketsResult, error) {
+ c.BosContext.Ctx = ctx
+ return api.ListBuckets(c, c.BosContext)
+}
+
+// ListObjects - list all objects of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+// - args: the optional arguments to list objects
+//
+// RETURNS:
+// - *api.ListObjectsResult: the all objects of the bucket
+// - error: the return error if any occurs
+func (c *Client) ListObjects(bucket string,
+ args *api.ListObjectsArgs) (*api.ListObjectsResult, error) {
+ return api.ListObjects(c, bucket, args, c.BosContext)
+}
+
+func (c *Client) ListObjectVersions(bucket string, args *api.ListObjectsArgs) (*api.ListObjectsResult, error) {
+ return api.ListObjectsVersions(c, bucket, args, c.BosContext)
+}
+
+// ListObjectsWithContext - support to cancel request by context.Context
+func (c *Client) ListObjectsWithContext(ctx context.Context, bucket string,
+ args *api.ListObjectsArgs) (*api.ListObjectsResult, error) {
+ c.BosContext.Ctx = ctx
+ return api.ListObjects(c, bucket, args, c.BosContext)
+}
+
+// SimpleListObjects - list all objects of the given bucket with simple arguments
+//
+// PARAMS:
+// - bucket: the bucket name
+// - prefix: the prefix for listing
+// - maxKeys: the max number of result objects
+// - marker: the marker to mark the beginning for the listing
+// - delimiter: the delimiter for list objects
+//
+// RETURNS:
+// - *api.ListObjectsResult: the all objects of the bucket
+// - error: the return error if any occurs
+func (c *Client) SimpleListObjects(bucket, prefix string, maxKeys int, marker,
+ delimiter string) (*api.ListObjectsResult, error) {
+ args := &api.ListObjectsArgs{
+ Delimiter: prefix,
+ Marker: marker,
+ MaxKeys: maxKeys,
+ Prefix: prefix,
+ VersionIdMarker: "",
+ }
+ return api.ListObjects(c, bucket, args, c.BosContext)
+}
+
+// HeadBucket - test the given bucket existed and access authority
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - error: nil if exists and have authority otherwise the specific error
+func (c *Client) HeadBucket(bucket string) error {
+ err, _ := api.HeadBucket(c, bucket, c.BosContext)
+ return err
+}
+
+// HeadBucket - support to cancel request by context.Context
+func (c *Client) HeadBucketWithContext(ctx context.Context, bucket string) error {
+ c.BosContext.Ctx = ctx
+ err, _ := api.HeadBucket(c, bucket, c.BosContext)
+ return err
+}
+
+// DoesBucketExist - test the given bucket existed or not
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - bool: true if exists and false if not exists or occurs error
+// - error: nil if exists or not exist, otherwise the specific error
+func (c *Client) DoesBucketExist(bucket string) (bool, error) {
+ err, _ := api.HeadBucket(c, bucket, c.BosContext)
+ if err == nil {
+ return true, nil
+ }
+ if realErr, ok := err.(*bce.BceServiceError); ok {
+ if realErr.StatusCode == http.StatusForbidden {
+ return true, nil
+ }
+ if realErr.StatusCode == http.StatusNotFound {
+ return false, nil
+ }
+ }
+ return false, err
+}
+
+// IsNsBucket - test the given bucket is namespace bucket or not
+func (c *Client) IsNsBucket(bucket string) bool {
+ err, resp := api.HeadBucket(c, bucket, c.BosContext)
+ if err == nil && resp.Header(sdk_http.BCE_BUCKET_TYPE) == api.NAMESPACE_BUCKET {
+ return true
+ }
+ if realErr, ok := err.(*bce.BceServiceError); ok {
+ if realErr.StatusCode == http.StatusForbidden &&
+ resp.Header(sdk_http.BCE_BUCKET_TYPE) == api.NAMESPACE_BUCKET {
+ return true
+ }
+ }
+ return false
+}
+
+// PutBucket - create a new bucket
+//
+// PARAMS:
+// - bucket: the new bucket name
+//
+// RETURNS:
+// - string: the location of the new bucket if create success
+// - error: nil if create success otherwise the specific error
+func (c *Client) PutBucket(bucket string) (string, error) {
+ return api.PutBucket(c, bucket, nil, c.BosContext)
+}
+
+func (c *Client) PutBucketWithArgs(bucket string, args *api.PutBucketArgs) (string, error) {
+ return api.PutBucket(c, bucket, args, c.BosContext)
+}
+
+// DeleteBucket - delete a empty bucket
+//
+// PARAMS:
+// - bucket: the bucket name to be deleted
+//
+// RETURNS:
+// - error: nil if delete success otherwise the specific error
+func (c *Client) DeleteBucket(bucket string) error {
+ return api.DeleteBucket(c, bucket, c.BosContext)
+}
+
+// GetBucketLocation - get the location fo the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - string: the location of the bucket
+// - error: nil if success otherwise the specific error
+func (c *Client) GetBucketLocation(bucket string) (string, error) {
+ return api.GetBucketLocation(c, bucket, c.BosContext)
+}
+
+// PutBucketAcl - set the acl of the given bucket with acl body stream
+//
+// PARAMS:
+// - bucket: the bucket name
+// - aclBody: the acl json body stream
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketAcl(bucket string, aclBody *bce.Body) error {
+ return api.PutBucketAcl(c, bucket, "", aclBody, c.BosContext)
+}
+
+// PutBucketAclFromCanned - set the canned acl of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+// - cannedAcl: the cannedAcl string
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketAclFromCanned(bucket, cannedAcl string) error {
+ return api.PutBucketAcl(c, bucket, cannedAcl, nil, c.BosContext)
+}
+
+// PutBucketAclFromFile - set the acl of the given bucket with acl json file name
+//
+// PARAMS:
+// - bucket: the bucket name
+// - aclFile: the acl file name
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketAclFromFile(bucket, aclFile string) error {
+ body, err := bce.NewBodyFromFile(aclFile)
+ if err != nil {
+ return err
+ }
+ return api.PutBucketAcl(c, bucket, "", body, c.BosContext)
+}
+
+// PutBucketAclFromString - set the acl of the given bucket with acl json string
+//
+// PARAMS:
+// - bucket: the bucket name
+// - aclString: the acl string with json format
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketAclFromString(bucket, aclString string) error {
+ body, err := bce.NewBodyFromString(aclString)
+ if err != nil {
+ return err
+ }
+ return api.PutBucketAcl(c, bucket, "", body, c.BosContext)
+}
+
+// PutBucketAclFromStruct - set the acl of the given bucket with acl data structure
+//
+// PARAMS:
+// - bucket: the bucket name
+// - aclObj: the acl struct object
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketAclFromStruct(bucket string, aclObj *api.PutBucketAclArgs) error {
+ jsonBytes, jsonErr := json.Marshal(aclObj)
+ if jsonErr != nil {
+ return jsonErr
+ }
+ body, err := bce.NewBodyFromBytes(jsonBytes)
+ if err != nil {
+ return err
+ }
+ return api.PutBucketAcl(c, bucket, "", body, c.BosContext)
+}
+
+// GetBucketAcl - get the acl of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - *api.GetBucketAclResult: the result of the bucket acl
+// - error: nil if success otherwise the specific error
+func (c *Client) GetBucketAcl(bucket string) (*api.GetBucketAclResult, error) {
+ return api.GetBucketAcl(c, bucket, c.BosContext)
+}
+
+// PutBucketLogging - set the loging setting of the given bucket with json stream
+//
+// PARAMS:
+// - bucket: the bucket name
+// - body: the json body
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketLogging(bucket string, body *bce.Body) error {
+ return api.PutBucketLogging(c, bucket, body, c.BosContext)
+}
+
+// PutBucketLoggingFromString - set the loging setting of the given bucket with json string
+//
+// PARAMS:
+// - bucket: the bucket name
+// - logging: the json format string
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketLoggingFromString(bucket, logging string) error {
+ body, err := bce.NewBodyFromString(logging)
+ if err != nil {
+ return err
+ }
+ return api.PutBucketLogging(c, bucket, body, c.BosContext)
+}
+
+// PutBucketLoggingFromStruct - set the loging setting of the given bucket with args object
+//
+// PARAMS:
+// - bucket: the bucket name
+// - obj: the logging setting object
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketLoggingFromStruct(bucket string, obj *api.PutBucketLoggingArgs) error {
+ jsonBytes, jsonErr := json.Marshal(obj)
+ if jsonErr != nil {
+ return jsonErr
+ }
+ body, err := bce.NewBodyFromBytes(jsonBytes)
+ if err != nil {
+ return err
+ }
+ return api.PutBucketLogging(c, bucket, body, c.BosContext)
+}
+
+// GetBucketLogging - get the logging setting of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - *api.GetBucketLoggingResult: the logging setting of the bucket
+// - error: nil if success otherwise the specific error
+func (c *Client) GetBucketLogging(bucket string) (*api.GetBucketLoggingResult, error) {
+ return api.GetBucketLogging(c, bucket, c.BosContext)
+}
+
+// DeleteBucketLogging - delete the logging setting of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) DeleteBucketLogging(bucket string) error {
+ return api.DeleteBucketLogging(c, bucket, c.BosContext)
+}
+
+// PutBucketLifecycle - set the lifecycle rule of the given bucket with raw stream
+//
+// PARAMS:
+// - bucket: the bucket name
+// - lifecycle: the lifecycle rule json body
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketLifecycle(bucket string, lifecycle *bce.Body) error {
+ return api.PutBucketLifecycle(c, bucket, lifecycle, c.BosContext)
+}
+
+// PutBucketLifecycleFromString - set the lifecycle rule of the given bucket with string
+//
+// PARAMS:
+// - bucket: the bucket name
+// - lifecycle: the lifecycle rule json format string body
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketLifecycleFromString(bucket, lifecycle string) error {
+ body, err := bce.NewBodyFromString(lifecycle)
+ if err != nil {
+ return err
+ }
+ return api.PutBucketLifecycle(c, bucket, body, c.BosContext)
+}
+
+// GetBucketLifecycle - get the lifecycle rule of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - *api.GetBucketLifecycleResult: the lifecycle rule of the bucket
+// - error: nil if success otherwise the specific error
+func (c *Client) GetBucketLifecycle(bucket string) (*api.GetBucketLifecycleResult, error) {
+ return api.GetBucketLifecycle(c, bucket, c.BosContext)
+}
+
+// DeleteBucketLifecycle - delete the lifecycle rule of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) DeleteBucketLifecycle(bucket string) error {
+ return api.DeleteBucketLifecycle(c, bucket, c.BosContext)
+}
+
+// PutBucketStorageclass - set the storage class of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+// - storageClass: the storage class string value
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketStorageclass(bucket, storageClass string) error {
+ return api.PutBucketStorageclass(c, bucket, storageClass, c.BosContext)
+}
+
+// GetBucketStorageclass - get the storage class of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - string: the storage class string value
+// - error: nil if success otherwise the specific error
+func (c *Client) GetBucketStorageclass(bucket string) (string, error) {
+ return api.GetBucketStorageclass(c, bucket, c.BosContext)
+}
+
+// PutBucketReplication - set the bucket replication config of different region
+//
+// PARAMS:
+// - bucket: the bucket name
+// - replicationConf: the replication config json body stream
+// - replicationRuleId: the replication rule id composed of [0-9 A-Z a-z _ -]
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketReplication(bucket string, replicationConf *bce.Body, replicationRuleId string) error {
+ return api.PutBucketReplication(c, bucket, replicationConf, replicationRuleId, c.BosContext)
+}
+
+// PutBucketReplicationFromFile - set the bucket replication config with json file name
+//
+// PARAMS:
+// - bucket: the bucket name
+// - confFile: the config json file name
+// - replicationRuleId: the replication rule id composed of [0-9 A-Z a-z _ -]
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketReplicationFromFile(bucket, confFile string, replicationRuleId string) error {
+ body, err := bce.NewBodyFromFile(confFile)
+ if err != nil {
+ return err
+ }
+ return api.PutBucketReplication(c, bucket, body, replicationRuleId, c.BosContext)
+}
+
+// PutBucketReplicationFromString - set the bucket replication config with json string
+//
+// PARAMS:
+// - bucket: the bucket name
+// - confString: the config string with json format
+// - replicationRuleId: the replication rule id composed of [0-9 A-Z a-z _ -]
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketReplicationFromString(bucket, confString string, replicationRuleId string) error {
+ body, err := bce.NewBodyFromString(confString)
+ if err != nil {
+ return err
+ }
+ return api.PutBucketReplication(c, bucket, body, replicationRuleId, c.BosContext)
+}
+
+// PutBucketReplicationFromStruct - set the bucket replication config with struct
+//
+// PARAMS:
+// - bucket: the bucket name
+// - confObj: the replication config struct object
+// - replicationRuleId: the replication rule id composed of [0-9 A-Z a-z _ -]
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketReplicationFromStruct(bucket string,
+ confObj *api.PutBucketReplicationArgs, replicationRuleId string) error {
+ jsonBytes, jsonErr := json.Marshal(confObj)
+ if jsonErr != nil {
+ return jsonErr
+ }
+ body, err := bce.NewBodyFromBytes(jsonBytes)
+ if err != nil {
+ return err
+ }
+ return api.PutBucketReplication(c, bucket, body, replicationRuleId, c.BosContext)
+}
+
+// GetBucketReplication - get the bucket replication config of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+// - replicationRuleId: the replication rule id composed of [0-9 A-Z a-z _ -]
+//
+// RETURNS:
+// - *api.GetBucketReplicationResult: the result of the bucket replication config
+// - error: nil if success otherwise the specific error
+func (c *Client) GetBucketReplication(bucket string, replicationRuleId string) (*api.GetBucketReplicationResult, error) {
+ return api.GetBucketReplication(c, bucket, replicationRuleId, c.BosContext)
+}
+
+// ListBucketReplication - get all replication config of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - *api.ListBucketReplicationResult: the list of the bucket replication config
+// - error: nil if success otherwise the specific error
+func (c *Client) ListBucketReplication(bucket string) (*api.ListBucketReplicationResult, error) {
+ return api.ListBucketReplication(c, bucket, c.BosContext)
+}
+
+// DeleteBucketReplication - delete the bucket replication config of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+// - replicationRuleId: the replication rule id composed of [0-9 A-Z a-z _ -]
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) DeleteBucketReplication(bucket string, replicationRuleId string) error {
+ return api.DeleteBucketReplication(c, bucket, replicationRuleId, c.BosContext)
+}
+
+// GetBucketReplicationProgress - get the bucket replication process of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+// - replicationRuleId: the replication rule id composed of [0-9 A-Z a-z _ -]
+//
+// RETURNS:
+// - *api.GetBucketReplicationProgressResult: the process of the bucket replication
+// - error: nil if success otherwise the specific error
+func (c *Client) GetBucketReplicationProgress(bucket string, replicationRuleId string) (
+ *api.GetBucketReplicationProgressResult, error) {
+ return api.GetBucketReplicationProgress(c, bucket, replicationRuleId, c.BosContext)
+}
+
+// PutBucketEncryption - set the bucket encryption config of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+// - algorithm: the encryption algorithm name
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketEncryption(bucket, algorithm string) error {
+ return api.PutBucketEncryption(c, bucket, algorithm, c.BosContext)
+}
+
+// GetBucketEncryption - get the bucket encryption config
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - string: the encryption algorithm name
+// - error: nil if success otherwise the specific error
+func (c *Client) GetBucketEncryption(bucket string) (string, error) {
+ return api.GetBucketEncryption(c, bucket, c.BosContext)
+}
+
+// DeleteBucketEncryption - delete the bucket encryption config of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) DeleteBucketEncryption(bucket string) error {
+ return api.DeleteBucketEncryption(c, bucket, c.BosContext)
+}
+
+// PutBucketStaticWebsite - set the bucket static website config
+//
+// PARAMS:
+// - bucket: the bucket name
+// - config: the static website config body stream
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketStaticWebsite(bucket string, config *bce.Body) error {
+ return api.PutBucketStaticWebsite(c, bucket, config, c.BosContext)
+}
+
+// PutBucketStaticWebsiteFromString - set the bucket static website config from json string
+//
+// PARAMS:
+// - bucket: the bucket name
+// - jsonConfig: the static website config json string
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketStaticWebsiteFromString(bucket, jsonConfig string) error {
+ body, err := bce.NewBodyFromString(jsonConfig)
+ if err != nil {
+ return err
+ }
+ return api.PutBucketStaticWebsite(c, bucket, body, c.BosContext)
+}
+
+// PutBucketStaticWebsiteFromStruct - set the bucket static website config from struct
+//
+// PARAMS:
+// - bucket: the bucket name
+// - confObj: the static website config object
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketStaticWebsiteFromStruct(bucket string,
+ confObj *api.PutBucketStaticWebsiteArgs) error {
+ jsonBytes, jsonErr := json.Marshal(confObj)
+ if jsonErr != nil {
+ return jsonErr
+ }
+ body, err := bce.NewBodyFromBytes(jsonBytes)
+ if err != nil {
+ return err
+ }
+ return api.PutBucketStaticWebsite(c, bucket, body, c.BosContext)
+}
+
+// SimplePutBucketStaticWebsite - simple set the bucket static website config
+//
+// PARAMS:
+// - bucket: the bucket name
+// - index: the static website config for index file name
+// - notFound: the static website config for notFound file name
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) SimplePutBucketStaticWebsite(bucket, index, notFound string) error {
+ confObj := &api.PutBucketStaticWebsiteArgs{Index: index, NotFound: notFound}
+ return c.PutBucketStaticWebsiteFromStruct(bucket, confObj)
+}
+
+// GetBucketStaticWebsite - get the bucket static website config
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - result: the static website config result object
+// - error: nil if success otherwise the specific error
+func (c *Client) GetBucketStaticWebsite(bucket string) (
+ *api.GetBucketStaticWebsiteResult, error) {
+ return api.GetBucketStaticWebsite(c, bucket, c.BosContext)
+}
+
+// DeleteBucketStaticWebsite - delete the bucket static website config of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) DeleteBucketStaticWebsite(bucket string) error {
+ return api.DeleteBucketStaticWebsite(c, bucket, c.BosContext)
+}
+
+// PutBucketCors - set the bucket CORS config
+//
+// PARAMS:
+// - bucket: the bucket name
+// - config: the bucket CORS config body stream
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketCors(bucket string, config *bce.Body) error {
+ return api.PutBucketCors(c, bucket, config, c.BosContext)
+}
+
+// PutBucketCorsFromFile - set the bucket CORS config from json config file
+//
+// PARAMS:
+// - bucket: the bucket name
+// - filename: the bucket CORS json config file name
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketCorsFromFile(bucket, filename string) error {
+ body, err := bce.NewBodyFromFile(filename)
+ if err != nil {
+ return err
+ }
+ return api.PutBucketCors(c, bucket, body, c.BosContext)
+}
+
+// PutBucketCorsFromString - set the bucket CORS config from json config string
+//
+// PARAMS:
+// - bucket: the bucket name
+// - filename: the bucket CORS json config string
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketCorsFromString(bucket, jsonConfig string) error {
+ body, err := bce.NewBodyFromString(jsonConfig)
+ if err != nil {
+ return err
+ }
+ return api.PutBucketCors(c, bucket, body, c.BosContext)
+}
+
+// PutBucketCorsFromStruct - set the bucket CORS config from json config object
+//
+// PARAMS:
+// - bucket: the bucket name
+// - filename: the bucket CORS json config object
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketCorsFromStruct(bucket string, confObj *api.PutBucketCorsArgs) error {
+ jsonBytes, jsonErr := json.Marshal(confObj)
+ if jsonErr != nil {
+ return jsonErr
+ }
+ body, err := bce.NewBodyFromBytes(jsonBytes)
+ if err != nil {
+ return err
+ }
+ return api.PutBucketCors(c, bucket, body, c.BosContext)
+}
+
+// GetBucketCors - get the bucket CORS config
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - result: the bucket CORS config result object
+// - error: nil if success otherwise the specific error
+func (c *Client) GetBucketCors(bucket string) (*api.GetBucketCorsResult, error) {
+ return api.GetBucketCors(c, bucket, c.BosContext)
+}
+
+// DeleteBucketCors - delete the bucket CORS config of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) DeleteBucketCors(bucket string) error {
+ return api.DeleteBucketCors(c, bucket, c.BosContext)
+}
+
+// PutBucketCopyrightProtection - set the copyright protection config of the given bucket
+//
+// PARAMS:
+// - cli: the client agent which can perform sending request
+// - bucket: the bucket name
+// - resources: the resource items in the bucket to be protected
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketCopyrightProtection(bucket string, resources ...string) error {
+ return api.PutBucketCopyrightProtection(c, c.BosContext, bucket, resources...)
+}
+
+// GetBucketCopyrightProtection - get the bucket copyright protection config
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - result: the bucket copyright protection config resources
+// - error: nil if success otherwise the specific error
+func (c *Client) GetBucketCopyrightProtection(bucket string) ([]string, error) {
+ return api.GetBucketCopyrightProtection(c, bucket, c.BosContext)
+}
+
+// DeleteBucketCopyrightProtection - delete the bucket copyright protection config
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) DeleteBucketCopyrightProtection(bucket string) error {
+ return api.DeleteBucketCopyrightProtection(c, bucket, c.BosContext)
+}
+
+// PutObject - upload a new object or rewrite the existed object with raw stream
+//
+// PARAMS:
+// - bucket: the name of the bucket to store the object
+// - object: the name of the object
+// - body: the object content body
+// - args: the optional arguments
+//
+// RETURNS:
+// - string: etag of the uploaded object
+// - error: the uploaded error if any occurs
+func (c *Client) PutObject(bucket, object string, body *bce.Body,
+ args *api.PutObjectArgs) (string, error) {
+ etag, _, err := api.PutObject(c, bucket, object, body, args, c.BosContext)
+ return etag, err
+}
+
+// PutObjectWithContext - support to cancel request by context.Context
+func (c *Client) PutObjectWithContext(ctx context.Context, bucket, object string, body *bce.Body,
+ args *api.PutObjectArgs) (string, error) {
+ c.BosContext.Ctx = ctx
+ etag, _, err := api.PutObject(c, bucket, object, body, args, c.BosContext)
+ return etag, err
+}
+
+// BasicPutObject - the basic interface of uploading an object
+//
+// PARAMS:
+// - bucket: the name of the bucket to store the object
+// - object: the name of the object
+// - body: the object content body
+//
+// RETURNS:
+// - string: etag of the uploaded object
+// - error: the uploaded error if any occurs
+func (c *Client) BasicPutObject(bucket, object string, body *bce.Body) (string, error) {
+ etag, _, err := api.PutObject(c, bucket, object, body, nil, c.BosContext)
+ return etag, err
+}
+
+// PutObjectFromBytes - upload a new object or rewrite the existed object from a byte array
+//
+// PARAMS:
+// - bucket: the name of the bucket to store the object
+// - object: the name of the object
+// - bytesArr: the content byte array
+// - args: the optional arguments
+//
+// RETURNS:
+// - string: etag of the uploaded object
+// - error: the uploaded error if any occurs
+func (c *Client) PutObjectFromBytes(bucket, object string, bytesArr []byte,
+ args *api.PutObjectArgs) (string, error) {
+ body, err := bce.NewBodyFromBytes(bytesArr)
+ if err != nil {
+ return "", err
+ }
+ etag, _, err := api.PutObject(c, bucket, object, body, args, c.BosContext)
+ return etag, err
+}
+
+// PutObjectFromBytesWithContext - support to cancel request by context.Context
+func (c *Client) PutObjectFromBytesWithContext(ctx context.Context, bucket, object string,
+ bytesArr []byte, args *api.PutObjectArgs) (string, error) {
+ body, err := bce.NewBodyFromBytes(bytesArr)
+ if err != nil {
+ return "", err
+ }
+ c.BosContext.Ctx = ctx
+ etag, _, err := api.PutObject(c, bucket, object, body, args, c.BosContext)
+ return etag, err
+}
+
+// PutObjectFromString - upload a new object or rewrite the existed object from a string
+//
+// PARAMS:
+// - bucket: the name of the bucket to store the object
+// - object: the name of the object
+// - content: the content string
+// - args: the optional arguments
+//
+// RETURNS:
+// - string: etag of the uploaded object
+// - error: the uploaded error if any occurs
+func (c *Client) PutObjectFromString(bucket, object, content string,
+ args *api.PutObjectArgs) (string, error) {
+ body, err := bce.NewBodyFromString(content)
+ if err != nil {
+ return "", err
+ }
+ etag, _, err := api.PutObject(c, bucket, object, body, args, c.BosContext)
+ return etag, err
+}
+
+// PutObjectFromStringWithContext - support to cancel request by context.Context
+func (c *Client) PutObjectFromStringWithContext(ctx context.Context,
+ bucket, object, content string, args *api.PutObjectArgs) (string, error) {
+ body, err := bce.NewBodyFromString(content)
+ if err != nil {
+ return "", err
+ }
+ c.BosContext.Ctx = ctx
+ etag, _, err := api.PutObject(c, bucket, object, body, args, c.BosContext)
+ return etag, err
+
+}
+
+// PutObjectFromFile - upload a new object or rewrite the existed object from a local file
+//
+// PARAMS:
+// - bucket: the name of the bucket to store the object
+// - object: the name of the object
+// - fileName: the local file full path name
+// - args: the optional arguments
+//
+// RETURNS:
+// - string: etag of the uploaded object
+// - error: the uploaded error if any occurs
+func (c *Client) PutObjectFromFile(bucket, object, fileName string,
+ args *api.PutObjectArgs) (string, error) {
+ body, err := bce.NewBodyFromFile(fileName)
+ if err != nil {
+ return "", err
+ }
+ etag, _, err := api.PutObject(c, bucket, object, body, args, c.BosContext)
+ return etag, err
+}
+
+// PutObjectFromFileWithContext - support to cancel request by context.Context
+func (c *Client) PutObjectFromFileWithContext(ctx context.Context,
+ bucket, object, fileName string, args *api.PutObjectArgs) (string, error) {
+ body, err := bce.NewBodyFromFile(fileName)
+ if err != nil {
+ return "", err
+ }
+ c.BosContext.Ctx = ctx
+ etag, _, err := api.PutObject(c, bucket, object, body, args, c.BosContext)
+ return etag, err
+}
+
+// PutObjectFromStream - upload a new object or rewrite the existed object from stream
+//
+// PARAMS:
+// - bucket: the name of the bucket to store the object
+// - object: the name of the object
+// - fileName: the local file full path name
+// - args: the optional arguments
+//
+// RETURNS:
+// - string: etag of the uploaded object
+// - error: the uploaded error if any occurs
+func (c *Client) PutObjectFromStream(bucket, object string, reader io.Reader,
+ args *api.PutObjectArgs) (string, error) {
+ body, err := bce.NewBodyFromSizedReader(reader, -1)
+ if err != nil {
+ return "", err
+ }
+ etag, _, err := api.PutObject(c, bucket, object, body, args, c.BosContext)
+ return etag, err
+}
+
+// PutObjectFromStreamWithContext - support to cancel request by context.Context
+func (c *Client) PutObjectFromStreamWithContext(ctx context.Context, bucket, object string,
+ reader io.Reader, args *api.PutObjectArgs) (string, error) {
+ body, err := bce.NewBodyFromSizedReader(reader, -1)
+ if err != nil {
+ return "", err
+ }
+ c.BosContext.Ctx = ctx
+ etag, _, err := api.PutObject(c, bucket, object, body, args, c.BosContext)
+ return etag, err
+}
+
+func (c *Client) PutObjectFromFileWithCallback(bucket, object, fileName string,
+ args *api.PutObjectArgs) (string, *api.PutObjectResult, error) {
+ body, err := bce.NewBodyFromFile(fileName)
+ if err != nil {
+ return "", nil, err
+ }
+ etag, putObjectResult, err := api.PutObject(c, bucket, object, body, args, c.BosContext)
+ return etag, putObjectResult, err
+}
+
+func (c *Client) PutObjectWithCallback(bucket, object string, body *bce.Body,
+ args *api.PutObjectArgs) (string, *api.PutObjectResult, error) {
+ etag, putObjectResult, err := api.PutObject(c, bucket, object, body, args, c.BosContext)
+ return etag, putObjectResult, err
+}
+
+// CopyObject - copy a remote object to another one
+//
+// PARAMS:
+// - bucket: the name of the destination bucket
+// - object: the name of the destination object
+// - srcBucket: the name of the source bucket
+// - srcObject: the name of the source object
+// - args: the optional arguments for copying object which are MetadataDirective, StorageClass,
+// IfMatch, IfNoneMatch, ifModifiedSince, IfUnmodifiedSince
+//
+// RETURNS:
+// - *api.CopyObjectResult: result struct which contains "ETag" and "LastModified" fields
+// - error: any error if it occurs
+func (c *Client) CopyObject(bucket, object, srcBucket, srcObject string,
+ args *api.CopyObjectArgs) (*api.CopyObjectResult, error) {
+ source := fmt.Sprintf("/%s/%s", srcBucket, srcObject)
+ return api.CopyObject(c, bucket, object, source, args, c.BosContext)
+}
+
+// CopyObjectWithContext - support to cancel request by context.Context
+func (c *Client) CopyObjectWithContext(ctx context.Context, bucket, object, srcBucket, srcObject string,
+ args *api.CopyObjectArgs) (*api.CopyObjectResult, error) {
+ source := fmt.Sprintf("/%s/%s", srcBucket, srcObject)
+ c.BosContext.Ctx = ctx
+ return api.CopyObject(c, bucket, object, source, args, c.BosContext)
+}
+
+// BasicCopyObject - the basic interface of copying a object to another one
+//
+// PARAMS:
+// - bucket: the name of the destination bucket
+// - object: the name of the destination object
+// - srcBucket: the name of the source bucket
+// - srcObject: the name of the source object
+//
+// RETURNS:
+// - *api.CopyObjectResult: result struct which contains "ETag" and "LastModified" fields
+// - error: any error if it occurs
+func (c *Client) BasicCopyObject(bucket, object, srcBucket,
+ srcObject string) (*api.CopyObjectResult, error) {
+ source := fmt.Sprintf("/%s/%s", srcBucket, srcObject)
+ return api.CopyObject(c, bucket, object, source, nil, c.BosContext)
+}
+
+// GetObject - get the given object with raw stream return
+//
+// PARAMS:
+// - bucket: the name of the bucket
+// - object: the name of the object
+// - args: the optional args in querysring
+// - ranges: the optional range start and end to get the given object
+//
+// RETURNS:
+// - *api.GetObjectResult: result struct which contains "Body" and header fields
+// for details reference https://cloud.baidu.com/doc/BOS/API.html#GetObject.E6.8E.A5.E5.8F.A3
+// - error: any error if it occurs
+func (c *Client) GetObject(bucket, object string, args map[string]string,
+ ranges ...int64) (*api.GetObjectResult, error) {
+ return api.GetObject(c, bucket, object, c.BosContext, args, ranges...)
+}
+
+// GetObjectWithContext - support to cancel request by context.Context
+func (c *Client) GetObjectWithContext(ctx context.Context, bucket, object string,
+ args map[string]string, ranges ...int64) (*api.GetObjectResult, error) {
+ c.BosContext.Ctx = ctx
+ return api.GetObject(c, bucket, object, c.BosContext, args, ranges...)
+}
+
+// BasicGetObject - the basic interface of geting the given object
+//
+// PARAMS:
+// - bucket: the name of the bucket
+// - object: the name of the object
+//
+// RETURNS:
+// - *api.GetObjectResult: result struct which contains "Body" and header fields
+// for details reference https://cloud.baidu.com/doc/BOS/API.html#GetObject.E6.8E.A5.E5.8F.A3
+// - error: any error if it occurs
+func (c *Client) BasicGetObject(bucket, object string) (*api.GetObjectResult, error) {
+ return api.GetObject(c, bucket, object, c.BosContext, nil)
+}
+
+// BasicGetObjectToFile - use basic interface to get the given object to the given file path
+//
+// PARAMS:
+// - bucket: the name of the bucket
+// - object: the name of the object
+// - filePath: the file path to store the object content
+//
+// RETURNS:
+// - error: any error if it occurs
+func (c *Client) BasicGetObjectToFile(bucket, object, filePath string) error {
+ res, err := api.GetObject(c, bucket, object, c.BosContext, nil)
+ if err != nil {
+ return err
+ }
+ defer res.Body.Close()
+
+ file, fileErr := os.OpenFile(filePath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
+ if fileErr != nil {
+ return fileErr
+ }
+ defer file.Close()
+
+ written, writeErr := io.CopyN(file, res.Body, res.ContentLength)
+ if writeErr != nil {
+ return writeErr
+ }
+ if written != res.ContentLength {
+ return fmt.Errorf("written content size does not match the response content")
+ }
+ return nil
+}
+
+// GetObjectToFileWithContext - support to cancel request by context.Context
+func (c *Client) GetObjectToFileWithContext(ctx context.Context, bucket, object, filePath string) error {
+ c.BosContext.Ctx = ctx
+ res, err := api.GetObject(c, bucket, object, c.BosContext, nil)
+ if err != nil {
+ return err
+ }
+ defer res.Body.Close()
+
+ file, fileErr := os.OpenFile(filePath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
+ if fileErr != nil {
+ return fileErr
+ }
+ defer file.Close()
+
+ written, writeErr := io.CopyN(file, res.Body, res.ContentLength)
+ if writeErr != nil {
+ return writeErr
+ }
+ if written != res.ContentLength {
+ return fmt.Errorf("written content size does not match the response content")
+ }
+ return nil
+}
+
+// GetObjectMeta - get the given object metadata
+//
+// PARAMS:
+// - bucket: the name of the bucket
+// - object: the name of the object
+//
+// RETURNS:
+// - *api.GetObjectMetaResult: metadata result, for details reference
+// https://cloud.baidu.com/doc/BOS/API.html#GetObjectMeta.E6.8E.A5.E5.8F.A3
+// - error: any error if it occurs
+func (c *Client) GetObjectMeta(bucket, object string) (*api.GetObjectMetaResult, error) {
+ return api.GetObjectMeta(c, bucket, object, c.BosContext)
+}
+
+// GetObjectMetaWithContext - support to cancel request by context.Context
+func (c *Client) GetObjectMetaWithContext(ctx context.Context,
+ bucket, object string) (*api.GetObjectMetaResult, error) {
+ c.BosContext.Ctx = ctx
+ return api.GetObjectMeta(c, bucket, object, c.BosContext)
+}
+
+// SelectObject - select the object content
+//
+// PARAMS:
+// - bucket: the name of the bucket
+// - object: the name of the object
+// - args: the optional arguments to select the object
+//
+// RETURNS:
+// - *api.SelectObjectResult: select object result
+// - error: any error if it occurs
+func (c *Client) SelectObject(bucket, object string, args *api.SelectObjectArgs) (*api.SelectObjectResult, error) {
+ return api.SelectObject(c, bucket, object, args, c.BosContext)
+}
+
+// SelectObjectWithContext - support to cancel request by context.Context
+func (c *Client) SelectObjectWithContext(ctx context.Context, bucket, object string,
+ args *api.SelectObjectArgs) (*api.SelectObjectResult, error) {
+ c.BosContext.Ctx = ctx
+ return api.SelectObject(c, bucket, object, args, c.BosContext)
+}
+
+// FetchObject - fetch the object content from the given source and store
+//
+// PARAMS:
+// - bucket: the name of the bucket to store
+// - object: the name of the object to store
+// - source: fetch source url
+// - args: the optional arguments to fetch the object
+//
+// RETURNS:
+// - *api.FetchObjectResult: result struct with Code, Message, RequestId and JobId fields
+// - error: any error if it occurs
+func (c *Client) FetchObject(bucket, object, source string,
+ args *api.FetchObjectArgs) (*api.FetchObjectResult, error) {
+ return api.FetchObject(c, bucket, object, source, args, c.BosContext)
+}
+
+// FetchObjectWithContext - support to cancel request by context.Context
+func (c *Client) FetchObjectWithContext(ctx context.Context, bucket, object, source string,
+ args *api.FetchObjectArgs) (*api.FetchObjectResult, error) {
+ c.BosContext.Ctx = ctx
+ return api.FetchObject(c, bucket, object, source, args, c.BosContext)
+}
+
+// BasicFetchObject - the basic interface of the fetch object api
+//
+// PARAMS:
+// - bucket: the name of the bucket to store
+// - object: the name of the object to store
+// - source: fetch source url
+//
+// RETURNS:
+// - *api.FetchObjectResult: result struct with Code, Message, RequestId and JobId fields
+// - error: any error if it occurs
+func (c *Client) BasicFetchObject(bucket, object, source string) (*api.FetchObjectResult, error) {
+ return api.FetchObject(c, bucket, object, source, nil, c.BosContext)
+}
+
+// SimpleFetchObject - fetch object with simple arguments interface
+//
+// PARAMS:
+// - bucket: the name of the bucket to store
+// - object: the name of the object to store
+// - source: fetch source url
+// - mode: fetch mode which supports sync and async
+// - storageClass: the storage class of the fetched object
+//
+// RETURNS:
+// - *api.FetchObjectResult: result struct with Code, Message, RequestId and JobId fields
+// - error: any error if it occurs
+func (c *Client) SimpleFetchObject(bucket, object, source, mode,
+ storageClass string) (*api.FetchObjectResult, error) {
+ args := &api.FetchObjectArgs{FetchMode: mode, StorageClass: storageClass, FetchCallBackAddress: ""}
+ return api.FetchObject(c, bucket, object, source, args, c.BosContext)
+}
+
+// AppendObject - append the given content to a new or existed object which is appendable
+//
+// PARAMS:
+// - bucket: the name of the bucket
+// - object: the name of the object
+// - content: the append object stream
+// - args: the optional arguments to append object
+//
+// RETURNS:
+// - *api.AppendObjectResult: the result of the appended object
+// - error: any error if it occurs
+func (c *Client) AppendObject(bucket, object string, content *bce.Body,
+ args *api.AppendObjectArgs) (*api.AppendObjectResult, error) {
+ return api.AppendObject(c, bucket, object, content, args, c.BosContext)
+}
+
+// AppendObjectWithContext - support to cancel request by context.Context
+func (c *Client) AppendObjectWithContext(ctx context.Context, bucket, object string,
+ content *bce.Body, args *api.AppendObjectArgs) (*api.AppendObjectResult, error) {
+ c.BosContext.Ctx = ctx
+ return api.AppendObject(c, bucket, object, content, args, c.BosContext)
+}
+
+// SimpleAppendObject - the interface to append object with simple offset argument
+//
+// PARAMS:
+// - bucket: the name of the bucket
+// - object: the name of the object
+// - content: the append object stream
+// - offset: the offset of where to append
+//
+// RETURNS:
+// - *api.AppendObjectResult: the result of the appended object
+// - error: any error if it occurs
+func (c *Client) SimpleAppendObject(bucket, object string, content *bce.Body,
+ offset int64) (*api.AppendObjectResult, error) {
+ return api.AppendObject(c, bucket, object, content, &api.AppendObjectArgs{Offset: offset}, c.BosContext)
+}
+
+// SimpleAppendObjectFromString - the simple interface of appending an object from a string
+//
+// PARAMS:
+// - bucket: the name of the bucket
+// - object: the name of the object
+// - content: the object string to append
+// - offset: the offset of where to append
+//
+// RETURNS:
+// - *api.AppendObjectResult: the result of the appended object
+// - error: any error if it occurs
+func (c *Client) SimpleAppendObjectFromString(bucket, object, content string,
+ offset int64) (*api.AppendObjectResult, error) {
+ body, err := bce.NewBodyFromString(content)
+ if err != nil {
+ return nil, err
+ }
+ return api.AppendObject(c, bucket, object, body, &api.AppendObjectArgs{Offset: offset}, c.BosContext)
+}
+
+// SimpleAppendObjectFromFile - the simple interface of appending an object from a file
+//
+// PARAMS:
+// - bucket: the name of the bucket
+// - object: the name of the object
+// - filePath: the full file path
+// - offset: the offset of where to append
+//
+// RETURNS:
+// - *api.AppendObjectResult: the result of the appended object
+// - error: any error if it occurs
+func (c *Client) SimpleAppendObjectFromFile(bucket, object, filePath string,
+ offset int64) (*api.AppendObjectResult, error) {
+ body, err := bce.NewBodyFromFile(filePath)
+ if err != nil {
+ return nil, err
+ }
+ return api.AppendObject(c, bucket, object, body, &api.AppendObjectArgs{Offset: offset}, c.BosContext)
+}
+
+// DeleteObject - delete the given object
+//
+// PARAMS:
+// - bucket: the name of the bucket to delete
+// - object: the name of the object to delete
+//
+// RETURNS:
+// - error: any error if it occurs
+func (c *Client) DeleteObject(bucket, object string) error {
+ return api.DeleteObject(c, bucket, object, "", c.BosContext)
+}
+
+func (c *Client) DeleteObjectVersion(bucket, object, versionId string) error {
+ return api.DeleteObject(c, bucket, object, versionId, c.BosContext)
+}
+
+// DeleteMultipleObjects - delete a list of objects
+//
+// PARAMS:
+// - bucket: the name of the bucket to delete
+// - objectListStream: the object list stream to be deleted
+//
+// RETURNS:
+// - *api.DeleteMultipleObjectsResult: the delete information
+// - error: any error if it occurs
+func (c *Client) DeleteMultipleObjects(bucket string,
+ objectListStream *bce.Body) (*api.DeleteMultipleObjectsResult, error) {
+ return api.DeleteMultipleObjects(c, bucket, objectListStream, c.BosContext)
+}
+
+// DeleteMultipleObjectsFromString - delete a list of objects with json format string
+//
+// PARAMS:
+// - bucket: the name of the bucket to delete
+// - objectListString: the object list string to be deleted
+//
+// RETURNS:
+// - *api.DeleteMultipleObjectsResult: the delete information
+// - error: any error if it occurs
+func (c *Client) DeleteMultipleObjectsFromString(bucket,
+ objectListString string) (*api.DeleteMultipleObjectsResult, error) {
+ body, err := bce.NewBodyFromString(objectListString)
+ if err != nil {
+ return nil, err
+ }
+ return api.DeleteMultipleObjects(c, bucket, body, c.BosContext)
+}
+
+// DeleteMultipleObjectsFromStruct - delete a list of objects with object list struct
+//
+// PARAMS:
+// - bucket: the name of the bucket to delete
+// - objectListStruct: the object list struct to be deleted
+//
+// RETURNS:
+// - *api.DeleteMultipleObjectsResult: the delete information
+// - error: any error if it occurs
+func (c *Client) DeleteMultipleObjectsFromStruct(bucket string,
+ objectListStruct *api.DeleteMultipleObjectsArgs) (*api.DeleteMultipleObjectsResult, error) {
+ jsonBytes, jsonErr := json.Marshal(objectListStruct)
+ if jsonErr != nil {
+ return nil, jsonErr
+ }
+ body, err := bce.NewBodyFromBytes(jsonBytes)
+ if err != nil {
+ return nil, err
+ }
+ return api.DeleteMultipleObjects(c, bucket, body, c.BosContext)
+}
+
+// DeleteMultipleObjectsFromKeyList - delete a list of objects with given key string array
+//
+// PARAMS:
+// - bucket: the name of the bucket to delete
+// - keyList: the key string list to be deleted
+//
+// RETURNS:
+// - *api.DeleteMultipleObjectsResult: the delete information
+// - error: any error if it occurs
+func (c *Client) DeleteMultipleObjectsFromKeyList(bucket string,
+ keyList []string) (*api.DeleteMultipleObjectsResult, error) {
+ if len(keyList) == 0 {
+ return nil, fmt.Errorf("the key list to be deleted is empty")
+ }
+ args := make([]api.DeleteObjectArgs, len(keyList))
+ for i, k := range keyList {
+ args[i].Key = k
+ }
+ argsContainer := &api.DeleteMultipleObjectsArgs{Objects: args}
+
+ jsonBytes, jsonErr := json.Marshal(argsContainer)
+ if jsonErr != nil {
+ return nil, jsonErr
+ }
+ body, err := bce.NewBodyFromBytes(jsonBytes)
+ if err != nil {
+ return nil, err
+ }
+ return api.DeleteMultipleObjects(c, bucket, body, c.BosContext)
+}
+
+// InitiateMultipartUpload - initiate a multipart upload to get a upload ID
+//
+// PARAMS:
+// - bucket: the bucket name
+// - object: the object name
+// - contentType: the content type of the object to be uploaded which should be specified,
+// otherwise use the default(application/octet-stream)
+// - args: the optional arguments
+//
+// RETURNS:
+// - *InitiateMultipartUploadResult: the result data structure
+// - error: nil if ok otherwise the specific error
+func (c *Client) InitiateMultipartUpload(bucket, object, contentType string,
+ args *api.InitiateMultipartUploadArgs) (*api.InitiateMultipartUploadResult, error) {
+ return api.InitiateMultipartUpload(c, bucket, object, contentType, args, c.BosContext)
+}
+
+// BasicInitiateMultipartUpload - basic interface to initiate a multipart upload
+//
+// PARAMS:
+// - bucket: the bucket name
+// - object: the object name
+//
+// RETURNS:
+// - *InitiateMultipartUploadResult: the result data structure
+// - error: nil if ok otherwise the specific error
+func (c *Client) BasicInitiateMultipartUpload(bucket,
+ object string) (*api.InitiateMultipartUploadResult, error) {
+ return api.InitiateMultipartUpload(c, bucket, object, "", nil, c.BosContext)
+}
+
+// UploadPart - upload the single part in the multipart upload process
+//
+// PARAMS:
+// - bucket: the bucket name
+// - object: the object name
+// - uploadId: the multipart upload id
+// - partNumber: the current part number
+// - content: the uploaded part content
+// - args: the optional arguments
+//
+// RETURNS:
+// - string: the etag of the uploaded part
+// - error: nil if ok otherwise the specific error
+func (c *Client) UploadPart(bucket, object, uploadId string, partNumber int,
+ content *bce.Body, args *api.UploadPartArgs) (string, error) {
+ return api.UploadPart(c, bucket, object, uploadId, partNumber, content, args, c.BosContext)
+}
+
+// UploadPartWithContext - support to cancel request by context.Context
+func (c *Client) UploadPartWithContext(ctx context.Context, bucket, object, uploadId string,
+ partNumber int, content *bce.Body, args *api.UploadPartArgs) (string, error) {
+ c.BosContext.Ctx = ctx
+ return api.UploadPart(c, bucket, object, uploadId, partNumber, content, args, c.BosContext)
+}
+
+// BasicUploadPart - basic interface to upload the single part in the multipart upload process
+//
+// PARAMS:
+// - bucket: the bucket name
+// - object: the object name
+// - uploadId: the multipart upload id
+// - partNumber: the current part number
+// - content: the uploaded part content
+//
+// RETURNS:
+// - string: the etag of the uploaded part
+// - error: nil if ok otherwise the specific error
+func (c *Client) BasicUploadPart(bucket, object, uploadId string, partNumber int,
+ content *bce.Body) (string, error) {
+ return api.UploadPart(c, bucket, object, uploadId, partNumber, content, nil, c.BosContext)
+}
+
+// UploadPartFromBytes - upload the single part in the multipart upload process
+//
+// PARAMS:
+// - bucket: the bucket name
+// - object: the object name
+// - uploadId: the multipart upload id
+// - partNumber: the current part number
+// - content: the uploaded part content
+// - args: the optional arguments
+//
+// RETURNS:
+// - string: the etag of the uploaded part
+// - error: nil if ok otherwise the specific error
+func (c *Client) UploadPartFromBytes(bucket, object, uploadId string, partNumber int,
+ content []byte, args *api.UploadPartArgs) (string, error) {
+ return api.UploadPartFromBytes(c, bucket, object, uploadId, partNumber, content, args, c.BosContext)
+}
+
+// UploadPartFromBytesWithContext - support to cancel request by context.Context
+func (c *Client) UploadPartFromBytesWithContext(ctx context.Context, bucket, object, uploadId string,
+ partNumber int, content []byte, args *api.UploadPartArgs) (string, error) {
+ c.BosContext.Ctx = ctx
+ return api.UploadPartFromBytes(c, bucket, object, uploadId, partNumber, content, args, c.BosContext)
+}
+
+// UploadPartCopy - copy the multipart object
+//
+// PARAMS:
+// - bucket: the destination bucket name
+// - object: the destination object name
+// - srcBucket: the source bucket
+// - srcObject: the source object
+// - uploadId: the multipart upload id
+// - partNumber: the current part number
+// - args: the optional arguments
+//
+// RETURNS:
+// - *CopyObjectResult: the lastModified and eTag of the part
+// - error: nil if ok otherwise the specific error
+func (c *Client) UploadPartCopy(bucket, object, srcBucket, srcObject, uploadId string,
+ partNumber int, args *api.UploadPartCopyArgs) (*api.CopyObjectResult, error) {
+ source := fmt.Sprintf("/%s/%s", srcBucket, srcObject)
+ return api.UploadPartCopy(c, bucket, object, source, uploadId, partNumber, args, c.BosContext)
+}
+
+// UploadPartCopyWithContext - support to cancel request by context.Context
+func (c *Client) UploadPartCopyWithContext(ctx context.Context, bucket, object, srcBucket, srcObject, uploadId string,
+ partNumber int, args *api.UploadPartCopyArgs) (*api.CopyObjectResult, error) {
+ source := fmt.Sprintf("/%s/%s", srcBucket, srcObject)
+ c.BosContext.Ctx = ctx
+ return api.UploadPartCopy(c, bucket, object, source, uploadId, partNumber, args, c.BosContext)
+}
+
+// BasicUploadPartCopy - basic interface to copy the multipart object
+//
+// PARAMS:
+// - bucket: the destination bucket name
+// - object: the destination object name
+// - srcBucket: the source bucket
+// - srcObject: the source object
+// - uploadId: the multipart upload id
+// - partNumber: the current part number
+//
+// RETURNS:
+// - *CopyObjectResult: the lastModified and eTag of the part
+// - error: nil if ok otherwise the specific error
+func (c *Client) BasicUploadPartCopy(bucket, object, srcBucket, srcObject, uploadId string,
+ partNumber int) (*api.CopyObjectResult, error) {
+ source := fmt.Sprintf("/%s/%s", srcBucket, srcObject)
+ return api.UploadPartCopy(c, bucket, object, source, uploadId, partNumber, nil, c.BosContext)
+}
+
+// CompleteMultipartUpload - finish a multipart upload operation with parts stream
+//
+// PARAMS:
+// - bucket: the destination bucket name
+// - object: the destination object name
+// - uploadId: the multipart upload id
+// - parts: all parts info stream
+// - meta: user defined meta data
+//
+// RETURNS:
+// - *CompleteMultipartUploadResult: the result data
+// - error: nil if ok otherwise the specific error
+func (c *Client) CompleteMultipartUpload(bucket, object, uploadId string,
+ body *bce.Body, args *api.CompleteMultipartUploadArgs) (*api.CompleteMultipartUploadResult, error) {
+ return api.CompleteMultipartUpload(c, bucket, object, uploadId, body, args, c.BosContext)
+}
+
+// CompleteMultipartUploadFromStruct - finish a multipart upload operation with parts struct
+//
+// PARAMS:
+// - bucket: the destination bucket name
+// - object: the destination object name
+// - uploadId: the multipart upload id
+// - args: args info struct object
+//
+// RETURNS:
+// - *CompleteMultipartUploadResult: the result data
+// - error: nil if ok otherwise the specific error
+func (c *Client) CompleteMultipartUploadFromStruct(bucket, object, uploadId string,
+ args *api.CompleteMultipartUploadArgs) (*api.CompleteMultipartUploadResult, error) {
+ jsonBytes, jsonErr := json.Marshal(args)
+ if jsonErr != nil {
+ return nil, jsonErr
+ }
+ body, err := bce.NewBodyFromBytes(jsonBytes)
+ if err != nil {
+ return nil, err
+ }
+ return api.CompleteMultipartUpload(c, bucket, object, uploadId, body, args, c.BosContext)
+}
+
+// AbortMultipartUpload - abort a multipart upload operation
+//
+// PARAMS:
+// - bucket: the destination bucket name
+// - object: the destination object name
+// - uploadId: the multipart upload id
+//
+// RETURNS:
+// - error: nil if ok otherwise the specific error
+func (c *Client) AbortMultipartUpload(bucket, object, uploadId string) error {
+ return api.AbortMultipartUpload(c, bucket, object, uploadId, c.BosContext)
+}
+
+// ListParts - list the successfully uploaded parts info by upload id
+//
+// PARAMS:
+// - bucket: the destination bucket name
+// - object: the destination object name
+// - uploadId: the multipart upload id
+// - args: the optional arguments
+//
+// RETURNS:
+// - *ListPartsResult: the uploaded parts info result
+// - error: nil if ok otherwise the specific error
+func (c *Client) ListParts(bucket, object, uploadId string,
+ args *api.ListPartsArgs) (*api.ListPartsResult, error) {
+ return api.ListParts(c, bucket, object, uploadId, args, c.BosContext)
+}
+
+// ListPartsWithContext - support to cancel request by context.Context
+func (c *Client) ListPartsWithContext(ctx context.Context, bucket, object, uploadId string,
+ args *api.ListPartsArgs) (*api.ListPartsResult, error) {
+ c.BosContext.Ctx = ctx
+ return api.ListParts(c, bucket, object, uploadId, args, c.BosContext)
+}
+
+// BasicListParts - basic interface to list the successfully uploaded parts info by upload id
+//
+// PARAMS:
+// - bucket: the destination bucket name
+// - object: the destination object name
+// - uploadId: the multipart upload id
+//
+// RETURNS:
+// - *ListPartsResult: the uploaded parts info result
+// - error: nil if ok otherwise the specific error
+func (c *Client) BasicListParts(bucket, object, uploadId string) (*api.ListPartsResult, error) {
+ return api.ListParts(c, bucket, object, uploadId, nil, c.BosContext)
+}
+
+// ListMultipartUploads - list the unfinished uploaded parts of the given bucket
+//
+// PARAMS:
+// - bucket: the destination bucket name
+// - args: the optional arguments
+//
+// RETURNS:
+// - *ListMultipartUploadsResult: the unfinished uploaded parts info result
+// - error: nil if ok otherwise the specific error
+func (c *Client) ListMultipartUploads(bucket string,
+ args *api.ListMultipartUploadsArgs) (*api.ListMultipartUploadsResult, error) {
+ return api.ListMultipartUploads(c, bucket, args, c.BosContext)
+}
+
+// ListMultipartUploadsWithContext - support to cancel request by context.Context
+func (c *Client) ListMultipartUploadsWithContext(ctx context.Context, bucket string,
+ args *api.ListMultipartUploadsArgs) (*api.ListMultipartUploadsResult, error) {
+ c.BosContext.Ctx = ctx
+ return api.ListMultipartUploads(c, bucket, args, c.BosContext)
+}
+
+// BasicListMultipartUploads - basic interface to list the unfinished uploaded parts
+//
+// PARAMS:
+// - bucket: the destination bucket name
+//
+// RETURNS:
+// - *ListMultipartUploadsResult: the unfinished uploaded parts info result
+// - error: nil if ok otherwise the specific error
+func (c *Client) BasicListMultipartUploads(bucket string) (
+ *api.ListMultipartUploadsResult, error) {
+ return api.ListMultipartUploads(c, bucket, nil, c.BosContext)
+}
+
+// UploadSuperFile - parallel upload the super file by using the multipart upload interface
+//
+// PARAMS:
+// - bucket: the destination bucket name
+// - object: the destination object name
+// - fileName: the local full path filename of the super file
+// - storageClass: the storage class to be set to the uploaded file
+//
+// RETURNS:
+// - error: nil if ok otherwise the specific error
+func (c *Client) UploadSuperFile(bucket, object, fileName, storageClass string) error {
+ // Get the file size and check the size for multipart upload
+ file, fileErr := os.Open(fileName)
+ if fileErr != nil {
+ return fileErr
+ }
+ oldTimeout := c.Config.ConnectionTimeoutInMillis
+ c.Config.ConnectionTimeoutInMillis = 0
+ defer func() {
+ c.Config.ConnectionTimeoutInMillis = oldTimeout
+ file.Close()
+ }()
+ fileInfo, infoErr := file.Stat()
+ if infoErr != nil {
+ return infoErr
+ }
+ size := fileInfo.Size()
+ if size < MIN_MULTIPART_SIZE || c.MultipartSize < MIN_MULTIPART_SIZE {
+ return bce.NewBceClientError("multipart size should not be less than 1MB")
+ }
+
+ // Calculate part size and total part number
+ partSize := (c.MultipartSize + MULTIPART_ALIGN - 1) / MULTIPART_ALIGN * MULTIPART_ALIGN
+ partNum := (size + partSize - 1) / partSize
+ if partNum > MAX_PART_NUMBER {
+ partSize = (size + MAX_PART_NUMBER - 1) / MAX_PART_NUMBER
+ partSize = (partSize + MULTIPART_ALIGN - 1) / MULTIPART_ALIGN * MULTIPART_ALIGN
+ partNum = (size + partSize - 1) / partSize
+ }
+ log.Debugf("starting upload super file, total parts: %d, part size: %d", partNum, partSize)
+
+ // Inner wrapper function of parallel uploading each part to get the ETag of the part
+ uploadPart := func(bucket, object, uploadId string, partNumber int, body *bce.Body,
+ result chan *api.UploadInfoType, ret chan error, id int64, pool chan int64) {
+ etag, err := c.BasicUploadPart(bucket, object, uploadId, partNumber, body)
+ if err != nil {
+ result <- nil
+ ret <- err
+ } else {
+ result <- &api.UploadInfoType{PartNumber: partNumber, ETag: etag}
+ }
+ pool <- id
+ }
+
+ // Do the parallel multipart upload
+ resp, err := c.InitiateMultipartUpload(bucket, object, "",
+ &api.InitiateMultipartUploadArgs{StorageClass: storageClass})
+ if err != nil {
+ return err
+ }
+ uploadId := resp.UploadId
+ uploadedResult := make(chan *api.UploadInfoType, partNum)
+ retChan := make(chan error, partNum)
+ workerPool := make(chan int64, c.MaxParallel)
+ for i := int64(0); i < c.MaxParallel; i++ {
+ workerPool <- i
+ }
+ for partId := int64(1); partId <= partNum; partId++ {
+ uploadSize := partSize
+ offset := (partId - 1) * partSize
+ left := size - offset
+ if uploadSize > left {
+ uploadSize = left
+ }
+ partBody, _ := bce.NewBodyFromSectionFile(file, offset, uploadSize)
+ select { // wait until get a worker to upload
+ case workerId := <-workerPool:
+ go uploadPart(bucket, object, uploadId, int(partId), partBody,
+ uploadedResult, retChan, workerId, workerPool)
+ case uploadPartErr := <-retChan:
+ c.AbortMultipartUpload(bucket, object, uploadId)
+ return uploadPartErr
+ }
+ }
+
+ // Check the return of each part uploading, and decide to complete or abort it
+ completeArgs := &api.CompleteMultipartUploadArgs{
+ Parts: make([]api.UploadInfoType, partNum),
+ }
+ for i := partNum; i > 0; i-- {
+ uploaded := <-uploadedResult
+ if uploaded == nil { // error occurs and not be caught in `select' statement
+ c.AbortMultipartUpload(bucket, object, uploadId)
+ return <-retChan
+ }
+ completeArgs.Parts[uploaded.PartNumber-1] = *uploaded
+ log.Debugf("upload part %d success, etag: %s", uploaded.PartNumber, uploaded.ETag)
+ }
+ if _, err := c.CompleteMultipartUploadFromStruct(bucket, object,
+ uploadId, completeArgs); err != nil {
+ c.AbortMultipartUpload(bucket, object, uploadId)
+ return err
+ }
+ return nil
+}
+
+// DownloadSuperFile - parallel download the super file using the get object with range
+//
+// PARAMS:
+// - bucket: the destination bucket name
+// - object: the destination object name
+// - fileName: the local full path filename to store the object
+//
+// RETURNS:
+// - error: nil if ok otherwise the specific error
+func (c *Client) DownloadSuperFile(bucket, object, fileName string) (err error) {
+ file, err := os.OpenFile(fileName, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
+ if err != nil {
+ return
+ }
+ oldTimeout := c.Config.ConnectionTimeoutInMillis
+ c.Config.ConnectionTimeoutInMillis = 0
+ defer func() {
+ c.Config.ConnectionTimeoutInMillis = oldTimeout
+ file.Close()
+ if err != nil {
+ os.Remove(fileName)
+ }
+ }()
+
+ meta, err := c.GetObjectMeta(bucket, object)
+ if err != nil {
+ return
+ }
+ size := meta.ContentLength
+ partSize := (c.MultipartSize + MULTIPART_ALIGN - 1) / MULTIPART_ALIGN * MULTIPART_ALIGN
+ partNum := (size + partSize - 1) / partSize
+ log.Debugf("starting download super file, total parts: %d, part size: %d", partNum, partSize)
+
+ doneChan := make(chan struct{}, partNum)
+ abortChan := make(chan struct{})
+
+ // Set up multiple goroutine workers to download the object
+ workerPool := make(chan int64, c.MaxParallel)
+ for i := int64(0); i < c.MaxParallel; i++ {
+ workerPool <- i
+ }
+ for i := int64(0); i < partNum; i++ {
+ rangeStart := i * partSize
+ rangeEnd := (i+1)*partSize - 1
+ if rangeEnd > size-1 {
+ rangeEnd = size - 1
+ }
+ select {
+ case workerId := <-workerPool:
+ go func(rangeStart, rangeEnd, workerId int64) {
+ res, rangeGetErr := c.GetObject(bucket, object, nil, rangeStart, rangeEnd)
+ if rangeGetErr != nil {
+ log.Errorf("download object part(offset:%d, size:%d) failed: %v",
+ rangeStart, res.ContentLength, rangeGetErr)
+ abortChan <- struct{}{}
+ err = rangeGetErr
+ return
+ }
+ defer res.Body.Close()
+ log.Debugf("writing part %d with offset=%d, size=%d", rangeStart/partSize,
+ rangeStart, res.ContentLength)
+ buf := make([]byte, 4096)
+ offset := rangeStart
+ for {
+ n, e := res.Body.Read(buf)
+ if e != nil && e != io.EOF {
+ abortChan <- struct{}{}
+ err = e
+ return
+ }
+ if n == 0 {
+ break
+ }
+ if _, writeErr := file.WriteAt(buf[:n], offset); writeErr != nil {
+ abortChan <- struct{}{}
+ err = writeErr
+ return
+ }
+ offset += int64(n)
+ }
+ log.Debugf("writing part %d done", rangeStart/partSize)
+ workerPool <- workerId
+ doneChan <- struct{}{}
+ }(rangeStart, rangeEnd, workerId)
+ case <-abortChan: // abort range get if error occurs during downloading any part
+ return
+ }
+ }
+
+ // Wait for writing to local file done
+ for i := partNum; i > 0; i-- {
+ <-doneChan
+ }
+ return nil
+}
+
+// GeneratePresignedUrl - generate an authorization url with expire time and optional arguments
+//
+// PARAMS:
+// - bucket: the target bucket name
+// - object: the target object name
+// - expireInSeconds: the expire time in seconds of the signed url
+// - method: optional sign method, default is GET
+// - headers: optional sign headers, default just set the Host
+// - params: optional sign params, default is empty
+//
+// RETURNS:
+// - string: the presigned url with authorization string
+func (c *Client) GeneratePresignedUrl(bucket, object string, expireInSeconds int, method string,
+ headers, params map[string]string) string {
+ return api.GeneratePresignedUrl(c.Config, c.Signer, bucket, object,
+ expireInSeconds, method, headers, params)
+}
+
+func (c *Client) GeneratePresignedUrlPathStyle(bucket, object string, expireInSeconds int, method string,
+ headers, params map[string]string) string {
+ return api.GeneratePresignedUrlPathStyle(c.Config, c.Signer, bucket, object,
+ expireInSeconds, method, headers, params)
+}
+
+// BasicGeneratePresignedUrl - basic interface to generate an authorization url with expire time
+//
+// PARAMS:
+// - bucket: the target bucket name
+// - object: the target object name
+// - expireInSeconds: the expire time in seconds of the signed url
+//
+// RETURNS:
+// - string: the presigned url with authorization string
+func (c *Client) BasicGeneratePresignedUrl(bucket, object string, expireInSeconds int) string {
+ return api.GeneratePresignedUrl(c.Config, c.Signer, bucket, object,
+ expireInSeconds, "", nil, nil)
+}
+
+// PutObjectAcl - set the ACL of the given object
+//
+// PARAMS:
+// - bucket: the bucket name
+// - object: the object name
+// - aclBody: the acl json body stream
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutObjectAcl(bucket, object string, aclBody *bce.Body) error {
+ return api.PutObjectAcl(c, bucket, object, "", nil, nil, aclBody, c.BosContext)
+}
+
+// PutObjectAclFromCanned - set the canned acl of the given object
+//
+// PARAMS:
+// - bucket: the bucket name
+// - object: the object name
+// - cannedAcl: the cannedAcl string
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutObjectAclFromCanned(bucket, object, cannedAcl string) error {
+ return api.PutObjectAcl(c, bucket, object, cannedAcl, nil, nil, nil, c.BosContext)
+}
+
+// PutObjectAclGrantRead - set the canned grant read acl of the given object
+//
+// PARAMS:
+// - bucket: the bucket name
+// - object: the object name
+// - ids: the user id list to grant read for this object
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutObjectAclGrantRead(bucket, object string, ids ...string) error {
+ return api.PutObjectAcl(c, bucket, object, "", ids, nil, nil, c.BosContext)
+}
+
+// PutObjectAclGrantFullControl - set the canned grant full-control acl of the given object
+//
+// PARAMS:
+// - bucket: the bucket name
+// - object: the object name
+// - ids: the user id list to grant full-control for this object
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutObjectAclGrantFullControl(bucket, object string, ids ...string) error {
+ return api.PutObjectAcl(c, bucket, object, "", nil, ids, nil, c.BosContext)
+}
+
+// PutObjectAclFromFile - set the acl of the given object with acl json file name
+//
+// PARAMS:
+// - bucket: the bucket name
+// - object: the object name
+// - aclFile: the acl file name
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutObjectAclFromFile(bucket, object, aclFile string) error {
+ body, err := bce.NewBodyFromFile(aclFile)
+ if err != nil {
+ return err
+ }
+ return api.PutObjectAcl(c, bucket, object, "", nil, nil, body, c.BosContext)
+}
+
+// PutObjectAclFromString - set the acl of the given object with acl json string
+//
+// PARAMS:
+// - bucket: the bucket name
+// - object: the object name
+// - aclString: the acl string with json format
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutObjectAclFromString(bucket, object, aclString string) error {
+ body, err := bce.NewBodyFromString(aclString)
+ if err != nil {
+ return err
+ }
+ return api.PutObjectAcl(c, bucket, object, "", nil, nil, body, c.BosContext)
+}
+
+// PutObjectAclFromStruct - set the acl of the given object with acl data structure
+//
+// PARAMS:
+// - bucket: the bucket name
+// - aclObj: the acl struct object
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutObjectAclFromStruct(bucket, object string, aclObj *api.PutObjectAclArgs) error {
+ jsonBytes, jsonErr := json.Marshal(aclObj)
+ if jsonErr != nil {
+ return jsonErr
+ }
+ body, err := bce.NewBodyFromBytes(jsonBytes)
+ if err != nil {
+ return err
+ }
+ return api.PutObjectAcl(c, bucket, object, "", nil, nil, body, c.BosContext)
+}
+
+// GetObjectAcl - get the acl of the given object
+//
+// PARAMS:
+// - bucket: the bucket name
+// - object: the object name
+//
+// RETURNS:
+// - *api.GetObjectAclResult: the result of the object acl
+// - error: nil if success otherwise the specific error
+func (c *Client) GetObjectAcl(bucket, object string) (*api.GetObjectAclResult, error) {
+ return api.GetObjectAcl(c, bucket, object, c.BosContext)
+}
+
+// DeleteObjectAcl - delete the acl of the given object
+//
+// PARAMS:
+// - bucket: the bucket name
+// - object: the object name
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) DeleteObjectAcl(bucket, object string) error {
+ return api.DeleteObjectAcl(c, bucket, object, c.BosContext)
+}
+
+// RestoreObject - restore the archive object
+//
+// PARAMS:
+// - bucket: the bucket name
+// - object: the object name
+// - restoreDays: the effective time of restore
+// - restoreTier: the tier of restore
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) RestoreObject(bucket string, object string, restoreDays int, restoreTier string) error {
+ if _, ok := api.VALID_RESTORE_TIER[restoreTier]; !ok {
+ return errors.New("invalid restore tier")
+ }
+
+ if restoreDays <= 0 {
+ return errors.New("invalid restore days")
+ }
+
+ args := api.ArchiveRestoreArgs{
+ RestoreTier: restoreTier,
+ RestoreDays: restoreDays,
+ }
+ return api.RestoreObject(c, bucket, object, args, c.BosContext)
+}
+
+// PutBucketTrash - put the bucket trash
+//
+// PARAMS:
+// - bucket: the bucket name
+// - trashReq: the trash request
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketTrash(bucket string, trashReq api.PutBucketTrashReq) error {
+ return api.PutBucketTrash(c, bucket, trashReq, c.BosContext)
+}
+
+// GetBucketTrash - get the bucket trash
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - *api.GetBucketTrashResult,: the result of the bucket trash
+// - error: nil if success otherwise the specific error
+func (c *Client) GetBucketTrash(bucket string) (*api.GetBucketTrashResult, error) {
+ return api.GetBucketTrash(c, bucket, c.BosContext)
+}
+
+// DeleteBucketTrash - delete the trash of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) DeleteBucketTrash(bucket string) error {
+ return api.DeleteBucketTrash(c, bucket, c.BosContext)
+}
+
+// PutBucketNotification - put the bucket notification
+//
+// PARAMS:
+// - bucket: the bucket name
+// - putBucketNotificationReq: the bucket notification request
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) PutBucketNotification(bucket string, putBucketNotificationReq api.PutBucketNotificationReq) error {
+ return api.PutBucketNotification(c, bucket, putBucketNotificationReq, c.BosContext)
+}
+
+// GetBucketNotification - get the bucket notification
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - *api.PutBucketNotificationReq,: the result of the bucket notification
+// - error: nil if success otherwise the specific error
+func (c *Client) GetBucketNotification(bucket string) (*api.PutBucketNotificationReq, error) {
+ return api.GetBucketNotification(c, bucket, c.BosContext)
+}
+
+// DeleteBucketNotification - delete the notification of the given bucket
+//
+// PARAMS:
+// - bucket: the bucket name
+//
+// RETURNS:
+// - error: nil if success otherwise the specific error
+func (c *Client) DeleteBucketNotification(bucket string) error {
+ return api.DeleteBucketNotification(c, bucket, c.BosContext)
+}
+
+// ParallelUpload - auto multipart upload object
+//
+// PARAMS:
+// - bucket: the bucket name
+// - object: the object name
+// - filename: the filename
+// - contentType: the content type default(application/octet-stream)
+// - args: the bucket name nil using default
+//
+// RETURNS:
+// - *api.CompleteMultipartUploadResult: multipart upload result
+// - error: nil if success otherwise the specific error
+func (c *Client) ParallelUpload(bucket string, object string, filename string, contentType string, args *api.InitiateMultipartUploadArgs) (*api.CompleteMultipartUploadResult, error) {
+
+ initiateMultipartUploadResult, err := api.InitiateMultipartUpload(c, bucket, object, contentType, args, c.BosContext)
+ if err != nil {
+ return nil, err
+ }
+
+ partEtags, err := c.parallelPartUpload(bucket, object, filename, initiateMultipartUploadResult.UploadId)
+ if err != nil {
+ c.AbortMultipartUpload(bucket, object, initiateMultipartUploadResult.UploadId)
+ return nil, err
+ }
+
+ completeMultipartUploadResult, err := c.CompleteMultipartUploadFromStruct(bucket, object, initiateMultipartUploadResult.UploadId, &api.CompleteMultipartUploadArgs{Parts: partEtags})
+ if err != nil {
+ c.AbortMultipartUpload(bucket, object, initiateMultipartUploadResult.UploadId)
+ return nil, err
+ }
+ return completeMultipartUploadResult, nil
+}
+
+// parallelPartUpload - single part upload
+//
+// PARAMS:
+// - bucket: the bucket name
+// - object: the object name
+// - filename: the uploadId
+// - uploadId: the uploadId
+//
+// RETURNS:
+// - []api.UploadInfoType: multipart upload result
+// - error: nil if success otherwise the specific error
+func (c *Client) parallelPartUpload(bucket string, object string, filename string, uploadId string) ([]api.UploadInfoType, error) {
+ file, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+ // 分块大小按MULTIPART_ALIGN=1MB对齐
+ partSize := (c.MultipartSize +
+ MULTIPART_ALIGN - 1) / MULTIPART_ALIGN * MULTIPART_ALIGN
+
+ // 获取文件大小,并计算分块数目,最大分块数MAX_PART_NUMBER=10000
+ fileInfo, _ := file.Stat()
+ fileSize := fileInfo.Size()
+ partNum := (fileSize + partSize - 1) / partSize
+ if partNum > MAX_PART_NUMBER { // 超过最大分块数,需调整分块大小
+ partSize = (fileSize + MAX_PART_NUMBER + 1) / MAX_PART_NUMBER
+ partSize = (partSize + MULTIPART_ALIGN - 1) / MULTIPART_ALIGN * MULTIPART_ALIGN
+ partNum = (fileSize + partSize - 1) / partSize
+ }
+
+ parallelChan := make(chan int, c.MaxParallel)
+
+ errChan := make(chan error, c.MaxParallel)
+
+ resultChan := make(chan api.UploadInfoType, partNum)
+
+ // 逐个分块上传
+ for i := int64(1); i <= partNum; i++ {
+ // 计算偏移offset和本次上传的大小uploadSize
+ uploadSize := partSize
+ offset := partSize * (i - 1)
+ left := fileSize - offset
+ if left < partSize {
+ uploadSize = left
+ }
+
+ // 创建指定偏移、指定大小的文件流
+ partBody, _ := bce.NewBodyFromSectionFile(file, offset, uploadSize)
+
+ select {
+ case err = <-errChan:
+ return nil, err
+ default:
+ select {
+ case err = <-errChan:
+ return nil, err
+ case parallelChan <- 1:
+ go c.singlePartUpload(bucket, object, uploadId, int(i), partBody, parallelChan, errChan, resultChan)
+ }
+
+ }
+ }
+
+ partEtags := make([]api.UploadInfoType, partNum)
+ for i := int64(0); i < partNum; i++ {
+ select {
+ case err := <-errChan:
+ return nil, err
+ case result := <-resultChan:
+ partEtags[result.PartNumber-1].PartNumber = result.PartNumber
+ partEtags[result.PartNumber-1].ETag = result.ETag
+ }
+ }
+ return partEtags, nil
+}
+
+// singlePartUpload - single part upload
+//
+// PARAMS:
+// - pararelChan: the pararelChan
+// - errChan: the error chan
+// - result: the upload result chan
+// - bucket: the bucket name
+// - object: the object name
+// - uploadId: the uploadId
+// - partNumber: the part number of the object
+// - content: the content of current part
+func (c *Client) singlePartUpload(
+ bucket string, object string, uploadId string,
+ partNumber int, content *bce.Body,
+ parallelChan chan int, errChan chan error, result chan api.UploadInfoType) {
+
+ defer func() {
+ if r := recover(); r != nil {
+ log.Fatal("parallelPartUpload recovered in f:", r)
+ errChan <- errors.New("parallelPartUpload panic")
+ }
+ <-parallelChan
+ }()
+
+ var args api.UploadPartArgs
+ args.ContentMD5 = content.ContentMD5()
+
+ etag, err := api.UploadPart(c, bucket, object, uploadId, partNumber, content, &args, c.BosContext)
+ if err != nil {
+ errChan <- err
+ log.Error("upload part fail,err:%v", err)
+ return
+ }
+ result <- api.UploadInfoType{PartNumber: partNumber, ETag: etag}
+ return
+}
+
+// ParallelCopy - auto multipart copy object
+//
+// PARAMS:
+// - srcBucketName: the src bucket name
+// - srcObjectName: the src object name
+// - destBucketName: the dest bucket name
+// - destObjectName: the dest object name
+// - args: the copy args
+// - srcClient: the src region client
+//
+// RETURNS:
+// - *api.CompleteMultipartUploadResult: multipart upload result
+// - error: nil if success otherwise the specific error
+func (c *Client) ParallelCopy(srcBucketName string, srcObjectName string,
+ destBucketName string, destObjectName string,
+ args *api.MultiCopyObjectArgs, srcClient *Client) (*api.CompleteMultipartUploadResult, error) {
+
+ if srcClient == nil {
+ srcClient = c
+ }
+ objectMeta, err := srcClient.GetObjectMeta(srcBucketName, srcObjectName)
+ if err != nil {
+ return nil, err
+ }
+
+ initArgs := api.InitiateMultipartUploadArgs{
+ CacheControl: objectMeta.CacheControl,
+ ContentDisposition: objectMeta.ContentDisposition,
+ Expires: objectMeta.Expires,
+ StorageClass: objectMeta.StorageClass,
+ }
+ if args != nil {
+ if len(args.StorageClass) != 0 {
+ initArgs.StorageClass = args.StorageClass
+ }
+ if len(args.ObjectTagging) != 0 {
+ initArgs.ObjectTagging = args.ObjectTagging
+ }
+ if len(args.TaggingDirective) != 0 {
+ initArgs.TaggingDirective = args.TaggingDirective
+ }
+ }
+ initiateMultipartUploadResult, err := api.InitiateMultipartUpload(c, destBucketName, destObjectName, objectMeta.ContentType, &initArgs, c.BosContext)
+
+ if err != nil {
+ return nil, err
+ }
+
+ source := fmt.Sprintf("/%s/%s", srcBucketName, srcObjectName)
+ partEtags, err := c.parallelPartCopy(*objectMeta, source, destBucketName, destObjectName, initiateMultipartUploadResult.UploadId)
+
+ if err != nil {
+ c.AbortMultipartUpload(destBucketName, destObjectName, initiateMultipartUploadResult.UploadId)
+ return nil, err
+ }
+
+ completeMultipartUploadResult, err := c.CompleteMultipartUploadFromStruct(destBucketName, destObjectName, initiateMultipartUploadResult.UploadId, &api.CompleteMultipartUploadArgs{Parts: partEtags})
+ if err != nil {
+ c.AbortMultipartUpload(destBucketName, destObjectName, initiateMultipartUploadResult.UploadId)
+ return nil, err
+ }
+ return completeMultipartUploadResult, nil
+}
+
+// parallelPartCopy - parallel part copy
+//
+// PARAMS:
+// - srcMeta: the copy source object meta
+// - source: the copy source
+// - bucket: the dest bucket name
+// - object: the dest object name
+// - uploadId: the uploadId
+//
+// RETURNS:
+// - []api.UploadInfoType: multipart upload result
+// - error: nil if success otherwise the specific error
+func (c *Client) parallelPartCopy(srcMeta api.GetObjectMetaResult, source string, bucket string, object string, uploadId string) ([]api.UploadInfoType, error) {
+ var err error
+ size := srcMeta.ContentLength
+ partSize := int64(DEFAULT_MULTIPART_SIZE)
+ if partSize*MAX_PART_NUMBER < size {
+ lowerLimit := int64(math.Ceil(float64(size) / MAX_PART_NUMBER))
+ partSize = int64(math.Ceil(float64(lowerLimit)/float64(partSize))) * partSize
+ }
+ partNum := (size + partSize - 1) / partSize
+
+ parallelChan := make(chan int, c.MaxParallel)
+
+ errChan := make(chan error, c.MaxParallel)
+
+ resultChan := make(chan api.UploadInfoType, partNum)
+
+ for i := int64(1); i <= partNum; i++ {
+ // 计算偏移offset和本次上传的大小uploadSize
+ uploadSize := partSize
+ offset := partSize * (i - 1)
+ left := size - offset
+ if left < partSize {
+ uploadSize = left
+ }
+
+ partCopyArgs := api.UploadPartCopyArgs{
+ SourceRange: fmt.Sprintf("bytes=%d-%d", (i-1)*partSize, (i-1)*partSize+uploadSize-1),
+ IfMatch: srcMeta.ETag,
+ }
+
+ select {
+ case err = <-errChan:
+ return nil, err
+ default:
+ select {
+ case err = <-errChan:
+ return nil, err
+ case parallelChan <- 1:
+ go c.singlePartCopy(source, bucket, object, uploadId, int(i), &partCopyArgs, parallelChan, errChan, resultChan)
+ }
+
+ }
+ }
+
+ partEtags := make([]api.UploadInfoType, partNum)
+ for i := int64(0); i < partNum; i++ {
+ select {
+ case err := <-errChan:
+ return nil, err
+ case result := <-resultChan:
+ partEtags[result.PartNumber-1].PartNumber = result.PartNumber
+ partEtags[result.PartNumber-1].ETag = result.ETag
+ }
+ }
+ return partEtags, nil
+}
+
+// singlePartCopy - single part copy
+//
+// PARAMS:
+// - pararelChan: the pararelChan
+// - errChan: the error chan
+// - result: the upload result chan
+// - source: the copy source
+// - bucket: the bucket name
+// - object: the object name
+// - uploadId: the uploadId
+// - partNumber: the part number of the object
+// - args: the copy args
+func (c *Client) singlePartCopy(source string, bucket string, object string, uploadId string,
+ partNumber int, args *api.UploadPartCopyArgs,
+ parallelChan chan int, errChan chan error, result chan api.UploadInfoType) {
+
+ defer func() {
+ if r := recover(); r != nil {
+ log.Fatal("parallelPartUpload recovered in f:", r)
+ errChan <- errors.New("parallelPartUpload panic")
+ }
+ <-parallelChan
+ }()
+
+ copyObjectResult, err := api.UploadPartCopy(c, bucket, object, source, uploadId, partNumber, args, c.BosContext)
+ if err != nil {
+ errChan <- err
+ log.Error("upload part fail,err:%v", err)
+ return
+ }
+ result <- api.UploadInfoType{PartNumber: partNumber, ETag: copyObjectResult.ETag}
+ return
+}
+
+// PutSymlink - create symlink for exist target object
+//
+// PARAMS:
+// - bucket: the name of the bucket
+// - object: the name of the object
+// - symlinkKey: the name of the symlink
+// - symlinkArgs: the optional arguments
+//
+// RETURNS:
+// - error: the put error if any occurs
+func (c *Client) PutSymlink(bucket string, object string, symlinkKey string, symlinkArgs *api.PutSymlinkArgs) error {
+ return api.PutObjectSymlink(c, bucket, object, symlinkKey, symlinkArgs, c.BosContext)
+}
+
+// PutSymlink - create symlink for exist target object
+//
+// PARAMS:
+// - bucket: the name of the bucket
+// - object: the name of the symlink
+//
+// RETURNS:
+// - string: the target of the symlink
+// - error: the put error if any occurs
+func (c *Client) GetSymlink(bucket string, object string) (string, error) {
+ return api.GetObjectSymlink(c, bucket, object, c.BosContext)
+}
+
+func (c *Client) PutBucketMirror(bucket string, putBucketMirrorArgs *api.PutBucketMirrorArgs) error {
+ return api.PutBucketMirror(c, bucket, putBucketMirrorArgs, c.BosContext)
+}
+
+func (c *Client) GetBucketMirror(bucket string) (*api.PutBucketMirrorArgs, error) {
+ return api.GetBucketMirror(c, bucket, c.BosContext)
+}
+
+func (c *Client) DeleteBucketMirror(bucket string) error {
+ return api.DeleteBucketMirror(c, bucket, c.BosContext)
+}
+
+func (c *Client) PutBucketTag(bucket string, putBucketTagArgs *api.PutBucketTagArgs) error {
+ return api.PutBucketTag(c, bucket, putBucketTagArgs, c.BosContext)
+}
+
+func (c *Client) GetBucketTag(bucket string) (*api.GetBucketTagResult, error) {
+ return api.GetBucketTag(c, bucket, c.BosContext)
+}
+
+func (c *Client) DeleteBucketTag(bucket string) error {
+ return api.DeleteBucketTag(c, bucket, c.BosContext)
+}
+
+func (c *Client) PutObjectTag(bucket string, object string, putObjectTagArgs *api.PutObjectTagArgs) error {
+ return api.PutObjectTag(c, bucket, object, putObjectTagArgs, c.BosContext)
+}
+
+func (c *Client) GetObjectTag(bucket string, object string) (map[string]interface{}, error) {
+ return api.GetObjectTag(c, bucket, object, c.BosContext)
+}
+
+func (c *Client) DeleteObjectTag(bucket string, object string) error {
+ return api.DeleteObjectTag(c, bucket, object, c.BosContext)
+}
+
+func (c *Client) BosShareLinkGet(bucket string, prefix string, shareCode string, duration int) (string, error) {
+ return api.GetBosShareLink(c, bucket, prefix, shareCode, duration)
+}
+
+func (c *Client) PutBucketVersioning(bucket string, putBucketVersioningArgs *api.BucketVersioningArgs) error {
+ return api.PutBucketVersioning(c, bucket, putBucketVersioningArgs, c.BosContext)
+}
+
+func (c *Client) GetBucketVersioning(bucket string) (*api.BucketVersioningArgs, error) {
+ return api.GetBucketVersioning(c, bucket, c.BosContext)
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/services/sts/api/mode.go b/vendor/github.com/baidubce/bce-sdk-go/services/sts/api/mode.go
new file mode 100644
index 00000000000..ef43330d800
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/services/sts/api/mode.go
@@ -0,0 +1,30 @@
+package api
+
+import "time"
+
+type GetSessionTokenResult struct {
+ AccessKeyId string
+ SecretAccessKey string
+ SessionToken string
+ CreateTime string
+ Expiration string
+ UserId string
+}
+
+type AssumeRoleArgs struct {
+ DurationSeconds int
+ AccountId string
+ UserId string
+ RoleName string
+ Acl string
+}
+
+type Credential struct {
+ AccessKeyId string
+ SecretAccessKey string
+ SessionToken string
+ CreateTime time.Time
+ Expiration time.Time
+ UserId string
+ RoleId string
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/services/sts/api/sts.go b/vendor/github.com/baidubce/bce-sdk-go/services/sts/api/sts.go
new file mode 100644
index 00000000000..0de76a0d23f
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/services/sts/api/sts.go
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// sts.go - define the response for STS service
+
+// Package api defines all APIs supported by the STS service of BCE.
+package api
+
+import (
+ "fmt"
+ "strconv"
+
+ "github.com/baidubce/bce-sdk-go/bce"
+ "github.com/baidubce/bce-sdk-go/http"
+)
+
+const (
+ DEFAULT_DURATION_SECONDS = 43200 // default duration is 12 hours
+ URI_PREFIX = bce.URI_PREFIX + "v1" // sts service not support uri without prefix "v1"
+ REQUEST_URI = "/sessionToken"
+
+ DEFAULT_ASSUMEROLE_DURATION_SECONDS = 7200 // default duration is 2 hours
+ REQUEST_ASSUMEROLE_URI = "/credential"
+)
+
+
+// GetSessionToken - get the session token from the STS service
+//
+// PARAMS:
+// - cli: the client object which can perform sending request
+// - durationSec: the duration seconds of the token
+// - acl: the acl string
+//
+// RETURNS:
+// - *GetSessionTokenResult: result of this api
+// - error: nil if ok otherwise the specific error
+func GetSessionToken(cli bce.Client, durationSec int, acl string) (*GetSessionTokenResult, error) {
+ // If the duration seconds is not a positive, use the default value
+ if durationSec <= 0 {
+ durationSec = DEFAULT_DURATION_SECONDS
+ }
+
+ // Build the request
+ req := &bce.BceRequest{}
+ req.SetUri(URI_PREFIX + REQUEST_URI)
+ req.SetMethod(http.POST)
+ req.SetParam("durationSeconds", strconv.Itoa(durationSec))
+ if len(acl) > 0 {
+ req.SetHeader(http.CONTENT_TYPE, bce.DEFAULT_CONTENT_TYPE)
+ body, err := bce.NewBodyFromString(acl)
+ if err != nil {
+ return nil, err
+ }
+ req.SetBody(body)
+ }
+
+ // Send requet and get response
+ resp := &bce.BceResponse{}
+ if err := cli.SendRequest(req, resp); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ jsonBody := &GetSessionTokenResult{}
+ if err := resp.ParseJsonBody(jsonBody); err != nil {
+ return nil, err
+ }
+ return jsonBody, nil
+}
+
+// AssumeRole - get the credential for the assume role from the STS service
+//
+// PARAMS:
+// - cli: the client object which can perform sending request
+// - args: the args for assumeRole
+// RETURNS:
+// - *Credential: result of this api
+// - error: nil if ok otherwise the specific error
+func AssumeRole(cli bce.Client, args *AssumeRoleArgs) (*Credential, error) {
+ // If the duration seconds is not a positive, use the default value
+ if args.DurationSeconds <= 0 {
+ args.DurationSeconds = DEFAULT_ASSUMEROLE_DURATION_SECONDS
+ }
+
+ if args.AccountId == "" {
+ return nil, fmt.Errorf("please set accountId")
+ }
+
+ if args.RoleName == "" {
+ return nil, fmt.Errorf("please set roleName")
+ }
+
+ // Build the request
+ req := &bce.BceRequest{}
+ req.SetUri(URI_PREFIX + REQUEST_ASSUMEROLE_URI)
+ req.SetMethod(http.POST)
+ req.SetParam("assumeRole", "")
+ req.SetParam("durationSeconds", strconv.Itoa(args.DurationSeconds))
+ req.SetParam("accountId", args.AccountId)
+ req.SetParam("roleName", args.RoleName)
+
+ if args.UserId != "" {
+ req.SetParam("userId", args.UserId)
+ }
+
+ if len(args.Acl) > 0 {
+ req.SetHeader(http.CONTENT_TYPE, bce.DEFAULT_CONTENT_TYPE)
+ body, err := bce.NewBodyFromString(args.Acl)
+ if err != nil {
+ return nil, err
+ }
+ req.SetBody(body)
+ }
+
+ // Send requet and get response
+ resp := &bce.BceResponse{}
+ if err := cli.SendRequest(req, resp); err != nil {
+ return nil, err
+ }
+ if resp.IsFail() {
+ return nil, resp.ServiceError()
+ }
+ jsonBody := &Credential{}
+ if err := resp.ParseJsonBody(jsonBody); err != nil {
+ return nil, err
+ }
+ return jsonBody, nil
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/services/sts/client.go b/vendor/github.com/baidubce/bce-sdk-go/services/sts/client.go
new file mode 100644
index 00000000000..2c5ef91b817
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/services/sts/client.go
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// client.go - define the client for STS service which is derived from BceClient
+
+// Package sts defines the STS service of BCE.
+// It contains the model sub package to implement the concrete request and response of the
+// GetSessionToken API.
+package sts
+
+import (
+ "github.com/baidubce/bce-sdk-go/auth"
+ "github.com/baidubce/bce-sdk-go/bce"
+ "github.com/baidubce/bce-sdk-go/services/sts/api"
+)
+
+const DEFAULT_SERVICE_DOMAIN = "sts." + bce.DEFAULT_REGION + "." + bce.DEFAULT_DOMAIN
+
+// Client of STS service is a kind of BceClient, so it derived from the BceClient and it only
+// supports the GetSessionToken API. There is no other fields needed.
+type Client struct {
+ *bce.BceClient
+}
+
+func (c *Client) GetSessionToken(duration int, acl string) (*api.GetSessionTokenResult, error) {
+ return api.GetSessionToken(c, duration, acl)
+}
+
+func (c *Client) AssumeRole(args *api.AssumeRoleArgs) (*api.Credential, error) {
+ return api.AssumeRole(c, args)
+}
+
+// NewClient make the STS service client with default configuration.
+// Use `cli.Config.xxx` to access the config or change it to non-default value.
+func NewClient(ak, sk string) (*Client, error) {
+ return NewStsClient(ak, sk, DEFAULT_SERVICE_DOMAIN)
+}
+
+func NewStsClient(ak, sk, endpoint string) (*Client, error) {
+ credentials, err := auth.NewBceCredentials(ak, sk)
+ if err != nil {
+ return nil, err
+ }
+ if len(endpoint) == 0 {
+ endpoint = DEFAULT_SERVICE_DOMAIN
+ }
+ defaultSignOptions := &auth.SignOptions{
+ HeadersToSign: auth.DEFAULT_HEADERS_TO_SIGN,
+ Timestamp: 0,
+ ExpireSeconds: auth.DEFAULT_EXPIRE_SECONDS}
+ defaultConf := &bce.BceClientConfiguration{
+ Endpoint: endpoint,
+ Region: bce.DEFAULT_REGION,
+ UserAgent: bce.DEFAULT_USER_AGENT,
+ Credentials: credentials,
+ SignOption: defaultSignOptions,
+ Retry: bce.DEFAULT_RETRY_POLICY,
+ ConnectionTimeoutInMillis: bce.DEFAULT_CONNECTION_TIMEOUT_IN_MILLIS}
+ v1Signer := &auth.BceV1Signer{}
+
+ client := &Client{bce.NewBceClient(defaultConf, v1Signer)}
+ return client, nil
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/util/log/logger.go b/vendor/github.com/baidubce/bce-sdk-go/util/log/logger.go
new file mode 100644
index 00000000000..81f54987cf1
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/util/log/logger.go
@@ -0,0 +1,393 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// logger.go - defines the logger structure and methods
+
+// Package log implements the log facilities for BCE. It supports log to stderr, stdout as well as
+// log to file with rotating. It is safe to be called by multiple goroutines.
+// By using the package level function to use the default logger:
+// log.SetLogHandler(log.STDOUT | log.FILE) // default is log to stdout
+// log.SetLogDir("/tmp") // default is /tmp
+// log.SetRotateType(log.ROTATE_DAY) // default is log.HOUR
+// log.SetRotateSize(1 << 30) // default is 1GB
+// log.SetLogLevel(log.INFO) // default is log.DEBUG
+// log.Debug(1, 1.2, "a")
+// log.Debugln(1, 1.2, "a")
+// log.Debugf(1, 1.2, "a")
+// User can also create new logger without using the default logger:
+// customLogger := log.NewLogger()
+// customLogger.SetLogHandler(log.FILE)
+// customLogger.Debug(1, 1.2, "a")
+// The log format can also support custom setting by using the following interface:
+// log.SetLogFormat([]string{log.FMT_LEVEL, log.FMT_TIME, log.FMT_MSG})
+// Most of the cases just use the default format is enough:
+// []string{FMT_LEVEL, FMT_LTIME, FMT_LOCATION, FMT_MSG}
+package log
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "time"
+)
+
+type Handler uint8
+
+// Constants for log handler flags, default is STDOUT
+const (
+ NONE Handler = 0
+ STDOUT Handler = 1
+ STDERR Handler = 1 << 1
+ FILE Handler = 1 << 2
+)
+
+type RotateStrategy uint8
+
+// Constants for log rotating strategy when logging to file, default is by hour
+const (
+ ROTATE_NONE RotateStrategy = iota
+ ROTATE_DAY
+ ROTATE_HOUR
+ ROTATE_MINUTE
+ ROTATE_SIZE
+
+ DEFAULT_ROTATE_TYPE = ROTATE_HOUR
+ DEFAULT_ROTATE_SIZE int64 = 1 << 30
+ DEFAULT_LOG_DIR = "/tmp"
+ ROTATE_SIZE_FILE_PREFIX = "rotating"
+)
+
+type Level uint8
+
+// Constants for log levels, default is DEBUG
+const (
+ DEBUG Level = iota
+ INFO
+ WARN
+ ERROR
+ FATAL
+ PANIC
+)
+
+var gLevelString = [...]string{"DEBUG", "INFO", "WARN", "ERROR", "FATAL", "PANIC"}
+
+// Constants of the log format components to support user custom specification
+const (
+ FMT_LEVEL = "level"
+ FMT_LTIME = "ltime" // long time with microsecond
+ FMT_TIME = "time" // just with second
+ FMT_LOCATION = "location" // caller's location with file, line, function
+ FMT_MSG = "msg"
+)
+
+var (
+ LOG_FMT_STR = map[string]string{
+ FMT_LEVEL: "[%s]",
+ FMT_LTIME: "2006-01-02 15:04:05.000000",
+ FMT_TIME: "2006-01-02 15:04:05",
+ FMT_LOCATION: "%s:%d:%s:",
+ FMT_MSG: "%s",
+ }
+ gDefaultLogFormat = []string{FMT_LEVEL, FMT_LTIME, FMT_LOCATION, FMT_MSG}
+)
+
+type writerArgs struct {
+ record string
+ rotateArgs interface{} // used for rotating: the size of the record or the logging time
+}
+
+// Logger defines the internal implementation of the log facility
+type logger struct {
+ writers map[Handler]io.WriteCloser // the destination writer to log message
+ writerChan chan *writerArgs // the writer channal to pass each record and time or size
+ logFormat []string
+ levelThreshold Level
+ handler Handler
+
+ // Fields that used when logging to file
+ logDir string
+ logFile string
+ rotateType RotateStrategy
+ rotateSize int64
+ done chan bool
+}
+
+func (l *logger) logging(level Level, format string, args ...interface{}) {
+ // Only log message that set the handler and is greater than or equal to the threshold
+ if l.handler == NONE || level < l.levelThreshold {
+ return
+ }
+
+ // Generate the log record string and pass it to the writer channel
+ now := time.Now()
+ pc, file, line, ok, funcname := uintptr(0), "???", 0, true, "???"
+ pc, file, line, ok = runtime.Caller(2)
+ if ok {
+ funcname = runtime.FuncForPC(pc).Name()
+ funcname = filepath.Ext(funcname)
+ funcname = strings.TrimPrefix(funcname, ".")
+ file = filepath.Base(file)
+ }
+ buf := make([]string, 0, len(l.logFormat))
+ msg := fmt.Sprintf(format, args...)
+ for _, f := range l.logFormat {
+ if _, exists := LOG_FMT_STR[f]; !exists { // skip not supported part
+ continue
+ }
+ fmtStr := LOG_FMT_STR[f]
+ switch f {
+ case FMT_LEVEL:
+ buf = append(buf, fmt.Sprintf(fmtStr, gLevelString[level]))
+ case FMT_LTIME:
+ buf = append(buf, now.Format(fmtStr))
+ case FMT_TIME:
+ buf = append(buf, now.Format(fmtStr))
+ case FMT_LOCATION:
+ buf = append(buf, fmt.Sprintf(fmtStr, file, line, funcname))
+ case FMT_MSG:
+ buf = append(buf, fmt.Sprintf(fmtStr, msg))
+ }
+ }
+ record := strings.Join(buf, " ")
+ if l.rotateType == ROTATE_SIZE {
+ l.writerChan <- &writerArgs{record, int64(len(record))}
+ } else {
+ l.writerChan <- &writerArgs{record, now}
+ }
+
+ // wait for current record done logging
+}
+
+func (l *logger) buildWriter(args interface{}) {
+ if l.handler&STDOUT == STDOUT {
+ l.writers[STDOUT] = os.Stdout
+ } else {
+ delete(l.writers, STDOUT)
+ }
+ if l.handler&STDERR == STDERR {
+ l.writers[STDERR] = os.Stderr
+ } else {
+ delete(l.writers, STDERR)
+ }
+ if l.handler&FILE == FILE {
+ l.writers[FILE] = l.buildFileWriter(args)
+ } else {
+ delete(l.writers, FILE)
+ }
+}
+
+func (l *logger) buildFileWriter(args interface{}) io.WriteCloser {
+ if l.handler&FILE != FILE {
+ return os.Stderr
+ }
+
+ if len(l.logDir) == 0 {
+ l.logDir = DEFAULT_LOG_DIR
+ }
+ if l.rotateType < ROTATE_NONE || l.rotateType > ROTATE_SIZE {
+ l.rotateType = DEFAULT_ROTATE_TYPE
+ }
+ if l.rotateType == ROTATE_SIZE && l.rotateSize == 0 {
+ l.rotateSize = DEFAULT_ROTATE_SIZE
+ }
+
+ logFile, needCreateFile := "", false
+ if l.rotateType == ROTATE_SIZE {
+ recordSize, _ := args.(int64)
+ logFile, needCreateFile = l.buildFileWriterBySize(recordSize)
+ } else {
+ recordTime, _ := args.(time.Time)
+ switch l.rotateType {
+ case ROTATE_NONE:
+ logFile = "default.log"
+ case ROTATE_DAY:
+ logFile = recordTime.Format("2006-01-02.log")
+ case ROTATE_HOUR:
+ logFile = recordTime.Format("2006-01-02_15.log")
+ case ROTATE_MINUTE:
+ logFile = recordTime.Format("2006-01-02_15-04.log")
+ }
+ if _, exist := getFileInfo(filepath.Join(l.logDir, logFile)); !exist {
+ needCreateFile = true
+ }
+ }
+ l.logFile = logFile
+ logFile = filepath.Join(l.logDir, l.logFile)
+
+ // Should create new file
+ if needCreateFile {
+ if w, ok := l.writers[FILE]; ok {
+ w.Close()
+ }
+ if writer, err := os.Create(logFile); err == nil {
+ return writer
+ } else {
+ return os.Stderr
+ }
+ }
+
+ // Already open the file
+ if w, ok := l.writers[FILE]; ok {
+ return w
+ }
+
+ // Newly open the file
+ if writer, err := os.OpenFile(logFile, os.O_WRONLY|os.O_APPEND, 0666); err == nil {
+ return writer
+ } else {
+ return os.Stderr
+ }
+}
+
+func (l *logger) buildFileWriterBySize(recordSize int64) (string, bool) {
+ logFile, needCreateFile := "", false
+ // First running the program and need to get filename by checking the existed files
+ if len(l.logFile) == 0 {
+ fname := fmt.Sprintf("%s-%s.0.log", ROTATE_SIZE_FILE_PREFIX, getSizeString(l.rotateSize))
+ for {
+ size, exist := getFileInfo(filepath.Join(l.logDir, fname))
+ if !exist {
+ logFile, needCreateFile = fname, true
+ break
+ }
+ if exist && size+recordSize <= l.rotateSize {
+ logFile, needCreateFile = fname, false
+ break
+ }
+ fname = getNextFileName(fname)
+ }
+ } else { // check the file size to append to the existed file or create a new file
+ currentFile := filepath.Join(l.logDir, l.logFile)
+ size, exist := getFileInfo(currentFile)
+ if !exist {
+ logFile, needCreateFile = l.logFile, true
+ } else {
+ if size+recordSize > l.rotateSize { // size exceeded
+ logFile, needCreateFile = getNextFileName(l.logFile), true
+ } else {
+ logFile, needCreateFile = l.logFile, false
+ }
+ }
+ }
+ return logFile, needCreateFile
+}
+
+func (l *logger) SetHandler(h Handler) { l.handler = h }
+
+func (l *logger) SetLogDir(dir string) { l.logDir = dir }
+
+func (l *logger) SetLogLevel(level Level) { l.levelThreshold = level }
+
+func (l *logger) SetLogFormat(format []string) { l.logFormat = format }
+
+func (l *logger) SetRotateType(rotate RotateStrategy) { l.rotateType = rotate }
+
+func (l *logger) SetRotateSize(size int64) { l.rotateSize = size }
+
+func (l *logger) Debug(msg ...interface{}) { l.logging(DEBUG, "%s\n", concat(msg...)) }
+
+func (l *logger) Debugln(msg ...interface{}) { l.logging(DEBUG, "%s\n", concat(msg...)) }
+
+func (l *logger) Debugf(f string, msg ...interface{}) { l.logging(DEBUG, f+"\n", msg...) }
+
+func (l *logger) Info(msg ...interface{}) { l.logging(INFO, "%s\n", concat(msg...)) }
+
+func (l *logger) Infoln(msg ...interface{}) { l.logging(INFO, "%s\n", concat(msg...)) }
+
+func (l *logger) Infof(f string, msg ...interface{}) { l.logging(INFO, f+"\n", msg...) }
+
+func (l *logger) Warn(msg ...interface{}) { l.logging(WARN, "%s\n", concat(msg...)) }
+
+func (l *logger) Warnln(msg ...interface{}) { l.logging(WARN, "%s\n", concat(msg...)) }
+
+func (l *logger) Warnf(f string, msg ...interface{}) { l.logging(WARN, f+"\n", msg...) }
+
+func (l *logger) Error(msg ...interface{}) { l.logging(ERROR, "%s\n", concat(msg...)) }
+
+func (l *logger) Errorln(msg ...interface{}) { l.logging(ERROR, "%s\n", concat(msg...)) }
+
+func (l *logger) Errorf(f string, msg ...interface{}) { l.logging(ERROR, f+"\n", msg...) }
+
+func (l *logger) Fatal(msg ...interface{}) { l.logging(FATAL, "%s\n", concat(msg...)) }
+
+func (l *logger) Fatalln(msg ...interface{}) { l.logging(FATAL, "%s\n", concat(msg...)) }
+
+func (l *logger) Fatalf(f string, msg ...interface{}) { l.logging(FATAL, f+"\n", msg...) }
+
+func (l *logger) Panic(msg ...interface{}) {
+ record := concat(msg...)
+ l.logging(PANIC, "%s\n", record)
+ panic(record)
+}
+
+func (l *logger) Panicln(msg ...interface{}) {
+ record := concat(msg...)
+ l.logging(PANIC, "%s\n", record)
+ panic(record)
+}
+
+func (l *logger) Panicf(format string, msg ...interface{}) {
+ record := fmt.Sprintf(format, msg...)
+ l.logging(PANIC, format+"\n", msg...)
+ panic(record)
+}
+
+func (l *logger) Close() {
+ select {
+ case <-l.done:
+ return
+ default:
+ }
+ l.writerChan <- nil
+}
+
+func NewLogger() *logger {
+ obj := &logger{
+ writers: make(map[Handler]io.WriteCloser, 3), // now only support 3 kinds of handler
+ writerChan: make(chan *writerArgs, 100),
+ logFormat: gDefaultLogFormat,
+ levelThreshold: DEBUG,
+ handler: NONE,
+ done: make(chan bool),
+ }
+ // The backend writer goroutine to write each log record
+ go func() {
+ defer func() {
+ if e := recover(); e != nil {
+ fmt.Println(e)
+ }
+ }()
+ for {
+ select {
+ case <-obj.done:
+ return
+ case args := <-obj.writerChan: // wait until a record comes to log
+ if args == nil {
+ close(obj.done)
+ close(obj.writerChan)
+ return
+ }
+ obj.buildWriter(args.rotateArgs)
+ for _, w := range obj.writers {
+ fmt.Fprint(w, args.record)
+ }
+ }
+ }
+ }()
+
+ return obj
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/util/log/util.go b/vendor/github.com/baidubce/bce-sdk-go/util/log/util.go
new file mode 100644
index 00000000000..23a6b39c55c
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/util/log/util.go
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// util.go - define the package-level default logger and functions for easily use.
+
+package log
+
+import (
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+)
+
+// The global default logger object to perform logging in this package-level functions
+var gDefaultLogger *logger
+
+func init() {
+ gDefaultLogger = NewLogger()
+}
+
+// Helper functions
+func getFileInfo(path string) (int64, bool) {
+ info, err := os.Stat(path)
+ if err != nil {
+ return -1, false
+ } else {
+ return info.Size(), true
+ }
+}
+
+func getSizeString(size int64) string {
+ if size < 0 {
+ return fmt.Sprintf("%d", size)
+ }
+ if size < (1 << 10) {
+ return fmt.Sprintf("%dB", size)
+ } else if size < (1 << 20) {
+ return fmt.Sprintf("%dkB", size>>10)
+ } else if size < (1 << 30) {
+ return fmt.Sprintf("%dMB", size>>20)
+ } else if size < (1 << 40) {
+ return fmt.Sprintf("%dGB", size>>30)
+ } else {
+ return "SUPER-LARGE"
+ }
+}
+
+func getNextFileName(nowFileName string) string {
+ pos1 := strings.Index(nowFileName, ".")
+ rest := nowFileName[pos1+1:]
+ pos2 := strings.Index(rest, ".")
+ num, _ := strconv.Atoi(rest[0:pos2])
+ return fmt.Sprintf("%s.%d.log", nowFileName[0:pos1], num+1)
+}
+
+func concat(msg ...interface{}) string {
+ buf := make([]string, 0, len(msg))
+ for _, m := range msg {
+ buf = append(buf, fmt.Sprintf("%v", m))
+ }
+ return strings.Join(buf, " ")
+}
+
+// Export package-level functions for logging messages by using the default logger. It supports 3
+// kinds of interface which is similar to the standard package "fmt": with suffix of "ln", "f" or
+// nothing.
+func Debug(msg ...interface{}) {
+ gDefaultLogger.logging(DEBUG, "%s\n", concat(msg...))
+}
+
+func Debugln(msg ...interface{}) {
+ gDefaultLogger.logging(DEBUG, "%s\n", concat(msg...))
+}
+
+func Debugf(format string, msg ...interface{}) {
+ gDefaultLogger.logging(DEBUG, format+"\n", msg...)
+}
+
+func Info(msg ...interface{}) {
+ gDefaultLogger.logging(INFO, "%s\n", concat(msg...))
+}
+
+func Infoln(msg ...interface{}) {
+ gDefaultLogger.logging(INFO, "%s\n", concat(msg...))
+}
+
+func Infof(format string, msg ...interface{}) {
+ gDefaultLogger.logging(INFO, format+"\n", msg...)
+}
+
+func Warn(msg ...interface{}) {
+ gDefaultLogger.logging(WARN, "%s\n", concat(msg...))
+}
+
+func Warnln(msg ...interface{}) {
+ gDefaultLogger.logging(WARN, "%s\n", concat(msg...))
+}
+
+func Warnf(format string, msg ...interface{}) {
+ gDefaultLogger.logging(WARN, format+"\n", msg...)
+}
+
+func Error(msg ...interface{}) {
+ gDefaultLogger.logging(ERROR, "%s\n", concat(msg...))
+}
+
+func Errorln(msg ...interface{}) {
+ gDefaultLogger.logging(ERROR, "%s\n", concat(msg...))
+}
+
+func Errorf(format string, msg ...interface{}) {
+ gDefaultLogger.logging(ERROR, format+"\n", msg...)
+}
+
+func Fatal(msg ...interface{}) {
+ gDefaultLogger.logging(FATAL, "%s\n", concat(msg...))
+}
+
+func Fatalln(msg ...interface{}) {
+ gDefaultLogger.logging(FATAL, "%s\n", concat(msg...))
+}
+
+func Fatalf(format string, msg ...interface{}) {
+ gDefaultLogger.logging(FATAL, format+"\n", msg...)
+}
+
+func Panic(msg ...interface{}) {
+ record := concat(msg...)
+ gDefaultLogger.logging(PANIC, "%s\n", record)
+ panic(record)
+}
+
+func Panicln(msg ...interface{}) {
+ record := concat(msg...)
+ gDefaultLogger.logging(PANIC, "%s\n", record)
+ panic(record)
+}
+
+func Panicf(format string, msg ...interface{}) {
+ record := fmt.Sprintf(format, msg...)
+ gDefaultLogger.logging(PANIC, format+"\n", msg...)
+ panic(record)
+}
+
+func Close() {
+ gDefaultLogger.Close()
+}
+
+// SetLogHandler - set the handler of the logger
+//
+// PARAMS:
+// - Handler: the handler defined in this package, now just support STDOUT, STDERR, FILE
+func SetLogHandler(h Handler) {
+ gDefaultLogger.handler = h
+}
+
+// SetLogLevel - set the level threshold of the logger, only level equal to or bigger than this
+// value will be logged.
+//
+// PARAMS:
+// - Level: the level defined in this package, now support 6 levels.
+func SetLogLevel(l Level) {
+ gDefaultLogger.levelThreshold = l
+}
+
+// SetLogFormat - set the log component of each record when logging it. The default log format is
+// {FMT_LEVEL, FMT_LTIME, FMT_LOCATION, FMT_MSG}.
+//
+// PARAMS:
+// - format: the format component array.
+func SetLogFormat(format []string) {
+ gDefaultLogger.logFormat = format
+}
+
+// SetLogDir - set the logging directory if logging to file.
+//
+// PARAMS:
+// - dir: the logging directory
+// RETURNS:
+// - error: check the directory and try to make it, otherwise return the error.
+func SetLogDir(dir string) error {
+ if _, err := os.Stat(dir); err != nil {
+ if os.IsNotExist(err) {
+ if mkErr := os.Mkdir(dir, os.ModePerm); mkErr != nil {
+ return mkErr
+ }
+ } else {
+ return err
+ }
+ }
+ gDefaultLogger.logDir = dir
+ return nil
+}
+
+// SetRotateType - set the rotating strategy if logging to file.
+//
+// PARAMS:
+// - RotateStrategy: the rotate strategy defined in this package, now support 5 strategy.
+func SetRotateType(r RotateStrategy) {
+ gDefaultLogger.rotateType = r
+}
+
+// SetRotateSize - set the rotating size if logging to file and set the strategy of size.
+//
+// PARAMS:
+// - size: the rotating size
+// RETURNS:
+// - error: check the value and return any error if error occurs.
+func SetRotateSize(size int64) error {
+ if size <= 0 {
+ return fmt.Errorf("%s", "rotate size should not be negative")
+ }
+ gDefaultLogger.rotateSize = size
+ return nil
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/util/mime.go b/vendor/github.com/baidubce/bce-sdk-go/util/mime.go
new file mode 100644
index 00000000000..42463444674
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/util/mime.go
@@ -0,0 +1,664 @@
+package util
+
+import (
+ "sync"
+)
+
+var DefaultMimeMap map[string]string
+var once sync.Once
+
+func LoadMimeMap() {
+ DefaultMimeMap = make(map[string]string)
+ DefaultMimeMap[".ez"] = "application/andrew-inset"
+ DefaultMimeMap[".atom"] = "application/atom+xml"
+ DefaultMimeMap[".atomcat"] = "application/atomcat+xml"
+ DefaultMimeMap[".atomsvc"] = "application/atomsvc+xml"
+ DefaultMimeMap[".ccxml"] = "application/ccxml+xml"
+ DefaultMimeMap[".davmount"] = "application/davmount+xml"
+ DefaultMimeMap[".ecma"] = "application/ecmascript"
+ DefaultMimeMap[".pfr"] = "application/font-tdpfr"
+ DefaultMimeMap[".stk"] = "application/hyperstudio"
+ DefaultMimeMap[".js"] = "text/javascript"
+ DefaultMimeMap[".json"] = "text/json"
+ DefaultMimeMap[".hqx"] = "application/mac-binhex40"
+ DefaultMimeMap[".cpt"] = "application/mac-compactpro"
+ DefaultMimeMap[".mrc"] = "application/marc"
+ DefaultMimeMap[".ma"] = "application/mathematica"
+ DefaultMimeMap[".nb"] = "application/mathematica"
+ DefaultMimeMap[".mb"] = "application/mathematica"
+ DefaultMimeMap[".mathml"] = "application/mathml+xml"
+ DefaultMimeMap[".mbox"] = "application/mbox"
+ DefaultMimeMap[".mscml"] = "application/mediaservercontrol+xml"
+ DefaultMimeMap[".mp4s"] = "application/mp4"
+ DefaultMimeMap[".doc"] = "application/msword"
+ DefaultMimeMap[".dot"] = "application/msword"
+ DefaultMimeMap[".mxf"] = "application/mxf"
+ DefaultMimeMap[".bin"] = "application/octet-stream"
+ DefaultMimeMap[".dms"] = "application/octet-stream"
+ DefaultMimeMap[".lha"] = "application/octet-stream"
+ DefaultMimeMap[".lzh"] = "application/octet-stream"
+ DefaultMimeMap[".class"] = "application/octet-stream"
+ DefaultMimeMap[".so"] = "application/octet-stream"
+ DefaultMimeMap[".iso"] = "application/octet-stream"
+ DefaultMimeMap[".dmg"] = "application/octet-stream"
+ DefaultMimeMap[".dist"] = "application/octet-stream"
+ DefaultMimeMap[".distz"] = "application/octet-stream"
+ DefaultMimeMap[".pkg"] = "application/octet-stream"
+ DefaultMimeMap[".bpk"] = "application/octet-stream"
+ DefaultMimeMap[".dump"] = "application/octet-stream"
+ DefaultMimeMap[".elc"] = "application/octet-stream"
+ DefaultMimeMap[".oda"] = "application/oda"
+ DefaultMimeMap[".ogg"] = "application/ogg"
+ DefaultMimeMap[".pdf"] = "application/pdf"
+ DefaultMimeMap[".pgp"] = "application/pgp-encrypted"
+ DefaultMimeMap[".asc"] = "application/pgp-signature"
+ DefaultMimeMap[".sig"] = "application/pgp-signature"
+ DefaultMimeMap[".prf"] = "application/pics-rules"
+ DefaultMimeMap[".p10"] = "application/pkcs10"
+ DefaultMimeMap[".p7m"] = "application/pkcs7-mime"
+ DefaultMimeMap[".p7c"] = "application/pkcs7-mime"
+ DefaultMimeMap[".p7s"] = "application/pkcs7-signature"
+ DefaultMimeMap[".cer"] = "application/pkix-cert"
+ DefaultMimeMap[".crl"] = "application/pkix-crl"
+ DefaultMimeMap[".pkipath"] = "application/pkix-pkipath"
+ DefaultMimeMap[".pki"] = "application/pkixcmp"
+ DefaultMimeMap[".pls"] = "application/pls+xml"
+ DefaultMimeMap[".ai"] = "application/postscript"
+ DefaultMimeMap[".eps"] = "application/postscript"
+ DefaultMimeMap[".ps"] = "application/postscript"
+ DefaultMimeMap[".cww"] = "application/prs.cww"
+ DefaultMimeMap[".rdf"] = "application/rdf+xml"
+ DefaultMimeMap[".rif"] = "application/reginfo+xml"
+ DefaultMimeMap[".rnc"] = "application/relax-ng-compact-syntax"
+ DefaultMimeMap[".rl"] = "application/resource-lists+xml"
+ DefaultMimeMap[".rs"] = "application/rls-services+xml"
+ DefaultMimeMap[".rsd"] = "application/rsd+xml"
+ DefaultMimeMap[".rss"] = "application/rss+xml"
+ DefaultMimeMap[".rtf"] = "application/rtf"
+ DefaultMimeMap[".sbml"] = "application/sbml+xml"
+ DefaultMimeMap[".scq"] = "application/scvp-cv-request"
+ DefaultMimeMap[".scs"] = "application/scvp-cv-response"
+ DefaultMimeMap[".spq"] = "application/scvp-vp-request"
+ DefaultMimeMap[".spp"] = "application/scvp-vp-response"
+ DefaultMimeMap[".sdp"] = "application/sdp"
+ DefaultMimeMap[".setpay"] = "application/set-payment-initiation"
+ DefaultMimeMap[".setreg"] = "application/set-registration-initiation"
+ DefaultMimeMap[".shf"] = "application/shf+xml"
+ DefaultMimeMap[".smi"] = "application/smil+xml"
+ DefaultMimeMap[".smil"] = "application/smil+xml"
+ DefaultMimeMap[".rq"] = "application/sparql-query"
+ DefaultMimeMap[".srx"] = "application/sparql-results+xml"
+ DefaultMimeMap[".gram"] = "application/srgs"
+ DefaultMimeMap[".grxml"] = "application/srgs+xml"
+ DefaultMimeMap[".ssml"] = "application/ssml+xml"
+ DefaultMimeMap[".plb"] = "application/vnd.3gpp.pic-bw-large"
+ DefaultMimeMap[".psb"] = "application/vnd.3gpp.pic-bw-small"
+ DefaultMimeMap[".pvb"] = "application/vnd.3gpp.pic-bw-var"
+ DefaultMimeMap[".tcap"] = "application/vnd.3gpp2.tcap"
+ DefaultMimeMap[".pwn"] = "application/vnd.3m.post-it-notes"
+ DefaultMimeMap[".aso"] = "application/vnd.accpac.simply.aso"
+ DefaultMimeMap[".imp"] = "application/vnd.accpac.simply.imp"
+ DefaultMimeMap[".acu"] = "application/vnd.acucobol"
+ DefaultMimeMap[".atc"] = "application/vnd.acucorp"
+ DefaultMimeMap[".acutc"] = "application/vnd.acucorp"
+ DefaultMimeMap[".xdp"] = "application/vnd.adobe.xdp+xml"
+ DefaultMimeMap[".xfdf"] = "application/vnd.adobe.xfdf"
+ DefaultMimeMap[".ami"] = "application/vnd.amiga.ami"
+ DefaultMimeMap[".cii"] = "application/vnd.anser-web-certificate-issue-initiation"
+ DefaultMimeMap[".fti"] = "application/vnd.anser-web-funds-transfer-initiation"
+ DefaultMimeMap[".atx"] = "application/vnd.antix.game-component"
+ DefaultMimeMap[".mpkg"] = "application/vnd.apple.installer+xml"
+ DefaultMimeMap[".aep"] = "application/vnd.audiograph"
+ DefaultMimeMap[".mpm"] = "application/vnd.blueice.multipass"
+ DefaultMimeMap[".bmi"] = "application/vnd.bmi"
+ DefaultMimeMap[".rep"] = "application/vnd.businessobjects"
+ DefaultMimeMap[".cdxml"] = "application/vnd.chemdraw+xml"
+ DefaultMimeMap[".mmd"] = "application/vnd.chipnuts.karaoke-mmd"
+ DefaultMimeMap[".cdy"] = "application/vnd.cinderella"
+ DefaultMimeMap[".cla"] = "application/vnd.claymore"
+ DefaultMimeMap[".c4g"] = "application/vnd.clonk.c4group"
+ DefaultMimeMap[".c4d"] = "application/vnd.clonk.c4group"
+ DefaultMimeMap[".c4f"] = "application/vnd.clonk.c4group"
+ DefaultMimeMap[".c4p"] = "application/vnd.clonk.c4group"
+ DefaultMimeMap[".c4u"] = "application/vnd.clonk.c4group"
+ DefaultMimeMap[".csp"] = "application/vnd.commonspace"
+ DefaultMimeMap[".cst"] = "application/vnd.commonspace"
+ DefaultMimeMap[".cdbcmsg"] = "application/vnd.contact.cmsg"
+ DefaultMimeMap[".cmc"] = "application/vnd.cosmocaller"
+ DefaultMimeMap[".clkx"] = "application/vnd.crick.clicker"
+ DefaultMimeMap[".clkk"] = "application/vnd.crick.clicker.keyboard"
+ DefaultMimeMap[".clkp"] = "application/vnd.crick.clicker.palette"
+ DefaultMimeMap[".clkt"] = "application/vnd.crick.clicker.template"
+ DefaultMimeMap[".clkw"] = "application/vnd.crick.clicker.wordbank"
+ DefaultMimeMap[".wbs"] = "application/vnd.criticaltools.wbs+xml"
+ DefaultMimeMap[".pml"] = "application/vnd.ctc-posml"
+ DefaultMimeMap[".ppd"] = "application/vnd.cups-ppd"
+ DefaultMimeMap[".curl"] = "application/vnd.curl"
+ DefaultMimeMap[".rdz"] = "application/vnd.data-vision.rdz"
+ DefaultMimeMap[".fe_launch"] = "application/vnd.denovo.fcselayout-link"
+ DefaultMimeMap[".dna"] = "application/vnd.dna"
+ DefaultMimeMap[".mlp"] = "application/vnd.dolby.mlp"
+ DefaultMimeMap[".dpg"] = "application/vnd.dpgraph"
+ DefaultMimeMap[".dfac"] = "application/vnd.dreamfactory"
+ DefaultMimeMap[".mag"] = "application/vnd.ecowin.chart"
+ DefaultMimeMap[".nml"] = "application/vnd.enliven"
+ DefaultMimeMap[".esf"] = "application/vnd.epson.esf"
+ DefaultMimeMap[".msf"] = "application/vnd.epson.msf"
+ DefaultMimeMap[".qam"] = "application/vnd.epson.quickanime"
+ DefaultMimeMap[".slt"] = "application/vnd.epson.salt"
+ DefaultMimeMap[".ssf"] = "application/vnd.epson.ssf"
+ DefaultMimeMap[".es3"] = "application/vnd.eszigno3+xml"
+ DefaultMimeMap[".et3"] = "application/vnd.eszigno3+xml"
+ DefaultMimeMap[".ez2"] = "application/vnd.ezpix-album"
+ DefaultMimeMap[".ez3"] = "application/vnd.ezpix-package"
+ DefaultMimeMap[".fdf"] = "application/vnd.fdf"
+ DefaultMimeMap[".gph"] = "application/vnd.flographit"
+ DefaultMimeMap[".ftc"] = "application/vnd.fluxtime.clip"
+ DefaultMimeMap[".fm"] = "application/vnd.framemaker"
+ DefaultMimeMap[".frame"] = "application/vnd.framemaker"
+ DefaultMimeMap[".maker"] = "application/vnd.framemaker"
+ DefaultMimeMap[".fnc"] = "application/vnd.frogans.fnc"
+ DefaultMimeMap[".ltf"] = "application/vnd.frogans.ltf"
+ DefaultMimeMap[".fsc"] = "application/vnd.fsc.weblaunch"
+ DefaultMimeMap[".oas"] = "application/vnd.fujitsu.oasys"
+ DefaultMimeMap[".oa2"] = "application/vnd.fujitsu.oasys2"
+ DefaultMimeMap[".oa3"] = "application/vnd.fujitsu.oasys3"
+ DefaultMimeMap[".fg5"] = "application/vnd.fujitsu.oasysgp"
+ DefaultMimeMap[".bh2"] = "application/vnd.fujitsu.oasysprs"
+ DefaultMimeMap[".ddd"] = "application/vnd.fujixerox.ddd"
+ DefaultMimeMap[".xdw"] = "application/vnd.fujixerox.docuworks"
+ DefaultMimeMap[".xbd"] = "application/vnd.fujixerox.docuworks.binder"
+ DefaultMimeMap[".fzs"] = "application/vnd.fuzzysheet"
+ DefaultMimeMap[".txd"] = "application/vnd.genomatix.tuxedo"
+ DefaultMimeMap[".kml"] = "application/vnd.google-earth.kml+xml"
+ DefaultMimeMap[".kmz"] = "application/vnd.google-earth.kmz"
+ DefaultMimeMap[".gqf"] = "application/vnd.grafeq"
+ DefaultMimeMap[".gqs"] = "application/vnd.grafeq"
+ DefaultMimeMap[".gac"] = "application/vnd.groove-account"
+ DefaultMimeMap[".ghf"] = "application/vnd.groove-help"
+ DefaultMimeMap[".gim"] = "application/vnd.groove-identity-message"
+ DefaultMimeMap[".grv"] = "application/vnd.groove-injector"
+ DefaultMimeMap[".gtm"] = "application/vnd.groove-tool-message"
+ DefaultMimeMap[".tpl"] = "application/vnd.groove-tool-template"
+ DefaultMimeMap[".vcg"] = "application/vnd.groove-vcard"
+ DefaultMimeMap[".zmm"] = "application/vnd.handheld-entertainment+xml"
+ DefaultMimeMap[".hbci"] = "application/vnd.hbci"
+ DefaultMimeMap[".les"] = "application/vnd.hhe.lesson-player"
+ DefaultMimeMap[".hpgl"] = "application/vnd.hp-hpgl"
+ DefaultMimeMap[".hpid"] = "application/vnd.hp-hpid"
+ DefaultMimeMap[".hps"] = "application/vnd.hp-hps"
+ DefaultMimeMap[".jlt"] = "application/vnd.hp-jlyt"
+ DefaultMimeMap[".pcl"] = "application/vnd.hp-pcl"
+ DefaultMimeMap[".pclxl"] = "application/vnd.hp-pclxl"
+ DefaultMimeMap[".x3d"] = "application/vnd.hzn-3d-crossword"
+ DefaultMimeMap[".mpy"] = "application/vnd.ibm.minipay"
+ DefaultMimeMap[".afp"] = "application/vnd.ibm.modcap"
+ DefaultMimeMap[".listafp"] = "application/vnd.ibm.modcap"
+ DefaultMimeMap[".list3820"] = "application/vnd.ibm.modcap"
+ DefaultMimeMap[".irm"] = "application/vnd.ibm.rights-management"
+ DefaultMimeMap[".sc"] = "application/vnd.ibm.secure-container"
+ DefaultMimeMap[".igl"] = "application/vnd.igloader"
+ DefaultMimeMap[".ivp"] = "application/vnd.immervision-ivp"
+ DefaultMimeMap[".ivu"] = "application/vnd.immervision-ivu"
+ DefaultMimeMap[".xpw"] = "application/vnd.intercon.formnet"
+ DefaultMimeMap[".xpx"] = "application/vnd.intercon.formnet"
+ DefaultMimeMap[".qbo"] = "application/vnd.intu.qbo"
+ DefaultMimeMap[".qfx"] = "application/vnd.intu.qfx"
+ DefaultMimeMap[".rcprofile"] = "application/vnd.ipunplugged.rcprofile"
+ DefaultMimeMap[".irp"] = "application/vnd.irepository.package+xml"
+ DefaultMimeMap[".xpr"] = "application/vnd.is-xpr"
+ DefaultMimeMap[".jam"] = "application/vnd.jam"
+ DefaultMimeMap[".rms"] = "application/vnd.jcp.javame.midlet-rms"
+ DefaultMimeMap[".jisp"] = "application/vnd.jisp"
+ DefaultMimeMap[".joda"] = "application/vnd.joost.joda-archive"
+ DefaultMimeMap[".ktz"] = "application/vnd.kahootz"
+ DefaultMimeMap[".ktr"] = "application/vnd.kahootz"
+ DefaultMimeMap[".karbon"] = "application/vnd.kde.karbon"
+ DefaultMimeMap[".chrt"] = "application/vnd.kde.kchart"
+ DefaultMimeMap[".kfo"] = "application/vnd.kde.kformula"
+ DefaultMimeMap[".flw"] = "application/vnd.kde.kivio"
+ DefaultMimeMap[".kon"] = "application/vnd.kde.kontour"
+ DefaultMimeMap[".kpr"] = "application/vnd.kde.kpresenter"
+ DefaultMimeMap[".kpt"] = "application/vnd.kde.kpresenter"
+ DefaultMimeMap[".ksp"] = "application/vnd.kde.kspread"
+ DefaultMimeMap[".kwd"] = "application/vnd.kde.kword"
+ DefaultMimeMap[".kwt"] = "application/vnd.kde.kword"
+ DefaultMimeMap[".htke"] = "application/vnd.kenameaapp"
+ DefaultMimeMap[".kia"] = "application/vnd.kidspiration"
+ DefaultMimeMap[".kne"] = "application/vnd.kinar"
+ DefaultMimeMap[".knp"] = "application/vnd.kinar"
+ DefaultMimeMap[".skp"] = "application/vnd.koan"
+ DefaultMimeMap[".skd"] = "application/vnd.koan"
+ DefaultMimeMap[".skt"] = "application/vnd.koan"
+ DefaultMimeMap[".skm"] = "application/vnd.koan"
+ DefaultMimeMap[".lbd"] = "application/vnd.llamagraphics.life-balance.desktop"
+ DefaultMimeMap[".lbe"] = "application/vnd.llamagraphics.life-balance.exchange+xml"
+ DefaultMimeMap[".123"] = "application/vnd.lotus-1-2-3"
+ DefaultMimeMap[".apr"] = "application/vnd.lotus-approach"
+ DefaultMimeMap[".pre"] = "application/vnd.lotus-freelance"
+ DefaultMimeMap[".nsf"] = "application/vnd.lotus-notes"
+ DefaultMimeMap[".org"] = "application/vnd.lotus-organizer"
+ DefaultMimeMap[".scm"] = "application/vnd.lotus-screencam"
+ DefaultMimeMap[".lwp"] = "application/vnd.lotus-wordpro"
+ DefaultMimeMap[".portpkg"] = "application/vnd.macports.portpkg"
+ DefaultMimeMap[".mcd"] = "application/vnd.mcd"
+ DefaultMimeMap[".mc1"] = "application/vnd.medcalcdata"
+ DefaultMimeMap[".cdkey"] = "application/vnd.mediastation.cdkey"
+ DefaultMimeMap[".mwf"] = "application/vnd.mfer"
+ DefaultMimeMap[".mfm"] = "application/vnd.mfmp"
+ DefaultMimeMap[".flo"] = "application/vnd.micrografx.flo"
+ DefaultMimeMap[".igx"] = "application/vnd.micrografx.igx"
+ DefaultMimeMap[".mif"] = "application/vnd.mif"
+ DefaultMimeMap[".daf"] = "application/vnd.mobius.daf"
+ DefaultMimeMap[".dis"] = "application/vnd.mobius.dis"
+ DefaultMimeMap[".mbk"] = "application/vnd.mobius.mbk"
+ DefaultMimeMap[".mqy"] = "application/vnd.mobius.mqy"
+ DefaultMimeMap[".msl"] = "application/vnd.mobius.msl"
+ DefaultMimeMap[".plc"] = "application/vnd.mobius.plc"
+ DefaultMimeMap[".txf"] = "application/vnd.mobius.txf"
+ DefaultMimeMap[".mpn"] = "application/vnd.mophun.application"
+ DefaultMimeMap[".mpc"] = "application/vnd.mophun.certificate"
+ DefaultMimeMap[".cil"] = "application/vnd.ms-artgalry"
+ DefaultMimeMap[".asf"] = "application/vnd.ms-asf"
+ DefaultMimeMap[".cab"] = "application/vnd.ms-cab-compressed"
+ DefaultMimeMap[".xls"] = "application/vnd.ms-excel"
+ DefaultMimeMap[".xlm"] = "application/vnd.ms-excel"
+ DefaultMimeMap[".xla"] = "application/vnd.ms-excel"
+ DefaultMimeMap[".xlc"] = "application/vnd.ms-excel"
+ DefaultMimeMap[".xlt"] = "application/vnd.ms-excel"
+ DefaultMimeMap[".xlw"] = "application/vnd.ms-excel"
+ DefaultMimeMap[".eot"] = "application/vnd.ms-fontobject"
+ DefaultMimeMap[".chm"] = "application/vnd.ms-htmlhelp"
+ DefaultMimeMap[".ims"] = "application/vnd.ms-ims"
+ DefaultMimeMap[".lrm"] = "application/vnd.ms-lrm"
+ DefaultMimeMap[".ppt"] = "application/vnd.ms-powerpoint"
+ DefaultMimeMap[".pps"] = "application/vnd.ms-powerpoint"
+ DefaultMimeMap[".pot"] = "application/vnd.ms-powerpoint"
+ DefaultMimeMap[".mpp"] = "application/vnd.ms-project"
+ DefaultMimeMap[".mpt"] = "application/vnd.ms-project"
+ DefaultMimeMap[".wps"] = "application/vnd.ms-works"
+ DefaultMimeMap[".wks"] = "application/vnd.ms-works"
+ DefaultMimeMap[".wcm"] = "application/vnd.ms-works"
+ DefaultMimeMap[".wdb"] = "application/vnd.ms-works"
+ DefaultMimeMap[".wpl"] = "application/vnd.ms-wpl"
+ DefaultMimeMap[".xps"] = "application/vnd.ms-xpsdocument"
+ DefaultMimeMap[".mseq"] = "application/vnd.mseq"
+ DefaultMimeMap[".mus"] = "application/vnd.musician"
+ DefaultMimeMap[".msty"] = "application/vnd.muvee.style"
+ DefaultMimeMap[".nlu"] = "application/vnd.neurolanguage.nlu"
+ DefaultMimeMap[".nnd"] = "application/vnd.noblenet-directory"
+ DefaultMimeMap[".nns"] = "application/vnd.noblenet-sealer"
+ DefaultMimeMap[".nnw"] = "application/vnd.noblenet-web"
+ DefaultMimeMap[".ngdat"] = "application/vnd.nokia.n-gage.data"
+ DefaultMimeMap[".n-gage"] = "application/vnd.nokia.n-gage.symbian.install"
+ DefaultMimeMap[".rpst"] = "application/vnd.nokia.radio-preset"
+ DefaultMimeMap[".rpss"] = "application/vnd.nokia.radio-presets"
+ DefaultMimeMap[".edm"] = "application/vnd.novadigm.edm"
+ DefaultMimeMap[".edx"] = "application/vnd.novadigm.edx"
+ DefaultMimeMap[".ext"] = "application/vnd.novadigm.ext"
+ DefaultMimeMap[".odc"] = "application/vnd.oasis.opendocument.chart"
+ DefaultMimeMap[".otc"] = "application/vnd.oasis.opendocument.chart-template"
+ DefaultMimeMap[".odf"] = "application/vnd.oasis.opendocument.formula"
+ DefaultMimeMap[".otf"] = "application/vnd.oasis.opendocument.formula-template"
+ DefaultMimeMap[".odg"] = "application/vnd.oasis.opendocument.graphics"
+ DefaultMimeMap[".otg"] = "application/vnd.oasis.opendocument.graphics-template"
+ DefaultMimeMap[".odi"] = "application/vnd.oasis.opendocument.image"
+ DefaultMimeMap[".oti"] = "application/vnd.oasis.opendocument.image-template"
+ DefaultMimeMap[".odp"] = "application/vnd.oasis.opendocument.presentation"
+ DefaultMimeMap[".otp"] = "application/vnd.oasis.opendocument.presentation-template"
+ DefaultMimeMap[".ods"] = "application/vnd.oasis.opendocument.spreadsheet"
+ DefaultMimeMap[".ots"] = "application/vnd.oasis.opendocument.spreadsheet-template"
+ DefaultMimeMap[".odt"] = "application/vnd.oasis.opendocument.text"
+ DefaultMimeMap[".otm"] = "application/vnd.oasis.opendocument.text-master"
+ DefaultMimeMap[".ott"] = "application/vnd.oasis.opendocument.text-template"
+ DefaultMimeMap[".oth"] = "application/vnd.oasis.opendocument.text-web"
+ DefaultMimeMap[".xo"] = "application/vnd.olpc-sugar"
+ DefaultMimeMap[".dd2"] = "application/vnd.oma.dd2+xml"
+ DefaultMimeMap[".oxt"] = "application/vnd.openofficeorg.extension"
+ DefaultMimeMap[".dp"] = "application/vnd.osgi.dp"
+ DefaultMimeMap[".prc"] = "application/vnd.palm"
+ DefaultMimeMap[".pdb"] = "application/vnd.palm"
+ DefaultMimeMap[".pqa"] = "application/vnd.palm"
+ DefaultMimeMap[".oprc"] = "application/vnd.palm"
+ DefaultMimeMap[".str"] = "application/vnd.pg.format"
+ DefaultMimeMap[".ei6"] = "application/vnd.pg.osasli"
+ DefaultMimeMap[".efif"] = "application/vnd.picsel"
+ DefaultMimeMap[".plf"] = "application/vnd.pocketlearn"
+ DefaultMimeMap[".pbd"] = "application/vnd.powerbuilder6"
+ DefaultMimeMap[".box"] = "application/vnd.previewsystems.box"
+ DefaultMimeMap[".mgz"] = "application/vnd.proteus.magazine"
+ DefaultMimeMap[".qps"] = "application/vnd.publishare-delta-tree"
+ DefaultMimeMap[".ptid"] = "application/vnd.pvi.ptid1"
+ DefaultMimeMap[".qxd"] = "application/vnd.quark.quarkxpress"
+ DefaultMimeMap[".qxt"] = "application/vnd.quark.quarkxpress"
+ DefaultMimeMap[".qwd"] = "application/vnd.quark.quarkxpress"
+ DefaultMimeMap[".qwt"] = "application/vnd.quark.quarkxpress"
+ DefaultMimeMap[".qxl"] = "application/vnd.quark.quarkxpress"
+ DefaultMimeMap[".qxb"] = "application/vnd.quark.quarkxpress"
+ DefaultMimeMap[".mxl"] = "application/vnd.recordare.musicxml"
+ DefaultMimeMap[".rm"] = "application/vnd.rn-realmedia"
+ DefaultMimeMap[".see"] = "application/vnd.seemail"
+ DefaultMimeMap[".sema"] = "application/vnd.sema"
+ DefaultMimeMap[".semd"] = "application/vnd.semd"
+ DefaultMimeMap[".semf"] = "application/vnd.semf"
+ DefaultMimeMap[".ifm"] = "application/vnd.shana.informed.formdata"
+ DefaultMimeMap[".itp"] = "application/vnd.shana.informed.formtemplate"
+ DefaultMimeMap[".iif"] = "application/vnd.shana.informed.interchange"
+ DefaultMimeMap[".ipk"] = "application/vnd.shana.informed.package"
+ DefaultMimeMap[".twd"] = "application/vnd.simtech-mindmapper"
+ DefaultMimeMap[".twds"] = "application/vnd.simtech-mindmapper"
+ DefaultMimeMap[".mmf"] = "application/vnd.smaf"
+ DefaultMimeMap[".sdkm"] = "application/vnd.solent.sdkm+xml"
+ DefaultMimeMap[".sdkd"] = "application/vnd.solent.sdkm+xml"
+ DefaultMimeMap[".dxp"] = "application/vnd.spotfire.dxp"
+ DefaultMimeMap[".sfs"] = "application/vnd.spotfire.sfs"
+ DefaultMimeMap[".sus"] = "application/vnd.sus-calendar"
+ DefaultMimeMap[".susp"] = "application/vnd.sus-calendar"
+ DefaultMimeMap[".svd"] = "application/vnd.svd"
+ DefaultMimeMap[".xsm"] = "application/vnd.syncml+xml"
+ DefaultMimeMap[".bdm"] = "application/vnd.syncml.dm+wbxml"
+ DefaultMimeMap[".xdm"] = "application/vnd.syncml.dm+xml"
+ DefaultMimeMap[".tao"] = "application/vnd.tao.intent-module-archive"
+ DefaultMimeMap[".tmo"] = "application/vnd.tmobile-livetv"
+ DefaultMimeMap[".tpt"] = "application/vnd.trid.tpt"
+ DefaultMimeMap[".mxs"] = "application/vnd.triscape.mxs"
+ DefaultMimeMap[".tra"] = "application/vnd.trueapp"
+ DefaultMimeMap[".ufd"] = "application/vnd.ufdl"
+ DefaultMimeMap[".ufdl"] = "application/vnd.ufdl"
+ DefaultMimeMap[".utz"] = "application/vnd.uiq.theme"
+ DefaultMimeMap[".umj"] = "application/vnd.umajin"
+ DefaultMimeMap[".unityweb"] = "application/vnd.unity"
+ DefaultMimeMap[".uoml"] = "application/vnd.uoml+xml"
+ DefaultMimeMap[".vcx"] = "application/vnd.vcx"
+ DefaultMimeMap[".vsd"] = "application/vnd.visio"
+ DefaultMimeMap[".vst"] = "application/vnd.visio"
+ DefaultMimeMap[".vss"] = "application/vnd.visio"
+ DefaultMimeMap[".vsw"] = "application/vnd.visio"
+ DefaultMimeMap[".vis"] = "application/vnd.visionary"
+ DefaultMimeMap[".vsf"] = "application/vnd.vsf"
+ DefaultMimeMap[".wbxml"] = "application/vnd.wap.wbxml"
+ DefaultMimeMap[".wmlc"] = "application/vnd.wap.wmlc"
+ DefaultMimeMap[".wmlsc"] = "application/vnd.wap.wmlscriptc"
+ DefaultMimeMap[".wtb"] = "application/vnd.webturbo"
+ DefaultMimeMap[".wpd"] = "application/vnd.wordperfect"
+ DefaultMimeMap[".wqd"] = "application/vnd.wqd"
+ DefaultMimeMap[".stf"] = "application/vnd.wt.stf"
+ DefaultMimeMap[".xar"] = "application/vnd.xara"
+ DefaultMimeMap[".xfdl"] = "application/vnd.xfdl"
+ DefaultMimeMap[".hvd"] = "application/vnd.yamaha.hv-dic"
+ DefaultMimeMap[".hvs"] = "application/vnd.yamaha.hv-script"
+ DefaultMimeMap[".hvp"] = "application/vnd.yamaha.hv-voice"
+ DefaultMimeMap[".saf"] = "application/vnd.yamaha.smaf-audio"
+ DefaultMimeMap[".spf"] = "application/vnd.yamaha.smaf-phrase"
+ DefaultMimeMap[".cmp"] = "application/vnd.yellowriver-custom-menu"
+ DefaultMimeMap[".zaz"] = "application/vnd.zzazz.deck+xml"
+ DefaultMimeMap[".vxml"] = "application/voicexml+xml"
+ DefaultMimeMap[".hlp"] = "application/winhlp"
+ DefaultMimeMap[".wsdl"] = "application/wsdl+xml"
+ DefaultMimeMap[".wspolicy"] = "application/wspolicy+xml"
+ DefaultMimeMap[".ace"] = "application/x-ace-compressed"
+ DefaultMimeMap[".bcpio"] = "application/x-bcpio"
+ DefaultMimeMap[".torrent"] = "application/x-bittorrent"
+ DefaultMimeMap[".bz"] = "application/x-bzip"
+ DefaultMimeMap[".bz2"] = "application/x-bzip2"
+ DefaultMimeMap[".boz"] = "application/x-bzip2"
+ DefaultMimeMap[".vcd"] = "application/x-cdlink"
+ DefaultMimeMap[".chat"] = "application/x-chat"
+ DefaultMimeMap[".pgn"] = "application/x-chess-pgn"
+ DefaultMimeMap[".cpio"] = "application/x-cpio"
+ DefaultMimeMap[".csh"] = "application/x-csh"
+ DefaultMimeMap[".dcr"] = "application/x-director"
+ DefaultMimeMap[".dir"] = "application/x-director"
+ DefaultMimeMap[".dxr"] = "application/x-director"
+ DefaultMimeMap[".fgd"] = "application/x-director"
+ DefaultMimeMap[".dvi"] = "application/x-dvi"
+ DefaultMimeMap[".spl"] = "application/x-futuresplash"
+ DefaultMimeMap[".gtar"] = "application/x-gtar"
+ DefaultMimeMap[".hdf"] = "application/x-hdf"
+ DefaultMimeMap[".latex"] = "application/x-latex"
+ DefaultMimeMap[".wmd"] = "application/x-ms-wmd"
+ DefaultMimeMap[".wmz"] = "application/x-ms-wmz"
+ DefaultMimeMap[".mdb"] = "application/x-msaccess"
+ DefaultMimeMap[".obd"] = "application/x-msbinder"
+ DefaultMimeMap[".crd"] = "application/x-mscardfile"
+ DefaultMimeMap[".clp"] = "application/x-msclip"
+ DefaultMimeMap[".exe"] = "application/x-msdownload"
+ DefaultMimeMap[".dll"] = "application/x-msdownload"
+ DefaultMimeMap[".com"] = "application/x-msdownload"
+ DefaultMimeMap[".bat"] = "application/x-msdownload"
+ DefaultMimeMap[".msi"] = "application/x-msdownload"
+ DefaultMimeMap[".mvb"] = "application/x-msmediaview"
+ DefaultMimeMap[".m13"] = "application/x-msmediaview"
+ DefaultMimeMap[".m14"] = "application/x-msmediaview"
+ DefaultMimeMap[".wmf"] = "application/x-msmetafile"
+ DefaultMimeMap[".mny"] = "application/x-msmoney"
+ DefaultMimeMap[".pub"] = "application/x-mspublisher"
+ DefaultMimeMap[".scd"] = "application/x-msschedule"
+ DefaultMimeMap[".trm"] = "application/x-msterminal"
+ DefaultMimeMap[".wri"] = "application/x-mswrite"
+ DefaultMimeMap[".nc"] = "application/x-netcdf"
+ DefaultMimeMap[".cdf"] = "application/x-netcdf"
+ DefaultMimeMap[".p12"] = "application/x-pkcs12"
+ DefaultMimeMap[".pfx"] = "application/x-pkcs12"
+ DefaultMimeMap[".p7b"] = "application/x-pkcs7-certificates"
+ DefaultMimeMap[".spc"] = "application/x-pkcs7-certificates"
+ DefaultMimeMap[".p7r"] = "application/x-pkcs7-certreqresp"
+ DefaultMimeMap[".rar"] = "application/x-rar-compressed"
+ DefaultMimeMap[".sh"] = "application/x-sh"
+ DefaultMimeMap[".shar"] = "application/x-shar"
+ DefaultMimeMap[".swf"] = "application/x-shockwave-flash"
+ DefaultMimeMap[".sit"] = "application/x-stuffit"
+ DefaultMimeMap[".sitx"] = "application/x-stuffitx"
+ DefaultMimeMap[".sv4cpio"] = "application/x-sv4cpio"
+ DefaultMimeMap[".sv4crc"] = "application/x-sv4crc"
+ DefaultMimeMap[".tar"] = "application/x-tar"
+ DefaultMimeMap[".tcl"] = "application/x-tcl"
+ DefaultMimeMap[".tex"] = "application/x-tex"
+ DefaultMimeMap[".texinfo"] = "application/x-texinfo"
+ DefaultMimeMap[".texi"] = "application/x-texinfo"
+ DefaultMimeMap[".ustar"] = "application/x-ustar"
+ DefaultMimeMap[".src"] = "application/x-wais-source"
+ DefaultMimeMap[".der"] = "application/x-x509-ca-cert"
+ DefaultMimeMap[".crt"] = "application/x-x509-ca-cert"
+ DefaultMimeMap[".xenc"] = "application/xenc+xml"
+ DefaultMimeMap[".xhtml"] = "application/xhtml+xml"
+ DefaultMimeMap[".xht"] = "application/xhtml+xml"
+ DefaultMimeMap[".xml"] = "text/xml"
+ DefaultMimeMap[".xsl"] = "application/xml"
+ DefaultMimeMap[".dtd"] = "application/xml-dtd"
+ DefaultMimeMap[".xop"] = "application/xop+xml"
+ DefaultMimeMap[".xslt"] = "application/xslt+xml"
+ DefaultMimeMap[".xspf"] = "application/xspf+xml"
+ DefaultMimeMap[".mxml"] = "application/xv+xml"
+ DefaultMimeMap[".xhvml"] = "application/xv+xml"
+ DefaultMimeMap[".xvml"] = "application/xv+xml"
+ DefaultMimeMap[".xvm"] = "application/xv+xml"
+ DefaultMimeMap[".zip"] = "application/zip"
+ DefaultMimeMap[".au"] = "audio/basic"
+ DefaultMimeMap[".snd"] = "audio/basic"
+ DefaultMimeMap[".mid"] = "audio/midi"
+ DefaultMimeMap[".midi"] = "audio/midi"
+ DefaultMimeMap[".kar"] = "audio/midi"
+ DefaultMimeMap[".rmi"] = "audio/midi"
+ DefaultMimeMap[".mp4a"] = "audio/mp4"
+ DefaultMimeMap[".mpga"] = "audio/mpeg"
+ DefaultMimeMap[".mp2"] = "audio/mpeg"
+ DefaultMimeMap[".mp2a"] = "audio/mpeg"
+ DefaultMimeMap[".mp3"] = "audio/mpeg"
+ DefaultMimeMap[".m2a"] = "audio/mpeg"
+ DefaultMimeMap[".m3a"] = "audio/mpeg"
+ DefaultMimeMap[".eol"] = "audio/vnd.digital-winds"
+ DefaultMimeMap[".lvp"] = "audio/vnd.lucent.voice"
+ DefaultMimeMap[".ecelp4800"] = "audio/vnd.nuera.ecelp4800"
+ DefaultMimeMap[".ecelp7470"] = "audio/vnd.nuera.ecelp7470"
+ DefaultMimeMap[".ecelp9600"] = "audio/vnd.nuera.ecelp9600"
+ DefaultMimeMap[".wav"] = "audio/wav"
+ DefaultMimeMap[".aif"] = "audio/x-aiff"
+ DefaultMimeMap[".aiff"] = "audio/x-aiff"
+ DefaultMimeMap[".aifc"] = "audio/x-aiff"
+ DefaultMimeMap[".m3u"] = "audio/x-mpegurl"
+ DefaultMimeMap[".wax"] = "audio/x-ms-wax"
+ DefaultMimeMap[".wma"] = "audio/x-ms-wma"
+ DefaultMimeMap[".ram"] = "audio/x-pn-realaudio"
+ DefaultMimeMap[".ra"] = "audio/x-pn-realaudio"
+ DefaultMimeMap[".rmp"] = "audio/x-pn-realaudio-plugin"
+ DefaultMimeMap[".cdx"] = "chemical/x-cdx"
+ DefaultMimeMap[".cif"] = "chemical/x-cif"
+ DefaultMimeMap[".cmdf"] = "chemical/x-cmdf"
+ DefaultMimeMap[".cml"] = "chemical/x-cml"
+ DefaultMimeMap[".csml"] = "chemical/x-csml"
+ DefaultMimeMap[".xyz"] = "chemical/x-xyz"
+ DefaultMimeMap[".bmp"] = "image/bmp"
+ DefaultMimeMap[".cgm"] = "image/cgm"
+ DefaultMimeMap[".g3"] = "image/g3fax"
+ DefaultMimeMap[".gif"] = "image/gif"
+ DefaultMimeMap[".ief"] = "image/ief"
+ DefaultMimeMap[".jpeg"] = "image/jpeg"
+ DefaultMimeMap[".jpg"] = "image/jpeg"
+ DefaultMimeMap[".jpe"] = "image/jpeg"
+ DefaultMimeMap[".png"] = "image/png"
+ DefaultMimeMap[".webp"] = "image/webp"
+ DefaultMimeMap[".heic"] = "image/heic"
+ DefaultMimeMap[".btif"] = "image/prs.btif"
+ DefaultMimeMap[".svg"] = "image/svg+xml"
+ DefaultMimeMap[".svgz"] = "image/svg+xml"
+ DefaultMimeMap[".tiff"] = "image/tiff"
+ DefaultMimeMap[".tif"] = "image/tiff"
+ DefaultMimeMap[".psd"] = "image/vnd.adobe.photoshop"
+ DefaultMimeMap[".djvu"] = "image/vnd.djvu"
+ DefaultMimeMap[".djv"] = "image/vnd.djvu"
+ DefaultMimeMap[".dwg"] = "image/vnd.dwg"
+ DefaultMimeMap[".dxf"] = "image/vnd.dxf"
+ DefaultMimeMap[".fbs"] = "image/vnd.fastbidsheet"
+ DefaultMimeMap[".fpx"] = "image/vnd.fpx"
+ DefaultMimeMap[".fst"] = "image/vnd.fst"
+ DefaultMimeMap[".mmr"] = "image/vnd.fujixerox.edmics-mmr"
+ DefaultMimeMap[".rlc"] = "image/vnd.fujixerox.edmics-rlc"
+ DefaultMimeMap[".mdi"] = "image/vnd.ms-modi"
+ DefaultMimeMap[".npx"] = "image/vnd.net-fpx"
+ DefaultMimeMap[".wbmp"] = "image/vnd.wap.wbmp"
+ DefaultMimeMap[".xif"] = "image/vnd.xiff"
+ DefaultMimeMap[".ras"] = "image/x-cmu-raster"
+ DefaultMimeMap[".cmx"] = "image/x-cmx"
+ DefaultMimeMap[".ico"] = "image/x-icon"
+ DefaultMimeMap[".pcx"] = "image/x-pcx"
+ DefaultMimeMap[".pic"] = "image/x-pict"
+ DefaultMimeMap[".pct"] = "image/x-pict"
+ DefaultMimeMap[".pnm"] = "image/x-portable-anymap"
+ DefaultMimeMap[".pbm"] = "image/x-portable-bitmap"
+ DefaultMimeMap[".pgm"] = "image/x-portable-graymap"
+ DefaultMimeMap[".ppm"] = "image/x-portable-pixmap"
+ DefaultMimeMap[".rgb"] = "image/x-rgb"
+ DefaultMimeMap[".xbm"] = "image/x-xbitmap"
+ DefaultMimeMap[".xpm"] = "image/x-xpixmap"
+ DefaultMimeMap[".xwd"] = "image/x-xwindowdump"
+ DefaultMimeMap[".eml"] = "message/rfc822"
+ DefaultMimeMap[".mime"] = "message/rfc822"
+ DefaultMimeMap[".mht"] = "message/rfc822"
+ DefaultMimeMap[".mhtml"] = "message/rfc822"
+ DefaultMimeMap[".igs"] = "model/iges"
+ DefaultMimeMap[".iges"] = "model/iges"
+ DefaultMimeMap[".msh"] = "model/mesh"
+ DefaultMimeMap[".mesh"] = "model/mesh"
+ DefaultMimeMap[".silo"] = "model/mesh"
+ DefaultMimeMap[".dwf"] = "model/vnd.dwf"
+ DefaultMimeMap[".gdl"] = "model/vnd.gdl"
+ DefaultMimeMap[".gtw"] = "model/vnd.gtw"
+ DefaultMimeMap[".mts"] = "model/vnd.mts"
+ DefaultMimeMap[".vtu"] = "model/vnd.vtu"
+ DefaultMimeMap[".wrl"] = "model/vrml"
+ DefaultMimeMap[".vrml"] = "model/vrml"
+ DefaultMimeMap[".ics"] = "text/calendar"
+ DefaultMimeMap[".ifb"] = "text/calendar"
+ DefaultMimeMap[".css"] = "text/css"
+ DefaultMimeMap[".csv"] = "text/csv"
+ DefaultMimeMap[".html"] = "text/html"
+ DefaultMimeMap[".htm"] = "text/html"
+ DefaultMimeMap[".txt"] = "text/plain"
+ DefaultMimeMap[".text"] = "text/plain"
+ DefaultMimeMap[".conf"] = "text/plain"
+ DefaultMimeMap[".def"] = "text/plain"
+ DefaultMimeMap[".list"] = "text/plain"
+ DefaultMimeMap[".log"] = "text/plain"
+ DefaultMimeMap[".in"] = "text/plain"
+ DefaultMimeMap[".dsc"] = "text/prs.lines.tag"
+ DefaultMimeMap[".rtx"] = "text/richtext"
+ DefaultMimeMap[".sgml"] = "text/sgml"
+ DefaultMimeMap[".sgm"] = "text/sgml"
+ DefaultMimeMap[".tsv"] = "text/tab-separated-values"
+ DefaultMimeMap[".t"] = "text/troff"
+ DefaultMimeMap[".tr"] = "text/troff"
+ DefaultMimeMap[".roff"] = "text/troff"
+ DefaultMimeMap[".man"] = "text/troff"
+ DefaultMimeMap[".me"] = "text/troff"
+ DefaultMimeMap[".ms"] = "text/troff"
+ DefaultMimeMap[".uri"] = "text/uri-list"
+ DefaultMimeMap[".uris"] = "text/uri-list"
+ DefaultMimeMap[".urls"] = "text/uri-list"
+ DefaultMimeMap[".fly"] = "text/vnd.fly"
+ DefaultMimeMap[".flx"] = "text/vnd.fmi.flexstor"
+ DefaultMimeMap[".3dml"] = "text/vnd.in3d.3dml"
+ DefaultMimeMap[".spot"] = "text/vnd.in3d.spot"
+ DefaultMimeMap[".jad"] = "text/vnd.sun.j2me.app-descriptor"
+ DefaultMimeMap[".wml"] = "text/vnd.wap.wml"
+ DefaultMimeMap[".wmls"] = "text/vnd.wap.wmlscript"
+ DefaultMimeMap[".s"] = "text/x-asm"
+ DefaultMimeMap[".asm"] = "text/x-asm"
+ DefaultMimeMap[".c"] = "text/x-c"
+ DefaultMimeMap[".cc"] = "text/x-c"
+ DefaultMimeMap[".cxx"] = "text/x-c"
+ DefaultMimeMap[".cpp"] = "text/x-c"
+ DefaultMimeMap[".h"] = "text/x-c"
+ DefaultMimeMap[".hh"] = "text/x-c"
+ DefaultMimeMap[".dic"] = "text/x-c"
+ DefaultMimeMap[".f"] = "text/x-fortran"
+ DefaultMimeMap[".for"] = "text/x-fortran"
+ DefaultMimeMap[".f77"] = "text/x-fortran"
+ DefaultMimeMap[".f90"] = "text/x-fortran"
+ DefaultMimeMap[".p"] = "text/x-pascal"
+ DefaultMimeMap[".pas"] = "text/x-pascal"
+ DefaultMimeMap[".java"] = "text/x-java-source"
+ DefaultMimeMap[".etx"] = "text/x-setext"
+ DefaultMimeMap[".uu"] = "text/x-uuencode"
+ DefaultMimeMap[".vcs"] = "text/x-vcalendar"
+ DefaultMimeMap[".vcf"] = "text/x-vcard"
+ DefaultMimeMap[".3gp"] = "video/3gpp"
+ DefaultMimeMap[".3g2"] = "video/3gpp2"
+ DefaultMimeMap[".h261"] = "video/h261"
+ DefaultMimeMap[".h263"] = "video/h263"
+ DefaultMimeMap[".h264"] = "video/h264"
+ DefaultMimeMap[".jpgv"] = "video/jpeg"
+ DefaultMimeMap[".jpm"] = "video/jpm"
+ DefaultMimeMap[".jpgm"] = "video/jpm"
+ DefaultMimeMap[".mj2"] = "video/mj2"
+ DefaultMimeMap[".mjp2"] = "video/mj2"
+ DefaultMimeMap[".mp4"] = "video/mp4"
+ DefaultMimeMap[".mp4v"] = "video/mp4"
+ DefaultMimeMap[".mpg4"] = "video/mp4"
+ DefaultMimeMap[".mpeg"] = "video/mpeg"
+ DefaultMimeMap[".mpg"] = "video/mpeg"
+ DefaultMimeMap[".mpe"] = "video/mpeg"
+ DefaultMimeMap[".m1v"] = "video/mpeg"
+ DefaultMimeMap[".m2v"] = "video/mpeg"
+ DefaultMimeMap[".qt"] = "video/quicktime"
+ DefaultMimeMap[".mov"] = "video/quicktime"
+ DefaultMimeMap[".fvt"] = "video/vnd.fvt"
+ DefaultMimeMap[".mxu"] = "video/vnd.mpegurl"
+ DefaultMimeMap[".m4u"] = "video/vnd.mpegurl"
+ DefaultMimeMap[".viv"] = "video/vnd.vivo"
+ DefaultMimeMap[".fli"] = "video/x-fli"
+ DefaultMimeMap[".wm"] = "video/x-ms-wm"
+ DefaultMimeMap[".wmv"] = "video/x-ms-wmv"
+ DefaultMimeMap[".wmx"] = "video/x-ms-wmx"
+ DefaultMimeMap[".wvx"] = "video/x-ms-wvx"
+ DefaultMimeMap[".avi"] = "video/x-msvideo"
+ DefaultMimeMap[".movie"] = "video/x-sgi-movie"
+ DefaultMimeMap[".ice"] = "x-conference/x-cooltalk"
+ DefaultMimeMap[".ipa"] = "application/vnd.iphone"
+ DefaultMimeMap[".apk"] = "application/vnd.android.package-archive"
+}
+
+func GetMimeMap() map[string]string {
+ once.Do(LoadMimeMap)
+ return DefaultMimeMap
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/util/string.go b/vendor/github.com/baidubce/bce-sdk-go/util/string.go
new file mode 100644
index 00000000000..3bb0b10de24
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/util/string.go
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// string.go - define the string util function
+
+// Package util defines the common utilities including string and time.
+package util
+
+import (
+ "bytes"
+ "crypto/hmac"
+ "crypto/md5"
+ "crypto/rand"
+ "crypto/sha256"
+ "encoding/base64"
+ "encoding/hex"
+ "fmt"
+ "io"
+)
+
+func HmacSha256Hex(key, str_to_sign string) string {
+ hasher := hmac.New(sha256.New, []byte(key))
+ hasher.Write([]byte(str_to_sign))
+ return hex.EncodeToString(hasher.Sum(nil))
+}
+
+func CalculateContentMD5(data io.Reader, size int64) (string, error) {
+ hasher := md5.New()
+ n, err := io.CopyN(hasher, data, size)
+ if err != nil {
+ return "", fmt.Errorf("calculate content-md5 occurs error: %v", err)
+ }
+ if n != size {
+ return "", fmt.Errorf("calculate content-md5 writing size %d != size %d", n, size)
+ }
+ return base64.StdEncoding.EncodeToString(hasher.Sum(nil)), nil
+}
+
+func UriEncode(uri string, encodeSlash bool) string {
+ var byte_buf bytes.Buffer
+ for _, b := range []byte(uri) {
+ if (b >= 'A' && b <= 'Z') || (b >= 'a' && b <= 'z') || (b >= '0' && b <= '9') ||
+ b == '-' || b == '_' || b == '.' || b == '~' || (b == '/' && !encodeSlash) {
+ byte_buf.WriteByte(b)
+ } else {
+ byte_buf.WriteString(fmt.Sprintf("%%%02X", b))
+ }
+ }
+ return byte_buf.String()
+}
+
+func NewUUID() string {
+ var buf [16]byte
+ for {
+ if _, err := rand.Read(buf[:]); err == nil {
+ break
+ }
+ }
+ buf[6] = (buf[6] & 0x0f) | (4 << 4)
+ buf[8] = (buf[8] & 0xbf) | 0x80
+
+ res := make([]byte, 36)
+ hex.Encode(res[0:8], buf[0:4])
+ res[8] = '-'
+ hex.Encode(res[9:13], buf[4:6])
+ res[13] = '-'
+ hex.Encode(res[14:18], buf[6:8])
+ res[18] = '-'
+ hex.Encode(res[19:23], buf[8:10])
+ res[23] = '-'
+ hex.Encode(res[24:], buf[10:])
+ return string(res)
+}
+
+func NewRequestId() string {
+ return NewUUID()
+}
diff --git a/vendor/github.com/baidubce/bce-sdk-go/util/time.go b/vendor/github.com/baidubce/bce-sdk-go/util/time.go
new file mode 100644
index 00000000000..e9f9e3d1bab
--- /dev/null
+++ b/vendor/github.com/baidubce/bce-sdk-go/util/time.go
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017 Baidu, Inc.
+ *
+ * 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.
+ */
+
+// time.go - define the time utility functions for BCE
+
+package util
+
+import "time"
+
+const (
+ RFC822Format = "Mon, 02 Jan 2006 15:04:05 MST"
+ ISO8601Format = "2006-01-02T15:04:05Z"
+)
+
+func NowUTCSeconds() int64 { return time.Now().UTC().Unix() }
+
+func NowUTCNanoSeconds() int64 { return time.Now().UTC().UnixNano() }
+
+func FormatRFC822Date(timestamp_second int64) string {
+ tm := time.Unix(timestamp_second, 0).UTC()
+ return tm.Format(RFC822Format)
+}
+
+func ParseRFC822Date(time_string string) (time.Time, error) {
+ return time.Parse(RFC822Format, time_string)
+}
+
+func FormatISO8601Date(timestamp_second int64) string {
+ tm := time.Unix(timestamp_second, 0).UTC()
+ return tm.Format(ISO8601Format)
+}
+
+func ParseISO8601Date(time_string string) (time.Time, error) {
+ return time.Parse(ISO8601Format, time_string)
+}
diff --git a/vendor/github.com/clbanning/mxj/LICENSE b/vendor/github.com/clbanning/mxj/LICENSE
new file mode 100644
index 00000000000..f27bccdf06e
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/LICENSE
@@ -0,0 +1,55 @@
+Copyright (c) 2012-2016 Charles Banning . 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.
+
+===============================================================================
+
+Go Language Copyright & License -
+
+Copyright 2009 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.
+
+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.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+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
+OWNER 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.
diff --git a/vendor/github.com/clbanning/mxj/anyxml.go b/vendor/github.com/clbanning/mxj/anyxml.go
new file mode 100644
index 00000000000..ec2f3dfddac
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/anyxml.go
@@ -0,0 +1,189 @@
+package mxj
+
+import (
+ "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:
+
+ somevalue
+ string
+ 3.14159265
+ true
+
+*/
+// 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(string)
+ p := new(pretty)
+
+ var ss string
+ var b []byte
+ switch v.(type) {
+ case []interface{}:
+ ss = "<" + rt + ">"
+ 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 = mapToXmlIndent(false, s, tag, val, p)
+ }
+ } else {
+ err = mapToXmlIndent(false, s, et, vv, p)
+ }
+ default:
+ err = mapToXmlIndent(false, s, et, vv, p)
+ }
+ if err != nil {
+ break
+ }
+ }
+ ss += *s + "" + rt + ">"
+ b = []byte(ss)
+ case map[string]interface{}:
+ m := Map(v.(map[string]interface{}))
+ b, err = m.Xml(rt)
+ default:
+ err = mapToXmlIndent(false, s, rt, v, p)
+ b = []byte(*s)
+ }
+
+ 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(string)
+ p := new(pretty)
+ p.indent = indent
+ p.padding = prefix
+
+ var ss string
+ var b []byte
+ switch v.(type) {
+ case []interface{}:
+ ss = "<" + rt + ">\n"
+ 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 = mapToXmlIndent(true, s, tag, val, p)
+ }
+ } else {
+ p.start = 1 // we 1 tag in
+ err = mapToXmlIndent(true, s, et, vv, p)
+ *s += "\n"
+ }
+ default:
+ p.start = 0 // in case trailing p.start = 1
+ err = mapToXmlIndent(true, s, et, vv, p)
+ }
+ if err != nil {
+ break
+ }
+ }
+ ss += *s + "" + rt + ">"
+ b = []byte(ss)
+ case map[string]interface{}:
+ m := Map(v.(map[string]interface{}))
+ b, err = m.XmlIndent(prefix, indent, rt)
+ default:
+ err = mapToXmlIndent(true, s, rt, v, p)
+ b = []byte(*s)
+ }
+
+ return b, err
+}
diff --git a/vendor/github.com/clbanning/mxj/atomFeedString.xml b/vendor/github.com/clbanning/mxj/atomFeedString.xml
new file mode 100644
index 00000000000..474575a41ca
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/atomFeedString.xml
@@ -0,0 +1,54 @@
+
+Code Review - My issueshttp://codereview.appspot.com/rietveld<>rietveld: an attempt at pubsubhubbub
+2009-10-04T01:35:58+00:00email-address-removedurn:md5:134d9179c41f806be79b3a5f7877d19a
+ 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 <link rel="hub" href="hub-server"> 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'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's actual URL in
+the link rel="self", 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).
+
+
+rietveld: correct tab handling
+2009-10-03T23:02:17+00:00email-address-removedurn:md5:0a2a4f19bb815101f0ba2904aed7c35a
+ 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'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.
+
+
+ `
+
diff --git a/vendor/github.com/clbanning/mxj/doc.go b/vendor/github.com/clbanning/mxj/doc.go
new file mode 100644
index 00000000000..8ed79a5a77a
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/doc.go
@@ -0,0 +1,134 @@
+// mxj - A collection of map[string]interface{} and associated XML and JSON utilities.
+// Copyright 2012-2015, 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
+
+/*
+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:
+ 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[]map[string]interface{}`values
+ where the `` value has "#text" and "#seq" keys - the "#text" key holds the
+ value for ``.
+ - 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:
+ - something 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 "".
+ NOTE: the operation is not symmetric as "" 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
diff --git a/vendor/github.com/clbanning/mxj/escapechars.go b/vendor/github.com/clbanning/mxj/escapechars.go
new file mode 100644
index 00000000000..bee0442c9c1
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/escapechars.go
@@ -0,0 +1,54 @@
+// 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 '&' will be re-escaped as '&'.
+//
+/*
+ The values are:
+ " "
+ ' '
+ < <
+ > >
+ & &
+*/
+func XMLEscapeChars(b bool) {
+ xmlEscapeChars = b
+}
+
+// Scan for '&' first, since 's' may contain "&" that is parsed to "&"
+// - or "<" that is parsed to "<".
+var escapechars = [][2][]byte{
+ {[]byte(`&`), []byte(`&`)},
+ {[]byte(`<`), []byte(`<`)},
+ {[]byte(`>`), []byte(`>`)},
+ {[]byte(`"`), []byte(`"`)},
+ {[]byte(`'`), []byte(`'`)},
+}
+
+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)
+}
+
diff --git a/vendor/github.com/clbanning/mxj/exists.go b/vendor/github.com/clbanning/mxj/exists.go
new file mode 100644
index 00000000000..2fb3084b599
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/exists.go
@@ -0,0 +1,7 @@
+package mxj
+
+// Checks whether the path exists
+func (mv Map) Exists(path string, subkeys ...string) bool {
+ v, err := mv.ValuesForPath(path, subkeys...)
+ return err == nil && len(v) > 0
+}
diff --git a/vendor/github.com/clbanning/mxj/files.go b/vendor/github.com/clbanning/mxj/files.go
new file mode 100644
index 00000000000..27e06e1e801
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/files.go
@@ -0,0 +1,287 @@
+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
+}
diff --git a/vendor/github.com/clbanning/mxj/files_test.badjson b/vendor/github.com/clbanning/mxj/files_test.badjson
new file mode 100644
index 00000000000..d18720044ac
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/files_test.badjson
@@ -0,0 +1,2 @@
+{ "this":"is", "a":"test", "file":"for", "files_test.go":"case" }
+{ "with":"some", "bad":JSON, "in":"it" }
diff --git a/vendor/github.com/clbanning/mxj/files_test.badxml b/vendor/github.com/clbanning/mxj/files_test.badxml
new file mode 100644
index 00000000000..4736ef973dd
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/files_test.badxml
@@ -0,0 +1,9 @@
+
+ test
+ for files.go
+
+
+ some
+ doc
+ test case
+
diff --git a/vendor/github.com/clbanning/mxj/files_test.json b/vendor/github.com/clbanning/mxj/files_test.json
new file mode 100644
index 00000000000..e9a3ddf40ec
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/files_test.json
@@ -0,0 +1,2 @@
+{ "this":"is", "a":"test", "file":"for", "files_test.go":"case" }
+{ "with":"just", "two":2, "JSON":"values", "true":true }
diff --git a/vendor/github.com/clbanning/mxj/files_test.xml b/vendor/github.com/clbanning/mxj/files_test.xml
new file mode 100644
index 00000000000..65cf021fb70
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/files_test.xml
@@ -0,0 +1,9 @@
+
+ test
+ for files.go
+
+
+ some
+ doc
+ test case
+
diff --git a/vendor/github.com/clbanning/mxj/files_test_dup.json b/vendor/github.com/clbanning/mxj/files_test_dup.json
new file mode 100644
index 00000000000..2becb6a4512
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/files_test_dup.json
@@ -0,0 +1 @@
+{"a":"test","file":"for","files_test.go":"case","this":"is"}{"JSON":"values","true":true,"two":2,"with":"just"}
\ No newline at end of file
diff --git a/vendor/github.com/clbanning/mxj/files_test_dup.xml b/vendor/github.com/clbanning/mxj/files_test_dup.xml
new file mode 100644
index 00000000000..f68d22e28ea
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/files_test_dup.xml
@@ -0,0 +1 @@
+for files.gotestdoctest casesome
\ No newline at end of file
diff --git a/vendor/github.com/clbanning/mxj/files_test_indent.json b/vendor/github.com/clbanning/mxj/files_test_indent.json
new file mode 100644
index 00000000000..6fde15634df
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/files_test_indent.json
@@ -0,0 +1,12 @@
+{
+ "a": "test",
+ "file": "for",
+ "files_test.go": "case",
+ "this": "is"
+}
+{
+ "JSON": "values",
+ "true": true,
+ "two": 2,
+ "with": "just"
+}
\ No newline at end of file
diff --git a/vendor/github.com/clbanning/mxj/files_test_indent.xml b/vendor/github.com/clbanning/mxj/files_test_indent.xml
new file mode 100644
index 00000000000..8c91a1dc20a
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/files_test_indent.xml
@@ -0,0 +1,8 @@
+
+ for files.go
+ test
+
+ doc
+ test case
+ some
+
\ No newline at end of file
diff --git a/vendor/github.com/clbanning/mxj/gob.go b/vendor/github.com/clbanning/mxj/gob.go
new file mode 100644
index 00000000000..d56c2fd6fe8
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/gob.go
@@ -0,0 +1,35 @@
+// 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
+}
diff --git a/vendor/github.com/clbanning/mxj/json.go b/vendor/github.com/clbanning/mxj/json.go
new file mode 100644
index 00000000000..eb2c05a1869
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/json.go
@@ -0,0 +1,323 @@
+// 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
+}
diff --git a/vendor/github.com/clbanning/mxj/keyvalues.go b/vendor/github.com/clbanning/mxj/keyvalues.go
new file mode 100644
index 00000000000..0b244c879ce
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/keyvalues.go
@@ -0,0 +1,671 @@
+// 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
+
+// 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
+}
+
+// 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 a hyphen, '-', e.g., "-seq:3".
+// - 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.
+
+// Retrieve 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 a hyphen, '-', e.g., "-seq:3".
+// - 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 --------------------------------
+
+// Get 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
+}
+
+// Extract 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 wrap 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
+}
+
+// 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]
+ switch str := val.(type) {
+ case string:
+ return str, nil
+ default:
+ return "", fmt.Errorf("ValueForPath: unsupported type: %T", str)
+ }
+}
+
+// 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
+}
diff --git a/vendor/github.com/clbanning/mxj/leafnode.go b/vendor/github.com/clbanning/mxj/leafnode.go
new file mode 100644
index 00000000000..cf413ebdd4f
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/leafnode.go
@@ -0,0 +1,112 @@
+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]
+}
diff --git a/vendor/github.com/clbanning/mxj/misc.go b/vendor/github.com/clbanning/mxj/misc.go
new file mode 100644
index 00000000000..5b4fab2165d
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/misc.go
@@ -0,0 +1,86 @@
+// 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)
+}
diff --git a/vendor/github.com/clbanning/mxj/mxj.go b/vendor/github.com/clbanning/mxj/mxj.go
new file mode 100644
index 00000000000..f0592f06c8e
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/mxj.go
@@ -0,0 +1,128 @@
+// 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]
+}
diff --git a/vendor/github.com/clbanning/mxj/newmap.go b/vendor/github.com/clbanning/mxj/newmap.go
new file mode 100644
index 00000000000..b293949056d
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/newmap.go
@@ -0,0 +1,184 @@
+// 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)
+ }
+}
diff --git a/vendor/github.com/clbanning/mxj/readme.md b/vendor/github.com/clbanning/mxj/readme.md
new file mode 100644
index 00000000000..6bb21dca82a
--- /dev/null
+++ b/vendor/github.com/clbanning/mxj/readme.md
@@ -0,0 +1,179 @@
+
mxj - to/from maps, XML and JSON
+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.
+
+
Related Packages
+
+https://github.com/clbanning/checkxml provides functions for validating XML data.
+
+
Refactor Decoder - 2015.11.15
+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
+
+
Notices
+
+ 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.
+
+
Basic Unmarshal XML to map[string]interface{}
+
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':
+
+
+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
+
+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:
+
+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:
+
+
+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.)
+
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
+
+
Usage
+
+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.
+
+
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[]map[string]interface{}`values
+ where the `` value has "#text" and "#seq" keys - the "#text" key holds the
+ value for ``.
+ - 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:
+ - `something` 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 ``.
+ NOTE: the operation is not symmetric as `` 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.
+
+
Running "go test"
+
+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.
+
+