diff --git a/glide.lock b/glide.lock index a2ae5d9..c88aed8 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: bd360fa297ed66950543990f9433cdcdf13c29dd99d9a01b49027e236b2cb9da -updated: 2017-07-09T03:33:16.672898431+08:00 +updated: 2017-07-11T23:49:28.770307187+08:00 imports: - name: github.com/boj/redistore version: 4562487a4bee9a7c272b72bfaeda4917d0a47ab9 @@ -44,10 +44,27 @@ imports: version: ee05b128a739a0fb76c7ebd3ae4810c1de808d6d - name: github.com/mattn/go-isatty version: fc9e8d8ef48496124e79ae0df75490096eccf6fe +- name: github.com/qiniu/api.v7 + version: 9c12a67868f8f94d6a75dd6bb59b095db8d40d77 + subpackages: + - api + - auth/qbox + - conf + - kodocli +- name: github.com/qiniu/x + version: f512abcf45ab4e2ba0fd4784c57b53d495997d66 + subpackages: + - bytes.v7 + - bytes.v7/seekable + - ctype.v7 + - log.v7 + - rpc.v7 + - url.v7 + - xlog.v7 - name: github.com/shurcooL/sanitized_anchor_name version: 541ff5ee47f1dddf6a5281af78307d921524bcb5 - name: golang.org/x/crypto - version: a48ac81e47fd6f9ed1258f3b60ae9e75f93cb7ed + version: dd85ac7e6a88fc6ca420478e934de5f1a42dd3c6 subpackages: - acme - acme/autocert @@ -56,7 +73,7 @@ imports: subpackages: - context - name: golang.org/x/sys - version: 739734461d1c916b6c72a63d7efda2b27edb369f + version: abf9c25f54453410d0c6668e519582a9e1115027 subpackages: - unix - name: gopkg.in/go-playground/validator.v8 @@ -71,24 +88,17 @@ imports: - name: gopkg.in/yaml.v2 version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b - name: qiniupkg.com/api.v7 - version: 33f18792da10ba96109ee98a0c7fe53cd6a8a65a + version: 9c12a67868f8f94d6a75dd6bb59b095db8d40d77 subpackages: - - api - - auth/qbox - - conf - kodo - kodocli - name: qiniupkg.com/x version: f512abcf45ab4e2ba0fd4784c57b53d495997d66 subpackages: - bytes.v7 - - bytes.v7/seekable - - ctype.v7 - log.v7 - reqid.v7 - - rpc.v7 - url.v7 - - xlog.v7 testImports: - name: github.com/davecgh/go-spew version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d diff --git a/vendor/github.com/qiniu/api.v7/.gitignore b/vendor/github.com/qiniu/api.v7/.gitignore new file mode 100644 index 0000000..daf913b --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/qiniu/api.v7/.travis.yml b/vendor/github.com/qiniu/api.v7/.travis.yml new file mode 100644 index 0000000..d2e2d07 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/.travis.yml @@ -0,0 +1,19 @@ +language: go +go: + - 1.7 + - 1.8 + - master +env: + global: + - QINIU_KODO_TEST=1 + - QINIU_ACCESS_KEY="QWYn5TFQsLLU1pL5MFEmX3s5DmHdUThav9WyOWOm" + - QINIU_SECRET_KEY="Bxckh6FA-Fbs9Yt3i3cbKVK22UPBmAOHJcL95pGz" + - QINIU_TEST_BUCKET="gosdk" + - QINIU_TEST_DOMAIN="gosdk.qiniudn.com" + +install: + - export QINIU_SRC=$HOME/gopath/src + - mkdir -p $QINIU_SRC/github.com/qiniu + - export TRAVIS_BUILD_DIR=$QINIU_SRC/github.com/qiniu/api.v7 + - cd $TRAVIS_BUILD_DIR + - go get github.com/qiniu/x \ No newline at end of file diff --git a/vendor/github.com/qiniu/api.v7/CHANGELOG.md b/vendor/github.com/qiniu/api.v7/CHANGELOG.md new file mode 100644 index 0000000..7fecb42 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/CHANGELOG.md @@ -0,0 +1,39 @@ +#Changelog + +## 7.1.0 (2016-6-22) + +### 增加 +* 增加多机房相关功能 + +## 7.0.5 (2015-11-20) + +### 增加 +* add delimiter support to Bucket.List +* 增加回调校验 + +## 7.0.4 (2015-09-03) + +### 增加 +* 上传返回参数PutRet增加PersistentId,用于获取上传对应的fop操作的id + +### 修复 +* token 覆盖问题 + +## 7.0.3 (2015-07-11) + +### 增加 +* support NestedObject + +## 7.0.2 (2015-07-7-10) + +### 增加 +* 增加跨空间移动文件(Bucket.MoveEx) + +## 7.0.1 (2015-07-7-10) + +### 增加 +* 完善 PutPolicy:支持 MimeLimit、CallbackHost、CallbackFetchKey、 CallbackBodyType、 Checksum + +## 7.0.0 (2016-06-29) + +* 重构,初始版本 diff --git a/vendor/github.com/qiniu/api.v7/README.md b/vendor/github.com/qiniu/api.v7/README.md new file mode 100644 index 0000000..4c9bbd5 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/README.md @@ -0,0 +1,25 @@ +github.com/qiniu/api.v7 (Qiniu Go SDK v7.x) +=============== + +[![Build Status](https://travis-ci.org/qiniu/api.v7.svg?branch=develop)](https://travis-ci.org/qiniu/api.v7) [![GoDoc](https://godoc.org/github.com/qiniu/api.v7?status.svg)](https://godoc.org/github.com/qiniu/api.v7) + +[![Qiniu Logo](http://open.qiniudn.com/logo.png)](http://qiniu.com/) + +# 下载 + +``` +go get -u github.com/qiniu/api.v7 +``` +如果碰到golang.org/x/net/context 不能下载,请把 http://devtools.qiniu.com/golang.org.x.net.context.tgz 下载到代码目录下并解压到src目录,或者直接下载全部 http://devtools.qiniu.com/qiniu_api_v7.tgz。 + +# 使用文档 + +## KODO Blob Storage (七牛对象存储) + +* [github.com/qiniu/api.v7/kodo](http://godoc.org/github.com/qiniu/api.v7/kodo) +* [github.com/qiniu/api.v7/kodocli](http://godoc.org/github.com/qiniu/api.v7/kodocli) + +如果您是在业务服务器(服务器端)调用七牛云存储的服务,请使用 [github.com/qiniu/api.v7/kodo](http://godoc.org/github.com/qiniu/api.v7/kodo)。 + +如果您是在客户端(比如:Android/iOS 设备、Windows/Mac/Linux 桌面环境)调用七牛云存储的服务,请使用 [github.com/qiniu/api.v7/kodocli](http://godoc.org/github.com/qiniu/api.v7/kodocli)。注意,在这种场合下您不应该在任何地方配置 AccessKey/SecretKey。泄露 AccessKey/SecretKey 如同泄露您的用户名/密码一样十分危险,会影响您的数据安全。 + diff --git a/vendor/github.com/qiniu/api.v7/api/api.go b/vendor/github.com/qiniu/api.v7/api/api.go new file mode 100644 index 0000000..4943d45 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/api/api.go @@ -0,0 +1,99 @@ +package api + +import ( + . "context" + "sync" + "time" + + "github.com/qiniu/x/rpc.v7" +) + +const DefaultApiHost string = "http://uc.qbox.me" + +var ( + bucketMu sync.RWMutex + bucketCache = make(map[string]BucketInfo) +) + +type Client struct { + *rpc.Client + host string + scheme string +} + +func NewClient(host string, scheme string) *Client { + if host == "" { + host = DefaultApiHost + } + client := rpc.DefaultClient + return &Client{&client, host, scheme} +} + +type BucketInfo struct { + UpHosts []string `json:"up"` + IoHost string `json:"io"` + Expire int64 `json:"expire"` // expire == 0 means no expire +} + +func (p *Client) GetBucketInfo(ak, bucketName string) (ret BucketInfo, err error) { + key := ak + ":" + bucketName + ":" + p.scheme + bucketMu.RLock() + bucketInfo, ok := bucketCache[key] + bucketMu.RUnlock() + if ok && (bucketInfo.Expire == 0 || bucketInfo.Expire > time.Now().Unix()) { + ret = bucketInfo + return + } + hostInfo, err := p.bucketHosts(ak, bucketName) + if err != nil { + return + } + ret.Expire = time.Now().Unix() + hostInfo.Ttl + if p.scheme == "https" { + ret.UpHosts = hostInfo.Https["up"] + if iohosts, ok := hostInfo.Https["io"]; ok && len(iohosts) != 0 { + ret.IoHost = iohosts[0] + } + } else { + ret.UpHosts = hostInfo.Http["up"] + if iohosts, ok := hostInfo.Http["io"]; ok && len(iohosts) != 0 { + ret.IoHost = iohosts[0] + } + } + bucketMu.Lock() + bucketCache[key] = ret + bucketMu.Unlock() + return +} + +type HostsInfo struct { + Ttl int64 `json:"ttl"` + Http map[string][]string `json:"http"` + Https map[string][]string `json:"https"` +} + +/* +请求包: + GET /v1/query?ak=&&bucket= +返回包: + 200 OK { + "ttl": , // 有效时间 + "http": { + "up": [], + "io": [], // 当bucket为global时,我们不需要iohost, io缺省 + }, + "https": { + "up": [], + "io": [], // 当bucket为global时,我们不需要iohost, io缺省 + } + } +*/ + +func (p *Client) bucketHosts(ak, bucket string) (info HostsInfo, err error) { + ctx := Background() + err = p.CallWithForm(ctx, &info, "GET", p.host+"/v1/query", map[string][]string{ + "ak": []string{ak}, + "bucket": []string{bucket}, + }) + return +} diff --git a/vendor/github.com/qiniu/api.v7/auth/qbox/qbox_auth.go b/vendor/github.com/qiniu/api.v7/auth/qbox/qbox_auth.go new file mode 100644 index 0000000..e8aa481 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/auth/qbox/qbox_auth.go @@ -0,0 +1,171 @@ +package qbox + +import ( + "crypto/hmac" + "crypto/sha1" + "encoding/base64" + "io" + "net/http" + + . "github.com/qiniu/api.v7/conf" + "github.com/qiniu/x/bytes.v7/seekable" +) + +// ---------------------------------------------------------- + +type Mac struct { + AccessKey string + SecretKey []byte +} + +func NewMac(accessKey, secretKey string) (mac *Mac) { + + if accessKey == "" { + accessKey, secretKey = ACCESS_KEY, SECRET_KEY + } + return &Mac{accessKey, []byte(secretKey)} +} + +func (mac *Mac) Sign(data []byte) (token string) { + + h := hmac.New(sha1.New, mac.SecretKey) + h.Write(data) + + sign := base64.URLEncoding.EncodeToString(h.Sum(nil)) + return mac.AccessKey + ":" + sign[:27] +} + +func (mac *Mac) SignWithData(b []byte) (token string) { + + blen := base64.URLEncoding.EncodedLen(len(b)) + + key := mac.AccessKey + nkey := len(key) + ret := make([]byte, nkey+30+blen) + + base64.URLEncoding.Encode(ret[nkey+30:], b) + + h := hmac.New(sha1.New, mac.SecretKey) + h.Write(ret[nkey+30:]) + digest := h.Sum(nil) + + copy(ret, key) + ret[nkey] = ':' + base64.URLEncoding.Encode(ret[nkey+1:], digest) + ret[nkey+29] = ':' + + return string(ret) +} + +func (mac *Mac) SignRequest(req *http.Request, incbody bool) (token string, err error) { + + h := hmac.New(sha1.New, mac.SecretKey) + + u := req.URL + data := u.Path + if u.RawQuery != "" { + data += "?" + u.RawQuery + } + io.WriteString(h, data+"\n") + + if incbody { + s2, err2 := seekable.New(req) + if err2 != nil { + return "", err2 + } + h.Write(s2.Bytes()) + } + + sign := base64.URLEncoding.EncodeToString(h.Sum(nil)) + token = mac.AccessKey + ":" + sign + return +} + +func (mac *Mac) VerifyCallback(req *http.Request) (bool, error) { + + auth := req.Header.Get("Authorization") + if auth == "" { + return false, nil + } + + token, err := mac.SignRequest(req, true) + if err != nil { + return false, err + } + + return auth == "QBox "+token, nil +} + +// --------------------------------------------------------------------------------------- + +func Sign(mac *Mac, data []byte) string { + + if mac == nil { + mac = NewMac(ACCESS_KEY, SECRET_KEY) + } + return mac.Sign(data) +} + +func SignWithData(mac *Mac, data []byte) string { + + if mac == nil { + mac = NewMac(ACCESS_KEY, SECRET_KEY) + } + return mac.SignWithData(data) +} + +// --------------------------------------------------------------------------------------- + +type Transport struct { + mac Mac + Transport http.RoundTripper +} + +func incBody(req *http.Request) bool { + + if req.Body == nil { + return false + } + if ct, ok := req.Header["Content-Type"]; ok { + switch ct[0] { + case "application/x-www-form-urlencoded": + return true + } + } + return false +} + +func (t *Transport) NestedObject() interface{} { + + return t.Transport +} + +func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) { + + token, err := t.mac.SignRequest(req, incBody(req)) + if err != nil { + return + } + req.Header.Set("Authorization", "QBox "+token) + return t.Transport.RoundTrip(req) +} + +func NewTransport(mac *Mac, transport http.RoundTripper) *Transport { + + if mac == nil { + mac = NewMac(ACCESS_KEY, SECRET_KEY) + } + if transport == nil { + transport = http.DefaultTransport + } + t := &Transport{mac: *mac, Transport: transport} + return t +} + +func NewClient(mac *Mac, transport http.RoundTripper) *http.Client { + + t := NewTransport(mac, transport) + return &http.Client{Transport: t} +} + +// --------------------------------------------------------------------------------------- diff --git a/vendor/github.com/qiniu/api.v7/cdn/anti_leech.go b/vendor/github.com/qiniu/api.v7/cdn/anti_leech.go new file mode 100644 index 0000000..ea6788d --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/cdn/anti_leech.go @@ -0,0 +1,35 @@ +package cdn + +import ( + "crypto/md5" + "fmt" + "net/url" + "time" +) + +// CreateTimestampAntileechURL 构建带时间戳防盗链的链接 +// encryptKey 七牛防盗链key +func CreateTimestampAntileechURL(urlStr string, encryptKey string, durationInSeconds int64) (antileechURL string, err error) { + + u, err := url.Parse(urlStr) + if err != nil { + return + } + + expireTime := time.Now().Add(time.Second * time.Duration(durationInSeconds)).Unix() + toSignStr := fmt.Sprintf("%s%s%x", encryptKey, u.EscapedPath(), expireTime) + signedStr := fmt.Sprintf("%x", md5.Sum([]byte(toSignStr))) + + q := url.Values{} + q.Add("sign", signedStr) + q.Add("t", fmt.Sprintf("%x", expireTime)) + + if u.RawQuery == "" { + antileechURL = u.String() + "?" + q.Encode() + } else { + + antileechURL = u.String() + "&" + q.Encode() + } + + return +} diff --git a/vendor/github.com/qiniu/api.v7/cdn/anti_leech_test.go b/vendor/github.com/qiniu/api.v7/cdn/anti_leech_test.go new file mode 100644 index 0000000..eda3f9c --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/cdn/anti_leech_test.go @@ -0,0 +1,37 @@ +package cdn + +import ( + "testing" +) + +func TestCreateTimestampAntiLeech(t *testing.T) { + type args struct { + urlStr string + encryptKey string + durationInSeconds int64 + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "antileech_1", + args: args{ + urlStr: "http://www.abc.com/abc.jpg?stat", + encryptKey: "abc", + durationInSeconds: 20, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := CreateTimestampAntileechURL(tt.args.urlStr, tt.args.encryptKey, tt.args.durationInSeconds) + if (err != nil) != tt.wantErr { + t.Errorf("CreateTimestampAntiLeech() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} diff --git a/vendor/github.com/qiniu/api.v7/cdn/api.go b/vendor/github.com/qiniu/api.v7/cdn/api.go new file mode 100644 index 0000000..6ea5d35 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/cdn/api.go @@ -0,0 +1,260 @@ +package cdn + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" + + "github.com/qiniu/api.v7/auth/qbox" + . "github.com/qiniu/api.v7/conf" +) + +var ( + FUSION_HOST = "http://fusion.qiniuapi.com" +) + +/* TrafficReqBody + +批量查询带宽/流量 请求内容 + + StartDate string 开始日期,例如:2016-07-01 + EndDate string 结束日期,例如:2016-07-03 + Granularity string 粒度,取值:5min / hour /day + Domains string 域名列表,以 ;分割 +*/ +type TrafficReqBody struct { + StartDate string `json:"startDate"` + EndDate string `json:"endDate"` + Granularity string `json:"granularity"` + Domains string `json:"domains"` +} + +// TrafficResp +// 带宽/流量查询响应内容 +type TrafficResp struct { + Code int `json:"code"` + Error string `json:"error"` + Time []string `json:"time,omitempty"` + Data map[string]TrafficRespData `json:"data,omitempty"` +} + +// TrafficRespData +// 带宽/流量数据 +type TrafficRespData struct { + DomainChina []int `json:"china"` + DomainOversea []int `json:"oversea"` +} + +/* +// BandWidth + +获取域名访问带宽数据 +http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html + + StartDate string 必须 开始日期,例如:2016-07-01 + EndDate string 必须 结束日期,例如:2016-07-03 + Granularity string 必须 粒度,取值:5min / hour /day + Domains []string 必须 域名列表 +*/ +func GetBandWidthData(startDate, endDate, granularity string, domainList []string) (bandwidthData TrafficResp, err error) { + + domains := strings.Join(domainList, ";") + reqBody := TrafficReqBody{ + StartDate: startDate, + EndDate: endDate, + Granularity: granularity, + Domains: domains, + } + + resData, reqErr := postRequest("v2/tune/bandwidth", reqBody) + if reqErr != nil { + err = reqErr + return + } + umErr := json.Unmarshal(resData, &bandwidthData) + if umErr != nil { + err = umErr + return + } + return +} + +/* Flux + +获取域名访问流量数据 +http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html + + StartDate string 必须 开始日期,例如:2016-07-01 + EndDate string 必须 结束日期,例如:2016-07-03 + Granularity string 必须 粒度,取值:5min / hour /day + Domains []string 必须 域名列表 +*/ +func GetFluxData(startDate, endDate, granularity string, domainList []string) (fluxData TrafficResp, err error) { + + domains := strings.Join(domainList, ";") + reqBody := TrafficReqBody{ + StartDate: startDate, + EndDate: endDate, + Granularity: granularity, + Domains: domains, + } + + resData, reqErr := postRequest("v2/tune/flux", reqBody) + if reqErr != nil { + err = reqErr + return + } + + umErr := json.Unmarshal(resData, &fluxData) + if umErr != nil { + err = umErr + return + } + + return +} + +// RefreshReq +// 缓存刷新请求内容 +type RefreshReq struct { + Urls []string `json:"urls"` + Dirs []string `json:"dirs"` +} + +// RefreshResp +// 缓存刷新响应内容 +type RefreshResp struct { + Code int `json:"code"` + Error string `json:"error"` + RequestID string `json:"requestId,omitempty"` + InvalidUrls []string `json:"invalidUrls,omitempty"` + InvalidDirs []string `json:"invalidDirs,omitempty"` + UrlQuotaDay int `json:"urlQuotaDay,omitempty"` + UrlSurplusDay int `json:"urlSurplusDay,omitempty"` + DirQuotaDay int `json:"dirQuotaDay,omitempty"` + DirSurplusDay int `json:"dirSurplusDay,omitempty"` +} + +/* RefreshUrlsAndDirs + +刷新链接列表,每次最多不可以超过100条链接 +http://developer.qiniu.com/article/fusion/api/refresh.html + + urls 要刷新的单个url列表,总数不超过100条;单个url,即一个具体的url,例如:http://bar.foo.com/index.html + dirs 要刷新的目录url列表,总数不超过10条;目录dir,即表示一个目录级的url,例如:http://bar.foo.com/dir/,也支持在尾部使用通配符,例如:http://bar.foo.com/dir/* +*/ +func RefreshUrlsAndDirs(urls, dirs []string) (result RefreshResp, err error) { + + reqBody := RefreshReq{ + Urls: urls, + Dirs: dirs, + } + + resData, reqErr := postRequest("v2/tune/refresh", reqBody) + if reqErr != nil { + err = reqErr + return + } + umErr := json.Unmarshal(resData, &result) + if umErr != nil { + err = reqErr + return + } + + return +} + +// RefreshUrls +// 刷新文件 +func RefreshUrls(urls []string) (result RefreshResp, err error) { + return RefreshUrlsAndDirs(urls, nil) +} + +// RefreshDirs +// 刷新目录 +func RefreshDirs(dirs []string) (result RefreshResp, err error) { + return RefreshUrlsAndDirs(nil, dirs) +} + +// PrefetchReq +// 文件预取请求内容 +type PrefetchReq struct { + Urls []string `json:"urls"` +} + +// PrefetchResp +// 文件预取响应内容 +type PrefetchResp struct { + Code int `json:"code"` + Error string `json:"error"` + RequestID string `json:"requestId,omitempty"` + InvalidUrls []string `json:"invalidUrls,omitempty"` + QuotaDay int `json:"quotaDay,omitempty"` + SurplusDay int `json:"surplusDay,omitempty"` +} + +// PrefetchUrls +// 预取文件链接,每次最多不可以超过100条 +// http://developer.qiniu.com/article/fusion/api/prefetch.html +func PrefetchUrls(urls []string) (result PrefetchResp, err error) { + + reqBody := PrefetchReq{ + Urls: urls, + } + + resData, reqErr := postRequest("v2/tune/prefetch", reqBody) + if reqErr != nil { + err = reqErr + return + } + + umErr := json.Unmarshal(resData, &result) + if umErr != nil { + err = umErr + return + } + + return +} + +// RequestWithBody +// 带body对api发出请求并且返回response body +func postRequest(path string, body interface{}) (resData []byte, err error) { + + urlStr := fmt.Sprintf("%s/%s", FUSION_HOST, path) + + reqData, _ := json.Marshal(body) + req, reqErr := http.NewRequest("POST", urlStr, bytes.NewReader(reqData)) + if reqErr != nil { + err = reqErr + return + } + + mac := qbox.NewMac(ACCESS_KEY, SECRET_KEY) + accessToken, signErr := mac.SignRequest(req, false) + if signErr != nil { + err = signErr + return + } + + req.Header.Add("Authorization", "QBox "+accessToken) + req.Header.Add("Content-Type", "application/json") + + resp, httpErr := http.DefaultClient.Do(req) + if httpErr != nil { + err = httpErr + return + } + defer resp.Body.Close() + + resData, ioErr := ioutil.ReadAll(resp.Body) + if ioErr != nil { + err = ioErr + return + } + + return +} diff --git a/vendor/github.com/qiniu/api.v7/cdn/api_test.go b/vendor/github.com/qiniu/api.v7/cdn/api_test.go new file mode 100644 index 0000000..ecd23a9 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/cdn/api_test.go @@ -0,0 +1,243 @@ +package cdn + +import ( + "math/rand" + "os" + "reflect" + "strconv" + "testing" + + "time" + + "github.com/qiniu/api.v7/kodo" +) + +var ( + ak = os.Getenv("QINIU_ACCESS_KEY") + sk = os.Getenv("QINIU_SECRET_KEY") + domain = os.Getenv("QINIU_TEST_DOMAIN") + testBucketName = os.Getenv("QINIU_TEST_BUCKET") + + testDate = time.Now().AddDate(0, 0, -3).Format("2006-01-02") + bucket = newBucket() + client *kodo.Client + testKey = "fusionTest" + testURL string +) + +func init() { + kodo.SetMac(ak, sk) + rand.Seed(time.Now().UnixNano()) + testKey += strconv.Itoa(rand.Int()) + bucket.PutFile(nil, nil, testKey, "doc.go", nil) + testURL = domain + "/" + testKey + +} + +func TestGetBandWidthData(t *testing.T) { + type args struct { + startDate string + endDate string + granularity string + domainList []string + } + tests := []struct { + name string + args args + wantTraffic TrafficResp + wantErr bool + }{ + { + name: "BandWidthTest_1", + args: args{ + testDate, + testDate, + "5min", + []string{domain}, + }, + }, + } + kodo.SetMac(ak, sk) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := GetBandWidthData(tt.args.startDate, tt.args.endDate, tt.args.granularity, tt.args.domainList) + if (err != nil) != tt.wantErr { + t.Errorf("GetBandWidthData() error = %v, wantErr %v", err, tt.wantErr) + return + } + + }) + } +} + +func TestGetFluxData(t *testing.T) { + type args struct { + startDate string + endDate string + granularity string + domainList []string + } + tests := []struct { + name string + args args + wantTraffic TrafficResp + wantErr bool + }{ + { + name: "BandWidthTest_1", + args: args{ + testDate, + testDate, + "5min", + []string{domain}, + }, + }, + } + kodo.SetMac(ak, sk) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := GetFluxData(tt.args.startDate, tt.args.endDate, tt.args.granularity, tt.args.domainList) + if (err != nil) != tt.wantErr { + t.Errorf("GetFluxData() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} + +func TestRefreshUrlsAndDirs(t *testing.T) { + kodo.SetMac(ak, sk) + + type args struct { + urls []string + dirs []string + } + tests := []struct { + name string + args args + wantResult RefreshResp + wantErr bool + }{ + { + name: "refresh_test_1", + args: args{ + urls: []string{testURL}, + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := RefreshUrlsAndDirs(tt.args.urls, tt.args.dirs) + if (err != nil) != tt.wantErr { + t.Errorf("RefreshUrlsAndDirs() error = %v, wantErr %v", err, tt.wantErr) + return + } + + }) + } +} + +func TestRefreshUrls(t *testing.T) { + type args struct { + urls []string + } + tests := []struct { + name string + args args + wantResult RefreshResp + wantErr bool + }{ + { + name: "refresh_test_1", + args: args{ + urls: []string{testURL}, + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := RefreshUrls(tt.args.urls) + if (err != nil) != tt.wantErr { + t.Errorf("RefreshUrls() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} + +func TestRefreshDirs(t *testing.T) { + type args struct { + dirs []string + } + tests := []struct { + name string + args args + wantResult RefreshResp + wantErr bool + }{} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotResult, err := RefreshDirs(tt.args.dirs) + if (err != nil) != tt.wantErr { + t.Errorf("RefreshDirs() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(gotResult, tt.wantResult) { + t.Errorf("RefreshDirs() = %v, want %v", gotResult, tt.wantResult) + } + }) + } +} + +func TestPrefetchUrls(t *testing.T) { + type args struct { + urls []string + } + tests := []struct { + name string + args args + wantResult PrefetchResp + wantErr bool + }{ + { + name: "refresh_test_1", + args: args{ + urls: []string{testURL}, + }, + wantErr: false, + }, + } + kodo.SetMac(ak, sk) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := PrefetchUrls(tt.args.urls) + if (err != nil) != tt.wantErr { + t.Errorf("PrefetchUrls() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} + +func newBucket() (bucket kodo.Bucket) { + + ak := os.Getenv("QINIU_ACCESS_KEY") + sk := os.Getenv("QINIU_SECRET_KEY") + if ak == "" || sk == "" { + panic("require ACCESS_KEY & SECRET_KEY") + } + kodo.SetMac(ak, sk) + + testBucketName = os.Getenv("QINIU_TEST_BUCKET") + domain = os.Getenv("QINIU_TEST_DOMAIN") + if testBucketName == "" || domain == "" { + panic("require test env") + } + client = kodo.NewWithoutZone(nil) + + return client.Bucket(testBucketName) +} diff --git a/vendor/github.com/qiniu/api.v7/cdn/doc.go b/vendor/github.com/qiniu/api.v7/cdn/doc.go new file mode 100644 index 0000000..ece1769 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/cdn/doc.go @@ -0,0 +1,25 @@ +/* +包 github.com/qiniu/api.v7/cdn 提供了七牛CDN的API功能 +首先,我们要配置下 AccessKey/SecretKey, + import "github.com/qiniu/api.v7/kodo" + + kodo.SetMac("ak", "sk") +设置了AccessKey/SecretKey 就可以使用cdn的各类功能 + +比如我们要生成一个带时间戳防盗链的链接: + q :=url.Values{}// url.Values 请求参数 + link, err := cdn.CreateTimestampAntileechURL(""http://www.qiniu.com/abc/bcc/aa-s.mp4?x=2&y=3", "encryptedkey", 20) + if err != nil { + fmt.Println(err) + } + fmt.Println(link) + +又或者我们要列出CDN日志及其下载地址: + resp, err := cdn.GetCdnLogList("2016-12-26", "x-mas.com") + if err != nil { + fmt.Println(err) + } + fmt.Println(resp) + +*/ +package cdn diff --git a/vendor/github.com/qiniu/api.v7/cdn/logs.go b/vendor/github.com/qiniu/api.v7/cdn/logs.go new file mode 100644 index 0000000..1b640f3 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/cdn/logs.go @@ -0,0 +1,92 @@ +package cdn + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "strings" + + "github.com/qiniu/api.v7/auth/qbox" +) + +const ( + LIST_LOG_API = "http://fusion.qiniuapi.com/v2/tune/log/list" +) + +// ListLogRequest 日志下载请求内容 +type ListLogRequest struct { + Day string `json:"day"` + Domains string `json:"domains"` +} + +// ListLogResult 日志下载相应内容 +type ListLogResult struct { + Code int `json:"code"` + Error string `json:"error"` + Data map[string][]LogDomainInfo `json:"data"` +} + +// LogDomainInfo 日志下载信息 +type LogDomainInfo struct { + Name string `json:"name"` + Size int64 `json:"size"` + ModifiedTime int64 `json:"mtime"` + URL string `json:"url"` +} + +// GetCdnLogList 获取CDN域名访问日志的下载链接 +// http://developer.qiniu.com/article/fusion/api/log.html +func GetCdnLogList(date, domains string) (domainLogs []LogDomainInfo, err error) { + + //new log query request + logReq := ListLogRequest{ + Day: date, + Domains: domains, + } + + logReqBytes, _ := json.Marshal(&logReq) + req, reqErr := http.NewRequest("POST", LIST_LOG_API, bytes.NewReader(logReqBytes)) + if reqErr != nil { + err = fmt.Errorf("New request error, %s", reqErr) + return + } + + mac := qbox.NewMac("", "") + token, signErr := mac.SignRequest(req, false) + if signErr != nil { + err = signErr + return + } + + req.Header.Add("Authorization", "QBox "+token) + req.Header.Add("Content-Type", "application/json") + + resp, respErr := http.DefaultClient.Do(req) + if respErr != nil { + err = fmt.Errorf("Get response error, %s", respErr) + return + } + defer resp.Body.Close() + + listLogResult := ListLogResult{} + decoder := json.NewDecoder(resp.Body) + if decodeErr := decoder.Decode(&listLogResult); decodeErr != nil { + err = fmt.Errorf("Parse response error, %s", decodeErr) + return + } + if resp.StatusCode != http.StatusOK { + err = fmt.Errorf("Get log list error, %d %s", listLogResult.Code, listLogResult.Error) + return + } + + domainItems := strings.Split(domains, ";") + + for _, domain := range domainItems { + for _, v := range listLogResult.Data[domain] { + domainLogs = append(domainLogs, v) + } + } + return + +} diff --git a/vendor/github.com/qiniu/api.v7/cdn/logs_test.go b/vendor/github.com/qiniu/api.v7/cdn/logs_test.go new file mode 100644 index 0000000..58870d7 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/cdn/logs_test.go @@ -0,0 +1,48 @@ +package cdn + +import ( + "fmt" + "reflect" + "testing" + + "github.com/qiniu/api.v7/kodo" +) + +func init() { + kodo.SetMac(ak, sk) +} + +func TestGetCdnLogList(t *testing.T) { + type args struct { + date string + domains string + } + tests := []struct { + name string + args args + wantDomainLogs []LogDomainInfo + wantErr bool + }{ + { + name: "getCdnLogListTest", + args: args{ + date: testDate, + domains: domain, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotDomainLogs, err := GetCdnLogList(tt.args.date, tt.args.domains) + if (err != nil) != tt.wantErr { + t.Errorf("GetCdnLogList() error = %v, wantErr %v", err, tt.wantErr) + return + } + fmt.Println(domain, gotDomainLogs) + if !reflect.DeepEqual(gotDomainLogs, tt.wantDomainLogs) { + t.Errorf("GetCdnLogList() = %v, want %v", gotDomainLogs, tt.wantDomainLogs) + } + }) + } +} diff --git a/vendor/github.com/qiniu/api.v7/conf/conf.go b/vendor/github.com/qiniu/api.v7/conf/conf.go new file mode 100644 index 0000000..d08a8d0 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/conf/conf.go @@ -0,0 +1,38 @@ +package conf + +import ( + "fmt" + "runtime" + "syscall" + + "github.com/qiniu/x/ctype.v7" + "github.com/qiniu/x/rpc.v7" +) + +var version = "7.1.0" + +var ACCESS_KEY string +var SECRET_KEY string + +// ---------------------------------------------------------- + +const ( + ctypeAppName = ctype.ALPHA | ctype.DIGIT | ctype.UNDERLINE | ctype.SPACE_BAR | ctype.SUB | ctype.DOT +) + +// userApp should be [A-Za-z0-9_\ \-\.]* +// +func SetAppName(userApp string) error { + if userApp != "" && !ctype.IsType(ctypeAppName, userApp) { + return syscall.EINVAL + } + rpc.UserAgent = fmt.Sprintf( + "QiniuGo/%s (%s; %s; %s) %s", version, runtime.GOOS, runtime.GOARCH, userApp, runtime.Version()) + return nil +} + +func init() { + SetAppName("") +} + +// ---------------------------------------------------------- diff --git a/vendor/github.com/qiniu/api.v7/conf/conf_test.go b/vendor/github.com/qiniu/api.v7/conf/conf_test.go new file mode 100644 index 0000000..a68c0ec --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/conf/conf_test.go @@ -0,0 +1,35 @@ +package conf + +import ( + "strings" + "testing" + + "github.com/qiniu/x/rpc.v7" +) + +func TestUA(t *testing.T) { + err := SetAppName("") + if err != nil { + t.Fatal("expect no error") + } + err = SetAppName("错误的UA") + if err == nil { + t.Fatal("expect an invalid ua format") + } + err = SetAppName("Test0-_.") + if err != nil { + t.Fatal("expect no error") + } +} + +func TestFormat(t *testing.T) { + str := "tesT0.-_" + SetAppName(str) + v := rpc.UserAgent + if !strings.Contains(v, str) { + t.Fatal("should include user") + } + if !strings.HasPrefix(v, "QiniuGo/"+version) { + t.Fatal("invalid format") + } +} diff --git a/vendor/github.com/qiniu/api.v7/doc.go b/vendor/github.com/qiniu/api.v7/doc.go new file mode 100644 index 0000000..6f0d9bd --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/doc.go @@ -0,0 +1,23 @@ +/* +包 github.com/qiniu/api.v7 是七牛 Go 语言 SDK v7.x 版本 + +七牛对象存储,我们取了一个好听的名字,叫 KODO Blob Storage。要使用它,你主要和以下两个包打交道: + + import "github.com/qiniu/api.v7/kodo" + import "github.com/qiniu/api.v7/kodocli" + +如果您是在业务服务器(服务器端)调用七牛云存储的服务,请使用 github.com/qiniu/api.v7/kodo。 + +如果您是在客户端(比如:Android/iOS 设备、Windows/Mac/Linux 桌面环境)调用七牛云存储的服务,请使用 github.com/qiniu/api.v7/kodocli。 +注意,在这种场合下您不应该在任何地方配置 AccessKey/SecretKey。泄露 AccessKey/SecretKey 如同泄露您的用户名/密码一样十分危险, +会影响您的数据安全。 +*/ +package api + +import ( + _ "github.com/qiniu/api.v7/auth/qbox" + _ "github.com/qiniu/api.v7/cdn" + _ "github.com/qiniu/api.v7/conf" + _ "github.com/qiniu/api.v7/kodo" + _ "github.com/qiniu/api.v7/kodocli" +) diff --git a/vendor/github.com/qiniu/api.v7/kodo/bucket.go b/vendor/github.com/qiniu/api.v7/kodo/bucket.go new file mode 100644 index 0000000..9692d4f --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/kodo/bucket.go @@ -0,0 +1,435 @@ +package kodo + +import ( + . "context" + "encoding/base64" + "fmt" + "io" + "net/url" + "strconv" + + "github.com/qiniu/api.v7/api" + "github.com/qiniu/x/log.v7" +) + +// ---------------------------------------------------------- + +// Batch 批量操作。 +// +func (p *Client) Batch(ctx Context, ret interface{}, op []string) (err error) { + + return p.CallWithForm(ctx, ret, "POST", p.RSHost+"/batch", map[string][]string{"op": op}) +} + +// ---------------------------------------------------------- + +type Bucket struct { + api.BucketInfo + Conn *Client + Name string +} + +// Buckets 获取所有地区的所有空间(bucket) +// +// shared 是否获取所有授权获得空间,true为包含授权空间 +// +func (p *Client) Buckets(ctx Context, shared bool) (buckets []string, err error) { + if shared { + err = p.Call(ctx, &buckets, "POST", p.RSHost+"/buckets?shared=trye") + } else { + err = p.Call(ctx, &buckets, "POST", p.RSHost+"/buckets") + } + return +} + +// Bucket 取七牛空间(bucket)的对象实例。 +// +// name 是创建该七牛空间(bucket)时采用的名称。 +// +func (p *Client) Bucket(name string) Bucket { + b, err := p.BucketWithSafe(name) + if err != nil { + log.Errorf("Bucket(%s) failed: %+v", name, err) + } + return b +} + +// BucketWithSafe 确认空间存在并获取七牛空间(bucket)的对象实例。 +func (p *Client) BucketWithSafe(name string) (Bucket, error) { + var info api.BucketInfo + if len(p.UpHosts) == 0 { + var err error + info, err = p.apiCli.GetBucketInfo(p.mac.AccessKey, name) + if err != nil { + return Bucket{}, err + } + } else { + info.IoHost = p.IoHost + info.UpHosts = p.UpHosts + } + return Bucket{info, p, name}, nil +} + +// Entry 资源元信息 +type Entry struct { + Hash string `json:"hash"` + Fsize int64 `json:"fsize"` + PutTime int64 `json:"putTime"` + MimeType string `json:"mimeType"` + Type int `json:"type"` + EndUser string `json:"endUser"` +} + +// Stat 取文件属性。 +// +// ctx 是请求的上下文。 +// key 是要访问的文件的访问路径。 +// +func (p Bucket) Stat(ctx Context, key string) (entry Entry, err error) { + err = p.Conn.Call(ctx, &entry, "POST", p.Conn.RSHost+URIStat(p.Name, key)) + return +} + +// Delete 删除一个文件。 +// +// ctx 是请求的上下文。 +// key 是要删除的文件的访问路径。 +// +func (p Bucket) Delete(ctx Context, key string) (err error) { + return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIDelete(p.Name, key)) +} + +// Move 移动一个文件。 +// +// ctx 是请求的上下文。 +// keySrc 是要移动的文件的旧路径。 +// keyDest 是要移动的文件的新路径。 +// +func (p Bucket) Move(ctx Context, keySrc, keyDest string) (err error) { + return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIMove(p.Name, keySrc, p.Name, keyDest)) +} + +// MoveEx 跨空间(bucket)移动一个文件。 +// +// ctx 是请求的上下文。 +// keySrc 是要移动的文件的旧路径。 +// bucketDest 是文件的目标空间。 +// keyDest 是要移动的文件的新路径。 +// +func (p Bucket) MoveEx(ctx Context, keySrc, bucketDest, keyDest string) (err error) { + return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIMove(p.Name, keySrc, bucketDest, keyDest)) +} + +// Copy 复制一个文件。 +// +// ctx 是请求的上下文。 +// keySrc 是要复制的文件的源路径。 +// keyDest 是要复制的文件的目标路径。 +// +func (p Bucket) Copy(ctx Context, keySrc, keyDest string) (err error) { + return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URICopy(p.Name, keySrc, p.Name, keyDest)) +} + +// ChangeMime 修改文件的MIME类型。 +// +// ctx 是请求的上下文。 +// key 是要修改的文件的访问路径。 +// mime 是要设置的新MIME类型。 +// +func (p Bucket) ChangeMime(ctx Context, key, mime string) (err error) { + return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIChangeMime(p.Name, key, mime)) +} + +// ChangeType 修改文件的存储类型。 +// +// ctx 是请求的上下文。 +// key 是要修改的文件的访问路径。 +// fileType 是要设置的新存储类型。0 表示标准存储;1 表示低频存储。 +// +func (p Bucket) ChangeType(ctx Context, key string, fileType int) (err error) { + return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIChangeType(p.Name, key, fileType)) +} + +// Fetch 从网上抓取一个资源并存储到七牛空间(bucket)中。 +// +// ctx 是请求的上下文。 +// key 是要存储的文件的访问路径。如果文件已经存在则覆盖。 +// url 是要抓取的资源的URL。 +// +func (p Bucket) Fetch(ctx Context, key string, url string) (err error) { + return p.Conn.Call(ctx, nil, "POST", p.IoHost+uriFetch(p.Name, key, url)) +} + +// DeleteAfterDays 更新文件生命周期 +// +// ctx 是请求的上下文。 +// key 是要更新的文件的访问路径。 +// deleteAfterDays 设置为0表示取消 lifecycle +// +func (p Bucket) DeleteAfterDays(ctx Context, key string, days int) (err error) { + return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIDeleteAfterDays(p.Name, key, days)) +} + +// Image 设置镜像源 +// +// srcSiteURL 镜像源的访问域名。必须设置为形如 `http://source.com/` 或 `http://114.114.114.114/` 的字符串 +// host 回源时使用的 Host 头部值 +// +// 镜像源地址支持两种格式: +// 格式 1:`http(s)://绑定域名/源站资源相对路径` +// 格式 2:`http(s)://绑定 IP/源站资源相对路径` +// +func (p Bucket) Image(ctx Context, srcSiteURL, host string) (err error) { + return p.Conn.Call(ctx, nil, "POST", "http://pu.qbox.me:10200"+URIImage(p.Name, srcSiteURL, host)) +} + +// UnImage 取消镜像源 +// +func (p Bucket) UnImage(ctx Context) (err error) { + return p.Conn.Call(ctx, nil, "POST", "http://pu.qbox.me:10200"+URIUnImage(p.Name)) +} + +// Prefetch 镜像资源更新 +// +// key 被抓取资源名称 +// +func (p Bucket) Prefetch(ctx Context, key string) (err error) { + return p.Conn.Call(ctx, nil, "POST", p.Conn.IoHost+URIPrefetch(p.Name, key)) +} + +// PfopResult pfop返回信息 +type PfopResult struct { + PersistentID string `json:"persistentId,omitempty"` +} + +// FopRet 持久化云处理结果 +type FopRet struct { + ID string `json:"id"` + Code int `json:"code"` + Desc string `json:"desc"` + InputBucket string `json:"inputBucket,omitempty"` + InputKey string `json:"inputKey,omitempty"` + Pipeline string `json:"pipeline,omitempty"` + Reqid string `json:"reqid,omitempty"` + Items []FopResult +} + +// FopResult 云处理操作列表,包含每个云处理操作的状态信息 +type FopResult struct { + Cmd string `json:"cmd"` + Code int `json:"code"` + Desc string `json:"desc"` + Error string `json:"error,omitempty"` + Hash string `json:"hash,omitempty"` + Key string `json:"key,omitempty"` + Keys []string `json:"keys,omitempty"` +} + +// Pfop 持久化数据处理 +// +// bucket 资源空间 +// key 源资源名 +// fops 云处理操作列表,用`;``分隔,如:`avthumb/flv;saveas/cWJ1Y2tldDpxa2V5`,是将上传的视频文件转码成flv格式后存储为 qbucket:qkey ,其中 cWJ1Y2tldDpxa2V5 是 qbucket:qkey 的URL安全的Base64编码结果。 +// notifyURL 处理结果通知接收 URL,七牛将会向你设置的 URL 发起 Content-Type: application/json 的 POST 请求。 +// pipeline 为空则表示使用公用队列,处理速度比较慢。建议指定私有队列,转码的时候使用独立的计算资源。 +// force 强制执行数据处理。当服务端发现 fops 指定的数据处理结果已经存在,那就认为已经处理成功,避免重复处理浪费资源。本字段设为 `true`,则可强制执行数据处理并覆盖原结果。 +// +func (p *Client) Pfop(ctx Context, bucket, key, fops, notifyURL, pipeline string, force bool) (persistentID string, err error) { + pfopParams := map[string][]string{ + "bucket": []string{bucket}, + "key": []string{key}, + "fops": []string{fops}, + } + if notifyURL != "" { + pfopParams["notifyURL"] = []string{notifyURL} + } + if pipeline != "" { + pfopParams["pipeline"] = []string{pipeline} + } + if force { + pfopParams["force"] = []string{"1"} + } + var ret PfopResult + err = p.CallWithForm(ctx, &ret, "POST", "http://api.qiniu.com/pfop/", pfopParams) + if err != nil { + return + } + + persistentID = ret.PersistentID + return +} + +// Prefop 持久化处理状态查询 +func (p *Client) Prefop(ctx Context, persistentID string) (ret FopRet, err error) { + err = p.Call(ctx, &ret, "GET", "http://api.qiniu.com/status/get/prefop?id="+persistentID) + return +} + +// ---------------------------------------------------------- + +// ListItem List借口返回结果 +type ListItem struct { + Key string `json:"key"` + Hash string `json:"hash"` + Fsize int64 `json:"fsize"` + PutTime int64 `json:"putTime"` + MimeType string `json:"mimeType"` + EndUser string `json:"endUser"` +} + +// List 首次请求,请将 marker 设置为 ""。 +// 无论 err 值如何,均应该先看 entries 是否有内容。 +// 如果后续没有更多数据,err 返回 EOF,markerOut 返回 ""(但不通过该特征来判断是否结束)。 +// +func (p Bucket) List( + ctx Context, prefix, delimiter, marker string, limit int) (entries []ListItem, commonPrefixes []string, markerOut string, err error) { + + listUrl := p.makeListURL(prefix, delimiter, marker, limit) + + var listRet struct { + Marker string `json:"marker"` + Items []ListItem `json:"items"` + Prefixes []string `json:"commonPrefixes"` + } + err = p.Conn.Call(ctx, &listRet, "POST", listUrl) + if err != nil { + return + } + if listRet.Marker == "" { + return listRet.Items, listRet.Prefixes, "", io.EOF + } + return listRet.Items, listRet.Prefixes, listRet.Marker, nil +} + +func (p Bucket) makeListURL(prefix, delimiter, marker string, limit int) string { + + query := make(url.Values) + query.Add("bucket", p.Name) + if prefix != "" { + query.Add("prefix", prefix) + } + if delimiter != "" { + query.Add("delimiter", delimiter) + } + if marker != "" { + query.Add("marker", marker) + } + if limit > 0 { + query.Add("limit", strconv.FormatInt(int64(limit), 10)) + } + return p.Conn.RSFHost + "/list?" + query.Encode() +} + +// ---------------------------------------------------------- + +type BatchStatItemRet struct { + Data Entry `json:"data"` + Error string `json:"error"` + Code int `json:"code"` +} + +// BatchStat 批量取文件属性 +func (p Bucket) BatchStat(ctx Context, keys ...string) (ret []BatchStatItemRet, err error) { + + b := make([]string, len(keys)) + for i, key := range keys { + b[i] = URIStat(p.Name, key) + } + err = p.Conn.Batch(ctx, &ret, b) + return +} + +type BatchItemRet struct { + Error string `json:"error"` + Code int `json:"code"` +} + +// BatchDelete 批量删除 +func (p Bucket) BatchDelete(ctx Context, keys ...string) (ret []BatchItemRet, err error) { + + b := make([]string, len(keys)) + for i, key := range keys { + b[i] = URIDelete(p.Name, key) + } + err = p.Conn.Batch(ctx, &ret, b) + return +} + +type KeyPair struct { + Src string + Dest string +} + +// BatchMove 批量移动文件 +func (p Bucket) BatchMove(ctx Context, entries ...KeyPair) (ret []BatchItemRet, err error) { + + b := make([]string, len(entries)) + for i, e := range entries { + b[i] = URIMove(p.Name, e.Src, p.Name, e.Dest) + } + err = p.Conn.Batch(ctx, &ret, b) + return +} + +// BatchCopy 批量复制文件 +func (p Bucket) BatchCopy(ctx Context, entries ...KeyPair) (ret []BatchItemRet, err error) { + + b := make([]string, len(entries)) + for i, e := range entries { + b[i] = URICopy(p.Name, e.Src, p.Name, e.Dest) + } + err = p.Conn.Batch(ctx, &ret, b) + return +} + +// ---------------------------------------------------------- + +func encodeURI(uri string) string { + return base64.URLEncoding.EncodeToString([]byte(uri)) +} + +func uriFetch(bucket, key, url string) string { + return "/fetch/" + encodeURI(url) + "/to/" + encodeURI(bucket+":"+key) +} + +func URIDelete(bucket, key string) string { + return "/delete/" + encodeURI(bucket+":"+key) +} + +func URIStat(bucket, key string) string { + return "/stat/" + encodeURI(bucket+":"+key) +} + +func URICopy(bucketSrc, keySrc, bucketDest, keyDest string) string { + return "/copy/" + encodeURI(bucketSrc+":"+keySrc) + "/" + encodeURI(bucketDest+":"+keyDest) +} + +func URIMove(bucketSrc, keySrc, bucketDest, keyDest string) string { + return "/move/" + encodeURI(bucketSrc+":"+keySrc) + "/" + encodeURI(bucketDest+":"+keyDest) +} + +func URIChangeMime(bucket, key, mime string) string { + return "/chgm/" + encodeURI(bucket+":"+key) + "/mime/" + encodeURI(mime) +} + +func URIChangeType(bucket, key string, fileType int) string { + return "/chtype/" + encodeURI(bucket+":"+key) + "/type/" + strconv.Itoa(fileType) +} + +func URIDeleteAfterDays(bucket, key string, days int) string { + return fmt.Sprintf("/deleteAfterDays/%s/%d", encodeURI(bucket+":"+key), days) +} + +func URIImage(bucket, srcSiteURL, host string) string { + return fmt.Sprintf("/image/%s/from/%s/host/%s", bucket, encodeURI(srcSiteURL), encodeURI(host)) +} + +func URIUnImage(bucket string) string { + return fmt.Sprintf("/unimage/%s", bucket) +} + +func URIPrefetch(bucket, key string) string { + return fmt.Sprintf("/prefetch/%s", encodeURI(bucket+":"+key)) +} + +// ---------------------------------------------------------- diff --git a/vendor/github.com/qiniu/api.v7/kodo/bucket_test.go b/vendor/github.com/qiniu/api.v7/kodo/bucket_test.go new file mode 100644 index 0000000..bdea185 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/kodo/bucket_test.go @@ -0,0 +1,167 @@ +package kodo + +import ( + "context" + "math/rand" + "strconv" + "testing" + "time" +) + +var ( + batchTestKey = "abatch" + batchTestNewKey1 = "abatch/newkey1" + batchTestNewKey2 = "abatch/newkey2" +) + +func init() { + + if skipTest() { + return + } + + rand.Seed(time.Now().UnixNano()) + batchTestKey += strconv.Itoa(rand.Int()) + batchTestNewKey1 += strconv.Itoa(rand.Int()) + batchTestNewKey2 += strconv.Itoa(rand.Int()) + // 删除 可能存在的 key + bucket.BatchDelete(nil, batchTestKey, batchTestNewKey1, batchTestNewKey2) +} + +func TestAll(t *testing.T) { + + if skipTest() { + return + } + + //上传一个文件用用于测试 + err := upFile("bucket_test.go", batchTestKey) + if err != nil { + t.Fatal(err) + } + defer bucket.Delete(nil, batchTestKey) + + testBatchStat(t) + testBatchCopy(t) + testBatchMove(t) + testBatchDelete(t) + testBatch(t) + testClient_MakeUptokenBucket(t) + testDeleteAfterDays(t) +} + +func testBatchStat(t *testing.T) { + + rets, err := bucket.BatchStat(nil, batchTestKey, batchTestKey, batchTestKey) + if err != nil { + t.Fatal("bucket.BatchStat failed:", err) + } + + if len(rets) != 3 { + t.Fatal("BatchStat failed: len(rets) = ", 3) + } + + stat, err := bucket.Stat(nil, batchTestKey) + if err != nil { + t.Fatal("bucket.Stat failed:", err) + } + + if rets[0].Data != stat || rets[1].Data != stat || rets[2].Data != stat { + t.Fatal("BatchStat failed : returns err") + } +} + +func testBatchMove(t *testing.T) { + + stat0, err := bucket.Stat(nil, batchTestKey) + if err != nil { + t.Fatal("BathMove get stat failed:", err) + } + + _, err = bucket.BatchMove(nil, KeyPair{batchTestKey, batchTestNewKey1}, KeyPair{batchTestNewKey1, batchTestNewKey2}) + if err != nil { + t.Fatal("bucket.BatchMove failed:", err) + } + defer bucket.Move(nil, batchTestNewKey2, batchTestKey) + + stat1, err := bucket.Stat(nil, batchTestNewKey2) + if err != nil { + t.Fatal("BathMove get stat failed:", err) + } + + if stat0.Hash != stat1.Hash { + t.Fatal("BatchMove failed : Move err", stat0, stat1) + } +} + +func testBatchCopy(t *testing.T) { + + _, err := bucket.BatchCopy(nil, KeyPair{batchTestKey, batchTestNewKey1}, KeyPair{batchTestKey, batchTestNewKey2}) + if err != nil { + t.Fatal(err) + } + defer bucket.Delete(nil, batchTestNewKey1) + defer bucket.Delete(nil, batchTestNewKey2) + + stat0, _ := bucket.Stat(nil, batchTestKey) + stat1, _ := bucket.Stat(nil, batchTestNewKey1) + stat2, _ := bucket.Stat(nil, batchTestNewKey2) + if stat0.Hash != stat1.Hash || stat0.Hash != stat2.Hash { + t.Fatal("BatchCopy failed : Copy err") + } +} + +func testBatchDelete(t *testing.T) { + + bucket.Copy(nil, batchTestKey, batchTestNewKey1) + bucket.Copy(nil, batchTestKey, batchTestNewKey2) + + _, err := bucket.BatchDelete(nil, batchTestNewKey1, batchTestNewKey2) + if err != nil { + t.Fatal(err) + } + + _, err1 := bucket.Stat(nil, batchTestNewKey1) + _, err2 := bucket.Stat(nil, batchTestNewKey2) + + //这里 err1 != nil,否则文件没被成功删除 + if err1 == nil || err2 == nil { + t.Fatal("BatchDelete failed : File do not delete") + } +} + +func testBatch(t *testing.T) { + + ops := []string{ + URICopy(bucketName, batchTestKey, bucketName, batchTestNewKey1), + URIDelete(bucketName, batchTestKey), + URIMove(bucketName, batchTestNewKey1, bucketName, batchTestKey), + } + + var rets []BatchItemRet + err := client.Batch(nil, &rets, ops) + if err != nil { + t.Fatal(err) + } + if len(rets) != 3 { + t.Fatal("len(rets) != 3") + } +} + +func testDeleteAfterDays(t *testing.T) { + + ctx := context.Background() + + err := bucket.DeleteAfterDays(ctx, batchTestNewKey1, 5) + if err == nil { + t.Fatal("Expect an error") + } + + bucket.Copy(ctx, batchTestKey, batchTestNewKey1) + + err = bucket.DeleteAfterDays(ctx, batchTestNewKey1, 5) + if err != nil { + t.Fatal(err) + } + +} diff --git a/vendor/github.com/qiniu/api.v7/kodo/doc.go b/vendor/github.com/qiniu/api.v7/kodo/doc.go new file mode 100644 index 0000000..a18e7de --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/kodo/doc.go @@ -0,0 +1,52 @@ +/* +包 github.com/qiniu/api.v7/kodo 提供了在您的业务服务器(服务端)调用七牛云存储服务的能力 + +首先,我们要配置下 AccessKey/SecretKey,这可以在七牛 Portal 中查到: + + kodo.SetMac("your-access-key", "your-secret-key") + +然后我们创建一个 Client 对象: + + zone := kodo.ZoneZ0 // 您空间(Bucket)所在的区域 + c := kodo.New(zone, nil) // 用默认配置创建 Client + +有了 Client,你就可以操作您的空间(Bucket)了,比如我们要上传一个文件: + + import "golang.org/x/net/context" + + bucket := c.Bucket("your-bucket-name") + ctx := context.Background() + ... + localFile := "/your/local/image/file.jpg" + err := bucket.PutFile(ctx, nil, "foo/bar.jpg", localFile, nil) + if err != nil { + ... // 上传文件失败处理 + return + } + // 上传文件成功 + // 这时登录七牛Portal,在 your-bucket-name 空间就可以看到一个 foo/bar.jpg 的文件了 + +当然,除了上传文件,各种空间(Bucket)相关的操作都可以有,最常见自然是增删改查了: + + entry, err := bucket.Stat(ctx, "foo/bar.jpg") // 看看空间中是否存在某个文件,其属性是什么 + bucket.Delete(ctx, "foo/bar.jpg") // 删除空间中的某个文件 + bucket.ChangeMime(ctx, "foo/bar.jpg", "image/jpeg") // 修改某个文件的 MIME 属性 + bucket.Move(ctx, "foo/bar.jpg", "new-name.jpg") // 移动文件 + bucket.Copy(ctx, "foo/bar.jpg", "new-copy-file.jpg") // 复制文件 + +等等... 请问怎么下载文件?如果是公开文件,我们只需要: + + import "net/http" + + domain := "domain-of-your-bucket.com" // 您的空间绑定的域名,这个可以在七牛的Portal中查到 + baseUrl := kodo.MakeBaseUrl(domain, "foo/bar.jpg") // 得到下载 url + resp, err := http.Get(baseUrl) + ... + +但是对于私有空间,事情要复杂一些,访问上面的 baseUrl 会被拒绝。我们需要多做一步: + + privateUrl := c.MakePrivateUrl(baseUrl, nil) // 用默认的下载策略去生成私有下载的 url + resp, err := http.Get(privateUrl) + ... +*/ +package kodo diff --git a/vendor/github.com/qiniu/api.v7/kodo/main.go b/vendor/github.com/qiniu/api.v7/kodo/main.go new file mode 100644 index 0000000..dc09200 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/kodo/main.go @@ -0,0 +1,154 @@ +package kodo + +import ( + "net/http" + + "github.com/qiniu/api.v7/api" + "github.com/qiniu/api.v7/auth/qbox" + "github.com/qiniu/api.v7/conf" + "github.com/qiniu/x/rpc.v7" +) + +// ---------------------------------------------------------- + +type zoneConfig struct { + IoHost string + UpHosts []string +} + +const ( + // ZoneZ0 华东机房 + ZoneZ0 = iota + // ZoneZ1 华北机房 + ZoneZ1 + // ZoneZ2 华南机房 + ZoneZ2 + // ZoneNa0 北美机房 + ZoneNa0 +) + +var zones = []zoneConfig{ + // z0 华东机房: + { + IoHost: "http://iovip.qbox.me", + UpHosts: []string{ + "http://up.qiniu.com", + "http://upload.qiniu.com", + "-H up.qiniu.com http://183.136.139.16", + }, + }, + // z1 华北机房: + { + IoHost: "http://iovip-z1.qbox.me", + UpHosts: []string{ + "http://up-z1.qiniu.com", + "http://upload-z1.qiniu.com", + "-H up-z1.qiniu.com http://106.38.227.27", + }, + }, + // z2 华南机房: + { + IoHost: "http://iovip-z2.qbox.me", + UpHosts: []string{ + "http://up-z2.qiniu.com", + "http://upload-z2.qiniu.com", + }, + }, + // na0 北美机房: + { + IoHost: "http://iovip-na0.qbox.me", + UpHosts: []string{ + "http://up-na0.qiniu.com", + "http://upload-na0.qiniu.com", + }, + }, +} + +const ( + defaultRsHost = "http://rs.qbox.me" + defaultRsfHost = "http://rsf.qbox.me" +) + +// ---------------------------------------------------------- + +type Config struct { + AccessKey string + SecretKey string + RSHost string + RSFHost string + APIHost string + Scheme string + IoHost string + UpHosts []string + Transport http.RoundTripper +} + +// ---------------------------------------------------------- + +type Client struct { + rpc.Client + mac *qbox.Mac + Config + + apiCli *api.Client +} + +func New(zone int, cfg *Config) (p *Client) { + + p = new(Client) + if cfg != nil { + p.Config = *cfg + } + + p.mac = qbox.NewMac(p.AccessKey, p.SecretKey) + p.Client = rpc.Client{qbox.NewClient(p.mac, p.Transport)} + + if p.RSHost == "" { + p.RSHost = defaultRsHost + } + if p.RSFHost == "" { + p.RSFHost = defaultRsfHost + } + if p.Scheme != "https" { + p.Scheme = "http" + } + if p.APIHost == "" { + p.APIHost = api.DefaultApiHost + } + p.apiCli = api.NewClient(p.APIHost, p.Scheme) + + if zone < 0 || zone >= len(zones) { + return + } + if len(p.UpHosts) == 0 { + p.UpHosts = zones[zone].UpHosts + } + if p.IoHost == "" { + p.IoHost = zones[zone].IoHost + } + return +} + +func NewWithoutZone(cfg *Config) (p *Client) { + return New(-1, cfg) +} + +// ---------------------------------------------------------- + +// 设置全局默认的 ACCESS_KEY, SECRET_KEY 变量。 +// +func SetMac(accessKey, secretKey string) { + + conf.ACCESS_KEY, conf.SECRET_KEY = accessKey, secretKey +} + +// ---------------------------------------------------------- + +// 设置使用这个SDK的应用程序名。userApp 必须满足 [A-Za-z0-9_\ \-\.]* +// +func SetAppName(userApp string) error { + + return conf.SetAppName(userApp) +} + +// ---------------------------------------------------------- diff --git a/vendor/github.com/qiniu/api.v7/kodo/main_test.go b/vendor/github.com/qiniu/api.v7/kodo/main_test.go new file mode 100644 index 0000000..3172a31 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/kodo/main_test.go @@ -0,0 +1,151 @@ +package kodo + +import ( + "fmt" + "math/rand" + "os" + "strconv" + "testing" + "time" +) + +var ( + key = "aa" + keyFetch = "afetch" + newkey1 = "bbbb" + newkey2 = "cccc" + fetchURL = "http://www-static.u.qiniucdn.com/public/v1645/img/css-sprite.png" + bucketName string + domain string + client *Client + bucket = newBucket() + + QINIU_KODO_TEST string +) + +func init() { + + if skipTest() { + return + } + rand.Seed(time.Now().UnixNano()) + key += strconv.Itoa(rand.Int()) + keyFetch += strconv.Itoa(rand.Int()) + newkey1 += strconv.Itoa(rand.Int()) + newkey2 += strconv.Itoa(rand.Int()) + bucket.BatchDelete(nil, key, keyFetch, newkey1, newkey2) +} + +func newBucket() (bucket Bucket) { + + QINIU_KODO_TEST = os.Getenv("QINIU_KODO_TEST") + if skipTest() { + println("[INFO] QINIU_KODO_TEST: skipping to test github.com/qiniu/api.v7") + return + } + + ak := os.Getenv("QINIU_ACCESS_KEY") + sk := os.Getenv("QINIU_SECRET_KEY") + if ak == "" || sk == "" { + panic("require ACCESS_KEY & SECRET_KEY") + } + SetMac(ak, sk) + + bucketName = os.Getenv("QINIU_TEST_BUCKET") + domain = os.Getenv("QINIU_TEST_DOMAIN") + if bucketName == "" || domain == "" { + panic("require test env") + } + client = NewWithoutZone(nil) + + return client.Bucket(bucketName) +} + +func skipTest() bool { + + return QINIU_KODO_TEST == "" +} + +func upFile(localFile, key string) error { + + return bucket.PutFile(nil, nil, key, localFile, nil) +} + +func TestFetch(t *testing.T) { + + if skipTest() { + return + } + + err := bucket.Fetch(nil, keyFetch, fetchURL) + if err != nil { + t.Fatal("bucket.Fetch failed:", err) + } + + entry, err := bucket.Stat(nil, keyFetch) + if err != nil || entry.MimeType != "image/png" { + t.Fatal("bucket.Fetch: Stat failed -", err, "entry:", entry) + } + fmt.Println(entry) +} + +func TestEntry(t *testing.T) { + + if skipTest() { + return + } + + //上传一个文件用用于测试 + err := upFile("doc.go", key) + if err != nil { + t.Fatal(err) + } + defer bucket.Delete(nil, key) + + einfo, err := bucket.Stat(nil, key) + if err != nil { + t.Fatal(err) + } + + mime := "text/plain" + err = bucket.ChangeMime(nil, key, mime) + if err != nil { + t.Fatal(err) + } + + einfo, err = bucket.Stat(nil, key) + if err != nil { + t.Fatal(err) + } + if einfo.MimeType != mime { + t.Fatal("mime type did not change") + } + + err = bucket.Copy(nil, key, newkey1) + if err != nil { + t.Fatal(err) + } + enewinfo, err := bucket.Stat(nil, newkey1) + if err != nil { + t.Fatal(err) + } + if einfo.Hash != enewinfo.Hash { + t.Fatal("invalid entryinfo:", einfo, enewinfo) + } + err = bucket.Move(nil, newkey1, newkey2) + if err != nil { + t.Fatal(err) + } + enewinfo2, err := bucket.Stat(nil, newkey2) + if err != nil { + t.Fatal(err) + } + if enewinfo.Hash != enewinfo2.Hash { + t.Fatal("invalid entryinfo:", enewinfo, enewinfo2) + } + + err = bucket.Delete(nil, newkey2) + if err != nil { + t.Fatal(err) + } +} diff --git a/vendor/github.com/qiniu/api.v7/kodo/token.go b/vendor/github.com/qiniu/api.v7/kodo/token.go new file mode 100644 index 0000000..7cae521 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/kodo/token.go @@ -0,0 +1,119 @@ +package kodo + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" + "time" + + "github.com/qiniu/api.v7/api" + "github.com/qiniu/api.v7/auth/qbox" + "github.com/qiniu/x/url.v7" +) + +// ---------------------------------------------------------- + +// 根据空间(Bucket)的域名,以及文件的 key,获得 baseUrl。 +// 如果空间是 public 的,那么通过 baseUrl 可以直接下载文件内容。 +// 如果空间是 private 的,那么需要对 baseUrl 进行私有签名得到一个临时有效的 privateUrl 进行下载。 +// +func MakeBaseUrl(domain, key string) (baseUrl string) { + + return "http://" + domain + "/" + url.Escape(key) +} + +// ---------------------------------------------------------- + +type GetPolicy struct { + Expires uint32 +} + +func (p *Client) MakePrivateUrl(baseUrl string, policy *GetPolicy) (privateUrl string) { + + var expires int64 + if policy == nil || policy.Expires == 0 { + expires = 3600 + } else { + expires = int64(policy.Expires) + } + deadline := time.Now().Unix() + expires + + if strings.Contains(baseUrl, "?") { + baseUrl += "&e=" + } else { + baseUrl += "?e=" + } + baseUrl += strconv.FormatInt(deadline, 10) + + token := qbox.Sign(p.mac, []byte(baseUrl)) + return baseUrl + "&token=" + token +} + +// -------------------------------------------------------------------------------- + +type PutPolicy struct { + Scope string `json:"scope"` + Expires uint32 `json:"deadline"` // 截止时间(以秒为单位) + InsertOnly uint16 `json:"insertOnly,omitempty"` // 若非0, 即使Scope为 Bucket:Key 的形式也是insert only + DetectMime uint8 `json:"detectMime,omitempty"` // 若非0, 则服务端根据内容自动确定 MimeType + CallbackFetchKey uint8 `json:"callbackFetchKey,omitempty"` + FsizeLimit int64 `json:"fsizeLimit,omitempty"` + MimeLimit string `json:"mimeLimit,omitempty"` + SaveKey string `json:"saveKey,omitempty"` + CallbackUrl string `json:"callbackUrl,omitempty"` + CallbackHost string `json:"callbackHost,omitempty"` + CallbackBody string `json:"callbackBody,omitempty"` + CallbackBodyType string `json:"callbackBodyType,omitempty"` + ReturnUrl string `json:"returnUrl,omitempty"` + ReturnBody string `json:"returnBody,omitempty"` + PersistentOps string `json:"persistentOps,omitempty"` + PersistentNotifyUrl string `json:"persistentNotifyUrl,omitempty"` + PersistentPipeline string `json:"persistentPipeline,omitempty"` + AsyncOps string `json:"asyncOps,omitempty"` + EndUser string `json:"endUser,omitempty"` + Checksum string `json:"checksum,omitempty"` // 格式::,目前支持 MD5/SHA1。 + UpHosts []string `json:"uphosts,omitempty"` + DeleteAfterDays int `json:"deleteAfterDays,omitempty"` + FileType int `json:"fileType,omitempty"` +} + +func (p *Client) MakeUptoken(policy *PutPolicy) string { + token, err := p.MakeUptokenWithSafe(policy) + if err != nil { + fmt.Errorf("makeuptoken failed: policy: %+v, error: %+v", policy, err) + } + return token +} + +func (p *Client) MakeUptokenWithSafe(policy *PutPolicy) (token string, err error) { + var rr = *policy + if len(rr.UpHosts) == 0 { + bucketName := getBucketNameFromPutPolicy(policy) + bucketInfo, err1 := p.GetBucketInfo(bucketName) + if err1 != nil { + err = err1 + return + } + rr.UpHosts = bucketInfo.UpHosts + } + if rr.Expires == 0 { + rr.Expires = 3600 + } + rr.Expires += uint32(time.Now().Unix()) + b, _ := json.Marshal(&rr) + token = qbox.SignWithData(p.mac, b) + return +} + +func getBucketNameFromPutPolicy(policy *PutPolicy) (bucketName string) { + scope := policy.Scope + bucketName = strings.Split(scope, ":")[0] + return +} + +func (p *Client) GetBucketInfo(bucketName string) (bucketInfo api.BucketInfo, err error) { + return p.apiCli.GetBucketInfo(p.mac.AccessKey, bucketName) +} + +// ---------------------------------------------------------- diff --git a/vendor/github.com/qiniu/api.v7/kodo/token_test.go b/vendor/github.com/qiniu/api.v7/kodo/token_test.go new file mode 100644 index 0000000..aa5060e --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/kodo/token_test.go @@ -0,0 +1,70 @@ +package kodo + +import ( + "crypto/sha1" + "encoding/base64" + "io" + "net/http" + "testing" +) + +func init() { + + if skipTest() { + return + } + + // 删除 可能存在的 key + bucket.BatchDelete(nil, key) +} + +func TestGetPrivateUrl(t *testing.T) { + + if skipTest() { + return + } + + // 上传一个文件用用于测试 + err := upFile("token.go", key) + if err != nil { + t.Fatal(err) + } + defer bucket.Delete(nil, key) + + baseUrl := MakeBaseUrl(domain, key) + privateUrl := client.MakePrivateUrl(baseUrl, nil) + + resp, err := http.Get(privateUrl) + if err != nil { + t.Fatal("http.Get failed:", err) + } + defer resp.Body.Close() + + h := sha1.New() + io.Copy(h, resp.Body) + etagExpected := base64.URLEncoding.EncodeToString(h.Sum([]byte{'\x16'})) + + etag := resp.Header.Get("Etag") + if etag[1:len(etag)-1] != etagExpected { + t.Fatal("http.Get etag failed:", etag, etagExpected) + } +} + +func testClient_MakeUptokenBucket(t *testing.T) { + c := New(0, nil) + token := c.MakeUptoken(&PutPolicy{ + Scope: "gosdk", + Expires: 3600, + }) + if token == "" { + t.Fatal("nil token") + } + + token, err := c.MakeUptokenWithSafe(&PutPolicy{ + Scope: "NotExistBucket", + Expires: 3600, + }) + if err == nil { + t.Fatal("make up token fail") + } +} diff --git a/vendor/github.com/qiniu/api.v7/kodo/upload.go b/vendor/github.com/qiniu/api.v7/kodo/upload.go new file mode 100644 index 0000000..eda8922 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/kodo/upload.go @@ -0,0 +1,179 @@ +package kodo + +import ( + . "context" + "io" + "net/http" + + "github.com/qiniu/api.v7/kodocli" + "github.com/qiniu/x/rpc.v7" +) + +type PutExtra kodocli.PutExtra +type RputExtra kodocli.RputExtra +type PutRet kodocli.PutRet + +// ---------------------------------------------------------- + +func (p Bucket) makeUptoken(key string) string { + + policy := &PutPolicy{ + Scope: p.Name + ":" + key, + Expires: 3600, + UpHosts: p.UpHosts, + } + return p.Conn.MakeUptoken(policy) +} + +func (p Bucket) makeUptokenWithoutKey() string { + + policy := &PutPolicy{ + Scope: p.Name, + Expires: 3600, + UpHosts: p.UpHosts, + } + return p.Conn.MakeUptoken(policy) +} + +func (p Bucket) makeUploader() kodocli.Uploader { + + c := &http.Client{Transport: p.Conn.Transport} + return kodocli.Uploader{Conn: rpc.Client{c}, UpHosts: p.UpHosts, ApiCli: p.Conn.apiCli} +} + +// ---------------------------------------------------------- + +// Put 上传一个文件。 +// +// ctx 是请求的上下文。 +// ret 是上传成功后返回的数据。返回的是 PutRet 结构。可选,可以传 nil 表示不感兴趣。 +// key 是要上传的文件访问路径。比如:"foo/bar.jpg"。注意我们建议 key 不要以 '/' 开头。另外,key 为空字符串是合法的。 +// data 是文件内容的访问接口(io.Reader)。 +// fsize 是要上传的文件大小。 +// extra 是上传的一些可选项。详细见 PutExtra 结构的描述。 +// +func (p Bucket) Put( + ctx Context, ret interface{}, key string, data io.Reader, size int64, extra *PutExtra) error { + + uploader := p.makeUploader() + uptoken := p.makeUptoken(key) + return uploader.Put(ctx, ret, uptoken, key, data, size, (*kodocli.PutExtra)(extra)) +} + +// PutWithoutKey 上传一个文件。自动以文件的 hash 作为文件的访问路径(key)。 +// +// ctx 是请求的上下文。 +// ret 是上传成功后返回的数据。返回的是 PutRet 结构。可选,可以传 nil 表示不感兴趣。 +// data 是文件内容的访问接口(io.Reader)。 +// fsize 是要上传的文件大小。 +// extra 是上传的一些可选项。详细见 PutExtra 结构的描述。 +// +func (p Bucket) PutWithoutKey( + ctx Context, ret interface{}, data io.Reader, size int64, extra *PutExtra) error { + + uploader := p.makeUploader() + uptoken := p.makeUptokenWithoutKey() + return uploader.PutWithoutKey(ctx, ret, uptoken, data, size, (*kodocli.PutExtra)(extra)) +} + +// PutFile 上传一个文件。 +// 和 Put 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.Reader 来访问。 +// +// ctx 是请求的上下文。 +// ret 是上传成功后返回的数据。返回的是 PutRet 结构。可选,可以传 nil 表示不感兴趣。 +// localFile 是要上传的文件的本地路径。 +// extra 是上传的一些可选项。详细见 PutExtra 结构的描述。 +// +func (p Bucket) PutFile( + ctx Context, ret interface{}, key, localFile string, extra *PutExtra) (err error) { + + uploader := p.makeUploader() + uptoken := p.makeUptoken(key) + return uploader.PutFile(ctx, ret, uptoken, key, localFile, (*kodocli.PutExtra)(extra)) +} + +// PutFileWithoutKey 上传一个文件。自动以文件的 hash 作为文件的访问路径(key)。 +// 和 PutWithoutKey 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.Reader 来访问。 +// +// ctx 是请求的上下文。 +// ret 是上传成功后返回的数据。返回的是 PutRet 结构。可选,可以传 nil 表示不感兴趣。 +// localFile 是要上传的文件的本地路径。 +// extra 是上传的一些可选项。详细见 PutExtra 结构的描述。 +// +func (p Bucket) PutFileWithoutKey( + ctx Context, ret interface{}, localFile string, extra *PutExtra) (err error) { + + uploader := p.makeUploader() + uptoken := p.makeUptokenWithoutKey() + return uploader.PutFileWithoutKey(ctx, ret, uptoken, localFile, (*kodocli.PutExtra)(extra)) +} + +// ---------------------------------------------------------- + +// Rput 上传一个文件,支持断点续传和分块上传。 +// +// ctx 是请求的上下文。 +// ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody,那么返回的数据结构是 PutRet 结构。 +// key 是要上传的文件访问路径。比如:"foo/bar.jpg"。注意我们建议 key 不要以 '/' 开头。另外,key 为空字符串是合法的。 +// data 是文件内容的访问接口。考虑到需要支持分块上传和断点续传,要的是 io.ReaderAt 接口,而不是 io.Reader。 +// fsize 是要上传的文件大小。 +// extra 是上传的一些可选项。详细见 RputExtra 结构的描述。 +// +func (p Bucket) Rput( + ctx Context, ret interface{}, key string, data io.ReaderAt, size int64, extra *RputExtra) error { + + uploader := p.makeUploader() + uptoken := p.makeUptoken(key) + return uploader.Rput(ctx, ret, uptoken, key, data, size, (*kodocli.RputExtra)(extra)) +} + +// RputWithoutKey 上传一个文件,支持断点续传和分块上传。自动以文件的 hash 作为文件的访问路径(key)。 +// +// ctx 是请求的上下文。 +// ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody,那么返回的数据结构是 PutRet 结构。 +// data 是文件内容的访问接口。考虑到需要支持分块上传和断点续传,要的是 io.ReaderAt 接口,而不是 io.Reader。 +// fsize 是要上传的文件大小。 +// extra 是上传的一些可选项。详细见 RputExtra 结构的描述。 +// +func (p Bucket) RputWithoutKey( + ctx Context, ret interface{}, data io.ReaderAt, size int64, extra *RputExtra) error { + + uploader := p.makeUploader() + uptoken := p.makeUptokenWithoutKey() + return uploader.RputWithoutKey(ctx, ret, uptoken, data, size, (*kodocli.RputExtra)(extra)) +} + +// RputFile 上传一个文件,支持断点续传和分块上传。 +// 和 Rput 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.ReaderAt 来访问。 +// +// ctx 是请求的上下文。 +// ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody,那么返回的数据结构是 PutRet 结构。 +// key 是要上传的文件访问路径。比如:"foo/bar.jpg"。注意我们建议 key 不要以 '/' 开头。另外,key 为空字符串是合法的。 +// localFile 是要上传的文件的本地路径。 +// extra 是上传的一些可选项。详细见 RputExtra 结构的描述。 +// +func (p Bucket) RputFile( + ctx Context, ret interface{}, key, localFile string, extra *RputExtra) (err error) { + + uploader := p.makeUploader() + uptoken := p.makeUptoken(key) + return uploader.RputFile(ctx, ret, uptoken, key, localFile, (*kodocli.RputExtra)(extra)) +} + +// RputFileWithoutKey 上传一个文件,支持断点续传和分块上传。自动以文件的 hash 作为文件的访问路径(key)。 +// 和 RputWithoutKey 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.ReaderAt 来访问。 +// +// ctx 是请求的上下文。 +// ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody,那么返回的数据结构是 PutRet 结构。 +// localFile 是要上传的文件的本地路径。 +// extra 是上传的一些可选项。详细见 RputExtra 结构的描述。 +// +func (p Bucket) RputFileWithoutKey( + ctx Context, ret interface{}, localFile string, extra *RputExtra) (err error) { + + uploader := p.makeUploader() + uptoken := p.makeUptokenWithoutKey() + return uploader.RputFileWithoutKey(ctx, ret, uptoken, localFile, (*kodocli.RputExtra)(extra)) +} + +// ---------------------------------------------------------- diff --git a/vendor/github.com/qiniu/api.v7/kodocli/doc.go b/vendor/github.com/qiniu/api.v7/kodocli/doc.go new file mode 100644 index 0000000..5a4eb1e --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/kodocli/doc.go @@ -0,0 +1,67 @@ +/* +包 github.com/qiniu/api.v7/kodocli 提供了在客户端调用七牛云存储部分服务的能力 + +客户端,严谨说是非可信环境,主要是指在用户端执行的环境,比如:Android/iOS 设备、Windows/Mac/Linux 桌面环境、也包括浏览器(如果浏览器能够执行 Go 语言代码的话)。 + +注意,在这种场合下您不应该在任何地方配置 AccessKey/SecretKey。泄露 AccessKey/SecretKey 如同泄露您的用户名/密码一样十分危险,会影响您的数据安全。 + +第一个问题是如何上传文件。因为是在非可信环境,所以我们首先是要授予它有上传文件的能力。答案是给它颁发上传凭证。假设服务端也是 Go 语言,那么: + + import "github.com/qiniu/api.v7/kodo" + + kodo.SetMac("your-access-key", "your-secret-key") // 配置 AccessKey/SecretKey + + zone := kodo.ZoneZ0 + c := kodo.New(zone, nil) // 创建一个 Client 对象 + + bucket := "your-bucket-name" + key := "foo/bar.jpg" + policy := &kodo.PutPolicy{ + Scope: bucket + ":" + key, // 上传文件的限制条件,这里限制只能上传一个名为 "foo/bar.jpg" 的文件 + Expires: 3600, // 这是限制上传凭证(uptoken)的过期时长,3600 是一小时 + ... + } + uptoken := c.MakeUptoken(policy) // 生成上传凭证 + +生成上传凭证之后,通过某种方式将 uptoken 发送到客户端。这样客户端就可以上传文件了: + + zone := 0 + uploader := kodocli.NewUploader(zone, nil) + ctx := context.Background() + + key := "foo/bar.jpg" + localFile := "/your/local/image/file.jpg" + err := uploader.PutFile(ctx, nil, uptoken, key, localFile, nil) + if err != nil { + ... // 上传文件失败处理 + return + } + +注意,如果客户端上传的 key 不是 uptoken 所要求的 "foo/bar.jpg",那么上传就会被拒绝。 + +如果我们希望一个 uptoken 可以上传多个文件,那么服务端颁发 uptoken 的代码需要调整下: + + bucket := "your-bucket-name" + policy := &kodo.PutPolicy{ + Scope: bucket, // 上传文件的限制条件,这里是只限制了要上传到 "your-bucket-name" 空间 + Expires: 3600, // 这是限制上传凭证(uptoken)的过期时长,3600 是一小时 + ... + } + uptoken := c.MakeUptoken(policy) + +颁发这样的 uptoken 给客户端,客户端就可以用它上传任意名字(key)的文件,前提是服务器上还没有同名的文件。 + +特别需要注意的是,这种情况下服务端会拒绝已经有同名文件的上传请求,而不是覆盖服务器上已有的文件。这是出于数据安全的考虑。我们并不非常推荐 uptoken 复用的做法,除了注意文件名可能冲突外,还需要注意 uptoken 是有时效的,过期就需要重新向服务器申请新的上传凭证。 + +搞定文件的上传后,第二个问题是如何下载文件。如果是公开文件,我们只需要: + + import "net/http" + + domain := "domain-of-your-bucket.com" // 您的空间绑定的域名,这个可以在七牛的Portal中查到 + baseUrl := kodocli.MakeBaseUrl(domain, "foo/bar.jpg") // 得到下载 url + resp, err := http.Get(baseUrl) + ... + +但是对于私有空间,事情要复杂一些,需要客户端向您的业务服务器申请下载该文件。业务服务器确认该用户有权访问,则返回一个临时有效的 privateUrl。具体如何生成 privateUrl,可以看服务端 SDK 相关的文档介绍。 +*/ +package kodocli diff --git a/vendor/github.com/qiniu/api.v7/kodocli/main.go b/vendor/github.com/qiniu/api.v7/kodocli/main.go new file mode 100644 index 0000000..b880567 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/kodocli/main.go @@ -0,0 +1,114 @@ +package kodocli + +import ( + "net/http" + + "github.com/qiniu/api.v7/api" + "github.com/qiniu/api.v7/conf" + "github.com/qiniu/x/rpc.v7" + "github.com/qiniu/x/url.v7" +) + +// ---------------------------------------------------------- + +type zoneConfig struct { + UpHosts []string +} + +var zones = []zoneConfig{ + // z0: + { + UpHosts: []string{ + "http://upload.qiniu.com", + "http://up.qiniu.com", + "-H up.qiniu.com http://183.136.139.16", + }, + }, + // z1: + { + UpHosts: []string{ + "http://upload-z1.qiniu.com", + "http://up-z1.qiniu.com", + "-H up-z1.qiniu.com http://106.38.227.27", + }, + }, + // z2 华南机房: + { + UpHosts: []string{ + "http://up-z2.qiniu.com", + "http://upload-z2.qiniu.com", + }, + }, + // na0 北美机房: + { + UpHosts: []string{ + "http://up-na0.qiniu.com", + "http://upload-na0.qiniu.com", + }, + }, +} + +// ---------------------------------------------------------- + +type UploadConfig struct { + UpHosts []string + Transport http.RoundTripper + + APIHost string + Scheme string +} + +type Uploader struct { + Conn rpc.Client + UpHosts []string + ApiCli *api.Client +} + +func NewUploader(zone int, cfg *UploadConfig) (p Uploader) { + + var uc UploadConfig + if cfg != nil { + uc = *cfg + } + if uc.Scheme != "https" { + uc.Scheme = "http" + } + if uc.APIHost == "" { + uc.APIHost = api.DefaultApiHost + } + if len(uc.UpHosts) == 0 { + if zone > 0 && zone < len(zones) { + uc.UpHosts = zones[zone].UpHosts + } + } + + p.UpHosts = uc.UpHosts + p.Conn.Client = &http.Client{Transport: uc.Transport} + p.ApiCli = api.NewClient(uc.APIHost, uc.Scheme) + return +} + +func NewUploaderWithoutZone(cfg *UploadConfig) (p Uploader) { + return NewUploader(-1, cfg) +} + +// ---------------------------------------------------------- + +// 根据空间(Bucket)的域名,以及文件的 key,获得 baseUrl。 +// 如果空间是 public 的,那么通过 baseUrl 可以直接下载文件内容。 +// 如果空间是 private 的,那么需要对 baseUrl 进行私有签名得到一个临时有效的 privateUrl 进行下载。 +// +func MakeBaseUrl(domain, key string) (baseUrl string) { + return "http://" + domain + "/" + url.Escape(key) +} + +// ---------------------------------------------------------- + +// 设置使用这个SDK的应用程序名。userApp 必须满足 [A-Za-z0-9_\ \-\.]* +// +func SetAppName(userApp string) error { + + return conf.SetAppName(userApp) +} + +// ---------------------------------------------------------- diff --git a/vendor/github.com/qiniu/api.v7/kodocli/resumable.go b/vendor/github.com/qiniu/api.v7/kodocli/resumable.go new file mode 100644 index 0000000..1f56a1c --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/kodocli/resumable.go @@ -0,0 +1,328 @@ +package kodocli + +import ( + . "context" + "encoding/base64" + "encoding/json" + "errors" + "io" + "os" + "strings" + "sync" + + "github.com/qiniu/x/xlog.v7" +) + +// ---------------------------------------------------------- + +var ( + ErrInvalidPutProgress = errors.New("invalid put progress") + ErrPutFailed = errors.New("resumable put failed") + ErrUnmatchedChecksum = errors.New("unmatched checksum") + ErrBadToken = errors.New("invalid token") +) + +const ( + InvalidCtx = 701 // UP: 无效的上下文(bput),可能情况:Ctx非法或者已经被淘汰(太久未使用) +) + +const ( + defaultWorkers = 4 + defaultChunkSize = 1 * 1024 * 1024 // 1M + defaultTryTimes = 3 +) + +type Settings struct { + TaskQsize int // 可选。任务队列大小。为 0 表示取 Workers * 4。 + Workers int // 并行 Goroutine 数目。 + ChunkSize int // 默认的Chunk大小,不设定则为1M + TryTimes int // 默认的尝试次数,不设定则为3 +} + +var settings = Settings{ + TaskQsize: defaultWorkers * 4, + Workers: defaultWorkers, + ChunkSize: defaultChunkSize, + TryTimes: defaultTryTimes, +} + +func SetSettings(v *Settings) { + + settings = *v + if settings.Workers == 0 { + settings.Workers = defaultWorkers + } + if settings.TaskQsize == 0 { + settings.TaskQsize = settings.Workers * 4 + } + if settings.ChunkSize == 0 { + settings.ChunkSize = defaultChunkSize + } + if settings.TryTimes == 0 { + settings.TryTimes = defaultTryTimes + } +} + +// ---------------------------------------------------------- + +var tasks chan func() + +func worker(tasks chan func()) { + for { + task := <-tasks + task() + } +} + +func initWorkers() { + + tasks = make(chan func(), settings.TaskQsize) + for i := 0; i < settings.Workers; i++ { + go worker(tasks) + } +} + +func notifyNil(blkIdx int, blkSize int, ret *BlkputRet) {} +func notifyErrNil(blkIdx int, blkSize int, err error) {} + +// ---------------------------------------------------------- + +const ( + blockBits = 22 + blockMask = (1 << blockBits) - 1 +) + +func BlockCount(fsize int64) int { + return int((fsize + blockMask) >> blockBits) +} + +// ---------------------------------------------------------- + +type BlkputRet struct { + Ctx string `json:"ctx"` + Checksum string `json:"checksum"` + Crc32 uint32 `json:"crc32"` + Offset uint32 `json:"offset"` + Host string `json:"host"` +} + +type RputExtra struct { + Params map[string]string // 可选。用户自定义参数,以"x:"开头 否则忽略 + MimeType string // 可选。 + ChunkSize int // 可选。每次上传的Chunk大小 + TryTimes int // 可选。尝试次数 + Progresses []BlkputRet // 可选。上传进度 + Notify func(blkIdx int, blkSize int, ret *BlkputRet) // 可选。进度提示(注意多个block是并行传输的) + NotifyErr func(blkIdx int, blkSize int, err error) +} + +var once sync.Once + +// ---------------------------------------------------------- + +type Policy struct { + Scope string `json:"scope"` + UpHosts []string `json:"uphosts"` +} + +func unmarshal(uptoken string, uptokenPolicy *Policy) (err error) { + parts := strings.Split(uptoken, ":") + if len(parts) != 3 { + err = ErrBadToken + return + } + b, err := base64.URLEncoding.DecodeString(parts[2]) + if err != nil { + return err + } + return json.Unmarshal(b, uptokenPolicy) +} + +func (p Uploader) getUpHostFromToken(uptoken string) (uphosts []string, err error) { + if len(p.UpHosts) != 0 { + uphosts = p.UpHosts + return + } + ak := strings.Split(uptoken, ":")[0] + uptokenPolicy := Policy{} + err = unmarshal(uptoken, &uptokenPolicy) + if err != nil { + return + } + if len(uptokenPolicy.UpHosts) == 0 { + bucketName := strings.Split(uptokenPolicy.Scope, ":")[0] + bucketInfo, err1 := p.ApiCli.GetBucketInfo(ak, bucketName) + if err1 != nil { + err = err1 + return + } + uphosts = bucketInfo.UpHosts + } else { + uphosts = uptokenPolicy.UpHosts + } + return +} + +// 上传一个文件,支持断点续传和分块上传。 +// +// ctx 是请求的上下文。 +// ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody,那么返回的数据结构是 PutRet 结构。 +// uptoken 是由业务服务器颁发的上传凭证。 +// key 是要上传的文件访问路径。比如:"foo/bar.jpg"。注意我们建议 key 不要以 '/' 开头。另外,key 为空字符串是合法的。 +// f 是文件内容的访问接口。考虑到需要支持分块上传和断点续传,要的是 io.ReaderAt 接口,而不是 io.Reader。 +// fsize 是要上传的文件大小。 +// extra 是上传的一些可选项。详细见 RputExtra 结构的描述。 +// +func (p Uploader) Rput( + ctx Context, ret interface{}, uptoken string, + key string, f io.ReaderAt, fsize int64, extra *RputExtra) error { + + return p.rput(ctx, ret, uptoken, key, true, f, fsize, extra) +} + +// 上传一个文件,支持断点续传和分块上传。文件的访问路径(key)自动生成。 +// 如果 uptoken 中设置了 SaveKey,那么按 SaveKey 要求的规则生成 key,否则自动以文件的 hash 做 key。 +// +// ctx 是请求的上下文。 +// ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody,那么返回的数据结构是 PutRet 结构。 +// uptoken 是由业务服务器颁发的上传凭证。 +// f 是文件内容的访问接口。考虑到需要支持分块上传和断点续传,要的是 io.ReaderAt 接口,而不是 io.Reader。 +// fsize 是要上传的文件大小。 +// extra 是上传的一些可选项。详细见 RputExtra 结构的描述。 +// +func (p Uploader) RputWithoutKey( + ctx Context, ret interface{}, uptoken string, f io.ReaderAt, fsize int64, extra *RputExtra) error { + + return p.rput(ctx, ret, uptoken, "", false, f, fsize, extra) +} + +// 上传一个文件,支持断点续传和分块上传。 +// 和 Rput 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.ReaderAt 来访问。 +// +// ctx 是请求的上下文。 +// ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody,那么返回的数据结构是 PutRet 结构。 +// uptoken 是由业务服务器颁发的上传凭证。 +// key 是要上传的文件访问路径。比如:"foo/bar.jpg"。注意我们建议 key 不要以 '/' 开头。另外,key 为空字符串是合法的。 +// localFile 是要上传的文件的本地路径。 +// extra 是上传的一些可选项。详细见 RputExtra 结构的描述。 +// +func (p Uploader) RputFile( + ctx Context, ret interface{}, uptoken, key, localFile string, extra *RputExtra) (err error) { + + return p.rputFile(ctx, ret, uptoken, key, true, localFile, extra) +} + +// 上传一个文件,支持断点续传和分块上传。文件的访问路径(key)自动生成。 +// 如果 uptoken 中设置了 SaveKey,那么按 SaveKey 要求的规则生成 key,否则自动以文件的 hash 做 key。 +// 和 RputWithoutKey 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.ReaderAt 来访问。 +// +// ctx 是请求的上下文。 +// ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody,那么返回的数据结构是 PutRet 结构。 +// uptoken 是由业务服务器颁发的上传凭证。 +// localFile 是要上传的文件的本地路径。 +// extra 是上传的一些可选项。详细见 RputExtra 结构的描述。 +// +func (p Uploader) RputFileWithoutKey( + ctx Context, ret interface{}, uptoken, localFile string, extra *RputExtra) (err error) { + + return p.rputFile(ctx, ret, uptoken, "", false, localFile, extra) +} + +// ---------------------------------------------------------- + +func (p Uploader) rput( + ctx Context, ret interface{}, uptoken string, + key string, hasKey bool, f io.ReaderAt, fsize int64, extra *RputExtra) error { + + once.Do(initWorkers) + + log := xlog.NewWith(ctx) + blockCnt := BlockCount(fsize) + + if extra == nil { + extra = new(RputExtra) + } + if extra.Progresses == nil { + extra.Progresses = make([]BlkputRet, blockCnt) + } else if len(extra.Progresses) != blockCnt { + return ErrInvalidPutProgress + } + + if extra.ChunkSize == 0 { + extra.ChunkSize = settings.ChunkSize + } + if extra.TryTimes == 0 { + extra.TryTimes = settings.TryTimes + } + if extra.Notify == nil { + extra.Notify = notifyNil + } + if extra.NotifyErr == nil { + extra.NotifyErr = notifyErrNil + } + uphosts, err := p.getUpHostFromToken(uptoken) + if err != nil { + return err + } + + var wg sync.WaitGroup + wg.Add(blockCnt) + + last := blockCnt - 1 + blkSize := 1 << blockBits + nfails := 0 + p.Conn.Client = newUptokenClient(uptoken, p.Conn.Transport) + + for i := 0; i < blockCnt; i++ { + blkIdx := i + blkSize1 := blkSize + if i == last { + offbase := int64(blkIdx) << blockBits + blkSize1 = int(fsize - offbase) + } + task := func() { + defer wg.Done() + tryTimes := extra.TryTimes + lzRetry: + err := p.resumableBput(ctx, uphosts, &extra.Progresses[blkIdx], f, blkIdx, blkSize1, extra) + if err != nil { + if tryTimes > 1 { + tryTimes-- + log.Info("resumable.Put retrying ...", blkIdx, "reason:", err) + goto lzRetry + } + log.Warn("resumable.Put", blkIdx, "failed:", err) + extra.NotifyErr(blkIdx, blkSize1, err) + nfails++ + } + } + tasks <- task + } + + wg.Wait() + if nfails != 0 { + return ErrPutFailed + } + + return p.mkfile(ctx, uphosts, ret, key, hasKey, fsize, extra) +} + +func (p Uploader) rputFile( + ctx Context, ret interface{}, uptoken string, + key string, hasKey bool, localFile string, extra *RputExtra) (err error) { + + f, err := os.Open(localFile) + if err != nil { + return + } + defer f.Close() + + fi, err := f.Stat() + if err != nil { + return + } + + return p.rput(ctx, ret, uptoken, key, hasKey, f, fi.Size(), extra) +} + +// ---------------------------------------------------------- diff --git a/vendor/github.com/qiniu/api.v7/kodocli/resumable_base.go b/vendor/github.com/qiniu/api.v7/kodocli/resumable_base.go new file mode 100644 index 0000000..7b84b66 --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/kodocli/resumable_base.go @@ -0,0 +1,172 @@ +package kodocli + +import ( + . "context" + "encoding/base64" + "fmt" + "hash/crc32" + "io" + "net/http" + "strconv" + + "github.com/qiniu/x/bytes.v7" + "github.com/qiniu/x/rpc.v7" + "github.com/qiniu/x/xlog.v7" +) + +// ---------------------------------------------------------- + +type uptokenTransport struct { + token string + Transport http.RoundTripper +} + +func (t *uptokenTransport) NestedObject() interface{} { + return t.Transport +} + +func (t *uptokenTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) { + req.Header.Set("Authorization", t.token) + return t.Transport.RoundTrip(req) +} + +func newUptokenTransport(token string, transport http.RoundTripper) *uptokenTransport { + if transport == nil { + transport = http.DefaultTransport + } + return &uptokenTransport{"UpToken " + token, transport} +} + +func newUptokenClient(token string, transport http.RoundTripper) *http.Client { + t := newUptokenTransport(token, transport) + return &http.Client{Transport: t} +} + +// ---------------------------------------------------------- + +func (p Uploader) mkblk( + ctx Context, uphosts []string, ret *BlkputRet, blockSize int, body io.Reader, size int) error { + + url := uphosts[0] + "/mkblk/" + strconv.Itoa(blockSize) + return p.Conn.CallWith(ctx, ret, "POST", url, "application/octet-stream", body, size) +} + +func (p Uploader) bput( + ctx Context, ret *BlkputRet, body io.Reader, size int) error { + + url := ret.Host + "/bput/" + ret.Ctx + "/" + strconv.FormatUint(uint64(ret.Offset), 10) + return p.Conn.CallWith(ctx, ret, "POST", url, "application/octet-stream", body, size) +} + +// ---------------------------------------------------------- + +func (p Uploader) resumableBput( + ctx Context, uphosts []string, ret *BlkputRet, f io.ReaderAt, blkIdx, blkSize int, extra *RputExtra) (err error) { + + log := xlog.NewWith(ctx) + h := crc32.NewIEEE() + offbase := int64(blkIdx) << blockBits + chunkSize := extra.ChunkSize + + var bodyLength int + + if ret.Ctx == "" { + + if chunkSize < blkSize { + bodyLength = chunkSize + } else { + bodyLength = blkSize + } + + body1 := io.NewSectionReader(f, offbase, int64(bodyLength)) + body := io.TeeReader(body1, h) + + err = p.mkblk(ctx, uphosts, ret, blkSize, body, bodyLength) + if err != nil { + return + } + if ret.Crc32 != h.Sum32() || int(ret.Offset) != bodyLength { + err = ErrUnmatchedChecksum + return + } + extra.Notify(blkIdx, blkSize, ret) + } + + for int(ret.Offset) < blkSize { + + if chunkSize < blkSize-int(ret.Offset) { + bodyLength = chunkSize + } else { + bodyLength = blkSize - int(ret.Offset) + } + + tryTimes := extra.TryTimes + + lzRetry: + h.Reset() + body1 := io.NewSectionReader(f, offbase+int64(ret.Offset), int64(bodyLength)) + body := io.TeeReader(body1, h) + + err = p.bput(ctx, ret, body, bodyLength) + if err == nil { + if ret.Crc32 == h.Sum32() { + extra.Notify(blkIdx, blkSize, ret) + continue + } + log.Warn("ResumableBlockput: invalid checksum, retry") + err = ErrUnmatchedChecksum + } else { + if ei, ok := err.(*rpc.ErrorInfo); ok && ei.Code == InvalidCtx { + ret.Ctx = "" // reset + log.Warn("ResumableBlockput: invalid ctx, please retry") + return + } + log.Warn("ResumableBlockput: bput failed -", err) + } + if tryTimes > 1 { + tryTimes-- + log.Info("ResumableBlockput retrying ...") + goto lzRetry + } + break + } + return +} + +// ---------------------------------------------------------- + +func (p Uploader) mkfile( + ctx Context, uphosts []string, ret interface{}, key string, hasKey bool, fsize int64, extra *RputExtra) (err error) { + + url := uphosts[0] + "/mkfile/" + strconv.FormatInt(fsize, 10) + + if extra.MimeType != "" { + url += "/mimeType/" + encode(extra.MimeType) + } + if hasKey { + url += "/key/" + encode(key) + } + for k, v := range extra.Params { + url += fmt.Sprintf("/%s/%s", k, encode(v)) + } + + buf := make([]byte, 0, 176*len(extra.Progresses)) + for _, prog := range extra.Progresses { + buf = append(buf, prog.Ctx...) + buf = append(buf, ',') + } + if len(buf) > 0 { + buf = buf[:len(buf)-1] + } + + return p.Conn.CallWith( + ctx, ret, "POST", url, "application/octet-stream", bytes.NewReader(buf), len(buf)) +} + +// ---------------------------------------------------------- + +func encode(raw string) string { + return base64.URLEncoding.EncodeToString([]byte(raw)) +} + +// ---------------------------------------------------------- diff --git a/vendor/github.com/qiniu/api.v7/kodocli/upload.go b/vendor/github.com/qiniu/api.v7/kodocli/upload.go new file mode 100644 index 0000000..02eab4e --- /dev/null +++ b/vendor/github.com/qiniu/api.v7/kodocli/upload.go @@ -0,0 +1,282 @@ +package kodocli + +import ( + "bytes" + . "context" + "fmt" + "hash/crc32" + "io" + "mime/multipart" + "net/textproto" + "os" + "path" + "path/filepath" + "strconv" + "strings" +) + +// ---------------------------------------------------------- + +const ( + DontCheckCrc = 0 + CalcAndCheckCrc = 1 + CheckCrc = 2 +) + +// 上传的额外可选项 +// +type PutExtra struct { + // 可选,用户自定义参数,必须以 "x:" 开头。若不以x:开头,则忽略。 + Params map[string]string + + // 可选,当为 "" 时候,服务端自动判断。 + MimeType string + + Crc32 uint32 + + // CheckCrc == 0 (DontCheckCrc): 表示不进行 crc32 校验 + // CheckCrc == 1 (CalcAndCheckCrc): 对于 Put 等同于 CheckCrc = 2;对于 PutFile 会自动计算 crc32 值 + // CheckCrc == 2 (CheckCrc): 表示进行 crc32 校验,且 crc32 值就是上面的 Crc32 变量 + CheckCrc uint32 + + // 上传事件:进度通知。这个事件的回调函数应该尽可能快地结束。 + OnProgress func(fsize, uploaded int64) +} + +// ---------------------------------------------------------- + +// 如果 uptoken 没有指定 ReturnBody,那么返回值是标准的 PutRet 结构 +// +type PutRet struct { + Hash string `json:"hash"` + PersistentId string `json:"persistentId"` + Key string `json:"key"` +} + +// ---------------------------------------------------------- + +// 上传一个文件。 +// 和 Put 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.Reader 来访问。 +// +// ctx 是请求的上下文。 +// ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody,那么返回的数据结构是 PutRet 结构。 +// uptoken 是由业务服务器颁发的上传凭证。 +// key 是要上传的文件访问路径。比如:"foo/bar.jpg"。注意我们建议 key 不要以 '/' 开头。另外,key 为空字符串是合法的。 +// localFile 是要上传的文件的本地路径。 +// extra 是上传的一些可选项。详细见 PutExtra 结构的描述。 +// +func (p Uploader) PutFile( + ctx Context, ret interface{}, uptoken, key, localFile string, extra *PutExtra) (err error) { + + return p.putFile(ctx, ret, uptoken, key, true, localFile, extra) +} + +// 上传一个文件。文件的访问路径(key)自动生成。 +// 如果 uptoken 中设置了 SaveKey,那么按 SaveKey 要求的规则生成 key,否则自动以文件的 hash 做 key。 +// 和 RputWithoutKey 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.Reader 来访问。 +// +// ctx 是请求的上下文。 +// ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody,那么返回的数据结构是 PutRet 结构。 +// uptoken 是由业务服务器颁发的上传凭证。 +// localFile 是要上传的文件的本地路径。 +// extra 是上传的一些可选项。详细见 PutExtra 结构的描述。 +// +func (p Uploader) PutFileWithoutKey( + ctx Context, ret interface{}, uptoken, localFile string, extra *PutExtra) (err error) { + + return p.putFile(ctx, ret, uptoken, "", false, localFile, extra) +} + +func (p Uploader) putFile( + ctx Context, ret interface{}, uptoken string, + key string, hasKey bool, localFile string, extra *PutExtra) (err error) { + + f, err := os.Open(localFile) + if err != nil { + return + } + defer f.Close() + + fi, err := f.Stat() + if err != nil { + return + } + fsize := fi.Size() + + if extra != nil && extra.CheckCrc == 1 { + extra.Crc32, err = getFileCrc32(f) + if err != nil { + return + } + } + return p.put(ctx, ret, uptoken, key, hasKey, f, fsize, extra, filepath.Base(localFile)) +} + +// ---------------------------------------------------------- + +// 上传一个文件。 +// +// ctx 是请求的上下文。 +// ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody,那么返回的数据结构是 PutRet 结构。 +// uptoken 是由业务服务器颁发的上传凭证。 +// key 是要上传的文件访问路径。比如:"foo/bar.jpg"。注意我们建议 key 不要以 '/' 开头。另外,key 为空字符串是合法的。 +// data 是文件内容的访问接口(io.Reader)。 +// fsize 是要上传的文件大小。 +// extra 是上传的一些可选项。详细见 PutExtra 结构的描述。 +// +func (p Uploader) Put( + ctx Context, ret interface{}, uptoken, key string, data io.Reader, size int64, extra *PutExtra) error { + + return p.put(ctx, ret, uptoken, key, true, data, size, extra, path.Base(key)) +} + +// 上传一个文件。文件的访问路径(key)自动生成。 +// 如果 uptoken 中设置了 SaveKey,那么按 SaveKey 要求的规则生成 key,否则自动以文件的 hash 做 key。 +// +// ctx 是请求的上下文。 +// ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody,那么返回的数据结构是 PutRet 结构。 +// uptoken 是由业务服务器颁发的上传凭证。 +// data 是文件内容的访问接口(io.Reader)。 +// fsize 是要上传的文件大小。 +// extra 是上传的一些可选项。详细见 PutExtra 结构的描述。 +// +func (p Uploader) PutWithoutKey( + ctx Context, ret interface{}, uptoken string, data io.Reader, size int64, extra *PutExtra) error { + + return p.put(ctx, ret, uptoken, "", false, data, size, extra, "filename") +} + +// ---------------------------------------------------------- + +var defaultPutExtra PutExtra + +func (p Uploader) put( + ctx Context, ret interface{}, uptoken string, + key string, hasKey bool, data io.Reader, size int64, extra *PutExtra, fileName string) error { + + uphosts, err := p.getUpHostFromToken(uptoken) + if err != nil { + return err + } + var b bytes.Buffer + writer := multipart.NewWriter(&b) + + if extra == nil { + extra = &defaultPutExtra + } + + if extra.OnProgress != nil { + data = &readerWithProgress{reader: data, fsize: size, onProgress: extra.OnProgress} + } + + err = writeMultipart(writer, uptoken, key, hasKey, extra, fileName) + if err != nil { + return err + } + + lastLine := fmt.Sprintf("\r\n--%s--\r\n", writer.Boundary()) + r := strings.NewReader(lastLine) + + bodyLen := int64(-1) + if size >= 0 { + bodyLen = int64(b.Len()) + size + int64(len(lastLine)) + } + mr := io.MultiReader(&b, data, r) + + contentType := writer.FormDataContentType() + err = p.Conn.CallWith64(ctx, ret, "POST", uphosts[0], contentType, mr, bodyLen) + if err != nil { + return err + } + if extra.OnProgress != nil { + extra.OnProgress(size, size) + } + return err +} + +// ---------------------------------------------------------- + +type readerWithProgress struct { + reader io.Reader + uploaded int64 + fsize int64 + onProgress func(fsize, uploaded int64) +} + +func (p *readerWithProgress) Read(b []byte) (n int, err error) { + + if p.uploaded > 0 { + p.onProgress(p.fsize, p.uploaded) + } + + n, err = p.reader.Read(b) + p.uploaded += int64(n) + return +} + +// ---------------------------------------------------------- + +func writeMultipart( + writer *multipart.Writer, uptoken, key string, hasKey bool, extra *PutExtra, fileName string) (err error) { + + //token + if err = writer.WriteField("token", uptoken); err != nil { + return + } + + //key + if hasKey { + if err = writer.WriteField("key", key); err != nil { + return + } + } + + //extra.Params + if extra.Params != nil { + for k, v := range extra.Params { + if strings.HasPrefix(k, "x:") { + err = writer.WriteField(k, v) + if err != nil { + return + } + } + } + } + + //extra.CheckCrc + if extra.CheckCrc != 0 { + err = writer.WriteField("crc32", strconv.FormatInt(int64(extra.Crc32), 10)) + if err != nil { + return + } + } + + //file + head := make(textproto.MIMEHeader) + head.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file"; filename="%s"`, escapeQuotes(fileName))) + if extra.MimeType != "" { + head.Set("Content-Type", extra.MimeType) + } + + _, err = writer.CreatePart(head) + return err +} + +var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"") + +func escapeQuotes(s string) string { + return quoteEscaper.Replace(s) +} + +// ---------------------------------------------------------- + +func getFileCrc32(f *os.File) (uint32, error) { + + h := crc32.NewIEEE() + _, err := io.Copy(h, f) + f.Seek(0, 0) + + return h.Sum32(), err +} + +// ---------------------------------------------------------- diff --git a/vendor/github.com/qiniu/x/.gitignore b/vendor/github.com/qiniu/x/.gitignore new file mode 100644 index 0000000..daf913b --- /dev/null +++ b/vendor/github.com/qiniu/x/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/qiniu/x/.travis.yml b/vendor/github.com/qiniu/x/.travis.yml new file mode 100644 index 0000000..17a587c --- /dev/null +++ b/vendor/github.com/qiniu/x/.travis.yml @@ -0,0 +1,10 @@ +language: go +install: + - export QINIU_SRC=$HOME/gopath/src + - mkdir -p $QINIU_SRC/qiniupkg.com + - mv $QINIU_SRC/github.com/qiniu/x $QINIU_SRC/qiniupkg.com/x + - export TRAVIS_BUILD_DIR=$QINIU_SRC/qiniupkg.com/x + - cd $TRAVIS_BUILD_DIR + - go get golang.org/x/net/context + - go get github.com/stretchr/testify/assert + diff --git a/vendor/github.com/qiniu/x/README.md b/vendor/github.com/qiniu/x/README.md new file mode 100644 index 0000000..12de17b --- /dev/null +++ b/vendor/github.com/qiniu/x/README.md @@ -0,0 +1,27 @@ +qiniupkg.com/x +=============== + +[![Build Status](https://travis-ci.org/qiniu/x.svg?branch=develop)](https://travis-ci.org/qiniu/x) [![GoDoc](https://godoc.org/qiniupkg.com/x?status.svg)](https://godoc.org/qiniupkg.com/x) + +# 下载 + +``` +go get qiniupkg.com/x +``` + +# 使用文档 + +* [qiniupkg.com/x/bytes.v7](http://godoc.org/qiniupkg.com/x/bytes.v7) +* [qiniupkg.com/x/cmdline.v7](http://godoc.org/qiniupkg.com/x/cmdline.v7) +* [qiniupkg.com/x/config.v7](http://godoc.org/qiniupkg.com/x/config.v7) +* [qiniupkg.com/x/ctype.v7](http://godoc.org/qiniupkg.com/x/ctype.v7) +* [qiniupkg.com/x/jsonutil.v7](http://godoc.org/qiniupkg.com/x/jsonutil.v7) +* [qiniupkg.com/x/log.v7](http://godoc.org/qiniupkg.com/x/log.v7) +* [qiniupkg.com/x/mockhttp.v7](http://godoc.org/qiniupkg.com/x/mockhttp.v7) +* [qiniupkg.com/x/reqid.v7](http://godoc.org/qiniupkg.com/x/reqid.v7) +* [qiniupkg.com/x/rpc.v7](http://godoc.org/qiniupkg.com/x/rpc.v7) +* [qiniupkg.com/x/rpc.v7/gob](http://godoc.org/qiniupkg.com/x/rpc.v7/gob) +* [qiniupkg.com/x/ts.v7](http://godoc.org/qiniupkg.com/x/ts.v7) +* [qiniupkg.com/x/url.v7](http://godoc.org/qiniupkg.com/x/url.v7) +* [qiniupkg.com/x/xlog.v7](http://godoc.org/qiniupkg.com/x/xlog.v7) + diff --git a/vendor/github.com/qiniu/x/bytes.v7/README.md b/vendor/github.com/qiniu/x/bytes.v7/README.md new file mode 100644 index 0000000..028478b --- /dev/null +++ b/vendor/github.com/qiniu/x/bytes.v7/README.md @@ -0,0 +1,4 @@ +qiniupkg.com/x/bytes.v7 +===== + +Extension module of golang bytes processing diff --git a/vendor/github.com/qiniu/x/bytes.v7/bytes.go b/vendor/github.com/qiniu/x/bytes.v7/bytes.go new file mode 100644 index 0000000..a4a4f63 --- /dev/null +++ b/vendor/github.com/qiniu/x/bytes.v7/bytes.go @@ -0,0 +1,177 @@ +package bytes + +import ( + "io" + "syscall" +) + +// --------------------------------------------------- + +type Reader struct { + b []byte + off int +} + +func NewReader(val []byte) *Reader { + return &Reader{val, 0} +} + +func (r *Reader) Len() int { + if r.off >= len(r.b) { + return 0 + } + return len(r.b) - r.off +} + +func (r *Reader) Bytes() []byte { + return r.b[r.off:] +} + +func (r *Reader) SeekToBegin() (err error) { + r.off = 0 + return +} + +func (r *Reader) Seek(offset int64, whence int) (ret int64, err error) { + switch whence { + case 0: + case 1: + offset += int64(r.off) + case 2: + offset += int64(len(r.b)) + default: + err = syscall.EINVAL + return + } + if offset < 0 { + err = syscall.EINVAL + return + } + if offset >= int64(len(r.b)) { + r.off = len(r.b) + } else { + r.off = int(offset) + } + ret = int64(r.off) + return +} + +func (r *Reader) Read(val []byte) (n int, err error) { + n = copy(val, r.b[r.off:]) + if n == 0 && len(val) != 0 { + err = io.EOF + return + } + r.off += n + return +} + +func (r *Reader) Close() (err error) { + return +} + +// --------------------------------------------------- + +type Writer struct { + b []byte + n int +} + +func NewWriter(buff []byte) *Writer { + return &Writer{buff, 0} +} + +func (p *Writer) Write(val []byte) (n int, err error) { + n = copy(p.b[p.n:], val) + if n == 0 && len(val) > 0 { + err = io.EOF + return + } + p.n += n + return +} + +func (p *Writer) Len() int { + return p.n +} + +func (p *Writer) Bytes() []byte { + return p.b[:p.n] +} + +func (p *Writer) Reset() { + p.n = 0 +} + +// --------------------------------------------------- + +type Buffer struct { + b []byte +} + +func NewBuffer() *Buffer { + return new(Buffer) +} + +func (p *Buffer) ReadAt(buf []byte, off int64) (n int, err error) { + ioff := int(off) + if len(p.b) <= ioff { + return 0, io.EOF + } + n = copy(buf, p.b[ioff:]) + if n != len(buf) { + err = io.EOF + } + return +} + +func (p *Buffer) WriteAt(buf []byte, off int64) (n int, err error) { + ioff := int(off) + iend := ioff + len(buf) + if len(p.b) < iend { + if len(p.b) == ioff { + p.b = append(p.b, buf...) + return len(buf), nil + } + zero := make([]byte, iend-len(p.b)) + p.b = append(p.b, zero...) + } + copy(p.b[ioff:], buf) + return len(buf), nil +} + +func (p *Buffer) WriteStringAt(buf string, off int64) (n int, err error) { + ioff := int(off) + iend := ioff + len(buf) + if len(p.b) < iend { + if len(p.b) == ioff { + p.b = append(p.b, buf...) + return len(buf), nil + } + zero := make([]byte, iend-len(p.b)) + p.b = append(p.b, zero...) + } + copy(p.b[ioff:], buf) + return len(buf), nil +} + +func (p *Buffer) Truncate(fsize int64) (err error) { + size := int(fsize) + if len(p.b) < size { + zero := make([]byte, size-len(p.b)) + p.b = append(p.b, zero...) + } else { + p.b = p.b[:size] + } + return nil +} + +func (p *Buffer) Buffer() []byte { + return p.b +} + +func (p *Buffer) Len() int { + return len(p.b) +} + +// --------------------------------------------------- diff --git a/vendor/github.com/qiniu/x/bytes.v7/bytes_test.go b/vendor/github.com/qiniu/x/bytes.v7/bytes_test.go new file mode 100644 index 0000000..76e9737 --- /dev/null +++ b/vendor/github.com/qiniu/x/bytes.v7/bytes_test.go @@ -0,0 +1,60 @@ +package bytes + +import ( + "io" + "testing" +) + +// --------------------------------------------------- + +func TestBuffer(t *testing.T) { + + b := NewBuffer() + n, err := b.WriteStringAt("Hello", 4) + if n != 5 || err != nil { + t.Fatal("WriteStringAt failed:", n, err) + } + if b.Len() != 9 { + t.Fatal("Buffer.Len invalid (9 is required):", b.Len()) + } + + buf := make([]byte, 10) + n, err = b.ReadAt(buf, 50) + if n != 0 || err != io.EOF { + t.Fatal("ReadAt failed:", n, err) + } + + n, err = b.ReadAt(buf, 6) + if n != 3 || err != io.EOF || string(buf[:n]) != "llo" { + t.Fatal("ReadAt failed:", n, err, string(buf[:n])) + } + + n, err = b.WriteAt([]byte("Hi h"), 1) + if n != 4 || err != nil { + t.Fatal("WriteAt failed:", n, err) + } + if b.Len() != 9 { + t.Fatal("Buffer.Len invalid (9 is required):", b.Len()) + } + + n, err = b.ReadAt(buf, 0) + if n != 9 || err != io.EOF || string(buf[:n]) != "\x00Hi hello" { + t.Fatal("ReadAt failed:", n, err) + } + + n, err = b.WriteStringAt("LO world!", 7) + if n != 9 || err != nil { + t.Fatal("WriteStringAt failed:", n, err) + } + if b.Len() != 16 { + t.Fatal("Buffer.Len invalid (16 is required):", b.Len()) + } + + buf = make([]byte, 17) + n, err = b.ReadAt(buf, 0) + if n != 16 || err != io.EOF || string(buf[:n]) != "\x00Hi helLO world!" { + t.Fatal("ReadAt failed:", n, err, string(buf[:n])) + } +} + +// --------------------------------------------------- diff --git a/vendor/github.com/qiniu/x/bytes.v7/doc.go b/vendor/github.com/qiniu/x/bytes.v7/doc.go new file mode 100644 index 0000000..cdec535 --- /dev/null +++ b/vendor/github.com/qiniu/x/bytes.v7/doc.go @@ -0,0 +1,34 @@ +/* +包 qiniupkg.com/x/bytes.v7 提供了 byte slice 相关的功能扩展 + +NewReader 创建一个 byte slice 的只读流: + + var slice []byte + ... + r := bytes.NewReader(slice) + ... + r.Seek(0, 0) // r.SeekToBegin() + ... + +和标准库的 bytes.NewReader 不同的是,这里的 Reader 支持 Seek。 + +NewWriter 创建一个有上限容量的写流: + + slice := make([]byte, 1024) + w := bytes.NewWriter(slice) + ... + writtenData := w.Bytes() + +如果我们向 w 里面写入超过 1024 字节的数据,那么多余的数据会被丢弃。 + +NewBuffer 创建一个可随机读写的内存文件,支持 ReadAt/WriteAt 方法,而不是 Read/Write: + + b := bytes.NewBuffer() + b.Truncate(100) + b.WriteAt([]byte("hello"), 100) + slice := make([]byte, 105) + n, err := b.ReadAt(slice, 0) + ... +*/ +package bytes + diff --git a/vendor/github.com/qiniu/x/bytes.v7/replace.go b/vendor/github.com/qiniu/x/bytes.v7/replace.go new file mode 100644 index 0000000..a62a161 --- /dev/null +++ b/vendor/github.com/qiniu/x/bytes.v7/replace.go @@ -0,0 +1,54 @@ +package bytes + +import ( + "bytes" +) + +// --------------------------------------------------- + +func ReplaceAt(b []byte, off, nsrc int, dest []byte) []byte { + + ndelta := len(dest) - nsrc + if ndelta < 0 { + left := b[off+nsrc:] + off += copy(b[off:], dest) + off += copy(b[off:], left) + return b[:off] + } + + if ndelta > 0 { + b = append(b, dest[:ndelta]...) + copy(b[off+len(dest):], b[off+nsrc:]) + copy(b[off:], dest) + } else { + copy(b[off:], dest) + } + return b +} + +func ReplaceOne(b []byte, from int, src, dest []byte) ([]byte, int) { + + pos := bytes.Index(b[from:], src) + if pos < 0 { + return b, -1 + } + + from += pos + return ReplaceAt(b, from, len(src), dest), from + len(dest) +} + +func Replace(b []byte, src, dest []byte, n int) []byte { + + from := 0 + for n != 0 { + b, from = ReplaceOne(b, from, src, dest) + if from < 0 { + break + } + n-- + } + return b +} + +// --------------------------------------------------- + diff --git a/vendor/github.com/qiniu/x/bytes.v7/replace_test.go b/vendor/github.com/qiniu/x/bytes.v7/replace_test.go new file mode 100644 index 0000000..bf1925f --- /dev/null +++ b/vendor/github.com/qiniu/x/bytes.v7/replace_test.go @@ -0,0 +1,54 @@ +package bytes + +import ( + "strings" + "testing" +) + +type replaceCase struct { + s string + src string + dest string + n int +} + +func stringReplace(b string, src, dest string, n int) string { + + return string(Replace([]byte(b), []byte(src), []byte(dest), n)) +} + +func TestReplace(t *testing.T) { + + cases := []replaceCase{ + {"hello, world!", "world", "xsw", -1}, + {"hello, world world world", "world", "xsw", 1}, + {"hello, world world world", "world", "xsw", 2}, + {"hello, world world world", "world", "xsw", -1}, + {"hello, xsw!", "xsw", "world", -1}, + {"hello, xsw xsw xsw", "xsw", "world", 1}, + {"hello, xsw xsw xsw", "xsw", "world", 2}, + {"hello, xsw xsw xsw", "xsw", "world", -1}, + } + + for _, c := range cases { + ret := stringReplace(c.s, c.src, c.dest, c.n) + expected := strings.Replace(c.s, c.src, c.dest, c.n) + if ret != expected { + t.Fatal("Replace failed:", c, "ret:", ret, "expected:", expected) + } + } +} + +func stringInsertAt(b string, off int, text string) string { + + return string(ReplaceAt([]byte(b), off, 0, []byte(text))) +} + +func TestInsertAt(t *testing.T) { + + ret := stringInsertAt("helloworld", 5, ", ") + if ret != "hello, world" { + t.Fatal("InsertAt failed:", ret) + } +} + diff --git a/vendor/github.com/qiniu/x/bytes.v7/seekable/seekable.go b/vendor/github.com/qiniu/x/bytes.v7/seekable/seekable.go new file mode 100644 index 0000000..3d718e1 --- /dev/null +++ b/vendor/github.com/qiniu/x/bytes.v7/seekable/seekable.go @@ -0,0 +1,63 @@ +// This package provide a method to read and replace http.Request's body. +package seekable + +import ( + "errors" + "io" + "io/ioutil" + "net/http" + + "qiniupkg.com/x/bytes.v7" +) + +// --------------------------------------------------- + +type Seekabler interface { + Bytes() []byte + Read(val []byte) (n int, err error) + SeekToBegin() error +} + +type SeekableCloser interface { + Seekabler + io.Closer +} + +// --------------------------------------------------- + +type readCloser struct { + Seekabler + io.Closer +} + +var ErrNoBody = errors.New("no body") + +func New(req *http.Request) (r SeekableCloser, err error) { + if req.Body == nil { + return nil, ErrNoBody + } + var ok bool + if r, ok = req.Body.(SeekableCloser); ok { + return + } + b, err2 := ReadAll(req) + if err2 != nil { + return nil, err2 + } + r = bytes.NewReader(b) + req.Body = readCloser{r, req.Body} + return +} + +func ReadAll(req *http.Request) (b []byte, err error) { + if req.ContentLength > 0 { + b = make([]byte, int(req.ContentLength)) + _, err = io.ReadFull(req.Body, b) + return + } else if req.ContentLength == 0 { + return nil, ErrNoBody + } + return ioutil.ReadAll(req.Body) +} + +// --------------------------------------------------- diff --git a/vendor/github.com/qiniu/x/bytes.v7/seekable/seekable_test.go b/vendor/github.com/qiniu/x/bytes.v7/seekable/seekable_test.go new file mode 100644 index 0000000..98d97f9 --- /dev/null +++ b/vendor/github.com/qiniu/x/bytes.v7/seekable/seekable_test.go @@ -0,0 +1,43 @@ +package seekable + +import ( + "bytes" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSeekable_EOFIfReqAlreadyParsed(t *testing.T) { + body := "a=1" + req, err := http.NewRequest("POST", "/a", bytes.NewBufferString(body)) + assert.NoError(t, err) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Content-Length", "3") + req.ParseForm() + _, err = New(req) + assert.Equal(t, err.Error(), "EOF") +} + +func TestSeekable_WorkaroundForEOF(t *testing.T) { + body := "a=1" + req, err := http.NewRequest("POST", "/a", bytes.NewBufferString(body)) + assert.NoError(t, err) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Content-Length", "3") + _, _ = New(req) + req.ParseForm() + assert.Equal(t, req.FormValue("a"), "1") + _, err = New(req) + assert.NoError(t, err) +} + +func TestSeekable(t *testing.T) { + body := "a=1" + req, err := http.NewRequest("POST", "/a", bytes.NewBufferString(body)) + assert.NoError(t, err) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Content-Length", "3") + _, err = New(req) + assert.NoError(t, err) +} diff --git a/vendor/github.com/qiniu/x/cmdline.v7/cmdline.go b/vendor/github.com/qiniu/x/cmdline.v7/cmdline.go new file mode 100644 index 0000000..30d7084 --- /dev/null +++ b/vendor/github.com/qiniu/x/cmdline.v7/cmdline.go @@ -0,0 +1,264 @@ +package cmdline + +import ( + "errors" + "strings" + + . "qiniupkg.com/x/ctype.v7" +) + +/* --------------------------------------------------------------------------- + +Shell 基础规则: + +* 多行字符串:用 '...' 或 "..."。其中 " 会自动转义 $(var),而 ' 不会。 +* 普通字符串:用 [ \t] 分隔。转义符以 \ 开头。 +* 外部命令:`...`。 + +七牛规则: + +* 外部命令: `...` 或 |...| 。 +* 多行字符串:用 '...' 或 ```\n...``` 或 ===\n...=== 不转义。用 "...",支持以 \ 开头的转义,也支持外部命令。 +* 普通字符串:用 [ \t] 分隔。转义符以 \ 开头,同时也支持外部命令。 +* 关于 $(var) 支持:每个命令自己执行 $(var) 的展开。不统一执行的原因是,在不同上下文需要不同的转义方式。 + +样例: + +post http://rs.qiniu.com/delete/`base64 Bucket:Key` +auth `qbox AccessKey SecretKey` +ret 200 + +post http://rs.qiniu.com/batch +auth qboxtest +form op=/delete/`base64 Bucket:Key`&op=/delete/`base64 Bucket2:Key2` +ret 200 + +post http://rs.qiniu.com/batch +auth qboxtest +form op=/delete/`base64 Bucket:Key`&op=/delete/`base64 Bucket:NotExistKey` +ret 298 +json '[ + {"code": 200}, {"code": 612} +]' +equal $(code1) 200 + +// -------------------------------------------------------------------------*/ + +var ( + EOF = errors.New("end of file") + ErrUnsupportedFeatureSubCmd = errors.New("unsupported feature: sub command") + ErrUnsupportedFeatureMultiCmds = errors.New("unsupported feature: multi commands") + ErrInvalidEscapeChar = errors.New("invalid escape char") + ErrIncompleteStringExpectQuot = errors.New("incomplete string, expect \"") + ErrIncompleteStringExpectSquot = errors.New("incomplete string, expect '") + ErrIncompleteStringExpectBacktick = errors.New("incomplete string, expect ` or |") +) + +var ( + errEOL = errors.New("end of line") +) + +// --------------------------------------------------------------------------- + +func Skip(str string, typeMask uint32) string { + + for i := 0; i < len(str); i++ { + if !Is(typeMask, rune(str[i])) { + return str[i:] + } + } + return "" +} + +func Find(str string, typeMask uint32) (n int) { + + for n = 0; n < len(str); n++ { + if Is(typeMask, rune(str[n])) { + break + } + } + return +} + +// --------------------------------------------------------------------------- + +// EOL = \r\n? | \n +// +func requireEOL(code string) (hasEOL bool, codeNext string) { + + if strings.HasPrefix(code, "\r") { + if strings.HasPrefix(code[1:], "\n") { + return true, code[2:] + } + } else if !strings.HasPrefix(code, "\n") { + return false, code + } + return true, code[1:] +} + +// --------------------------------------------------------------------------- + +type Parser struct { + ExecSub func(code string) (string, error) + Escape func(c byte) string + comment bool +} + +func NewParser() *Parser { + + return &Parser{ + ExecSub: defaultExecSub, + Escape: defaultEscape, + } +} + +func defaultExecSub(code string) (string, error) { + return "", ErrUnsupportedFeatureSubCmd +} + +// --------------------------------------------------------------------------- + +const ( + endOfLine = EOL | SEMICOLON // [\r\n;] + blanks = SPACE_BAR | TAB + blankAndEOLs = SPACE_BAR | TAB | endOfLine +) + +const ( + endMask_QuotString = RDIV | BACKTICK | OR | QUOT // [\\`|"] + endMask_NonquotString = RDIV | BACKTICK | OR | blankAndEOLs // [\\`| \t\r\n;] +) + +func (p *Parser) parseString( + code string, endMask uint32) (item string, ok bool, codeNext string, err error) { + + codeNext = code + for { + n := Find(codeNext, endMask) + if n > 0 { + item += codeNext[:n] + ok = true + } + if len(codeNext) == n { + codeNext = "" + if endMask == endMask_QuotString { + err = ErrIncompleteStringExpectQuot + } else { + err = EOF + } + return + } + switch codeNext[n] { + case '\\': + if len(codeNext) == n+1 { + err = ErrInvalidEscapeChar + return + } + item += p.Escape(codeNext[n+1]) + codeNext = codeNext[n+2:] + case '`', '|': + c := codeNext[n] + codeNext = codeNext[n+1:] + len := strings.IndexByte(codeNext, c) + if len < 0 { + err = ErrIncompleteStringExpectBacktick + return + } + if !p.comment { + valSub, errSub := p.ExecSub(codeNext[:len]) + if errSub != nil { + err = errors.New("Exec `" + codeNext[:len] + "` failed: " + errSub.Error()) + return + } + item += valSub + } + codeNext = codeNext[len+1:] + case '"': + ok = true + codeNext = codeNext[n+1:] + return + default: + if Is(endOfLine, rune(codeNext[n])) { + err = errEOL + } + codeNext = codeNext[n+1:] + return + } + ok = true + } + return +} + +func (p *Parser) parseItem( + code string, skipMask uint32) (item string, ok bool, codeNext string, err error) { + + codeNext = Skip(code, skipMask) + if len(codeNext) == 0 { + err = EOF + return + } + + switch codeNext[0] { + case '"': + return p.parseString(codeNext[1:], endMask_QuotString) + case '\'': + codeNext = codeNext[1:] + len := strings.IndexByte(codeNext, '\'') + if len < 0 { + err = ErrIncompleteStringExpectSquot + return + } + return codeNext[:len], true, codeNext[len+1:], nil + default: + if strings.HasPrefix(codeNext, "```") || strings.HasPrefix(codeNext, "===") { + endMark := codeNext[:3] + _, codeNext = requireEOL(codeNext[3:]) + len := strings.Index(codeNext, endMark) + if len < 0 { + err = errors.New("incomplete string, expect " + endMark) + return + } + return codeNext[:len], true, codeNext[len+3:], nil + } + return p.parseString(codeNext, endMask_NonquotString) + } +} + +func (p *Parser) ParseCmd(cmdline string) (cmd []string, err error) { + + cmd, _, err = p.ParseCode(cmdline) + if err == EOF && len(cmd) > 0 { + return cmd, nil + } + if err == nil { + err = ErrUnsupportedFeatureMultiCmds + } + return +} + +func (p *Parser) ParseCode(code string) (cmd []string, codeNext string, err error) { + + item, ok, codeNext, err := p.parseItem(code, blankAndEOLs) + if !ok { + return + } + p.comment = strings.HasPrefix(item, "#") + + cmd = append(cmd, item) + for err == nil { + item, ok, codeNext, err = p.parseItem(codeNext, blanks) + if ok { + cmd = append(cmd, item) + } + } + if err == errEOL { + err = nil + } + if p.comment { + cmd = nil + } + return +} + +// --------------------------------------------------------------------------- + diff --git a/vendor/github.com/qiniu/x/cmdline.v7/cmdline_test.go b/vendor/github.com/qiniu/x/cmdline.v7/cmdline_test.go new file mode 100644 index 0000000..33e2157 --- /dev/null +++ b/vendor/github.com/qiniu/x/cmdline.v7/cmdline_test.go @@ -0,0 +1,173 @@ +package cmdline + +import ( + "reflect" + "testing" +) + +// --------------------------------------------------------------------------- + +func equalErr(err error, errExp interface{}) bool { + + if err == nil || errExp == nil { + return err == nil && errExp == nil + } + return err.Error() == errExp.(string) +} + +// --------------------------------------------------------------------------- + +func TestComment(t *testing.T) { + + execSub := false + ctx := Parser{ + ExecSub: func(code string) (string, error) { + execSub = true + return "[" + code + "]", nil + }, + Escape: func(c byte) string { + return string(c) + }, + } + + cmd, codeNext, err := ctx.ParseCode("#abc `calc $(a)+$(b)`") + if err != EOF || codeNext != "" { + t.Fatal("ParseCode: eof is expected") + } + if execSub { + t.Fatal("don't execSub") + } + if len(cmd) != 0 { + t.Fatal("len(cmd) != 0") + } +} + +// --------------------------------------------------------------------------- + +type caseParse struct { + code string + cmd []string + codeNext string + err interface{} +} + +func TestParse(t *testing.T) { + + cases := []caseParse{ + { + code: ";b", + cmd: []string{"b"}, + codeNext: "", + err: "end of file", + }, + { + code: ";b;abc", + cmd: []string{"b"}, + codeNext: "abc", + err: nil, + }, + { + code: "a`b`\\c", + cmd: []string{"a[b]c"}, + codeNext: "", + err: "end of file", + }, + { + code: "a`b`c 'c\\n`123`' \"c\\n\"", + cmd: []string{"a[b]c", "c\\n`123`", "cn"}, + codeNext: "", + err: "end of file", + }, + { + code: "auth qboxtest 'mac AccessKey SecretKey'", + cmd: []string{"auth", "qboxtest", "mac AccessKey SecretKey"}, + codeNext: "", + err: "end of file", + }, + { + code: "post http://rs.qiniu.com/delete/`base64 Bucket:Key`", + cmd: []string{"post", "http://rs.qiniu.com/delete/[base64 Bucket:Key]"}, + codeNext: "", + err: "end of file", + }, + { + code: "post http://rs.qiniu.com/delete `base64 Bucket:Key`", + cmd: []string{"post", "http://rs.qiniu.com/delete", "[base64 Bucket:Key]"}, + codeNext: "", + err: "end of file", + }, + { + code: "post http://rs.qiniu.com/delete/|base64 Bucket:Key|", + cmd: []string{"post", "http://rs.qiniu.com/delete/[base64 Bucket:Key]"}, + codeNext: "", + err: "end of file", + }, + { + code: `json '[ + {"code": 200}, {"code": 612} +]'`, + cmd: []string{"json", `[ + {"code": 200}, {"code": 612} +]`}, + codeNext: "", + err: "end of file", + }, + { + code: "auth qboxtest ```\nmac AccessKey SecretKey```", + cmd: []string{"auth", "qboxtest", "mac AccessKey SecretKey"}, + codeNext: "", + err: "end of file", + }, + { + code: "auth qboxtest ===\nmac AccessKey SecretKey```", + cmd: []string{"auth", "qboxtest"}, + codeNext: "mac AccessKey SecretKey```", + err: "incomplete string, expect ===", + }, + { + code: "auth qboxtest ===\rmac AccessKey SecretKey===", + cmd: []string{"auth", "qboxtest", "mac AccessKey SecretKey"}, + codeNext: "", + err: "end of file", + }, + { + code: "auth qboxtest ===\n\rmac AccessKey SecretKey===", + cmd: []string{"auth", "qboxtest", "\rmac AccessKey SecretKey"}, + codeNext: "", + err: "end of file", + }, + { + code: "auth qboxtest ===\r\n\nmac AccessKey SecretKey===", + cmd: []string{"auth", "qboxtest", "\nmac AccessKey SecretKey"}, + codeNext: "", + err: "end of file", + }, + { + code: "auth qboxtest ===mac AccessKey SecretKey===", + cmd: []string{"auth", "qboxtest", "mac AccessKey SecretKey"}, + codeNext: "", + err: "end of file", + }, + } + + ctx := Parser{ + ExecSub: func(code string) (string, error) { + return "[" + code + "]", nil + }, + Escape: func(c byte) string { + return string(c) + }, + } + for _, c := range cases { + cmd, codeNext, err := ctx.ParseCode(c.code) + if !equalErr(err, c.err) { + t.Fatal("Parse failed:", c, err) + } + if !reflect.DeepEqual(cmd, c.cmd) || codeNext != c.codeNext { + t.Fatal("Parse failed:", c, cmd, codeNext) + } + } +} + +// --------------------------------------------------------------------------- + diff --git a/vendor/github.com/qiniu/x/cmdline.v7/escape.go b/vendor/github.com/qiniu/x/cmdline.v7/escape.go new file mode 100644 index 0000000..d0f8b6b --- /dev/null +++ b/vendor/github.com/qiniu/x/cmdline.v7/escape.go @@ -0,0 +1,98 @@ +package cmdline + +// --------------------------------------------------------------------------- + +const ( + escTableBaseChar = '0' + escTableLen = ('z' - escTableBaseChar + 1) +) + +var escTable = []byte{ + 0, // 0 [48] + 49, // 1 [49] + 50, // 2 [50] + 51, // 3 [51] + 52, // 4 [52] + 53, // 5 [53] + 54, // 6 [54] + 55, // 7 [55] + 56, // 8 [56] + 57, // 9 [57] + 58, // : [58] + 59, // ; [59] + 60, // < [60] + 61, // = [61] + 62, // > [62] + 63, // ? [63] + 64, // @ [64] + 65, // A [65] + 66, // B [66] + 67, // C [67] + 68, // D [68] + 69, // E [69] + 70, // F [70] + 71, // G [71] + 72, // H [72] + 73, // I [73] + 74, // J [74] + 75, // K [75] + 76, // L [76] + 77, // M [77] + 78, // N [78] + 79, // O [79] + 80, // P [80] + 81, // Q [81] + 82, // R [82] + 83, // S [83] + 84, // T [84] + 85, // U [85] + 86, // V [86] + 87, // W [87] + 88, // X [88] + 89, // Y [89] + 90, // Z [90] + 91, // [ [91] + 92, // \ [92] + 93, // ] [93] + 94, // ^ [94] + 95, // _ [95] + 96, // ` [96] + 97, // a [97] + 98, // b [98] + 99, // c [99] + 100, // d [100] + 101, // e [101] + 102, // f [102] + 103, // g [103] + 104, // h [104] + 105, // i [105] + 106, // j [106] + 107, // k [107] + 108, // l [108] + 109, // m [109] + '\n', // n [110] + 111, // o [111] + 112, // p [112] + 113, // q [113] + '\r', // r [114] + 115, // s [115] + '\t', // t [116] + 117, // u [117] + 118, // v [118] + 119, // w [119] + 120, // x [120] + 121, // y [121] + 122, // z [122] + 123, // { [123] +} + +func defaultEscape(c byte) string { + + if c - escTableBaseChar < escTableLen { + c = escTable[c - escTableBaseChar] + } + return string(c) +} + +// --------------------------------------------------------------------------- + diff --git a/vendor/github.com/qiniu/x/cmdline.v7/escape_test.go b/vendor/github.com/qiniu/x/cmdline.v7/escape_test.go new file mode 100644 index 0000000..7164216 --- /dev/null +++ b/vendor/github.com/qiniu/x/cmdline.v7/escape_test.go @@ -0,0 +1,41 @@ +package cmdline + +import ( + "testing" +) + +// --------------------------------------------------------------------------- + +func TestEscape(t *testing.T) { + + for i := 0; i < escTableBaseChar; i++ { + checkEscapeChar(t, i, i) + } + + table := make([]int, escTableLen) + for i := 0; i < escTableLen; i++ { + table[i] = escTableBaseChar + i + } + table['0'-escTableBaseChar] = 0 + table['r'-escTableBaseChar] = '\r' + table['t'-escTableBaseChar] = '\t' + table['n'-escTableBaseChar] = '\n' + for i := 0; i < escTableLen; i++ { + checkEscapeChar(t, escTableBaseChar+i, table[i]) + } + + for i := int(escTableBaseChar + escTableLen); i < 256; i++ { + checkEscapeChar(t, i, i) + } +} + +func checkEscapeChar(t *testing.T, i, exp int) { + + ret := defaultEscape(byte(i)) + if ret != string(exp) { + t.Fatal("escapeChar failed:", i) + } +} + +// --------------------------------------------------------------------------- + diff --git a/vendor/github.com/qiniu/x/config.v7/getdir.go b/vendor/github.com/qiniu/x/config.v7/getdir.go new file mode 100644 index 0000000..637e877 --- /dev/null +++ b/vendor/github.com/qiniu/x/config.v7/getdir.go @@ -0,0 +1,41 @@ +package config + +import ( + "errors" + "os" +) + +var homeEnvNames = [][]string{ + {"HOME"}, + {"HOMEDRIVE", "HOMEPATH"}, +} + +var ( + ErrHomeNotFound = errors.New("$HOME not found") +) + +func getEnv(name []string) (v string) { + + if len(name) == 1 { + return os.Getenv(name[0]) + } + for _, k := range name { + v += os.Getenv(k) + } + return +} + +func GetDir(app string) (dir string, err error) { + + for _, name := range homeEnvNames { + home := getEnv(name) + if home == "" { + continue + } + dir = home + "/." + app + err = os.MkdirAll(dir, 0777) + return + } + return "", ErrHomeNotFound +} + diff --git a/vendor/github.com/qiniu/x/config.v7/load_conf.go b/vendor/github.com/qiniu/x/config.v7/load_conf.go new file mode 100644 index 0000000..1993342 --- /dev/null +++ b/vendor/github.com/qiniu/x/config.v7/load_conf.go @@ -0,0 +1,116 @@ +package config + +import ( + "bytes" + "encoding/json" + "flag" + "io/ioutil" + + "qiniupkg.com/x/log.v7" +) + +var ( + confName *string +) + +func Init(cflag, app, default_conf string) { + + confDir, _ := GetDir(app) + confName = flag.String(cflag, confDir+"/"+default_conf, "the config file") +} + +func GetPath() string { + + if confName != nil { + return *confName + } + return "" +} + +func Load(conf interface{}) (err error) { + + if !flag.Parsed() { + flag.Parse() + } + + log.Info("Use the config file of ", *confName) + return LoadEx(conf, *confName) +} + +func LoadEx(conf interface{}, confName string) (err error) { + + data, err := ioutil.ReadFile(confName) + if err != nil { + log.Error("Load conf failed:", err) + return + } + data = trimComments(data) + + err = json.Unmarshal(data, conf) + if err != nil { + log.Error("Parse conf failed:", err) + } + return +} + +func LoadFile(conf interface{}, confName string) (err error) { + + data, err := ioutil.ReadFile(confName) + if err != nil { + return + } + data = trimComments(data) + + return json.Unmarshal(data, conf) +} + +func LoadBytes(conf interface{}, data []byte) (err error) { + + return json.Unmarshal(trimComments(data), conf) +} + +func LoadString(conf interface{}, data string) (err error) { + + return json.Unmarshal(trimComments([]byte(data)), conf) +} + +func trimComments(data []byte) (data1 []byte) { + + var line []byte + + data1 = data[:0] + for { + pos := bytes.IndexByte(data, '\n') + if pos < 0 { + line = data + } else { + line = data[:pos+1] + } + data1 = append(data1, trimCommentsLine(line)...) + if pos < 0 { + return + } + data = data[pos+1:] + } +} + +func trimCommentsLine(line []byte) []byte { + + n := len(line) + quoteCount := 0 + for i := 0; i < n; i++ { + c := line[i] + switch c { + case '\\': + i++ + case '"': + quoteCount++ + case '#': + if (quoteCount&1) == 0 { + return line[:i] + } + } + } + return line +} + diff --git a/vendor/github.com/qiniu/x/config.v7/load_conf_test.go b/vendor/github.com/qiniu/x/config.v7/load_conf_test.go new file mode 100644 index 0000000..6a82857 --- /dev/null +++ b/vendor/github.com/qiniu/x/config.v7/load_conf_test.go @@ -0,0 +1,59 @@ +package config_test + +import ( + "bytes" + "encoding/json" + "testing" + + "qiniupkg.com/x/config.v7" +) + +func TestTrimComments(t *testing.T) { + + confData := `{ + "debug_level": 0, # 调试级别 + "rs_host": "http://localhost:15001", #RS服务 + "limit": 5, #限制数 + "retryTimes": 56, + "quote0": "###", + "quote": "quo\\\"\\#", + "ant": "ant\\#" #123 +}` + + confDataExp := `{ + "debug_level": 0, + "rs_host": "http://localhost:15001", + "limit": 5, + "retryTimes": 56, + "quote0": "###", + "quote": "quo\\\"\\#", + "ant": "ant\\#" +}` + + var ( + conf, confExp interface{} + ) + err := config.LoadString(&conf, confData) + if err != nil { + t.Fatal("config.LoadString(conf) failed:", err) + } + err = config.LoadString(&confExp, confDataExp) + if err != nil { + t.Fatal("config.LoadString(confExp) failed:", err) + } + + b, err := json.Marshal(conf) + if err != nil { + t.Fatal("json.Marshal failed:", err) + } + + bExp, err := json.Marshal(confExp) + if err != nil { + t.Fatal("json.Marshal(exp) failed:", err) + } + + if !bytes.Equal(b, bExp) { + t.Fatal("b != bExp") + } +} + diff --git a/vendor/github.com/qiniu/x/ctype.v7/ctype.go b/vendor/github.com/qiniu/x/ctype.v7/ctype.go new file mode 100644 index 0000000..6c5ae08 --- /dev/null +++ b/vendor/github.com/qiniu/x/ctype.v7/ctype.go @@ -0,0 +1,237 @@ +package ctype + +const ( + UPPER = 0x01 /* upper case letter[A-Z] */ + LOWER = 0x02 /* lower case letter[a-z] */ + DIGIT = 0x04 /* digit[0-9] */ + UNDERLINE = 0x08 /* underline[_] */ + XDIGIT = 0x10 /* xdigit[0-9a-fA-F] */ + EOL = 0x20 /* [\r\n] */ + ADD = 0x40 /* [+] */ + SUB = 0x80 /* [-] */ + MUL = 0x100 /* [*] */ + DIV = 0x200 /* [/] */ + LT = 0x400 /* [<] */ + GT = 0x800 /* [>] */ + EQ = 0x1000 /* [=] */ + RDIV = 0x2000 /* [\\], right-division, anti-slash */ + DOT = 0x4000 /* [.] */ + COLON = 0x8000 /* [:], colon */ + PERCENT = 0x10000 /* [%] */ + AND = 0x20000 /* [&] */ + OR = 0x40000 /* [|] */ + SPACE_BAR = 0x80000 /* [ ] */ + LCAP_R = 0x100000 /* [r] */ + LCAP_T = 0x200000 /* [t] */ + LCAP_N = 0x400000 /* [n] */ + LCAP_W = 0x800000 /* [w] */ + COMMA = 0x1000000 /* [,] */ + SEMICOLON = 0x2000000 /* [;] */ + TAB = 0x4000000 /* [\t] */ + QUOT = 0x8000000 /* ["] */ + BACKTICK = 0x10000000 /* [`] */ +) + +const ( + BLANK = SPACE_BAR + TSPACE = TAB | EOL + SPACE = SPACE_BAR | TSPACE + PATH_SEP = DIV | RDIV + ALPHA = UPPER | LOWER + SYMBOL_FIRST_CHAR = ALPHA + SYMBOL_NEXT_CHAR = SYMBOL_FIRST_CHAR | DIGIT + CSYMBOL_FIRST_CHAR = ALPHA | UNDERLINE + CSYMBOL_NEXT_CHAR = CSYMBOL_FIRST_CHAR | DIGIT + XMLSYMBOL_FIRST_CHAR = CSYMBOL_FIRST_CHAR + XMLSYMBOL_NEXT_CHAR = CSYMBOL_NEXT_CHAR | SUB + DOMAIN_CHAR = ALPHA | DIGIT | SUB | ADD | DOT + BASE64 = ALPHA | DIGIT | ADD | DIV // [a-zA-Z0-9+/] + URLSAFE_BASE64 = ALPHA | DIGIT | SUB | UNDERLINE // [a-zA-Z0-9\-_] +) + +// ----------------------------------------------------------- + +var table = []uint32{ + 0, // [0] + 0, // [1] + 0, // [2] + 0, // [3] + 0, // [4] + 0, // [5] + 0, // [6] + 0, // [7] + 0, // [8] + TAB, // [9] + EOL, // [10] + 0, // [11] + 0, // [12] + EOL, // [13] + 0, // [14] + 0, // [15] + 0, // [16] + 0, // [17] + 0, // [18] + 0, // [19] + 0, // [20] + 0, // [21] + 0, // [22] + 0, // [23] + 0, // [24] + 0, // [25] + 0, // [26] + 0, // [27] + 0, // [28] + 0, // [29] + 0, // [30] + 0, // [31] + SPACE_BAR, // [32] + 0, // ! [33] + QUOT, // " [34] + 0, // # [35] + 0, // $ [36] + PERCENT, // % [37] + AND, // & [38] + 0, // ' [39] + 0, // ( [40] + 0, // ) [41] + MUL, // * [42] + ADD, // + [43] + COMMA, // , [44] + SUB, // - [45] + DOT, // . [46] + DIV, // / [47] + DIGIT | XDIGIT, // 0 [48] + DIGIT | XDIGIT, // 1 [49] + DIGIT | XDIGIT, // 2 [50] + DIGIT | XDIGIT, // 3 [51] + DIGIT | XDIGIT, // 4 [52] + DIGIT | XDIGIT, // 5 [53] + DIGIT | XDIGIT, // 6 [54] + DIGIT | XDIGIT, // 7 [55] + DIGIT | XDIGIT, // 8 [56] + DIGIT | XDIGIT, // 9 [57] + COLON, // : [58] + SEMICOLON, // ; [59] + LT, // < [60] + EQ, // = [61] + GT, // > [62] + 0, // ? [63] + 0, // @ [64] + UPPER | XDIGIT, // A [65] + UPPER | XDIGIT, // B [66] + UPPER | XDIGIT, // C [67] + UPPER | XDIGIT, // D [68] + UPPER | XDIGIT, // E [69] + UPPER | XDIGIT, // F [70] + UPPER, // G [71] + UPPER, // H [72] + UPPER, // I [73] + UPPER, // J [74] + UPPER, // K [75] + UPPER, // L [76] + UPPER, // M [77] + UPPER, // N [78] + UPPER, // O [79] + UPPER, // P [80] + UPPER, // Q [81] + UPPER, // R [82] + UPPER, // S [83] + UPPER, // T [84] + UPPER, // U [85] + UPPER, // V [86] + UPPER, // W [87] + UPPER, // X [88] + UPPER, // Y [89] + UPPER, // Z [90] + 0, // [ [91] + RDIV, // \ [92] + 0, // ] [93] + 0, // ^ [94] + UNDERLINE, // _ [95] + BACKTICK, // ` [96] + LOWER | XDIGIT, // a [97] + LOWER | XDIGIT, // b [98] + LOWER | XDIGIT, // c [99] + LOWER | XDIGIT, // d [100] + LOWER | XDIGIT, // e [101] + LOWER | XDIGIT, // f [102] + LOWER, // g [103] + LOWER, // h [104] + LOWER, // i [105] + LOWER, // j [106] + LOWER, // k [107] + LOWER, // l [108] + LOWER, // m [109] + LCAP_N | LOWER, // n [110] + LOWER, // o [111] + LOWER, // p [112] + LOWER, // q [113] + LCAP_R | LOWER, // r [114] + LOWER, // s [115] + LCAP_T | LOWER, // t [116] + LOWER, // u [117] + LOWER, // v [118] + LCAP_W | LOWER, // w [119] + LOWER, // x [120] + LOWER, // y [121] + LOWER, // z [122] + 0, // { [123] + OR, // | [124] + 0, // } [125] + 0, // ~ [126] + 0, // del [127] +} + +// ----------------------------------------------------------- + +func Is(typeMask uint32, c rune) bool { + + if uint(c) < uint(len(table)) { + return (typeMask & table[c]) != 0 + } + return false +} + +func IsType(typeMask uint32, str string) bool { + + if str == "" { + return false + } + for _, c := range str { + if !Is(typeMask, c) { + return false + } + } + return true +} + +func IsTypeEx(typeFirst, typeNext uint32, str string) bool { + + if str == "" { + return false + } + for i, c := range str { + if i > 0 { + if !Is(typeNext, c) { + return false + } + } else { + if !Is(typeFirst, c) { + return false + } + } + } + return true +} + +func IsCSymbol(str string) bool { + + return IsTypeEx(CSYMBOL_FIRST_CHAR, CSYMBOL_NEXT_CHAR, str) +} + +func IsXmlSymbol(str string) bool { + + return IsTypeEx(XMLSYMBOL_FIRST_CHAR, XMLSYMBOL_NEXT_CHAR, str) +} + +// ----------------------------------------------------------- diff --git a/vendor/github.com/qiniu/x/ctype.v7/ctype_test.go b/vendor/github.com/qiniu/x/ctype.v7/ctype_test.go new file mode 100644 index 0000000..ba3e57a --- /dev/null +++ b/vendor/github.com/qiniu/x/ctype.v7/ctype_test.go @@ -0,0 +1,72 @@ +package ctype + +import ( + "testing" +) + +type testCase struct { + c rune + mask uint32 + is bool +} + +type stringTestCase struct { + str string + maskFirst uint32 + maskNext uint32 + is bool +} + +var isCases = []testCase{ + {'-', DOMAIN_CHAR, true}, + {'.', DOMAIN_CHAR, true}, + {'_', DOMAIN_CHAR, false}, + {'+', DOMAIN_CHAR, true}, + {'a', DOMAIN_CHAR, true}, + {'A', DOMAIN_CHAR, true}, + {'0', DOMAIN_CHAR, true}, + {':', DOMAIN_CHAR, false}, + {'1', ALPHA, false}, + {'a', ALPHA, true}, + {'A', ALPHA, true}, +} + +var strCases = []stringTestCase{ + {"", CSYMBOL_FIRST_CHAR, CSYMBOL_NEXT_CHAR, false}, + {"123", CSYMBOL_FIRST_CHAR, CSYMBOL_NEXT_CHAR, false}, + {"_", CSYMBOL_FIRST_CHAR, CSYMBOL_NEXT_CHAR, true}, + {"_123", CSYMBOL_FIRST_CHAR, CSYMBOL_NEXT_CHAR, true}, + {"x_123", CSYMBOL_FIRST_CHAR, CSYMBOL_NEXT_CHAR, true}, + {"x_", CSYMBOL_FIRST_CHAR, CSYMBOL_NEXT_CHAR, true}, + {"_x", CSYMBOL_FIRST_CHAR, CSYMBOL_NEXT_CHAR, true}, + + {"", CSYMBOL_FIRST_CHAR, CSYMBOL_FIRST_CHAR, false}, + {"x_123", CSYMBOL_FIRST_CHAR, CSYMBOL_FIRST_CHAR, false}, + {"x_", CSYMBOL_FIRST_CHAR, CSYMBOL_FIRST_CHAR, true}, + {"_x", CSYMBOL_FIRST_CHAR, CSYMBOL_FIRST_CHAR, true}, + {"_", CSYMBOL_FIRST_CHAR, CSYMBOL_FIRST_CHAR, true}, +} + +func TestIs(t *testing.T) { + for _, a := range isCases { + f := Is(a.mask, a.c) + if f != a.is { + t.Fatal("case:", a, "result:", f) + } + } +} + +func TestIsTypeEx(t *testing.T) { + for _, a := range strCases { + f := IsTypeEx(a.maskFirst, a.maskNext, a.str) + if f != a.is { + t.Fatal("case:", a, "result:", f) + } + if a.maskFirst == a.maskNext { + f = IsType(a.maskFirst, a.str) + if f != a.is { + t.Fatal("case:", a, "result:", f) + } + } + } +} diff --git a/vendor/github.com/qiniu/x/doc.go b/vendor/github.com/qiniu/x/doc.go new file mode 100644 index 0000000..ca3bc32 --- /dev/null +++ b/vendor/github.com/qiniu/x/doc.go @@ -0,0 +1,9 @@ +package x + +import ( + _ "qiniupkg.com/x/bytes.v7" + _ "qiniupkg.com/x/ctype.v7" + _ "qiniupkg.com/x/rpc.v7" + _ "qiniupkg.com/x/url.v7" +) + diff --git a/vendor/github.com/qiniu/x/errors.v7/error_info.go b/vendor/github.com/qiniu/x/errors.v7/error_info.go new file mode 100644 index 0000000..28fe763 --- /dev/null +++ b/vendor/github.com/qiniu/x/errors.v7/error_info.go @@ -0,0 +1,146 @@ +package errors + +import ( + "errors" + "fmt" + "runtime" + "strconv" + "strings" +) + +const ( + prefix = " ==> " +) + +// -------------------------------------------------------------------- + +func New(msg string) error { + return errors.New(msg) +} + +// -------------------------------------------------------------------- + +type appendDetailer interface { + AppendErrorDetail(b []byte) []byte +} + +func appendErrorDetail(b []byte, err error) []byte { + if e, ok := err.(appendDetailer); ok { + return e.AppendErrorDetail(b) + } + b = append(b, prefix...) + return append(b, err.Error()...) +} + +// -------------------------------------------------------------------- + +type errorDetailer interface { + ErrorDetail() string +} + +func Detail(err error) string { + if e, ok := err.(errorDetailer); ok { + return e.ErrorDetail() + } + return err.Error() +} + +// -------------------------------------------------------------------- + +type summaryErr interface { + SummaryErr() error +} + +func Err(err error) error { + if e, ok := err.(summaryErr); ok { + return e.SummaryErr() + } + return err +} + +// -------------------------------------------------------------------- + +type ErrorInfo struct { + err error + why error + cmd []interface{} + pc uintptr +} + +func shortFile(file string) string { + pos := strings.LastIndex(file, "/src/") + if pos != -1 { + return file[pos+5:] + } + return file +} + +func Info(err error, cmd ...interface{}) *ErrorInfo { + pc, _, _, ok := runtime.Caller(1) + if !ok { + pc = 0 + } + return &ErrorInfo{cmd: cmd, err: Err(err), pc: pc} +} + +func InfoEx(calldepth int, err error, cmd ...interface{}) *ErrorInfo { + pc, _, _, ok := runtime.Caller(calldepth+1) + if !ok { + pc = 0 + } + return &ErrorInfo{cmd: cmd, err: Err(err), pc: pc} +} + +func (r *ErrorInfo) Detail(err error) *ErrorInfo { + r.why = err + return r +} + +func (r *ErrorInfo) NestedObject() interface{} { + return r.err +} + +func (r *ErrorInfo) SummaryErr() error { + return r.err +} + +func (r *ErrorInfo) Error() string { + return r.err.Error() +} + +func (r *ErrorInfo) ErrorDetail() string { + b := make([]byte, 1, 64) + b[0] = '\n' + b = r.AppendErrorDetail(b) + return string(b) +} + +func (r *ErrorInfo) AppendErrorDetail(b []byte) []byte { + b = append(b, prefix...) + if r.pc != 0 { + f := runtime.FuncForPC(r.pc) + if f != nil { + file, line := f.FileLine(r.pc) + b = append(b, shortFile(file)...) + b = append(b, ':') + b = append(b, strconv.Itoa(line)...) + b = append(b, ':', ' ') + + fnName := f.Name() + fnName = fnName[strings.LastIndex(fnName, "/")+1:] + fnName = fnName[strings.Index(fnName, ".")+1:] + b = append(b, '[') + b = append(b, fnName...) + b = append(b, ']', ' ') + } + } + b = append(b, Detail(r.err)...) + b = append(b, ' ', '~', ' ') + b = append(b, fmt.Sprintln(r.cmd...)...) + if r.why != nil { + b = appendErrorDetail(b, r.why) + } + return b +} + +// -------------------------------------------------------------------- diff --git a/vendor/github.com/qiniu/x/errors.v7/error_info_test.go b/vendor/github.com/qiniu/x/errors.v7/error_info_test.go new file mode 100644 index 0000000..3b04fce --- /dev/null +++ b/vendor/github.com/qiniu/x/errors.v7/error_info_test.go @@ -0,0 +1,30 @@ +package errors + +import ( + "errors" + "syscall" + "testing" +) + +func MysqlError(err error, cmd ...interface{}) error { + + return InfoEx(1, syscall.EINVAL, cmd...).Detail(err) +} + +func (r *ErrorInfo) makeError() error { + + err := errors.New("detail error") + return MysqlError(err, "do sth failed") +} + +func TestErrorsInfo(t *testing.T) { + + err := new(ErrorInfo).makeError() + msg := Detail(err) + if msg != ` + ==> qiniupkg.com/x/errors.v7/error_info_test.go:17: [(*ErrorInfo).makeError] invalid argument ~ do sth failed + ==> detail error` { + t.Fatal("TestErrorsInfo failed") + } +} + diff --git a/vendor/github.com/qiniu/x/jsonutil.v7/unmarshal_string.go b/vendor/github.com/qiniu/x/jsonutil.v7/unmarshal_string.go new file mode 100644 index 0000000..ffcda35 --- /dev/null +++ b/vendor/github.com/qiniu/x/jsonutil.v7/unmarshal_string.go @@ -0,0 +1,19 @@ +package jsonutil + +import ( + "encoding/json" + "reflect" + "unsafe" +) + +// ---------------------------------------------------------- + +func Unmarshal(data string, v interface{}) error { + + sh := *(*reflect.StringHeader)(unsafe.Pointer(&data)) + arr := (*[1<<30]byte)(unsafe.Pointer(sh.Data)) + return json.Unmarshal(arr[:sh.Len], v) +} + +// ---------------------------------------------------------- + diff --git a/vendor/github.com/qiniu/x/jsonutil.v7/unmarshal_test.go b/vendor/github.com/qiniu/x/jsonutil.v7/unmarshal_test.go new file mode 100644 index 0000000..e134bf1 --- /dev/null +++ b/vendor/github.com/qiniu/x/jsonutil.v7/unmarshal_test.go @@ -0,0 +1,19 @@ +package jsonutil + +import ( + "testing" +) + +func Test(t *testing.T) { + + var ret struct { + Id string `json:"id"` + } + err := Unmarshal(`{"id": "123"}`, &ret) + if err != nil { + t.Fatal("Unmarshal failed:", err) + } + if ret.Id != "123" { + t.Fatal("Unmarshal uncorrect:", ret.Id) + } +} diff --git a/vendor/github.com/qiniu/x/log.v7/README.md b/vendor/github.com/qiniu/x/log.v7/README.md new file mode 100644 index 0000000..a5e831b --- /dev/null +++ b/vendor/github.com/qiniu/x/log.v7/README.md @@ -0,0 +1,4 @@ +log +=== + +Extension module of golang logging \ No newline at end of file diff --git a/vendor/github.com/qiniu/x/log.v7/logext.go b/vendor/github.com/qiniu/x/log.v7/logext.go new file mode 100644 index 0000000..48e1ff4 --- /dev/null +++ b/vendor/github.com/qiniu/x/log.v7/logext.go @@ -0,0 +1,521 @@ +package log + +import ( + "bytes" + "fmt" + "io" + "os" + "runtime" + "strings" + "sync" + "time" +) + +// These flags define which text to prefix to each log entry generated by the Logger. +const ( + // Bits or'ed together to control what's printed. There is no control over the + // order they appear (the order listed here) or the format they present (as + // described in the comments). A colon appears after these items: + // 2009/0123 01:23:23.123123 /a/b/c/d.go:23: message + Ldate = 1 << iota // the date: 2009/0123 + Ltime // the time: 01:23:23 + Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime. + Llongfile // full file name and line number: /a/b/c/d.go:23 + Lshortfile // final file name element and line number: d.go:23. overrides Llongfile + Lmodule // module name + Llevel // level: 0(Debug), 1(Info), 2(Warn), 3(Error), 4(Panic), 5(Fatal) + LstdFlags = Ldate | Ltime | Lmicroseconds // initial values for the standard logger + Ldefault = Lmodule | Llevel | Lshortfile | LstdFlags +) // [prefix][time][level][module][shortfile|longfile] + +const ( + Ldebug = iota + Linfo + Lwarn + Lerror + Lpanic + Lfatal +) + +var levels = []string{ + "[DEBUG]", + "[INFO]", + "[WARN]", + "[ERROR]", + "[PANIC]", + "[FATAL]", +} + +// A Logger represents an active logging object that generates lines of +// output to an io.Writer. Each logging operation makes a single call to +// the Writer's Write method. A Logger can be used simultaneously from +// multiple goroutines; it guarantees to serialize access to the Writer. +type Logger struct { + mu sync.Mutex // ensures atomic writes; protects the following fields + prefix string // prefix to write at beginning of each line + flag int // properties + Level int // debug level + out io.Writer // destination for output + buf bytes.Buffer // for accumulating text to write + levelStats [6]int64 +} + +// New creates a new Logger. +// The out variable sets the destination to which log data will be written. +// The prefix appears at the beginning of each generated log line. +// The flag argument defines the logging properties. +func New(out io.Writer, prefix string, flag int) *Logger { + return &Logger{out: out, prefix: prefix, Level: 1, flag: flag} +} + +var Std = New(os.Stderr, "", Ldefault) + +// Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding. +// Knows the buffer has capacity. +func itoa(buf *bytes.Buffer, i int, wid int) { + var u uint = uint(i) + if u == 0 && wid <= 1 { + buf.WriteByte('0') + return + } + + // Assemble decimal in reverse order. + var b [32]byte + bp := len(b) + for ; u > 0 || wid > 0; u /= 10 { + bp-- + wid-- + b[bp] = byte(u%10) + '0' + } + + // avoid slicing b to avoid an allocation. + for bp < len(b) { + buf.WriteByte(b[bp]) + bp++ + } +} + +func shortFile(file string, flag int) string { + sep := "/" + if (flag & Lmodule) != 0 { + sep = "/src/" + } + pos := strings.LastIndex(file, sep) + if pos != -1 { + return file[pos+5:] + } + return file +} + +func (l *Logger) formatHeader(buf *bytes.Buffer, t time.Time, file string, line int, lvl int, reqId string) { + if l.prefix != "" { + buf.WriteString(l.prefix) + } + if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 { + if l.flag&Ldate != 0 { + year, month, day := t.Date() + itoa(buf, year, 4) + buf.WriteByte('/') + itoa(buf, int(month), 2) + buf.WriteByte('/') + itoa(buf, day, 2) + buf.WriteByte(' ') + } + if l.flag&(Ltime|Lmicroseconds) != 0 { + hour, min, sec := t.Clock() + itoa(buf, hour, 2) + buf.WriteByte(':') + itoa(buf, min, 2) + buf.WriteByte(':') + itoa(buf, sec, 2) + if l.flag&Lmicroseconds != 0 { + buf.WriteByte('.') + itoa(buf, t.Nanosecond()/1e3, 6) + } + buf.WriteByte(' ') + } + } + if reqId != "" { + buf.WriteByte('[') + buf.WriteString(reqId) + buf.WriteByte(']') + } + if l.flag&Llevel != 0 { + buf.WriteString(levels[lvl]) + } + if l.flag&(Lshortfile|Llongfile) != 0 { + if l.flag&Lshortfile != 0 { + file = shortFile(file, l.flag) + } + buf.WriteByte(' ') + buf.WriteString(file) + buf.WriteByte(':') + itoa(buf, line, -1) + buf.WriteString(": ") + } +} + +// Output writes the output for a logging event. The string s contains +// the text to print after the prefix specified by the flags of the +// Logger. A newline is appended if the last character of s is not +// already a newline. Calldepth is used to recover the PC and is +// provided for generality, although at the moment on all pre-defined +// paths it will be 2. +func (l *Logger) Output(reqId string, lvl int, calldepth int, s string) error { + if lvl < l.Level { + return nil + } + now := time.Now() // get this early. + var file string + var line int + l.mu.Lock() + defer l.mu.Unlock() + if l.flag&(Lshortfile|Llongfile|Lmodule) != 0 { + // release lock while getting caller info - it's expensive. + l.mu.Unlock() + var ok bool + _, file, line, ok = runtime.Caller(calldepth) + if !ok { + file = "???" + line = 0 + } + l.mu.Lock() + } + l.levelStats[lvl]++ + l.buf.Reset() + l.formatHeader(&l.buf, now, file, line, lvl, reqId) + l.buf.WriteString(s) + if len(s) > 0 && s[len(s)-1] != '\n' { + l.buf.WriteByte('\n') + } + _, err := l.out.Write(l.buf.Bytes()) + return err +} + +// ----------------------------------------- + +// Printf calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Printf. +func (l *Logger) Printf(format string, v ...interface{}) { + l.Output("", Linfo, 2, fmt.Sprintf(format, v...)) +} + +// Print calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Print. +func (l *Logger) Print(v ...interface{}) { l.Output("", Linfo, 2, fmt.Sprint(v...)) } + +// Println calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Println. +func (l *Logger) Println(v ...interface{}) { l.Output("", Linfo, 2, fmt.Sprintln(v...)) } + +// ----------------------------------------- + +func (l *Logger) Debugf(format string, v ...interface{}) { + if Ldebug < l.Level { + return + } + l.Output("", Ldebug, 2, fmt.Sprintf(format, v...)) +} + +func (l *Logger) Debug(v ...interface{}) { + if Ldebug < l.Level { + return + } + l.Output("", Ldebug, 2, fmt.Sprintln(v...)) +} + +// ----------------------------------------- + +func (l *Logger) Infof(format string, v ...interface{}) { + if Linfo < l.Level { + return + } + l.Output("", Linfo, 2, fmt.Sprintf(format, v...)) +} + +func (l *Logger) Info(v ...interface{}) { + if Linfo < l.Level { + return + } + l.Output("", Linfo, 2, fmt.Sprintln(v...)) +} + +// ----------------------------------------- + +func (l *Logger) Warnf(format string, v ...interface{}) { + l.Output("", Lwarn, 2, fmt.Sprintf(format, v...)) +} + +func (l *Logger) Warn(v ...interface{}) { l.Output("", Lwarn, 2, fmt.Sprintln(v...)) } + +// ----------------------------------------- + +func (l *Logger) Errorf(format string, v ...interface{}) { + l.Output("", Lerror, 2, fmt.Sprintf(format, v...)) +} + +func (l *Logger) Error(v ...interface{}) { l.Output("", Lerror, 2, fmt.Sprintln(v...)) } + +// ----------------------------------------- + +func (l *Logger) Fatal(v ...interface{}) { + l.Output("", Lfatal, 2, fmt.Sprint(v...)) + os.Exit(1) +} + +// Fatalf is equivalent to l.Printf() followed by a call to os.Exit(1). +func (l *Logger) Fatalf(format string, v ...interface{}) { + l.Output("", Lfatal, 2, fmt.Sprintf(format, v...)) + os.Exit(1) +} + +// Fatalln is equivalent to l.Println() followed by a call to os.Exit(1). +func (l *Logger) Fatalln(v ...interface{}) { + l.Output("", Lfatal, 2, fmt.Sprintln(v...)) + os.Exit(1) +} + +// ----------------------------------------- + +// Panic is equivalent to l.Print() followed by a call to panic(). +func (l *Logger) Panic(v ...interface{}) { + s := fmt.Sprint(v...) + l.Output("", Lpanic, 2, s) + panic(s) +} + +// Panicf is equivalent to l.Printf() followed by a call to panic(). +func (l *Logger) Panicf(format string, v ...interface{}) { + s := fmt.Sprintf(format, v...) + l.Output("", Lpanic, 2, s) + panic(s) +} + +// Panicln is equivalent to l.Println() followed by a call to panic(). +func (l *Logger) Panicln(v ...interface{}) { + s := fmt.Sprintln(v...) + l.Output("", Lpanic, 2, s) + panic(s) +} + +// ----------------------------------------- + +func (l *Logger) Stack(v ...interface{}) { + s := fmt.Sprint(v...) + s += "\n" + buf := make([]byte, 1024*1024) + n := runtime.Stack(buf, true) + s += string(buf[:n]) + s += "\n" + l.Output("", Lerror, 2, s) +} + +func (l *Logger) SingleStack(v ...interface{}) { + s := fmt.Sprint(v...) + s += "\n" + buf := make([]byte, 1024*1024) + n := runtime.Stack(buf, false) + s += string(buf[:n]) + s += "\n" + l.Output("", Lerror, 2, s) +} + +// ----------------------------------------- + +func (l *Logger) Stat() (stats []int64) { + l.mu.Lock() + v := l.levelStats + l.mu.Unlock() + return v[:] +} + +// Flags returns the output flags for the logger. +func (l *Logger) Flags() int { + l.mu.Lock() + defer l.mu.Unlock() + return l.flag +} + +// SetFlags sets the output flags for the logger. +func (l *Logger) SetFlags(flag int) { + l.mu.Lock() + defer l.mu.Unlock() + l.flag = flag +} + +// Prefix returns the output prefix for the logger. +func (l *Logger) Prefix() string { + l.mu.Lock() + defer l.mu.Unlock() + return l.prefix +} + +// SetPrefix sets the output prefix for the logger. +func (l *Logger) SetPrefix(prefix string) { + l.mu.Lock() + defer l.mu.Unlock() + l.prefix = prefix +} + +// SetOutputLevel sets the output level for the logger. +func (l *Logger) SetOutputLevel(lvl int) { + l.mu.Lock() + defer l.mu.Unlock() + l.Level = lvl +} + +// SetOutput sets the output destination for the standard logger. +func SetOutput(w io.Writer) { + Std.mu.Lock() + defer Std.mu.Unlock() + Std.out = w +} + +// Flags returns the output flags for the standard logger. +func Flags() int { + return Std.Flags() +} + +// SetFlags sets the output flags for the standard logger. +func SetFlags(flag int) { + Std.SetFlags(flag) +} + +// Prefix returns the output prefix for the standard logger. +func Prefix() string { + return Std.Prefix() +} + +// SetPrefix sets the output prefix for the standard logger. +func SetPrefix(prefix string) { + Std.SetPrefix(prefix) +} + +func SetOutputLevel(lvl int) { + Std.SetOutputLevel(lvl) +} + +func GetOutputLevel() int { + return Std.Level +} + +// ----------------------------------------- + +// Print calls Output to print to the standard logger. +// Arguments are handled in the manner of fmt.Print. +func Print(v ...interface{}) { + Std.Output("", Linfo, 2, fmt.Sprint(v...)) +} + +// Printf calls Output to print to the standard logger. +// Arguments are handled in the manner of fmt.Printf. +func Printf(format string, v ...interface{}) { + Std.Output("", Linfo, 2, fmt.Sprintf(format, v...)) +} + +// Println calls Output to print to the standard logger. +// Arguments are handled in the manner of fmt.Println. +func Println(v ...interface{}) { + Std.Output("", Linfo, 2, fmt.Sprintln(v...)) +} + +// ----------------------------------------- + +func Debugf(format string, v ...interface{}) { + if Ldebug < Std.Level { + return + } + Std.Output("", Ldebug, 2, fmt.Sprintf(format, v...)) +} + +func Debug(v ...interface{}) { + if Ldebug < Std.Level { + return + } + Std.Output("", Ldebug, 2, fmt.Sprintln(v...)) +} + +// ----------------------------------------- + +func Infof(format string, v ...interface{}) { + if Linfo < Std.Level { + return + } + Std.Output("", Linfo, 2, fmt.Sprintf(format, v...)) +} + +func Info(v ...interface{}) { + if Linfo < Std.Level { + return + } + Std.Output("", Linfo, 2, fmt.Sprintln(v...)) +} + +// ----------------------------------------- + +func Warnf(format string, v ...interface{}) { + Std.Output("", Lwarn, 2, fmt.Sprintf(format, v...)) +} + +func Warn(v ...interface{}) { Std.Output("", Lwarn, 2, fmt.Sprintln(v...)) } + +// ----------------------------------------- + +func Errorf(format string, v ...interface{}) { + Std.Output("", Lerror, 2, fmt.Sprintf(format, v...)) +} + +func Error(v ...interface{}) { Std.Output("", Lerror, 2, fmt.Sprintln(v...)) } + +// ----------------------------------------- + +// Fatal is equivalent to Print() followed by a call to os.Exit(1). +func Fatal(v ...interface{}) { + Std.Output("", Lfatal, 2, fmt.Sprint(v...)) + os.Exit(1) +} + +// Fatalf is equivalent to Printf() followed by a call to os.Exit(1). +func Fatalf(format string, v ...interface{}) { + Std.Output("", Lfatal, 2, fmt.Sprintf(format, v...)) + os.Exit(1) +} + +// Fatalln is equivalent to Println() followed by a call to os.Exit(1). +func Fatalln(v ...interface{}) { + Std.Output("", Lfatal, 2, fmt.Sprintln(v...)) + os.Exit(1) +} + +// ----------------------------------------- + +// Panic is equivalent to Print() followed by a call to panic(). +func Panic(v ...interface{}) { + s := fmt.Sprint(v...) + Std.Output("", Lpanic, 2, s) + panic(s) +} + +// Panicf is equivalent to Printf() followed by a call to panic(). +func Panicf(format string, v ...interface{}) { + s := fmt.Sprintf(format, v...) + Std.Output("", Lpanic, 2, s) + panic(s) +} + +// Panicln is equivalent to Println() followed by a call to panic(). +func Panicln(v ...interface{}) { + s := fmt.Sprintln(v...) + Std.Output("", Lpanic, 2, s) + panic(s) +} + +// ----------------------------------------- + +func Stack(v ...interface{}) { + Std.Stack(v...) +} + +func SingleStack(v ...interface{}) { + Std.SingleStack(v...) +} diff --git a/vendor/github.com/qiniu/x/log.v7/logext_test.go b/vendor/github.com/qiniu/x/log.v7/logext_test.go new file mode 100644 index 0000000..16e1f48 --- /dev/null +++ b/vendor/github.com/qiniu/x/log.v7/logext_test.go @@ -0,0 +1,79 @@ +package log + +import ( + "bytes" + "regexp" + + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLog(t *testing.T) { + // keep Std clean + std := Std + SetOutputLevel(Ldebug) + + Debugf("Debug: foo\n") + Debug("Debug: foo") + + Infof("Info: foo\n") + Info("Info: foo") + + Warnf("Warn: foo\n") + Warn("Warn: foo") + + Errorf("Error: foo\n") + Error("Error: foo") + + SetOutputLevel(Linfo) + + Debugf("Debug: foo\n") + Debug("Debug: foo") + + Infof("Info: foo\n") + Info("Info: foo") + + Warnf("Warn: foo\n") + Warn("Warn: foo") + + Errorf("Error: foo\n") + Error("Error: foo") + + Std = std +} + +func TestLog_Time(t *testing.T) { + // keep Std clean + std := Std + out := bytes.Buffer{} + Std = New(&out, Std.Prefix(), Std.Flags()) + + assert.Equal(t, Std.Level, Linfo) + Info("test") + outStr := out.String() + assert.True(t, regexp.MustCompile(`^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}.\d{6}$`).MatchString(outStr[:26])) + assert.Equal(t, outStr[26:], " [INFO] qiniupkg.com/x/log.v7/logext_test.go:53: test\n") + + Std = std +} + +func TestLog_Level(t *testing.T) { + // keep Std clean + std := Std + + out := bytes.Buffer{} + Std = New(&out, Std.Prefix(), Std.Flags()) + SetOutputLevel(Lwarn) + + assert.Equal(t, Std.Level, Lwarn) + Debug("test") + assert.Equal(t, out.String(), "") + Info("test") + assert.Equal(t, out.String(), "") + Warn("test") + outStr := out.String() + assert.Equal(t, outStr[26:], " [WARN] qiniupkg.com/x/log.v7/logext_test.go:74: test\n") + + Std = std +} diff --git a/vendor/github.com/qiniu/x/mockhttp.v7/mockhttp.go b/vendor/github.com/qiniu/x/mockhttp.v7/mockhttp.go new file mode 100644 index 0000000..0ecc6e6 --- /dev/null +++ b/vendor/github.com/qiniu/x/mockhttp.v7/mockhttp.go @@ -0,0 +1,110 @@ +package mockhttp + +import ( + "errors" + "io" + "io/ioutil" + "net/http" + "net/http/httptest" + "strconv" + + "qiniupkg.com/x/log.v7" +) + +var ( + ErrServerNotFound = errors.New("server not found") +) + +// -------------------------------------------------------------------- + +type mockServerRequestBody struct { + reader io.Reader + closeSignal bool +} + +func (r *mockServerRequestBody) Read(p []byte) (int, error) { + if r.closeSignal || r.reader == nil { + return 0, io.EOF + } + return r.reader.Read(p) +} + +func (r *mockServerRequestBody) Close() error { + r.closeSignal = true + if c, ok := r.reader.(io.Closer); ok { + return c.Close() + } + return nil +} + +// -------------------------------------------------------------------- +// type Transport + +type Transport struct { + route map[string]http.Handler +} + +func NewTransport() *Transport { + + return &Transport{ + route: make(map[string]http.Handler), + } +} + +func (p *Transport) ListenAndServe(host string, h http.Handler) { + + if h == nil { + h = http.DefaultServeMux + } + p.route[host] = h +} + +func (p *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) { + + h := p.route[req.URL.Host] + if h == nil { + log.Warn("Server not found:", req.Host) + return nil, ErrServerNotFound + } + + cp := *req + cp.URL.Scheme = "" + cp.URL.Host = "" + cp.RemoteAddr = "127.0.0.1:8000" + cp.Body = &mockServerRequestBody{req.Body, false} + req = &cp + + rw := httptest.NewRecorder() + h.ServeHTTP(rw, req) + + req.Body.Close() + + ctlen := int64(-1) + if v := rw.HeaderMap.Get("Content-Length"); v != "" { + ctlen, _ = strconv.ParseInt(v, 10, 64) + } + + return &http.Response{ + Status: "", + StatusCode: rw.Code, + Header: rw.HeaderMap, + Body: ioutil.NopCloser(rw.Body), + ContentLength: ctlen, + TransferEncoding: nil, + Close: false, + Trailer: nil, + Request: req, + }, nil +} + +// -------------------------------------------------------------------- + +var DefaultTransport = NewTransport() +var DefaultClient = &http.Client{Transport: DefaultTransport} + +func ListenAndServe(host string, h http.Handler) { + + DefaultTransport.ListenAndServe(host, h) +} + +// -------------------------------------------------------------------- diff --git a/vendor/github.com/qiniu/x/mockhttp.v7/mockhttp_test.go b/vendor/github.com/qiniu/x/mockhttp.v7/mockhttp_test.go new file mode 100644 index 0000000..d381332 --- /dev/null +++ b/vendor/github.com/qiniu/x/mockhttp.v7/mockhttp_test.go @@ -0,0 +1,114 @@ +package mockhttp_test + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "strconv" + "strings" + "testing" + + "qiniupkg.com/x/mockhttp.v7" + "qiniupkg.com/x/rpc.v7" +) + +// -------------------------------------------------------------------- + +func reply(w http.ResponseWriter, code int, data interface{}) { + + msg, _ := json.Marshal(data) + h := w.Header() + h.Set("Content-Length", strconv.Itoa(len(msg))) + h.Set("Content-Type", "application/json") + w.WriteHeader(code) + w.Write(msg) +} + +// -------------------------------------------------------------------- + +type FooRet struct { + A int `json:"a"` + B string `json:"b"` + C string `json:"c"` +} + +type HandleRet map[string]string + +type FooServer struct{} + +func (p *FooServer) foo(w http.ResponseWriter, req *http.Request) { + reply(w, 200, &FooRet{1, req.Host, req.URL.Path}) +} + +func (p *FooServer) handle(w http.ResponseWriter, req *http.Request) { + reply(w, 200, HandleRet{"foo": "1", "bar": "2"}) +} + +func (p *FooServer) postDump(w http.ResponseWriter, req *http.Request) { + req.Body.Close() + io.Copy(w, req.Body) +} + +func (p *FooServer) RegisterHandlers(mux *http.ServeMux) { + mux.HandleFunc("/foo", func(w http.ResponseWriter, req *http.Request) { p.foo(w, req) }) + mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { p.handle(w, req) }) + mux.HandleFunc("/dump", func(w http.ResponseWriter, req *http.Request) { p.postDump(w, req) }) +} + +// -------------------------------------------------------------------- + +func TestBasic(t *testing.T) { + + server := new(FooServer) + server.RegisterHandlers(http.DefaultServeMux) + + mockhttp.ListenAndServe("foo.com", nil) + + c := rpc.Client{mockhttp.DefaultClient} + { + var foo FooRet + err := c.Call(nil, &foo, "POST", "http://foo.com/foo") + if err != nil { + t.Fatal("call foo failed:", err) + } + if foo.A != 1 || foo.B != "foo.com" || foo.C != "/foo" { + t.Fatal("call foo: invalid ret") + } + fmt.Println(foo) + } + { + var ret map[string]string + err := c.Call(nil, &ret, "POST", "http://foo.com/bar") + if err != nil { + t.Fatal("call foo failed:", err) + } + if ret["foo"] != "1" || ret["bar"] != "2" { + t.Fatal("call bar: invalid ret") + } + fmt.Println(ret) + } + { + resp, err := c.Post("http://foo.com/dump", "", nil) + if err != nil { + t.Fatal("post foo failed:", err) + } + resp.Body.Close() + resp, err = c.Post("http://foo.com/dump", "", strings.NewReader("abc")) + if err != nil { + t.Fatal("post foo failed:", err) + } + defer resp.Body.Close() + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatal("ioutil.ReadAll:", err) + } + if len(b) != 0 { + t.Fatal("body should be empty:", string(b)) + } + } +} + +// -------------------------------------------------------------------- + diff --git a/vendor/github.com/qiniu/x/reqid.v7/reqid.go b/vendor/github.com/qiniu/x/reqid.v7/reqid.go new file mode 100644 index 0000000..fc1528b --- /dev/null +++ b/vendor/github.com/qiniu/x/reqid.v7/reqid.go @@ -0,0 +1,52 @@ +package reqid + +import ( + "encoding/binary" + "encoding/base64" + "net/http" + "time" + + . "golang.org/x/net/context" +) + +// -------------------------------------------------------------------- + +var pid = uint32(time.Now().UnixNano() % 4294967291) + +func genReqId() string { + var b [12]byte + binary.LittleEndian.PutUint32(b[:], pid) + binary.LittleEndian.PutUint64(b[4:], uint64(time.Now().UnixNano())) + return base64.URLEncoding.EncodeToString(b[:]) +} + +// -------------------------------------------------------------------- + +type key int // key is unexported and used for Context + +const ( + reqidKey key = 0 +) + +func NewContext(ctx Context, reqid string) Context { + return WithValue(ctx, reqidKey, reqid) +} + +func NewContextWith(ctx Context, w http.ResponseWriter, req *http.Request) Context { + reqid := req.Header.Get("X-Reqid") + if reqid == "" { + reqid = genReqId() + req.Header.Set("X-Reqid", reqid) + } + h := w.Header() + h.Set("X-Reqid", reqid) + return WithValue(ctx, reqidKey, reqid) +} + +func FromContext(ctx Context) (reqid string, ok bool) { + reqid, ok = ctx.Value(reqidKey).(string) + return +} + +// -------------------------------------------------------------------- + diff --git a/vendor/github.com/qiniu/x/rpc.v7/gob/gobrpc_client.go b/vendor/github.com/qiniu/x/rpc.v7/gob/gobrpc_client.go new file mode 100644 index 0000000..09f4083 --- /dev/null +++ b/vendor/github.com/qiniu/x/rpc.v7/gob/gobrpc_client.go @@ -0,0 +1,107 @@ +package gob + +import ( + "bytes" + "encoding/gob" + "io" + "io/ioutil" + "net/http" + "strconv" + + "qiniupkg.com/x/rpc.v7" + + . "golang.org/x/net/context" +) + +// --------------------------------------------------------------------------- + +func Register(value interface{}) { + + gob.Register(value) +} + +func RegisterName(name string, value interface{}) { + + gob.RegisterName(name, value) +} + +// --------------------------------------------------------------------------- + +func ResponseError(resp *http.Response) (err error) { + + e := &rpc.ErrorInfo{ + Reqid: resp.Header.Get("X-Reqid"), + Code: resp.StatusCode, + } + if resp.StatusCode > 299 { + e.Err = resp.Header.Get("X-Err") + if errno := resp.Header.Get("X-Errno"); errno != "" { + v, err2 := strconv.ParseInt(errno, 10, 32) + if err2 != nil { + e.Err = err2.Error() + } + e.Errno = int(v) + } + } + return e +} + +func CallRet(ctx Context, ret interface{}, resp *http.Response) (err error) { + + defer func() { + io.Copy(ioutil.Discard, resp.Body) + resp.Body.Close() + }() + + if resp.StatusCode/100 == 2 { + if ret != nil && resp.ContentLength != 0 { + err = gob.NewDecoder(resp.Body).Decode(ret) + if err != nil { + return + } + } + if resp.StatusCode == 200 { + return nil + } + } + return ResponseError(resp) +} + +// --------------------------------------------------------------------------- + +type Client struct { + rpc.Client +} + +var ( + DefaultClient = Client{rpc.DefaultClient} +) + +func (r Client) Call( + ctx Context, ret interface{}, method, url1 string) (err error) { + + resp, err := r.DoRequestWith(ctx, method, url1, "application/gob", nil, 0) + if err != nil { + return err + } + return CallRet(ctx, ret, resp) +} + +func (r Client) CallWithGob( + ctx Context, ret interface{}, method, url1 string, params interface{}) (err error) { + + var b bytes.Buffer + err = gob.NewEncoder(&b).Encode(params) + if err != nil { + return err + } + + resp, err := r.DoRequestWith(ctx, method, url1, "application/gob", &b, b.Len()) + if err != nil { + return err + } + return CallRet(ctx, ret, resp) +} + +// --------------------------------------------------------------------------- + diff --git a/vendor/github.com/qiniu/x/rpc.v7/rpc_client.go b/vendor/github.com/qiniu/x/rpc.v7/rpc_client.go new file mode 100644 index 0000000..49624d7 --- /dev/null +++ b/vendor/github.com/qiniu/x/rpc.v7/rpc_client.go @@ -0,0 +1,344 @@ +package rpc + +import ( + "bytes" + "encoding/json" + "errors" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" + + "qiniupkg.com/x/reqid.v7" + + . "golang.org/x/net/context" +) + +var ( + UserAgent = "Golang qiniu/rpc package" +) + +var ( + ErrInvalidRequestURL = errors.New("invalid request url") +) + +// -------------------------------------------------------------------- + +type Client struct { + *http.Client +} + +var ( + DefaultClient = Client{&http.Client{Transport: http.DefaultTransport}} +) + +// -------------------------------------------------------------------- + +func newRequest(method, url1 string, body io.Reader) (req *http.Request, err error) { + + var host string + + // url1 = "-H http://[:]/" + // + if strings.HasPrefix(url1, "-H") { + url2 := strings.TrimLeft(url1[2:], " \t") + pos := strings.Index(url2, " ") + if pos <= 0 { + return nil, ErrInvalidRequestURL + } + host = url2[:pos] + url1 = strings.TrimLeft(url2[pos+1:], " \t") + } + + req, err = http.NewRequest(method, url1, body) + if err != nil { + return + } + if host != "" { + req.Host = host + } + return +} + +func (r Client) DoRequest(ctx Context, method, url string) (resp *http.Response, err error) { + + req, err := newRequest(method, url, nil) + if err != nil { + return + } + return r.Do(ctx, req) +} + +func (r Client) DoRequestWith( + ctx Context, method, url1 string, + bodyType string, body io.Reader, bodyLength int) (resp *http.Response, err error) { + + req, err := newRequest(method, url1, body) + if err != nil { + return + } + req.Header.Set("Content-Type", bodyType) + req.ContentLength = int64(bodyLength) + return r.Do(ctx, req) +} + +func (r Client) DoRequestWith64( + ctx Context, method, url1 string, + bodyType string, body io.Reader, bodyLength int64) (resp *http.Response, err error) { + + req, err := newRequest(method, url1, body) + if err != nil { + return + } + req.Header.Set("Content-Type", bodyType) + req.ContentLength = bodyLength + return r.Do(ctx, req) +} + +func (r Client) DoRequestWithForm( + ctx Context, method, url1 string, data map[string][]string) (resp *http.Response, err error) { + + msg := url.Values(data).Encode() + if method == "GET" || method == "HEAD" || method == "DELETE" { + if strings.ContainsRune(url1, '?') { + url1 += "&" + } else { + url1 += "?" + } + return r.DoRequest(ctx, method, url1 + msg) + } + return r.DoRequestWith( + ctx, method, url1, "application/x-www-form-urlencoded", strings.NewReader(msg), len(msg)) +} + +func (r Client) DoRequestWithJson( + ctx Context, method, url1 string, data interface{}) (resp *http.Response, err error) { + + msg, err := json.Marshal(data) + if err != nil { + return + } + return r.DoRequestWith( + ctx, method, url1, "application/json", bytes.NewReader(msg), len(msg)) +} + +func (r Client) Do(ctx Context, req *http.Request) (resp *http.Response, err error) { + + if ctx == nil { + ctx = Background() + } + + if reqid, ok := reqid.FromContext(ctx); ok { + req.Header.Set("X-Reqid", reqid) + } + + if _, ok := req.Header["User-Agent"]; !ok { + req.Header.Set("User-Agent", UserAgent) + } + + transport := r.Transport // don't change r.Transport + if transport == nil { + transport = http.DefaultTransport + } + + // avoid cancel() is called before Do(req), but isn't accurate + select { + case <-ctx.Done(): + err = ctx.Err() + return + default: + } + + if tr, ok := getRequestCanceler(transport); ok { // support CancelRequest + reqC := make(chan bool, 1) + go func() { + resp, err = r.Client.Do(req) + reqC <- true + }() + select { + case <-reqC: + case <-ctx.Done(): + tr.CancelRequest(req) + <-reqC + err = ctx.Err() + } + } else { + resp, err = r.Client.Do(req) + } + return +} + +// -------------------------------------------------------------------- + +type ErrorInfo struct { + Err string `json:"error,omitempty"` + Key string `json:"key,omitempty"` + Reqid string `json:"reqid,omitempty"` + Errno int `json:"errno,omitempty"` + Code int `json:"code"` +} + +func (r *ErrorInfo) ErrorDetail() string { + + msg, _ := json.Marshal(r) + return string(msg) +} + +func (r *ErrorInfo) Error() string { + + return r.Err +} + +func (r *ErrorInfo) RpcError() (code, errno int, key, err string) { + + return r.Code, r.Errno, r.Key, r.Err +} + +func (r *ErrorInfo) HttpCode() int { + + return r.Code +} + +// -------------------------------------------------------------------- + +func parseError(e *ErrorInfo, r io.Reader) { + + body, err1 := ioutil.ReadAll(r) + if err1 != nil { + e.Err = err1.Error() + return + } + + var ret struct { + Err string `json:"error"` + Key string `json:"key"` + Errno int `json:"errno"` + } + if json.Unmarshal(body, &ret) == nil && ret.Err != "" { + // qiniu error msg style returns here + e.Err, e.Key, e.Errno = ret.Err, ret.Key, ret.Errno + return + } + e.Err = string(body) +} + +func ResponseError(resp *http.Response) (err error) { + + e := &ErrorInfo{ + Reqid: resp.Header.Get("X-Reqid"), + Code: resp.StatusCode, + } + if resp.StatusCode > 299 { + if resp.ContentLength != 0 { + ct, ok := resp.Header["Content-Type"] + if ok && strings.HasPrefix(ct[0], "application/json") { + parseError(e, resp.Body) + } + } + } + return e +} + +func CallRet(ctx Context, ret interface{}, resp *http.Response) (err error) { + + defer func() { + io.Copy(ioutil.Discard, resp.Body) + resp.Body.Close() + }() + + if resp.StatusCode/100 == 2 { + if ret != nil && resp.ContentLength != 0 { + err = json.NewDecoder(resp.Body).Decode(ret) + if err != nil { + return + } + } + if resp.StatusCode == 200 { + return nil + } + } + return ResponseError(resp) +} + +func (r Client) CallWithForm( + ctx Context, ret interface{}, method, url1 string, param map[string][]string) (err error) { + + resp, err := r.DoRequestWithForm(ctx, method, url1, param) + if err != nil { + return err + } + return CallRet(ctx, ret, resp) +} + +func (r Client) CallWithJson( + ctx Context, ret interface{}, method, url1 string, param interface{}) (err error) { + + resp, err := r.DoRequestWithJson(ctx, method, url1, param) + if err != nil { + return err + } + return CallRet(ctx, ret, resp) +} + +func (r Client) CallWith( + ctx Context, ret interface{}, method, url1, bodyType string, body io.Reader, bodyLength int) (err error) { + + resp, err := r.DoRequestWith(ctx, method, url1, bodyType, body, bodyLength) + if err != nil { + return err + } + return CallRet(ctx, ret, resp) +} + +func (r Client) CallWith64( + ctx Context, ret interface{}, method, url1, bodyType string, body io.Reader, bodyLength int64) (err error) { + + resp, err := r.DoRequestWith64(ctx, method, url1, bodyType, body, bodyLength) + if err != nil { + return err + } + return CallRet(ctx, ret, resp) +} + +func (r Client) Call( + ctx Context, ret interface{}, method, url1 string) (err error) { + + resp, err := r.DoRequestWith(ctx, method, url1, "application/x-www-form-urlencoded", nil, 0) + if err != nil { + return err + } + return CallRet(ctx, ret, resp) +} + +// --------------------------------------------------------------------------- + +type requestCanceler interface { + CancelRequest(req *http.Request) +} + +type nestedObjectGetter interface { + NestedObject() interface{} +} + +func getRequestCanceler(tp http.RoundTripper) (rc requestCanceler, ok bool) { + + if rc, ok = tp.(requestCanceler); ok { + return + } + + p := interface{}(tp) + for { + getter, ok1 := p.(nestedObjectGetter) + if !ok1 { + return + } + p = getter.NestedObject() + if rc, ok = p.(requestCanceler); ok { + return + } + } +} + +// -------------------------------------------------------------------- + diff --git a/vendor/github.com/qiniu/x/rpc.v7/rpc_client_test.go b/vendor/github.com/qiniu/x/rpc.v7/rpc_client_test.go new file mode 100644 index 0000000..150eeaa --- /dev/null +++ b/vendor/github.com/qiniu/x/rpc.v7/rpc_client_test.go @@ -0,0 +1,66 @@ +package rpc + +import ( + "fmt" + "net/http" + "testing" +) + +// -------------------------------------------------------------------- + +func TestNewRequest(t *testing.T) { + + req, err := http.NewRequest("GET", "-H\t abc.com \thttp://127.0.0.1/foo/bar", nil) + if err != nil { + t.Fatal("http.NewRequest failed") + } + if req.Host != "" { + t.Fatal(`http.NewRequest: req.Host != ""`) + } + + req, err = newRequest("GET", "-H\t abc.com \thttp://127.0.0.1/foo/bar", nil) + if err != nil { + t.Fatal("newRequest failed:", err) + } + + fmt.Println("Host:", req.Host, "path:", req.URL.Path, "url.host:", req.URL.Host) + + if req.Host != "abc.com" || req.URL.Path != "/foo/bar" || req.URL.Host != "127.0.0.1" { + t.Fatal(`req.Host != "abc.com" || req.URL.Path != "/foo/bar" || req.URL.Host != "127.0.0.1"`) + } +} + +// -------------------------------------------------------------------- + +type transport struct { + a http.RoundTripper +} + +func (p *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) { + return p.a.RoundTrip(req) +} + +func (p *transport) NestedObject() interface{} { + return p.a +} + +func Test_getRequestCanceler(t *testing.T) { + + p := &transport{a: http.DefaultTransport} + if _, ok := getRequestCanceler(p); !ok { + t.Fatal("getRequestCanceler failed") + } + + p2 := &transport{a: p} + if _, ok := getRequestCanceler(p2); !ok { + t.Fatal("getRequestCanceler(p2) failed") + } + + p3 := &transport{} + if _, ok := getRequestCanceler(p3); ok { + t.Fatal("getRequestCanceler(p3)?") + } +} + +// -------------------------------------------------------------------- + diff --git a/vendor/github.com/qiniu/x/ts.v7/testing.go b/vendor/github.com/qiniu/x/ts.v7/testing.go new file mode 100644 index 0000000..09ddbe8 --- /dev/null +++ b/vendor/github.com/qiniu/x/ts.v7/testing.go @@ -0,0 +1,79 @@ +package ts + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strconv" + "testing" +) + +const logStackLevel = 2 + +func log(skip int, t *testing.T, args ...interface{}) { + + _, file, line, _ := runtime.Caller(skip) + _, fname := filepath.Split(file) + args1 := make([]interface{}, len(args)+1) + args1[0] = fname + ":" + strconv.Itoa(line) + ":" + copy(args1[1:], args) + + if os.PathSeparator == '/' { + fmt.Fprintln(os.Stdout, args1...) + } else { + t.Log(args1...) + } +} + +func logf(skip int, t *testing.T, format string, args ...interface{}) { + + _, file, line, _ := runtime.Caller(skip) + _, fname := filepath.Split(file) + args2 := make([]interface{}, len(args)+2) + args2[0] = fname + args2[1] = line + copy(args2[2:], args) + + if os.PathSeparator == '/' { + fmt.Fprintf(os.Stderr, "%s:%d: "+format+"\n", args2...) + } else { + t.Logf("%s:%d: "+format, args2...) + } +} + +// Log formats its arguments using default formatting, analogous to Print(), +// and records the text in the error log. +func Log(t *testing.T, args ...interface{}) { + log(logStackLevel, t, args...) +} + +// Logf formats its arguments according to the format, analogous to Printf(), +// and records the text in the error log. +func Logf(t *testing.T, format string, args ...interface{}) { + logf(logStackLevel, t, format, args...) +} + +// Error is equivalent to Log() followed by Fail(). +func Error(t *testing.T, args ...interface{}) { + log(logStackLevel, t, args...) + t.Fail() +} + +// Errorf is equivalent to Logf() followed by Fail(). +func Errorf(t *testing.T, format string, args ...interface{}) { + logf(logStackLevel, t, format, args...) + t.Fail() +} + +// Fatal is equivalent to Log() followed by FailNow(). +func Fatal(t *testing.T, args ...interface{}) { + log(logStackLevel, t, args...) + t.FailNow() +} + +// Fatalf is equivalent to Logf() followed by FailNow(). +func Fatalf(t *testing.T, format string, args ...interface{}) { + logf(logStackLevel, t, format, args...) + t.FailNow() +} diff --git a/vendor/github.com/qiniu/x/url.v7/urlescape.go b/vendor/github.com/qiniu/x/url.v7/urlescape.go new file mode 100644 index 0000000..3a2d1fd --- /dev/null +++ b/vendor/github.com/qiniu/x/url.v7/urlescape.go @@ -0,0 +1,208 @@ +package url + +import ( + "strconv" +) + +type Encoding int + +const ( + EncodePath Encoding = 1 + iota + EncodeUserPassword + EncodeQueryComponent + EncodeFragment +) + +type EscapeError string + +func (e EscapeError) Error() string { + return "invalid URL escape " + strconv.Quote(string(e)) +} + +func ishex(c byte) bool { + switch { + case '0' <= c && c <= '9': + return true + case 'a' <= c && c <= 'f': + return true + case 'A' <= c && c <= 'F': + return true + } + return false +} + +func unhex(c byte) byte { + switch { + case '0' <= c && c <= '9': + return c - '0' + case 'a' <= c && c <= 'f': + return c - 'a' + 10 + case 'A' <= c && c <= 'F': + return c - 'A' + 10 + } + return 0 +} + +// Return true if the specified character should be escaped when +// appearing in a URL string, according to RFC 3986. +// When 'all' is true the full range of reserved characters are matched. +func shouldEscape(c byte, mode Encoding) bool { + + // §2.3 Unreserved characters (alphanum) + if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' { + return false + } + + switch c { + case '-', '_', '.', '~': // §2.3 Unreserved characters (mark) + return false + + case '$', '&', '+', ',', '/', ':', ';', '=', '?', '@': // §2.2 Reserved characters (reserved) + // Different sections of the URL allow a few of + // the reserved characters to appear unescaped. + switch mode { + case EncodePath: // §3.3 + // The RFC allows : @ & = + $ but saves / ; , for assigning + // meaning to individual path segments. This package + // only manipulates the path as a whole, so we allow those + // last two as well. That leaves only ? to escape. + return c == '?' + + case EncodeUserPassword: // §3.2.2 + // The RFC allows ; : & = + $ , in userinfo, so we must escape only @ and /. + // The parsing of userinfo treats : as special so we must escape that too. + return c == '@' || c == '/' || c == ':' + + case EncodeQueryComponent: // §3.4 + // The RFC reserves (so we must escape) everything. + return true + + case EncodeFragment: // §4.1 + // The RFC text is silent but the grammar allows + // everything, so escape nothing. + return false + } + } + + // Everything else must be escaped. + return true +} + +// QueryUnescape does the inverse transformation of QueryEscape, converting +// %AB into the byte 0xAB and '+' into ' ' (space). It returns an error if +// any % is not followed by two hexadecimal digits. +func QueryUnescape(s string) (string, error) { + + return UnescapeEx(s, EncodeQueryComponent) +} + +func Unescape(s string) (string, error) { + + return UnescapeEx(s, EncodePath) +} + +// UnescapeEx unescapes a string; the mode specifies +// which section of the URL string is being unescaped. +func UnescapeEx(s string, mode Encoding) (string, error) { + + // Count %, check that they're well-formed. + n := 0 + hasPlus := false + for i := 0; i < len(s); { + switch s[i] { + case '%': + n++ + if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) { + s = s[i:] + if len(s) > 3 { + s = s[0:3] + } + return "", EscapeError(s) + } + i += 3 + case '+': + hasPlus = mode == EncodeQueryComponent + i++ + default: + i++ + } + } + + if n == 0 && !hasPlus { + return s, nil + } + + t := make([]byte, len(s)-2*n) + j := 0 + for i := 0; i < len(s); { + switch s[i] { + case '%': + t[j] = unhex(s[i+1])<<4 | unhex(s[i+2]) + j++ + i += 3 + case '+': + if mode == EncodeQueryComponent { + t[j] = ' ' + } else { + t[j] = '+' + } + j++ + i++ + default: + t[j] = s[i] + j++ + i++ + } + } + return string(t), nil +} + +// QueryEscape escapes the string so it can be safely placed +// inside a URL query. +func QueryEscape(s string) string { + + return EscapeEx(s, EncodeQueryComponent) +} + +func Escape(s string) string { + + return EscapeEx(s, EncodePath) +} + +func EscapeEx(s string, mode Encoding) string { + + spaceCount, hexCount := 0, 0 + for i := 0; i < len(s); i++ { + c := s[i] + if shouldEscape(c, mode) { + if c == ' ' && mode == EncodeQueryComponent { + spaceCount++ + } else { + hexCount++ + } + } + } + + if spaceCount == 0 && hexCount == 0 { + return s + } + + t := make([]byte, len(s)+2*hexCount) + j := 0 + for i := 0; i < len(s); i++ { + switch c := s[i]; { + case c == ' ' && mode == EncodeQueryComponent: + t[j] = '+' + j++ + case shouldEscape(c, mode): + t[j] = '%' + t[j+1] = "0123456789ABCDEF"[c>>4] + t[j+2] = "0123456789ABCDEF"[c&15] + j += 3 + default: + t[j] = s[i] + j++ + } + } + return string(t) +} diff --git a/vendor/github.com/qiniu/x/xlog.v7/xlog.go b/vendor/github.com/qiniu/x/xlog.v7/xlog.go new file mode 100644 index 0000000..2f627da --- /dev/null +++ b/vendor/github.com/qiniu/x/xlog.v7/xlog.go @@ -0,0 +1,211 @@ +package xlog + +import ( + "fmt" + "io" + "os" + "runtime" + + "qiniupkg.com/x/log.v7" + "qiniupkg.com/x/reqid.v7" + + . "golang.org/x/net/context" +) + +const ( + Ldate = log.Ldate + Ltime = log.Ltime + Lmicroseconds = log.Lmicroseconds + Llongfile = log.Llongfile + Lshortfile = log.Lshortfile + Lmodule = log.Lmodule + Llevel = log.Llevel + LstdFlags = log.LstdFlags + Ldefault = log.Ldefault +) + +const ( + Ldebug = log.Ldebug + Linfo = log.Linfo + Lwarn = log.Lwarn + Lerror = log.Lerror + Lpanic = log.Lpanic + Lfatal = log.Lfatal +) + +// ============================================================================ +// type *Logger + +type Logger struct { + ReqId string +} + +func New(reqId string) *Logger { + + return &Logger{reqId} +} + +func NewWith(ctx Context) *Logger { + + reqId, ok := reqid.FromContext(ctx) + if !ok { + log.Debug("xlog.New: reqid isn't find in context") + } + return &Logger{reqId} +} + +func (xlog *Logger) Spawn(child string) *Logger { + + return &Logger{xlog.ReqId + "." + child} +} + +// ============================================================================ + +// Print calls Output to print to the standard Logger. +// Arguments are handled in the manner of fmt.Print. +func (xlog *Logger) Print(v ...interface{}) { + log.Std.Output(xlog.ReqId, log.Linfo, 2, fmt.Sprint(v...)) +} + +// Printf calls Output to print to the standard Logger. +// Arguments are handled in the manner of fmt.Printf. +func (xlog *Logger) Printf(format string, v ...interface{}) { + log.Std.Output(xlog.ReqId, log.Linfo, 2, fmt.Sprintf(format, v...)) +} + +// Println calls Output to print to the standard Logger. +// Arguments are handled in the manner of fmt.Println. +func (xlog *Logger) Println(v ...interface{}) { + log.Std.Output(xlog.ReqId, log.Linfo, 2, fmt.Sprintln(v...)) +} + +// ----------------------------------------- + +func (xlog *Logger) Debugf(format string, v ...interface{}) { + if log.Ldebug < log.Std.Level { + return + } + log.Std.Output(xlog.ReqId, log.Ldebug, 2, fmt.Sprintf(format, v...)) +} + +func (xlog *Logger) Debug(v ...interface{}) { + if log.Ldebug < log.Std.Level { + return + } + log.Std.Output(xlog.ReqId, log.Ldebug, 2, fmt.Sprintln(v...)) +} + +// ----------------------------------------- + +func (xlog *Logger) Infof(format string, v ...interface{}) { + if log.Linfo < log.Std.Level { + return + } + log.Std.Output(xlog.ReqId, log.Linfo, 2, fmt.Sprintf(format, v...)) +} + +func (xlog *Logger) Info(v ...interface{}) { + if log.Linfo < log.Std.Level { + return + } + log.Std.Output(xlog.ReqId, log.Linfo, 2, fmt.Sprintln(v...)) +} + +// ----------------------------------------- + +func (xlog *Logger) Warnf(format string, v ...interface{}) { + log.Std.Output(xlog.ReqId, log.Lwarn, 2, fmt.Sprintf(format, v...)) +} + +func (xlog *Logger) Warn(v ...interface{}) { + log.Std.Output(xlog.ReqId, log.Lwarn, 2, fmt.Sprintln(v...)) +} + +// ----------------------------------------- + +func (xlog *Logger) Errorf(format string, v ...interface{}) { + log.Std.Output(xlog.ReqId, log.Lerror, 2, fmt.Sprintf(format, v...)) +} + +func (xlog *Logger) Error(v ...interface{}) { + log.Std.Output(xlog.ReqId, log.Lerror, 2, fmt.Sprintln(v...)) +} + +// ----------------------------------------- + +// Fatal is equivalent to Print() followed by a call to os.Exit(1). +func (xlog *Logger) Fatal(v ...interface{}) { + log.Std.Output(xlog.ReqId, log.Lfatal, 2, fmt.Sprint(v...)) + os.Exit(1) +} + +// Fatalf is equivalent to Printf() followed by a call to os.Exit(1). +func (xlog *Logger) Fatalf(format string, v ...interface{}) { + log.Std.Output(xlog.ReqId, log.Lfatal, 2, fmt.Sprintf(format, v...)) + os.Exit(1) +} + +// Fatalln is equivalent to Println() followed by a call to os.Exit(1). +func (xlog *Logger) Fatalln(v ...interface{}) { + log.Std.Output(xlog.ReqId, log.Lfatal, 2, fmt.Sprintln(v...)) + os.Exit(1) +} + +// ----------------------------------------- + +// Panic is equivalent to Print() followed by a call to panic(). +func (xlog *Logger) Panic(v ...interface{}) { + s := fmt.Sprint(v...) + log.Std.Output(xlog.ReqId, log.Lpanic, 2, s) + panic(s) +} + +// Panicf is equivalent to Printf() followed by a call to panic(). +func (xlog *Logger) Panicf(format string, v ...interface{}) { + s := fmt.Sprintf(format, v...) + log.Std.Output(xlog.ReqId, log.Lpanic, 2, s) + panic(s) +} + +// Panicln is equivalent to Println() followed by a call to panic(). +func (xlog *Logger) Panicln(v ...interface{}) { + s := fmt.Sprintln(v...) + log.Std.Output(xlog.ReqId, log.Lpanic, 2, s) + panic(s) +} + +func (xlog *Logger) Stack(v ...interface{}) { + s := fmt.Sprint(v...) + s += "\n" + buf := make([]byte, 1024*1024) + n := runtime.Stack(buf, true) + s += string(buf[:n]) + s += "\n" + log.Std.Output(xlog.ReqId, log.Lerror, 2, s) +} + +func (xlog *Logger) SingleStack(v ...interface{}) { + s := fmt.Sprint(v...) + s += "\n" + buf := make([]byte, 1024*1024) + n := runtime.Stack(buf, false) + s += string(buf[:n]) + s += "\n" + log.Std.Output(xlog.ReqId, log.Lerror, 2, s) +} + +// ============================================================================ + +func SetOutput(w io.Writer) { + log.SetOutput(w) +} + +func SetFlags(flag int) { + log.SetFlags(flag) +} + +func SetOutputLevel(lvl int) { + log.SetOutputLevel(lvl) +} + +// ============================================================================ diff --git a/vendor/golang.org/x/crypto/blake2b/blake2b.go b/vendor/golang.org/x/crypto/blake2b/blake2b.go index ce62241..7f0a86e 100644 --- a/vendor/golang.org/x/crypto/blake2b/blake2b.go +++ b/vendor/golang.org/x/crypto/blake2b/blake2b.go @@ -2,9 +2,19 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package blake2b implements the BLAKE2b hash algorithm as -// defined in RFC 7693. -package blake2b // import "golang.org/x/crypto/blake2b" +// Package blake2b implements the BLAKE2b hash algorithm defined by RFC 7693 +// and the extendable output function (XOF) BLAKE2Xb. +// +// For a detailed specification of BLAKE2b see https://blake2.net/blake2.pdf +// and for BLAKE2Xb see https://blake2.net/blake2x.pdf +// +// If you aren't sure which function you need, use BLAKE2b (Sum512 or New512). +// If you need a secret-key MAC (message authentication code), use the New512 +// function with a non-nil key. +// +// BLAKE2X is a construction to compute hash values larger than 64 bytes. It +// can produce hash values between 0 and 4 GiB. +package blake2b import ( "encoding/binary" @@ -171,7 +181,13 @@ func (d *digest) Write(p []byte) (n int, err error) { return } -func (d *digest) Sum(b []byte) []byte { +func (d *digest) Sum(sum []byte) []byte { + var hash [Size]byte + d.finalize(&hash) + return append(sum, hash[:d.size]...) +} + +func (d *digest) finalize(hash *[Size]byte) { var block [BlockSize]byte copy(block[:], d.block[:d.offset]) remaining := uint64(BlockSize - d.offset) @@ -185,10 +201,7 @@ func (d *digest) Sum(b []byte) []byte { h := d.h hashBlocks(&h, &c, 0xFFFFFFFFFFFFFFFF, block[:]) - var sum [Size]byte - for i, v := range h[:(d.size+7)/8] { - binary.LittleEndian.PutUint64(sum[8*i:], v) + for i, v := range h { + binary.LittleEndian.PutUint64(hash[8*i:], v) } - - return append(b, sum[:d.size]...) } diff --git a/vendor/golang.org/x/crypto/blake2b/blake2b_test.go b/vendor/golang.org/x/crypto/blake2b/blake2b_test.go index 7954346..4aa4957 100644 --- a/vendor/golang.org/x/crypto/blake2b/blake2b_test.go +++ b/vendor/golang.org/x/crypto/blake2b/blake2b_test.go @@ -9,6 +9,7 @@ import ( "encoding/hex" "fmt" "hash" + "io" "testing" ) @@ -44,6 +45,30 @@ func TestHashes(t *testing.T) { testHashes(t) } +func TestHashes2X(t *testing.T) { + defer func(sse4, avx, avx2 bool) { + useSSE4, useAVX, useAVX2 = sse4, avx, avx2 + }(useSSE4, useAVX, useAVX2) + + if useAVX2 { + t.Log("AVX2 version") + testHashes2X(t) + useAVX2 = false + } + if useAVX { + t.Log("AVX version") + testHashes2X(t) + useAVX = false + } + if useSSE4 { + t.Log("SSE4 version") + testHashes2X(t) + useSSE4 = false + } + t.Log("generic version") + testHashes2X(t) +} + func testHashes(t *testing.T) { key, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f") @@ -77,6 +102,72 @@ func testHashes(t *testing.T) { } } +func testHashes2X(t *testing.T) { + key, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f") + + input := make([]byte, 256) + for i := range input { + input[i] = byte(i) + } + + for i, expectedHex := range hashes2X { + length := uint32(len(expectedHex) / 2) + sum := make([]byte, int(length)) + + h, err := NewXOF(length, key) + if err != nil { + t.Fatalf("#%d: error from NewXOF: %v", i, err) + } + + if _, err := h.Write(input); err != nil { + t.Fatalf("#%d (single write): error from Write: %v", i, err) + } + if _, err := h.Read(sum); err != nil { + t.Fatalf("#%d (single write): error from Read: %v", i, err) + } + if n, err := h.Read(sum); n != 0 || err != io.EOF { + t.Fatalf("#%d (single write): Read did not return (0, os.EOF) after exhaustion, got (%v, %v)", i, n, err) + } + if gotHex := fmt.Sprintf("%x", sum); gotHex != expectedHex { + t.Fatalf("#%d (single write): got %s, wanted %s", i, gotHex, expectedHex) + } + + h.Reset() + for j := 0; j < len(input); j++ { + h.Write(input[j : j+1]) + } + for j := 0; j < len(sum); j++ { + h = h.Clone() + if _, err := h.Read(sum[j : j+1]); err != nil { + t.Fatalf("#%d (byte-by-byte) - Read %d: error from Read: %v", i, j, err) + } + } + if gotHex := fmt.Sprintf("%x", sum); gotHex != expectedHex { + t.Fatalf("#%d (byte-by-byte): got %s, wanted %s", i, gotHex, expectedHex) + } + } + + h, err := NewXOF(OutputLengthUnknown, key) + if err != nil { + t.Fatalf("#unknown length: error from NewXOF: %v", err) + } + if _, err := h.Write(input); err != nil { + t.Fatalf("#unknown length: error from Write: %v", err) + } + + var result [64]byte + if n, err := h.Read(result[:]); err != nil { + t.Fatalf("#unknown length: error from Read: %v", err) + } else if n != len(result) { + t.Fatalf("#unknown length: Read returned %d bytes, want %d", n, len(result)) + } + + const expected = "3dbba8516da76bf7330055c66ea36cf1005e92714262b24d9710f51d9e126406e1bcd6497059f9331f1091c3634b695428d475ed432f987040575520a1c29f5e" + if fmt.Sprintf("%x", result) != expected { + t.Fatalf("#unknown length: bad result %x, wanted %s", result, expected) + } +} + func generateSequence(out []byte, seed uint32) { a := 0xDEAD4BAD * seed // prime b := uint32(1) @@ -446,3 +537,262 @@ var hashes = []string{ "d444bfa2362a96df213d070e33fa841f51334e4e76866b8139e8af3bb3398be2dfaddcbc56b9146de9f68118dc5829e74b0c28d7711907b121f9161cb92b69a9", "142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461", } + +var hashes2X = []string{ + "64", + "f457", + "e8c045", + "a74c6d0d", + "eb02ae482a", + "be65b981275e", + "8540ccd083a455", + "074a02fa58d7c7c0", + "da6da05e10db3022b6", + "542a5aae2f28f2c3b68c", + "ca3af2afc4afe891da78b1", + "e0f66b8dcebf4edc85f12c85", + "744224d383733b3fa2c53bfcf5", + "b09b653e85b72ef5cdf8fcfa95f3", + "dd51877f31f1cf7b9f68bbb09064a3", + "f5ebf68e7ebed6ad445ffc0c47e82650", + "ebdcfe03bcb7e21a9091202c5938c0a1bb", + "860fa5a72ff92efafc48a89df1632a4e2809", + "0d6d49daa26ae2818041108df3ce0a4db48c8d", + "e5d7e1bc5715f5ae991e4043e39533af5d53e47f", + "5232028a43b9d4dfa7f37439b49495926481ab8a29", + "c118803c922f9ae2397fb676a2ab7603dd9c29c21fe4", + "2af924f48b9bd7076bfd68794bba6402e2a7ae048de3ea", + "61255ac38231087c79ea1a0fa14538c26be1c851b6f318c0", + "f9712b8e42f0532162822f142cb946c40369f2f0e77b6b186e", + "76da0b89558df66f9b1e66a61d1e795b178ce77a359087793ff2", + "9036fd1eb32061bdecebc4a32aa524b343b8098a16768ee774d93c", + "f4ce5a05934e125d159678bea521f585574bcf9572629f155f63efcc", + "5e1c0d9fae56393445d3024d6b82692d1339f7b5936f68b062c691d3bf", + "538e35f3e11111d7c4bab69f83b30ade4f67addf1f45cdd2ac74bf299509", + "17572c4dcbb17faf8785f3bba9f6903895394352eae79b01ebd758377694cc", + "29f6bb55de7f8868e053176c878c9fe6c2055c4c5413b51ab0386c277fdbac75", + "bad026c8b2bd3d294907f2280a7145253ec2117d76e3800357be6d431b16366e41", + "386b7cb6e0fd4b27783125cbe80065af8eb9981fafc3ed18d8120863d972fa7427d9", + "06e8e6e26e756fff0b83b226dce974c21f970e44fb5b3e5bbada6e4b12f81cca666f48", + "2f9bd300244f5bc093ba6dcdb4a89fa29da22b1de9d2c9762af919b5fedf6998fbda305b", + "cf6bdcc46d788074511f9e8f0a4b86704365b2d3f98340b8db53920c385b959a38c8869ae7", + "1171e603e5cdeb4cda8fd7890222dd8390ede87b6f3284cac0f0d832d8250c9200715af7913d", + "bda7b2ad5d02bd35ffb009bdd72b7d7bc9c28b3a32f32b0ba31d6cbd3ee87c60b7b98c03404621", + "2001455324e748503aa08eff2fb2e52ae0170e81a6e9368ada054a36ca340fb779393fb045ac72b3", + "45f0761aefafbf87a68f9f1f801148d9bba52616ad5ee8e8ac9207e9846a782f487d5cca8b20355a18", + "3a7e05708be62f087f17b41ac9f20e4ef8115c5ab6d08e84d46af8c273fb46d3ce1aabebae5eea14e018", + "ea318da9d042ca337ccdfb2bee3e96ecb8f907876c8d143e8e44569178353c2e593e4a82c265931ba1dd79", + "e0f7c08f5bd712f87094b04528fadb283d83c9ceb82a3e39ec31c19a42a1a1c3bee5613b5640abe069b0d690", + "d35e63fb1f3f52ab8f7c6cd7c8247e9799042e53922fbaea808ab979fa0c096588cfea3009181d2f93002dfc11", + "b8b0ab69e3ae55a8699eb481dd665b6a2424c89bc6b7cca02d15fdf1b9854139cab49d34de498b50b2c7e8b910cf", + "fb65e3222a2950eae1701d4cdd4736266f65bf2c0d2e77968996eadb60ef74fb786f6234973a2524bdfe32d100aa0e", + "f28b4bb3a2e2c4d5c01a23ff134558559a2d3d704b75402983ee4e0f71d273ae056842c4153b18ee5c47e2bfa54313d4", + "7bb78794e58a53c3e4b1aeb161e756af051583d14e0a5a3205e094b7c9a8cf62d098fa9ea1db12f330a51ab9852c17f983", + "a879a8ebae4d0987789bcc58ec3448e35ba1fa1ee58c668d8295aba4eaeaf2762b053a677e25404f635a53037996974d418a", + "695865b353ec701ecc1cb38f3154489eed0d39829fc192bb68db286d20fa0a64235cde5639137819f7e99f86bd89afcef84a0f", + "a6ec25f369f71176952fb9b33305dc768589a6070463ee4c35996e1ced4964a865a5c3dc8f0d809eab71366450de702318e4834d", + "604749f7bfadb069a036409ffac5ba291fa05be8cba2f141554132f56d9bcb88d1ce12f2004cd3ade1aa66a26e6ef64e327514096d", + "daf9fa7dc2464a899533594e7916fc9bc585bd29dd60c930f3bfa78bc47f6c8439448043a45119fc9228c15bce5fd24f46baf9de736b", + "943ea5647a8666763084da6a6f15dcf0e8dc24f27fd0d9194805d25180fe3a6d98f4b2b5e0d6a04e9b41869817030f16ae975dd41fc35c", + "af4f73cbfc093760dfeb52d57ef45207bbd1a515f5523404e5d95a73c237d97ae65bd195b472de6d514c2c448b12fafc282166da132258e9", + "605f4ed72ed7f5046a342fe4cf6808100d4632e610d59f7ebb016e367d0ff0a95cf45b02c727ba71f147e95212f52046804d376c918cadd260", + "3750d8ab0a6b13f78e51d321dfd1aa801680e958de45b7b977d05732ee39f856b27cb2bcce8fbf3db6666d35e21244c2881fdcc27fbfea6b1672", + "8f1b929e80ab752b58abe9731b7b34eb61369536995abef1c0980d93903c1880da3637d367456895f0cb4769d6de3a979e38ed6f5f6ac4d48e9b32", + "d8469b7aa538b36cdc711a591d60dafecca22bd421973a70e2deef72f69d8014a6f0064eabfbebf5383cbb90f452c6e113d2110e4b1092c54a38b857", + "7d1f1ad2029f4880e1898af8289c23bc933a40863cc4ab697fead79c58b6b8e25b68cf5324579b0fe879fe7a12e6d03907f0140dfe7b29d33d6109ecf1", + "87a77aca6d551642288a0dff66078225ae39d288801607429d6725ca949eed7a6f199dd8a65523b4ee7cfa4187400e96597bfffc3e38ade0ae0ab88536a9", + "e101f43179d8e8546e5ce6a96d7556b7e6b9d4a7d00e7aade5579d085d527ce34a9329551ebcaf6ba946949bbe38e30a62ae344c1950b4bde55306b3bac432", + "4324561d76c370ef35ac36a4adf8f3773a50d86504bd284f71f7ce9e2bc4c1f1d34a7fb2d67561d101955d448b67577eb30dfee96a95c7f921ef53e20be8bc44", + "78f0ed6e220b3da3cc9381563b2f72c8dc830cb0f39a48c6ae479a6a78dcfa94002631dec467e9e9b47cc8f0887eb680e340aec3ec009d4a33d241533c76c8ca8c", + "9f6589c31a472e0a736f4eb22b6c70a9d332cc15304ccb66a6b97cd051b6ed82f8990e1d9bee2e4bb1c3c45e550ae0e7b96e93ae23f2fb8f63b309131e72b36cba6a", + "c138077ee4ed3d7ffa85ba851dfdf6e9843fc1dc00889d117237bfaad9aa757192f73556b959f98e6d24886ce48869f2a01a48c371785f12b6484eb2078f08c22066e1", + "f83e7c9e0954a500576ea1fc90a3db2cbd7994eaef647dab5b34e88ab9dc0b47addbc807b21c8e6dd3d0bd357f008471d4f3e0abb18450e1d4919e03a34545b9643f870e", + "3277a11f2628544fc66f50428f1ad56bcba6ee36ba2ca6ecdf7e255effc0c30235c039d13e01f04cf1efe95b5c2033ab72adda30994b62f2851d17c9920eadca9a251752dc", + "c2a834281a06fe7b730d3a03f90761daf02714c066e33fc07e1f59ac801ec2f4433486b5a2da8faa51a0cf3c34e29b2960cd0013378938dbd47c3a3d12d70db01d7d06c3e91e", + "47680182924a51cabe142a6175c9253e8ba7ea579ece8d9bcb78b1e9ca00db844fa08abcf41702bd758ee2c608d9612fed50e85854469cb4ef3038acf1e35b6ba4390561d8ae82", + "cec45830cd71869e83b109a99a3cd7d935f83a95de7c582f3adbd34e4938fa2f3f922f52f14f169c38cc6618d3f306a8a4d607b345b8a9c48017136fbf825aecf7b620e85f837fae", + "46fb53c70ab105079d5d78dc60eaa30d938f26e4d0b9df122e21ec85deda94744c1daf8038b8a6652d1ff3e7e15376f5abd30e564784a999f665078340d66b0e939e0c2ef03f9c08bb", + "7b0dcb52791a170cc52f2e8b95d8956f325c3751d3ef3b2b83b41d82d4496b46228a750d02b71a96012e56b0720949ca77dc68be9b1ef1ad6d6a5ceb86bf565cb972279039e209dddcdc", + "7153fd43e6b05f5e1a4401e0fef954a737ed142ec2f60bc4daeef9ce73ea1b40a0fcaf1a1e03a3513f930dd5335723632f59f7297fe3a98b68e125eadf478eb045ed9fc4ee566d13f537f5", + "c7f569c79c801dab50e9d9ca6542f25774b3841e49c83efe0b89109f569509ce7887bc0d2b57b50320eb81fab9017f16c4c870e59edb6c26620d93748500231d70a36f48a7c60747ca2d5986", + "0a81e0c547648595adca65623ce783411aac7f7d30c3ad269efafab288e7186f6895261972f5137877669c550f34f5128850ebb50e1884814ea1055ee29a866afd04b2087abed02d9592573428", + "6a7b6769e1f1c95314b0c7fe77013567891bd23416374f23e4f43e27bc4c55cfada13b53b1581948e07fb96a50676baa2756db0988077b0f27d36ac088e0ff0fe72eda1e8eb4b8facff3218d9af0", + "a399474595cb1ccab6107f18e80f03b1707745c7bf769fc9f260094dc9f8bc6fe09271cb0b131ebb2acd073de4a6521c8368e664278be86be216d1622393f23435fae4fbc6a2e7c961282a777c2d75", + "4f0fc590b2755a515ae6b46e9628092369d9c8e589e3239320639aa8f7aa44f8111c7c4b3fdbe6e55e036fbf5ebc9c0aa87a4e66851c11e86f6cbf0bd9eb1c98a378c7a7d3af900f55ee108b59bc9e5c", + "ed96a046f08dd675107331d267379c6fce3c352a9f8d7b243008a74cb4e9410836afaabe871dab6038ca94ce5f6d41fa922ce08aba58169f94cfc86d9f688f396abd24c11a6a9b0830572105a477c33e92", + "379955f539abf0eb2972ee99ed9546c4bbee363403991833005dc27904c271ef22a799bc32cb39f08d2e4ba6717d55153feb692d7c5efae70890bf29d96df02333c7b05ccc314e4835b018fec9141a82c745", + "e16cc8d41b96547ede0d0cf4d908c5fa393399daa4a9696e76a4c1f6a2a9fef70f17fb53551a8145ed88f18db8fe780a079d94732437023f7c1d1849ef69ad536a76204239e8ba5d97e507c36c7d042f87fe0e", + "a81de50750ece3f84536728f227208bf01ec5b7721579d007de72c88ee20663318332efe5bc7c09ad1fa8342be51f0609046ccf760a7957a7d8dc88941adb93666a4521ebe76618e5ddc2dd3261493d400b50073", + "b72c5fb7c7f60d243928fa41a2d711157b96aef290185c64b4de3dcfa3d644da67a8f37c2ac55caad79ec695a473e8b481f658c497edb8a191526592b11a412282d2a4010c90ef4647bd6ce745ebc9244a71d4876b", + "9550703877079c90e200e830f277b605624954c549e729c359ee01ee2b07741ecc4255cb37f96682dafcdbaade1063e2c5ccbd1918fb669926a67744101fb6de3ac016be4c74165a1e5a696b704ba2ebf4a953d44b95", + "a17eb44d4de502dc04a80d5a5e9507d17f27c96467f24c79b06bc98a4c410741d4ac2db98ec02c2a976d788531f1a4451b6c6204cef6dae1b6ebbcd0bde23e6fffb02754043c8fd3c783d90a670b16879ce68b5554fe1c", + "41d3ea1eaba5be4a206732dbb5b70b79b66a6e5908795ad4fb7cf9e67efb13f06fef8f90acb080ce082aadec6a1b543af759ab63fa6f1d3941186482b0c2b312f1151ea8386253a13ed3708093279b8eb04185636488b226", + "5e7cdd8373dc42a243c96013cd29df9283b5f28bb50453a903c85e2ce57f35861bf93f03029072b70dac0804e7d51fd0c578c8d9fa619f1e9ce3d8044f65d55634dba611280c1d5cfb59c836a595c803124f696b07ddfac718", + "26a14c4aa168907cb5de0d12a82e1373a128fb21f2ed11feba108b1bebce934ad63ed89f4ed7ea5e0bc8846e4fc10142f82de0bebd39d68f7874f615c3a9c896bab34190e85df05aaa316e14820b5e478d838fa89dfc94a7fc1e", + "0211dfc3c35881adc170e4ba6daab1b702dff88933db9a6829a76b8f4a7c2a6d658117132a974f0a0b3a38ceea1efc2488da21905345909e1d859921dc2b5054f09bce8eeb91fa2fc6d048ce00b9cd655e6aafbdaa3a2f19270a16", + "ddf015b01b68c4f5f72c3145d54049867d99ee6bef24282abf0eecdb506e295bacf8f23ffa65a4cd891f76a046b9dd82cae43a8d01e18a8dff3b50aeb92672be69d7c087ec1fa2d3b2a39196ea5b49b7baede37a586fea71aded587f", + "6ee721f71ca4dd5c9ce7873c5c04c6ce76a2c824b984251c15535afc96adc9a4d48ca314bfeb6b8ee65092f14cf2a7ca9614e1dcf24c2a7f0f0c11207d3d8aed4af92873b56e8b9ba2fbd659c3f4ca90fa24f113f74a37181bf0fdf758", + "689bd150e65ac123612524f720f54def78c095eaab8a87b8bcc72b443408e3227f5c8e2bd5af9bcac684d497bc3e41b7a022c28fb5458b95e8dfa2e8caccde0492936ff1902476bb7b4ef2125b19aca2cd3384d922d9f36dddbcd96ae0d6", + "3a3c0ef066fa4390ec76ad6be1dc9c31ddf45fef43fbfa1f49b439caa2eb9f3042253a9853e96a9cf86b4f873785a5d2c5d3b05f6501bc876e09031188e05f48937bf3c9b667d14800db62437590b84ce96aa70bb5141ee2ea41b55a6fd944", + "741ce384e5e0edaebb136701ce38b3d33215415197758ae81235307a4115777d4dab23891db530c6d28f63a957428391421f742789a0e04c99c828373d9903b64dd57f26b3a38b67df829ae243feef731ead0abfca049924667fdec49d40f665", + "a513f450d66cd5a48a115aee862c65b26e836f35a5eb6894a80519e2cd96cc4cad8ed7eb922b4fc9bbc55c973089d627b1da9c3a95f6c019ef1d47143cc545b15e4244424be28199c51a5efc7234dcd94e72d229897c392af85f523c2633427825", + "71f1554d2d49bb7bd9e62e71fa049fb54a2c097032f61ebda669b3e1d4593962e47fc62a0ab5d85706aebd6a2f9a192c88aa1ee2f6a46710cf4af6d3c25b7e68ad5c3db23ac009c8f13625ff85dc8e50a9a1b2682d3329330b973ec8cbb7bb73b2bd", + "167cc1067bc08a8d2c1a0c10041ebe1fc327b37043f6bd8f1c63569e9d36ded58519e66b162f34b6d8f1107ef1e3de199d97b36b44141a1fc4f49b883f40507ff11f909a017869dc8a2357fc7336ae68703d25f75710b0ff5f9765321c0fa53a51675c", + "cb859b35dc70e264efaad2a809fea1e71cd4a3f924be3b5a13f8687a1166b538c40b2ad51d5c3e47b0de482497382673140f547068ff0b3b0fb7501209e1bf36082509ae85f60bb98fd02ac50d883a1a8daa704952d83c1f6da60c9624bc7c99912930bf", + "afb1f0c6b7125b04fa2578dd40f60cb411b35ebc7026c702e25b3f0ae3d4695d44cfdf37cb755691dd9c365edadf21ee44245620e6a24d4c2497135b37cd7ac67e3bd0aaee9f63f107746f9b88859ea902bc7d6895406aa2161f480cad56327d0a5bba2836", + "13e9c0522587460d90c7cb354604de8f1bf850e75b4b176bda92862d35ec810861f7d5e7ff6ba9302f2c2c8642ff8b7776a2f53665790f570fcef3cac069a90d50db42227331c4affb33d6c040d75b9aeafc9086eb83ced38bb02c759e95ba08c92b17031288", + "0549812d62d3ed497307673a4806a21060987a4dbbf43d352b9b170a29240954cf04bc3e1e250476e6800b79e843a8bd8253b7d743de01ab336e978d4bea384eaff700ce020691647411b10a60acacb6f8837fb08ad666b8dcc9eaa87ccb42aef6914a3f3bc30a", + "3a263efbe1f2d463f20526e1d0fd735035fd3f808925f058b32c4d8788aeeab9b8ce233b3c34894731cd73361f465bd350395aebcabd2fb63010298ca025d849c1fa3cd573309b74d7f824bbfe383f09db24bcc565f636b877333206a6ad70815c3bef5574c5fc1c", + "3c6a7d8a84ef7e3eaa812fc1eb8e85105467230d2c9e4562edbfd808f4d1ac15d16b786cc6a02959c2bc17149c2ce74c6f85ee5ef22a8a96b9be1f197cffd214c1ab02a06a9227f37cd432579f8c28ff2b5ac91cca8ffe6240932739d56788c354e92c591e1dd76499", + "b571859294b02af17541a0b5e899a5f67d6f5e36d38255bc417486e69240db56b09cf2607fbf4f95d085a779358a8a8b41f36503438c1860c8f361ce0f2783a08b21bd7232b50ca6d35428335272a5c05b436b2631d8d5c84d60e8040083768ce56a250727fb0579dd5c", + "98ee1b7269d2a0dd490ca38d447279870ea55326571a1b430adbb2cf65c492131136f504145df3ab113a13abfb72c33663266b8bc9c458db4bf5d7ef03e1d3b8a99d5de0c024be8fabc8dc4f5dac82a0342d8ed65c329e7018d6997e69e29a01350516c86beaf153da65ac", + "41c5c95f088df320d35269e5bf86d10248f17aec6776f0fe653f1c356aae409788c938befeb67c86d1c8870e8099ca0ce61a80fbb5a6654c44529368f70fc9b9c2f912f5092047d0ffc339577d24142300e34948e086f62e23ecaca410d24f8a36b5c8c5a80e0926bc8aa16a", + "9f93c41f533b2a82a4df893c78faaaa793c1506974ba2a604cd33101713ca4adfd30819ffd8403402b8d40aff78106f3357f3e2c24312c0d3603a17184d7b999fc9908d14d50192aebabd90d05073da7af4be37dd3d81c90acc80e8333df546f17ab6874f1ec204392d1c0571e", + "3da5207245ac270a915fc91cdb314e5a2577c4f8e269c4e701f0d7493ba716de79935918b917a2bd5db98050dbd1eb3894b65fac5abf13e075abebc011e651c03cafb6127147771a5c8418223e1548137a89206635c26ca9c235ccc108dc25cf846e4732444bd0c2782b197b262b", + "96011af3965bb941dc8f749932ea484eccb9ba94e34b39f24c1e80410f96ce1d4f6e0aa5be606def4f54301e930493d4b55d484d93ab9dd4dc2c9cfb79345363af31ad42f4bd1aa6c77b8afc9f0d551bef7570b13b927afe3e7ac4de7603a0876d5edb1ad9be05e9ee8b53941e8f59", + "51dbbf2a7ca224e524e3454fe82ddc901fafd2120fa8603bc343f129484e9600f688586e040566de0351d1693829045232d04ff31aa6b80125c763faab2a9b233313d931903dcfaba490538b06e4688a35886dc24cdd32a13875e6acf45454a8eb8a315ab95e608ad8b6a49aef0e299a", + "5a6a422529e22104681e8b18d64bc0463a45df19ae2633751c7aae412c250f8fb2cd5e1270d3d0cf009c8aa69688ccd4e2b6536f5747a5bc479b20c135bf4e89d33a26118705a614c6be7ecfe766932471ad4ba01c4f045b1abb5070f90ec78439a27a1788db9327d1c32f939e5fb1d5ba", + "5d26c983642093cb12ff0afabd87b7c56e211d01844ad6da3f623b9f20a0c968034299f2a65e6673530c5980a532beb831c7d0697d12760445986681076dfb6fae5f3a4d8f17a0db5008ce8619f566d2cfe4cf2a6d6f9c3664e3a48564a351c0b3c945c5ee24587521e4112c57e318be1b6a", + "52641dbc6e36be4d905d8d60311e303e8e859cc47901ce30d6f67f152343e3c4030e3a33463793c19effd81fb7c4d631a9479a7505a983a052b1e948ce093b30efa595fab3a00f4cef9a2f664ceeb07ec61719212d58966bca9f00a7d7a8cb4024cf6476bab7fbccee5fd4e7c3f5e2b2975aa2", + "a34ce135b37bf3db1c4aaa4878b4499bd2ee17b85578fcaf605d41e1826b45fdaa1b083d8235dc642787f11469a5493e36806504fe2a2063905e821475e2d5ee217057950370492f5024995e77b82aa51b4f5bd8ea24dc71e0a8a640b0592c0d80c24a726169cf0a10b40944747113d03b52708c", + "46b3cdf4946e15a5334fc3244d6680f5fc132afa67bf43bfade23d0c9e0ec64e7dab76faaeca1870c05f96b7d019411d8b0873d9fed04fa5057c039d5949a4d592827f619471359d6171691cfa8a5d7cb07ef2804f6ccad4821c56d4988bea7765f660f09ef87405f0a80bcf8559efa111f2a0b419", + "8b9fc21691477f11252fca050b121c5334eb4280aa11659e267297de1fec2b2294c7ccee9b59a149b9930b08bd320d3943130930a7d931b71d2f10234f4480c67f1de883d9894ada5ed5071660e221d78ae402f1f05af47761e13fec979f2671e3c63fb0ae7aa1327cf9b8313adab90794a52686bbc4", + "cd6598924ce847de7ff45b20ac940aa6292a8a99b56a74eddc24f2cfb45797188614a21d4e8867e23ff75afd7cd324248d58fcf1ddc73fbd115dfa8c09e62022fab540a59f87c989c12a86ded05130939f00cd2f3b512963dfe0289f0e54acad881c1027d2a0292138fdee902d67d9669c0ca1034a9456", + "594e1cd7337248704e691854af0fdb021067ddf7832b049ba7b684438c32b029eded2df2c89a6ff5f2f2c311522ae2dc6db5a815afc60637b15ec24ef9541f1550409db2a006da3affffe548a1eaee7bd114e9b805d0756c8e90c4dc33cb05226bc2b393b18d953f8730d4c7ae693159cdba758ad28964e2", + "1f0d292453f04406ada8be4c161b82e3cdd69099a8637659e0ee40b8f6da46005cfc6085db9804852decfbe9f7b4dda019a7112612895a144ed430a960c8b2f5458d3d56b7f427cee6358915aee7146278aed2a0296cdd929e4d21ef95a3adf8b7a6beba673cdccdbdcfb2474711732d972ad054b2dc64f38d", + "b65a72d4e1f9f9f75911cc46ad0806b9b18c87d105332a3fe183f45f063a746c892dc6c4b9181b1485b3e3a2cc3b453eba2d4c39d6905a774ed3fb755468beb190925ecd8e57ecb0d985125741650c6b6a1b2a3a50e93e3892c21d47ed5884eed83aa94e1602288f2f49fe286624de9d01fcb54433a0dc4ad70b", + "705ce0ffa469250782aff725248fc88fe98eb76659e8407edc1c4842c9867d61fe64fb86f74e980598b92bc213d06f337bd5654fc28643c7ba769a4c31563427543c00808b627a19c90d86c322f33566ce020121cc322229c3337943d46f68ef939d613dcef0077269f88151d6398b6b009abb763410b154ad76a3", + "7fa881ce87498440ab6af13854f0d851a7e0404de33896999a9b3292a5d2f5b3ad033530c558168fe5d2fdb9b89a2354c46cf32a0e612afc6c6485d789511bfef26800c74bf1a4cfbe30bda310d5f6029c3dccdedb6149e4971274e276dccfabd63bc4b9955e8303feb57f8a688db55ecb4b33d1f9fe1b3a8ba7ac32", + "23a98f71c01c0408ae16843dc03be7db0aeaf055f951709d4e0dfdf64fffbffaf900ee592ee10929648e56f6c1e9f5be5793f7df66453eb56502c7c56c0f0c88da77abc8fa371e434104627ef7c663c49f40998dbad63fa6c7aa4fac17ae138d8bbe081f9bd168cd33c1fbc92fa35ed687679f48a64b87db1fe5bae675", + "7b8970b6a33237e5a7bcb39272703edb92285c55842b30b9a48834b1b507cc02a6764739f2f7ee6ae02a7b715a1c455e59e8c77a1ae98abb10161853f1234d20da99016588cd8602d6b7ec7e177d4011edfa61e6b3766a3c6f8d6e9eac893c568903eb6e6aba9c4725774f6b4343b7acaa6c031593a36eef6c72806ff309", + "f7f4d328ba108b7b1de4443e889a985ed52f485f3ca4e0c246aa5526590cbed344e9f4fe53e4eea0e761c82324649206ca8c2b45152157d4115e68c818644b03b65bb47ad79f94d37cb03c1d953b74c2b8adfa0e1c418bda9c518ddcd7050e0f149044740a2b16479413b63fc13c36144f80c73687513dca761ba8642a8ae0", + "2d7dc80c19a1d12d5fe3963569547a5d1d3e821e6f06c5d5e2c09401f946c9f7e13cd019f2f9a878b62dd850453b6294b99ccaa068e542993524b0f63832d48e865be31e8ec1ee103c718340c904b32efb69170b67f038d50a3252794b1b4076c0620621ab3d91215d55ffea99f23d54e161a90d8d4902fda5931d9f6a27146a", + "77dff4c7ad30c954338c4b23639dae4b275086cbe654d401a2343528065e4c9f1f2eca22aa025d49ca823e76fdbb35df78b1e5075ff2c82b680bca385c6d57f7ea7d1030bb392527b25dd73e9eeff97bea397cf3b9dda0c817a9c870ed12c006cc054968c64000e0da874e9b7d7d621b0679866912243ea096c7b38a1344e98f74", + "83bed0d556798f2b419f7056e6d3ffada06e939b95a688d0ec8c6ac5ea45ab73a4cf01043e0a170766e21395f27ab4b78c435f5f0dfe6e93ab80df38610e41158429ddf20296f53a06a017723359fe22dc08b5da33f0800a4fe50118e8d7eab2f83a85cd764bf8a166903bd0e9dcfeeceba44ff4ca4439846458d31ea2bb564645d1", + "ea12cf5a113543e39504123036f15a5bafa9c555562469f99cd29996a4dfaaab2a34b00557ccf15f37fc0cc1b3be427e725f2cd952e50af7970dda9200cd5ce252b1f29c40067fea3027ed686190803b59d834179d1b8f5b55abe55ad174b2a1188f7753ec0ae2fc01316e7d498b68ee3598a0e9baaaa664a60f7fb4f90edbed494ad7", + "55266358332d8d9e68bd13432088beadf95833aab67a0eb3b10650414255f299e2670c3e1a5b2976159a46c72a7ce57d59b7be14c15798e09ed50fa312a431b0264d7a1396aa6168bde897e208ece53d2cfc83786113b1e6eac5e9bb98984abb6c8d64eebb991903254abc650c999bb9958a5d7937434b869bc940e21b9dc1cc8982f2ba", + "4d6104ded730aefe02873f4c741232c8234a6d66d85393aff57fbf56ba6347666988dfc4d58f3cc895a0da598822edeee4533d24ec0ee292fd5e1ad04898ffbc1ff4bef14dec220babcb0f28fffe32a6e2c28aaaac16442bf4feb02917d18bb3a415d84fa9358d5a9852688d846c92271911f934181c30f82434d915f93f155a1ffbf0b125", + "eb5f579a4c476af554aac11e5719d378549497e613b35a929d6f36bb8831d7a466aa76de9be24ebb55543f1c13924f64cfd648a5b3fa90387315c16174dbf1e9a183c196d9bb8f84af65f1f8212429aadc11ef2426d07d4716062b85c8d5d2dff8e21b9e62b7fa7dbd57d72633054b464fb28583a56ca13ccc5ddc74dae942492f31731e7046", + "ebddec3dcaf18063e45a76ebeac39af85a1adc2818881ccce48c106288f5988365cca2b4b1d7f037322da46840f42bebdcbc7193838d426e101087d8cea03aaff743d573eb4f4e9a71a2c884390769a6503874125d194bee8d46a3a0d5e4fcf28ff8465887d8e9df771d70157e75df3642b331d2778ceb32ceba868640171ab7a5d22eede1ee44", + "26d87ec70b57691e3bb359633d3ddba17f029d62cdfe977f5fd42274d79b444a32494d1c01e9f72d03cce78c806df96e93ea78da3a054209924ed765edc4d570f66168dc25ee3114e4017e387440349c8f0a94804761c3055f88e4fda2a49b860b1486a9609095f6250f268b6a4d1aecc03a505632ebf0b9dc22d0755a736faf7ad7000858b5864b", + "3880f5cc2d08fa70ef44b1f263fcf534d062a298c1bd5ee2eee8c3265806c4ce50b004f3a1fc1fa5b024aaac7f528c023c8181f67c6e1c357425dc4d573bd46b93a542afa3a19bdb140a2ce666e1a01f5c4d2dcd681fa9f5839b797813c394738d5ee4971386c12c7c117d17c7bec324b760aa30cda9ab2aa850284ba6fa97946f710f02449d1883c6", + "3317d2f452105dd3f4a96f9257af8285a80be58066b50f6f54bd633749b49f6ab9d57d45652d2ae852a2f6940cd5ec3159dd7f333358b12f502325df38843508faf7e246352d201280babd90b14fbf7722641c3601d0e458474439973c611bb5502fd0eb3078f87124ca7e1a016fcb6cfeff65f6a565985aca7122cfa8c5a11da0cb47797c5132333179", + "f2c5c955d0224e784a46b9125f8fef8a5e1271e145eb08bbbd07ca8e1cfc848cef14fa3b36221ac62006403dbb7f7d77958ccc54a8566c837858b809f3e310ace8ca682515bc655d2a397cab238a663b464d511f02dc5d033dad4cb5e0e519e94a54b62a3896e460ec70e5716b5921bf8396aa86a60123e6287e34570bb01bdc602e113670bf498af2ff10", + "180e275205691a83630cf4b0c7b80e6df8fad6ef1c23ba8013d2f09aef7abade1827f23af230de90676240b4b3b0673f8afdea0327330055041741f65560d90348de696d34ca80dfe8afae582fe4879d4594b80e9408fb53e800e01ca58552b905c365e7f1416e51c080f517d6bbd30e64ae1535d59decdc76c6624d737868f49f2f719da39ba1344d59eab9", + "c517a84e4631a7f65ace170d1e5c2fdb259841535d88da323e68c0883e6af7b041cfe05908815a5a9d1b14fa712c2c16fadcf1ca54d3aa954d411240df331b2aebdfb65aced84d0b8aace56ec0aa7c13ec7d75ca883b6bcf6db74c9e98463c484a8262684f29910373430651f90ecffe18b072170e61ee58de20e2a6ff67b3ab00fccbb80af943f20b56b98107", + "d1a56a5ee990e02b84b5862fde62f69ec07567be2d7ccb769a461c4989d11fdda6c945d942fb8b2da795ed97e43a5b7dbdde7f8fd2ff7154544336d5c50fb7380341e660d4898c7fbc39b2b782f28defac6873523c7c1de8e52c65e4395c686ba483c35a220b0416d46357a063fa4c33fa9c52d5c207a1304ae141c791e62ba6a7374ed922b8dd94079b72b69302", + "4720b88d6bfb1ab43958e26827730d852d9ec30173ebd0fe0d273edcece2e788558984cd9306fe5978086a5cb6d37975755d2a3daeb16f99a8a11544b8247a8b7ed5587afc5bea1daf85dcea5703c5905cf56ae7cc76408ccabb8fcc25cacc5ff456db3f62fa559c45b9c71505eb5073df1f10fc4c9060843f0cd68bbb4e8edfb48d0fd81d9c21e53b28a2aae4f7ba", + "f4639b511db9e092823d47d2947efacbaae0e5b912dec3b284d2350b9262f3a51796a0cd9f8bc5a65879d6578ec24a060e293100c2e12ad82d5b2a0e9d22965858030e7cdf2ab3562bfa8ac084c6e8237aa22f54b94c4e92d69f22169ced6c85a293f5e16bfc326153bf629cdd6393675c6627cd949cd367eef02e0f54779f4d5210197698e4754a5fe490a3a7521c1c", + "3d9e7a860a718565e3670c29079ce80e381969fea91017cfd5952e0d8a4a79bb08e2cd1e26161f30ee03a24891d1bfa8c212861b51618d07429fb48000ff87ef09c6fca526567777e9c076d58a642d5c521b1caa5fb0fb3a4b8982dc14a444732b72b239b8f01fc8ba8ee86b3013b5d3e98a92b2aeaecd4879fca5d5e9e0bd880dbfffa6f96f94f3998812aac6a714f331", + "4d9bf551d7fd531e7482e2ec875c0651b0bcc6caa738f7497befd11e67ae0e036c9d7ae4301cc3c7906f0d0e1ed4738753f414f9b3cd9b8a71176e325c4c74ce020680ecbfb146889597f5b40487e93f974cd866817fb9fb24c7c7c16177e6e120bfe349e83aa82ba40e59e917565788658a2b254f25cf99bc65070b3794cea2259eb10e42bb54852cba3110baa773dcd70c", + "b91f65ab5bc059bfa5b43b6ebae243b1c46826f3da061338b5af02b2da76bb5ebad2b426de3c3134a633499c7c36a120369727cb48a0c6cbab0acecdda137057159aa117a5d687c4286868f561a272e0c18966b2fec3e55d75abea818ce2d339e26adc005c2658493fe06271ad0cc33fcb25065e6a2a286af45a518aee5e2532f81ec9256f93ff2d0d41c9b9a2efdb1a2af899", + "736f6e387acb9acbee026a6080f8a9eb8dbb5d7c54ac7053ce75dd184b2cb7b942e22a3497419ddb3a04cf9e4eb9340a1a6f9474c06ee1dcfc8513979fee1fc4768087617fd424f4d65f54782c787a1d2de6efc81534343e855f20b3f3589027a5436201eee747d45b9b8375e4294d72ab6a52e04dfbb2914db92ee58f134b026527ed52d4f794459e02a43a17b0d51ea69bd7f3", + "9242d3eb31d26d923b99d66954cfade94f25a18912e6356810b63b971ae74bb53bc58b3c01424208ea1e0b1499936daea27e63d904f9ed65fdf69de40780a3027b2e89d94bdf214f585472613ce328f628f4f0d56217dfb53db5f7a07f54c8d71db16e27de7cdb8d23988837b49b65c12f1771d979e8b192c9f4a16b8d9fba917bcf74ce5a82aac2075608ba6c2d485fa59864b9de", + "5da68704f4b592d41f08aca08f62d85e2e2466e5f3be010315d11d113db674c4b98764a509a2f5aacc7ae72c9deff2bcc42810b47f64d429b35745b9efff0b18c58653461e968aaa3c2c7fc455bc5771a8f10cd184be831040df767201ab8d32cb9a58c89afbebecb524502c9b940c1b838f8361bbcde90d272715017f67609ea39b20fac985332d82daaa023999e3f8bfa5f3758bb8", + "71ea2af9c8ac2e5ae44a176662882e01027ca3cdb41ec2c6785606a07d7231cd4a2bded7155c2feef3d44d8fd42afa73265cef826f6e03aa761c5c51d5b1f129ddc27503ff50d9c2d748322df4b13dd5cdc7d46381528ab22b79b0049011e4d2e57fe2735e0d58d8d56e92c75dbeac8c76c4239d7f3f24fb56697593b3e4afa6671d5bbc96c079a1c154fe20212ade67b05d49ceaa7a84", + "1d133170582fa4bff59a21953ebbc01bc202d43cd79c083d1f5c02fa15a43a0f519e36acb710bdabac880f04bc003800641c2487930de9c03c0e0deb347fa815efca0a38c6c5de694db698743bc955581f6a945deec4ae988ef7cdf40498b77796ddea3fae0ea844891ab751c7ee20917c5a4af53cd4ebd82170078f41ada2795e6eea17593fa90cbf5290a1095e299fc7f507f360f187cd", + "5ec4ac45d48fc15c72471d795066bdf8e99a483d5fdd599511b9cdc408de7c0616491b73924d0266da34a495331a935c4b8884f57d7ad8cce4cbe586875aa52482215ed39d7626cce55d50349c7767981c8bd6890f132a196184247343566fc972b86fe3c5369d6a6519e9f07942f0522b77ad01c751dcf7defe31e471a0ec00963765dd8518144a3b8c3c978ad108056516a25dbe3092e73c", + "0d5e74b78290c689f2b3cfea45fc9b6a84c822639cd438a7f05c07c374adced42cdc12d2a9233a4ffe80307efc1ac13cb04300e165f8d90dd01c0ea955e7657332c6e86ad6b43e78ba4c13c675aed83192d8427866fb6484e6a3071b2369a46fba9005f31232da7ffec7952f831aaaddf63e225263531c2cf387f8cc14fa856c8795137142c3a52ffa69b8e30ebc88ce3bbc227597bcc8dddd89", + "a0fe36f983259921dc2fa7d89002b3066241d63bfc2448caf7e10522a35562be0bfedc3dce49cfce2e614a04d4c64cfc0ab898873a7fc26928dc1927c009d12f6f9b7a278205d3d0057604f4ac746f8b9287c3bc6b929832bf253b6586192ac43fdd29ba585dbd9059aab9c6ff6000a7867c67fec1457b733f6b620881166b8fed92bc8d84f0426002e7be7fcd6ee0abf3755e2babfe5636ca0b37", + "1d29b6d8eca793bb801becf90b7d7de215b17618ec32340da4bac707cdbb58b951d5036ec02e105d83b5960e2a72002d19b7fa8e1128cc7c5049ed1f76b82a59eac6ed09e56eb73d9ade38a6739f0e07155afa6ec0d9f5cf13c4b30f5f9a465b162a9c3ba04b5a0b3363c2a63f13f2a3b57c590ec6aa7f64f4dcf7f1582d0ca157eb3b3e53b20e306b1f24e9bda87397d413f01b453ceffeca1fb1e7", + "6a2860c110cd0fc5a19bcaafcd30762ee10242d34739638e716bd89fd537ea4dc630e6f85d1bd88a25ad3892ca554c232c9830bd56980c9f08d378d28f7fa6fa7df4fcbf6ad98b1adfff3ec1f63310e50f920c99a5200b8e64c2c2ca249399a149942261f737d5d72da949e914c024d57c4b639cb89990fed2b38a37e5bcd24d17ca12dfcd36ce04691fd03c32f6ed5de2a2191ed7c826375ba81f78d0", + "7132aa291ddc9210c60dbe7eb3c19f9053f2dd74742cf57fdc5df98312adbf4710a73245de4a0c3b24e21ab8b466a77ae29d15500d5142555ef3088cbccbe685ed9119a10755148f0b9f0dbcf02b2b9bcadc8517c88346ea4e78285e9cbab122f824cc18faf53b742a87c008bb6aa47eed8e1c8709b8c2b9adb4cc4f07fb423e5830a8e503ab4f7945a2a02ab0a019b65d4fd71dc364d07bdc6e637990e3", + "3e664da330f2c6007bff0d5101d88288aaacd3c07913c09e871cce16e55a39fde1ce4db6b8379977c46cce08983ca686778afe0a77a41baf447854b9aa286c398c2b83c95a127b053101b6799c1638e5efd67273b2618df6ec0b96d8d040e8c1ee01a99b9b5c8fe63fea2f749e6c90d31f6fae4e1469ac09884c4fe1a8539acb313f42c941224a0e79c059e18affc2bcb6724975c436f7bf949ebdd8aef51c", + "7a6ea63a271eb49470f5ce77519ed61ae9b2f1be07a96855726bc3df1d0723af3a703fdfc2e739c9d31d25814daf661a23558b50982e66ee37ad880f5c8f11c8130fac8a5d0250583700d5a324894fae6d61993f6bf9327214f8674649f355b23fd634940b2c467973a839e659169c773119919f5b81ee171edb2e5f6940d7551f9e5a70625d9ea88711ad0ed8ab2da720ad358bef954456cb2d5636425717c2", + "c5106bbda114168c449172e49590c7eeb827fa4e1a2a7a87a3c1f721a9047d0c0a50fbf244731be1b7eb1a2ef30f5ae846a9f38f0df44f32af61b68dbdcd0226e741dfb6ef81a2503691af5e4b3171f48c59ba4ef91eba344b5b697f261df7bbbb734ca6e6daebaa4a179feb17002823281b8534d55a6531c59305f6e3fd3fa63b747bcf0deb654c392a02fe687a269effb1238f38bcaea6b208b221c45fe7fbe7", + "597716a5ebeebc4bf524c15518816f0b5dcda39cc833c3d66b6368ce39f3fd02ceba8d12072bfe6137c68d3acd50c849873150928b320b4fbc31c1456679ea1d0acaeeabf666d1f1bad3e6b9312c5cbdecf9b799d3e30b0316bed5f41245107b693366accc8b2bcef2a6be54209ffabc0bb6f93377abdcd57d1b25a89e046f16d8fd00f99d1c0cd247aafa72234386ae484510c084ee609f08aad32a005a0a5710cb", + "0771ffe789f4135704b6970b617bae41666bc9a6939d47bd04282e140d5a861c44cf05e0aa57190f5b02e298f1431265a365d29e3127d6fccd86ec0df600e26bcdda2d8f487d2e4b38fbb20f1667591f9b5730930788f2691b9ee1564829d1ada15fffc53e785e0c5e5dd11705a5a71e390ca66f4a592785be188fefe89b4bd085b2024b22a210cb7f4a71c2ad215f082ec63746c7367c22aedb5601f513d9f1ffc1f3", + "be6556c94313739c115895a7bad2b620c0708e24f0390daa55521c31d2c6782acf41156271238885c367a57c72b4fe999c160e804ad58d8e565edbce14a2dd90e443eb80626b3eab9d7ab75d6f8a062d7ca89b7af8eb292c98eaf87ad1dfd0db103d1bb6188bd7e7a63502153cf3ce23d43b60c5782602bac8ad92fb2324f5a79453898c5de18415639ecc5c7974d3077f76fc1df5b956723bb19a624d7ea3ec13ba3d86", + "4bc33729f14cd2f1dc2ff459abee8f6860dda1062845e4adab78b53c835d106bdfa35dd9e77219eaef403d4e80488ca6bd1c93dd76ef9d543fbb7c8904dccc5f71509a6214f73d0f4e467c3e038ea639b29e7fc442ee29f57117740576188ada15a739827c647a46b0271817ab235c023c30c90f2115e5c90cd8501e7b286962fc66ffc3fe7e8978746168314908a41998bd83a1eeffda9d714b864f4d490fdeb9c7a6edfa", + "ab12faea205b3d3a803cf6cb32b9698c32301a1e7f7c6c23a20174c95e98b7c3cfe93fffb3c970face8f5751312a261741141b948d777b8a2ea286fe69fc8ac84d34116a4674bb09a1a0b6af90a748e511749de4697908f4acb22be08e96ebc58ab1690acf73914286c198a2b57f1dd70ea8a52325d3045b8bdfe9a09792521526b7564a2a5fcd01e291f1f8894017ce7d3e8a5dba15332fb410fcfc8d62195a48a9e7c86fc4", + "7d421e59a567af70594757a49809a9c22e07fe14061090b9a041875bb77933deae36c823a9b47044fa0599187c75426b6b5ed94982ab1af7882d9e952eca399ee80a8903c4bc8ebe7a0fb035b6b26a2a013536e57fa9c94b16f8c2753c9dd79fb568f638966b06da81ce87cd77ac0793b7a36c45b8687c995bf4414d28289dbee977e77bf05d931b4feaa359a397ca41be529910077c8d498e0e8fb06e8e660cc6ebf07b77a02f", + "0c18ab727725d62fd3a2714b7185c09faca130438eff1675b38beca7f93a6962d7b98cb300ea33067a2035cdd694348784aa2eda2f16c731eca119a050d3b3ce7d5c0fd6c234354a1da98c0642451922f670984d035f8c6f35031d6188bbeb31a95e99e21b26f6eb5e2af3c7f8eea426357b3b5f83e0029f4c4732bca366c9aa625748297f039327c276cd8d9c9bf692a47af098aa50ca97b99961bef8bc2a7a802e0b8cfdb84319", + "92d5909d18a8b2b9971cd1627b461e98a74ba377186a6a9df5bd133635250b300abccb2254cacb775df6d99f7c7d0952653c28e6909b9f9a45adce691f7adc1afffcd9b06e49f775364cc2c62825b9c1a86089080e26b57e732aac98d80d009bfe50df01b95205aa07ed8ec5c873da3b92d00d53af825aa64b3c634c5ece40bff152c331222d3453fd92e0ca17cef19ecb96a6eed4961b627aca48b12fecd091754f770d52ba861546", + "802f22e4a388e874927fef24c797408254e03910bab5bf372320207f8067f2b1ea543917d4a27df89f5bf936ba12e04302bde23119533d0976beca9e20cc16b4dbf17a2ddc44b66aba76c61ad59d5e90de02a88327ead0a8b75463a1a68e307a6e2e53ecc1986274b9ee80bc9f3140671d5285bc5fb57b281042a8978a1175900c6073fd7bd740122956602c1aa773dd2896674d0a6beab24454b107f7c847acb31a0d332b4dfc5e3f2f", + "3844fe65db11c92fb90bf15e2e0cd216b5b5be91604baf3b84a0ca480e41ecfaca3709b32f8c6e8761406a635b88eec91e075c48799a16ca08f295d9766d74475c47f3f2a274eae8a6ee1d191a7f37ee413a4bf42cad52acd5564a651715ae42ac2cddd52f819c692ecdef52ecb763270322cdca7bd5aef71428fa73e844568b96b43c89bf1ed42a0abf209ffad0eeec286c6f141e8af073ba4adfbbdeda253752ae36c9957dfc905b4c49", + "329377f7bf3c8d74991a7d61b0cf39baff5d485d79751b0d5ad017d23bec570fb19810105bab79ab5acb102ab972165224d4ec888ec7de5148077fa9c1bb6820e0d91ae4e2591a21fec2f820606ce4bafc1e377f8dc3a5bd1a9e2772a57abccd0b757164d768872c91d02789545ab5b203f688d71dd08522a3fd2f5bcd7df507aebf1ca27ddff0a82afb7aa9c180008f49d1325adf97d047e77238fc75f56356de4e87d8c961575c9f6362c9", + "f7f269929b0d71ea8eef7120e55ccba691c582dd534692abef35c0fe9dec7dae973cd9702e5ad420d278fe0e653fdcb22fdcb63148109ec7e94f2d0750b28157dd1764376ae10fdb0a4aef3b304bd82793e0595f941226a2d72abbc929f53134dc495b0d65ced409914f94c2523f3dfbbdeeac84ae247ab5d1b9ea33dce1a808885a55be1f3683b46f4be73d9b62eec2585f690056858dfc427aabf591cd276724885bcd4c00b93bb51fb7484d", + "ac022309aa2c4d7fb628255b8b7fb4c3e3ae64b1cb65e0de711a6def1653d95d8088871cb8905fe8ae76423604988a8f77589f3f776dc1e4b30dbe9dd262b2187db02518a132d219bd1a06ebac13132b5164b6c420b37dd2ccee7d69b3b7fa12e54f0a53b853d490a68379ea1fa2d79762830ffb71bf86aab506b51f85c4b6a41b69325c7d0c7aa85b93b7144489d213e8f33dbb879fce22849865337b620b155cb2d2d36a68832889e30194d36d", + "d009c2b78a8f02e5e5dbb586ef71fc324b375092e15913ca1a5bfd22d516baadb96867bee3562e77c4a4852344a1a76c30728be5e22400b4cc41711f66754c246a520498d8c24f0205b9c873748dbeb67fe1ad099ad04cf89f4b517f0aa481136d9f6de2d727df01c6aa4099da59d4382b51e25fd47c33d9842c32b62331e50794bfe8b61b3ba9de1b8b704779c6d65edff3af00f121ab4a7ea384edabe47c6d0098a48991f387ca4444135ec59d46", + "c00bab36cce69899817d1425016d222d7303197ed3e3fdcac744705e7f178a1ac745968900f69299163e19b3161f3e0a4cc55aa2e4e71e0ee6ac427d1f4d14e063f68d303ddfbb18118335cfa7a6a90d99c38319ee76f7a884846a9e0b68030bf28e78bfbd56359b9368842814da42b04cb0e307d5d846dc22f049147bae31b9a956d17676a8cc348dafa3cabc2007a30e730e3894dddf9999fb8819086311f0703e141613ed6dcd7af8510e2dc435b0", + "c9789152a9fc29698d49ed95f09bd11b75f18a8c5615a73dbe54ae5e550027fd0ae6a8b60667040c1b12de3d1ee3f6bf061c78c951a3210effc912e19f482dd4de152063c588c44903bc11761706fd935afa040df085b08144d83d0dde32b46ab52f4fae98ac116c7ff11d7f553450c2e37b9c5f0b1dd9e0b8640a24cba6f2a5246c41f197f46e3dc8a29131c79bef3351c6e277a0a34442274d546ccd058891277473d668420f121750d19cd684267405", + "06a15a0731ce52557e368bcbaa11ef3399299e36fb9f2eda6e5726907c1d29c5c6fc581405ba48c7e2e522206a8f128d7c1c939d1132a00bd7d6366aa82724e968964eb2e373563f607dfa649590dcf5589114df69da5547fef8d1604cc4c6de1ed5783c8746918a4dd31168d6bc8784cd0c769206bd803d6ca8557b66748770402b075ef44b38157d4c0da7c6281725a2065d087b1f7b23455fa673bdeeba45b983311c44eabe9ef4b7bde3420ae9881863", + "d08aacef2d7a41aec09473bd8a44f628e15addb7b9e5b77a1e09c8ab4942f379a0bfcb324d580b774666f18ae78dd36710824ff12393f059068fe4b559c53662c2b0e6c69e23785c8f32554e837ec1714bee902e60737b639dd933af4f68cb9d7de77e1f3b28e5b122891afce62b79acd5b1ab4ba411662cc77d806449e69c5a45a143b742d98ac84a0826d68433b9b700ace6cd472ba2d58a90847f42ce9c43f38ffc017db4bf40450b2eee1f4594dc740c0f", + "6a6058b0a498b7ea76a93c646eb9b8629f0cba4a0c726420c5f67ba9b0412cade356abdf0a4fb94384bad32ce0d5dd9e23dcaae1d6f28ff8683616b30f1392890c67b3a2c04b360893b801f127e527e4da82e239f4c878da13f4a4f1c76db07190e77ec123995168102fb274434a2d1e12913b9b5cbab4aacaad2bd89d88b3ca2b8e60dacf7c22c9379097ff60880f552e320ca3b571994f52534470feee2b39e0dadb5cd88257a3e459a4cc6f12f17b8d54e1bb", + "adeced01fc5671531cbb45679f5ddd42b3a95151677b6125aaf6f5e8f82fbabaa5ecf7c3552c2458587224f0042870f178f5fca5465250e75d71352e652eeed23cdb7f915f5ebb44099b6db116ca1be45530ac8ed32b7f161d60ed4397ad3d7d649ae6bf75ca5bec891d8e595605be9764f3a03965e1fe0eaffbf212e3df4f0fa35e08ff9d0091e6d4ac4748edfe43b611085a6ffec163014655fdd839fd9e81b63b1fa8cae4ec335ec343289758e389a79ceedfae", + "d014592f3a83ba40af366f137c674724916c3cdd3f6cf9d4c5c7c8d6d51ebf26e315e2c12b3546be56fb52382904046ecbd2f5b883aa4ff473de6f0c26ab862c3fa34bf3d880cc1911ce39a4088c6617c179dc5faf68a2c488bbde12d67b50f73abcfab0e3b062e68c95363e11f5f1de8ec36ed01ea21442518089045df67d346135283ad5b3fff80cf57f20876849f6db9fa139728358415a90610f69ec720fc92d8234e3e122551e9df2c644c4a2c4e3734d07de8e", + "c0d0c37838873ba8757d6e41b409605043bc1635edcd731219587676d94217e9f0ab44b71de25000661ce7303b7015f45e6eaa7b7ebef92b8f4a34c902c908d2172185505fa33aca5a41be83079316cdfdd430fc2c45f505f85d867e6d516f7e1bf19c001d9f43018968aab65ec031b3801399231c83ec9e622dab5629922a6b424cab938c135ff7310501c2c02971bfd2f577e25904d1a618baf0859f77f4e8b1d0cde9544e95ec52ff710c0672fdb3d891feeea2b017", + "7022e7f00902219ba97baa0e940e8ac7727f58955aa068c29680fac4a16bcd812c03eeb5adbcfe867a7f7c6b5d89f4641adb9173b76a1a8438866f9b4f640ce2aedf5f1080c890bcf515b4be4e3e512352f1e5323c62ec46cb73f3d71be8235fee55a154763f7c3f9aeb61ffd28f4cd93d3310f608e2133586bf1ab3f102de96f64c68a4668de8acb2a76a7ce0cddddc8fa3df5e9d230823da16ed9ebb402d36e38e6e018795e5a71517ecab5f9ca472b9ced8ff69d2d195", + "acaf4baf3681ab865ab9abfae41697141ead9d5e98523c2e0e1eeb6373dd15405242a3393611e19b693cabaa4e45ac866cc66663a6e898dc73095a4132d43fb78ff7166724f06562fc6c546c78f2d5087467fcfb780478ec871ac38d9516c2f62bdb66c00218747e959b24f1f1795fafe39ee4109a1f84e3f82e96436a3f8e2c74ef1a665b0daaa459c7a80757b52c905e2fb4e30c4a3f882e87bce35d70e2925a1671205c28c89886a49e045e31434abaab4a7aed077ff22c", + "84cb6ec8a2da4f6c3b15edf77f9af9e44e13d67acc17b24bd4c7a33980f37050c0301ba3aa15ad92efe842cd3ebd3636cf945bb1f199fe0682037b9dacf86f162dadabfa625239c37f8b8db9901df0e618ff56fa62a57499f7ba83baebc085eaf3dda850835520344a67e09419368d81012168e5de5ea45158397af9a5c6a1657b26f319b66f816cd2c28996547d697e8df2bb163ccb9dda4d6691dffd102a13667ab9cde60ffbfb872187d9c425a7f67c1d9fffff9276ed0aeb", + "6a52c9bbbba454c14540b2be58230d78ecbeb391646a0c6fcce2f789086a78364b81ae85d5396d7cfa8b46bda41e3083ec5cf7b4c47dc601c8a697df52f557defca248506dbebab25657f5a561d09625b7f4b2f0119a12beeac087efc9d350a735c35d2431c1da7dda99befb17f41a3dc4da0f00bb95366be128538ce27763d81f832fe3c1d4efc07b5b08ad8dc9e65fb5e48546664e18cb2d3bb3fe1f56fa7aae718c5e3bbdeaf70e15023f6a25b72a2d177fcfd04211d40664fe", + "c3c4d3b31f1f5f9538923df3478c84fffaef411520a542da9a220ee4132eabb9d718b5076fb2f985485e8ba058330aed27ddfd3afa3db34aa60301088caec3d0053828c0c2bc87e2e61db5ea5a29f62fdad9c8b5fc5063ec4ee865e5b2e35fac0c7a835d5f57a1b1079833c25fc38fcb14311c54f8a3bd251bca19342d69e5785f9c2e43cf189d421c76c8e8db925d70fa0fae5ee3a28c4047c23a2b8a167ce53f35ced33bec822b88b06f41558c47d4fed1bfa3e21eb060df4d8ba1", + "8d55e92136992ba23856c1aea109766fc44772477efc932b3194af2265e433ed77d63b44d2a1cff2e8680eff120a430fe012f0f09c6201d546e13ad46fc4ce910eab27bb1569879abed2d9c37fae9f1267c2216ec5debcb20d4de58461a621e6ce8946899de81c0add44d35e27b7982a97f2a5e6314901caebe41dbba35f48bc9244ca6dca2bdde7306435892f287036df088633a070c2e385815ab3e2bfc1a47c05a5b9fe0e80dd6e38e4713a70c8f82bd32475eea8400c7bc67f59cf", + "5016284e20362610fa05ca9d789cad25f6d43263787e7e085476764ce4a8908ce99b262b375e9d106170b1bec1f473d5e777e0c1896533040e39c8c1465e07907ef5860e14e4d8310013e35f12090e0bfc687474b1f15f3dd2033a0edac5246102da4deec7e188c3517d84d9c2a0a4497a4c5f82a30f1ba009e45ee6eb3ab4368c720ea6feee428ffd2c4cc52debb8d634a64176572c72368f94a66689f23f8a01218f532117af5a8060d140e7ca435a92882fcb5630ebe14a4805f1dc83", + "05456ec59b8d41bbd736727976b96b38c43827f9e16169be673ff37870c2ecd5f0d1ea1a136be4cc7b047a02a4421d484fd2a12ece418e42ee391a13a0b1df5a0162b29ab70d3fe3e04ba6ab26b37d62b7cf05a5e2f033611bf970b8e1f30e198e483e740fa9618c1e8677e07b61296b94a9787a68fba622d7653b5568f4a8628025939b0f74389ea8fced6098c065bf2a869fd8e07d705eadb53006be2abb716a3114ceb0236d7e916f037cb954cf977720855d12be76d900ca124a2a66bb", + "eb6f60b83fcee77060ff346aaf6ec34d82a8af469947d3b5074cde8eb26566eb1fa039bcc707738df1e95869bd827c246e88436f0614d9834ead5392ef376105c4a9f370071cdeaaff6ca0f18b74c3a48d19a717253c49bd9009ccbfdd5728a08b7d112a2ed8dbafbbb46d7a75dc9a05e09bfde1a0a92d74a51887f9d123d7896e9f9d0057b660ed7d55454c069d3c5260411db4cdc67e7b74f680d7ac4b9dcc2f8baf72e15e6b3cafebcdf449a6436ed2c398b675f79c644747c57553bf7ea2", + "187a88e88514f6c4157c1ba40b442baae1ae563a6c989277443b12a219aa484cb9fa8adbb9a29d429f50155321b15664926317477079c7060dfdaa84c1d74bba78892c34e6f21ad35208d2ae622012401696bff5cd57b6485944b3db7b9071fa5f57fbfb1085d91bb9cff5808d662cdc6c8157249478262c44b7fbc397ed42a4977b202e817717bfccc9f0467294062313f7705251ed09573f16d23429361fada259dfb300369c4198f07341b38e84d02cdb74af5de6aab1fc2026208ea7c418c0", + "be31bc96606d0fab007e5caeded2f1c9f747c759777e9b6eef962bed49e45a1d4fc993e279d024915e600865ecb087b960584be18c41114d3c43f92169b9e0e1f85a0ebcd4e196376ccdc920e66103cd3b1c58407d0aafd0e003c4e341a1daddb9f4faba974362a32f35db83384b05ae8e3322d728893861afd8b1c940de5a17f691e763ce4969b6d94f67fb4a0235d100225bd8602f291388f0ca4a568748ad0d6040f1262eac2aede6cd27419bb78a394c1ffad72c262be8c3f9d9619d633e51d0", + "4d83d85ca838b4518588f2a90228a4dd18f14dd5b4c012d26298a97d848abbd825d221d02cceb6e8c701b4ad00e1dee4889b5c533e4bb60f1f41a4a61ee5478be2c1b1016c30345afd7a5253668260515e70751f22c8b4022d7fe4877d7bbce90b46531507dd3e89549e7fd58ea28f4cb23d33662bd003c1345ba94cc4b06867f778957901a8c441bee0f3b12e16463a51f7e50690356971dd73a686a49fda1eae46c9d54fba262811d698025d0ee053f1c58591c3bb3cbde69de0b31549ef5b69cf10", + "cdeb07d36dc5f9a1cd717a9e9cca37a2ce93caa298eee63571f7d6c5fde2a11c666cf53cf2dcb41ca2ea2319e7230ca68e38c647905928713a13982bf47fe33d7095ebd50b2df976208920a43eb2e29b942f32467403c45cea18bf44e0f6aeb155b48a8e5c471fec972a9d62f7ae093d2758f0aaec7ca50cb4725bfa219f1a3a46ad6bde7361f445f86b94d66b8ece080e56c510250693a5d0ea0ae87b4421860b853bcf0381eae4f1bf7c5c0472a93ad18407bc88475ab8560d344a921d3e86a02da397", + "a598fad52852c5d51ae3b10528fc1f722e21d44fbd42ae5acdf20e85a28532e646a223d27fd907bfd38eb8bb75175636892f8242877aab89e8c0824d368f3339ce7a82aa4e5af6db1f3b588a4d667a00f67bee37cfd2724dde06d2909fb9e58d892f4cfd2c4ca85acdf8256f5458b030a6bda151154ff2e6d7a8da90b54a2884c8a99fab5a4ac211ff23dc0975f4f592fd1b6b9dc7783bdcd2d4ca4e68d2902f2013e122cb62e2bff6b0a98ec55ba25837e21f1cfe67739b568d43e6413dab2bd1dc471e5a", + "17b68c74c9fe4926e8102070916a4e381b9fe25f5973c9bd4b04ce25749fc18931f37a65a356d3f5e5a1ef125d546f4f0ea797c15fb2efea6fbfcc5739c564693d47adeb12dcb3d98a2830719b13247792cb2491dca159a28138c6cff925aca42f4fdb02e73fbd508ec49b25c60703a7595a3e8f44b155b371d525e48e7e5dc84ac7b17c52bf5e526a67e7187234a2f19f57c548c70fc0b27183df73ffa53fa58b658034c896fa791ae9a7fd2620f5e46ce84c842a6e60e9324ae4db224ffc87d9617cb85ca2", + "b9e4267ea39e1de1fed0579f93bb351007c9f8fcdd811053fae33f09e2753d7428f04e1a9efcd45ea701a5d87a35b3afb2e6b65365dee6ead0bbb611b7797b212ac688653f542e604a39df277f12514ddfee3b4e27b98395c2cd97a203f1f1153c50327965770802ec2c9783edc428271762b275471e7ac65ac36523df28b0d7e6e6ccc7674268a132a63411fc82c0738dbb68af003b769a0bf9e6587b36476cb465350fee13f88ea355d47ffac7b0f964f4139db11b7642cb8d75fe1bc74d859b6d9e884f75ac", + "8ca704fe7208fe5f9c23110c0b3b4eee0ef632cae82bda68d8db2436ad409aa05cf159223586e1e6d8bdae9f316ea786809fbe7fe81ec61c61552d3a83cd6beaf652d1263862664df6aae321d0323440430f400f291c3efbe5d5c690b0cc6b0bf871b3933befb40bc870e2ee1ebb68025a2dcc11b68daadef6be29b5f21e440374301bde1e80dcfade4c9d681480e65ec494a6af48df232c3d51447b9d06be714949249c44c43cf73ed13ef0d533e770284e51369d94ae241a5fb2f163893071b2b4c118aeaf9eae", + "4fd8dd01012bb4df82bf42e0683f998e6f52dd9c5617bae33f867d6c0b69798cead8179346d70acc941abbbdd26e3229d5651361d2252c72ff22db2938d06ff6fc29a42fdf800ae967d06479bc7bbb8e71f40b1190a4b7189ffc9a7096cdb76d40aec424e1388e1eb7ef4ac3b34f3f089da8fda7d1927f5d775c0b2801d22dd1265c973158f640cec93edfed06dc80b20ef8c496b98289d54d46ccd205951cbb0f4e7daeb866b60bacb483411e4382b6f04d472843186bd0e31fbaa93e5c901ec028efafeb45fc551a", + "e9ee1b22b04b321a5fdd8301627011f583887d77560fb0f35552e207561f81e38ac58a0d0aeaf832d1ee72d913720d01f75574e9a321864fe95f4d0d8f0b8db97649a53e71e940aede5c40b4b9105daa42a6fb2811b61209247534cbaf830b07abe338d75d2f5f4eb1c3cf151e9edabe2c8d5f6fff08fac1495ef48160b100d30dcb0676700bcceb28723a29980ab0766a93abb8cb3d1963007db8458ed99b689d2a7c28c788743c80e8c1239b20982c81dadd0eed6740c65fbc4ef15c7b5569cb9fc997c6550a34b3b2", + "ec01e3a60964360f7f23ab0b22e021815765ad706f242265ebc19a2bb9e4eac94393952dcf61aae47682671a10f9165f0b20adf83a6706bfbdcf04c6faba6114653a35584267267873291c6fe7ff5f7695243143421509502c8875aafa9e9afe5be5ef2c851c7f35d69be5d3896000ccdbbfab5c238bb34d607cfe2d55d748880545b4aa7ca61137992925189025c62654b1f20d49c3ccd75aa73ce99cd7258dabedd6480a9f5185531fc0118beb68cc0a9cd182f6973287cf9252e12be5b619f15c25b65c71b7a316ebfd", + "db51a2f84704b78414093aa93708ec5e78573595c6e3a16c9e15744fa0f98ec78a1b3ed1e16f9717c01f6cab1bff0d56367ffc516c2e33261074935e0735ccf0d018744b4d28450f9a4db0dcf7ff504d3183aa967f76a507357948da9018fc38f150db53e2df6cea14466f03792f8bc11bdb5266dd6d508cde9e12ff04305c0295de29de19d491ad86e766774bb517e7e65befb1c5e2c267f013e235d8483e177214f89978b4cdc81aa7eff8b39f2825ad3a1b6ac1424e30edd49b067d770f16e74dd7a9c3af2ad74289a676", + "00e40f30ae3746edad0f5dd03d0e640933cf3d1694804c1e1ed6399ac36611d405196ee48f129344a8512feda16a354517871322bd5d9c6a1b592933eab531923efb393ffb23d9109cbe1075cebfa5fb917b40df028a621460ff6783c798792cb1d9635b5a6f84ec13918fa302924649b5c7fcb1f7007f0d2f06e9cfd7c27491e565a96c68a0c3644f92cd8f38857258c33801c5d537a83dfe583cba59d7eec7e394199c0a2660a62fabe3ed2099d57f315a6cd8de1a4ade29d977f15d65759cff433e5ac0c182aef3761163e1", + "3c5ea24d0d9b618294a263f062b2414a722be4eb10dfc346a6ec3b821d7396eba61cd6ef33618b04cd087a811f299d4606820227f16000d7c839062b96d3e3f59cd1a082448d13fc8f56b3fa7fb5f66d0350aa3b72dd7c165d590282f7da2e12cfe9e60e1796122bb8c2d40fdc2997af634b9c6b127a893dfb3467909378300db3da911be1d7b616bb8e0572433e65527e15d936500a2c60e9f9909dcf22ab5e4b6700f0238c205b4a813626fac3d945bab2637fb08203044a73d20c9a3fcf7c3fc4eb7807c3276dd5f73ce89597", + "9271aeeebfac46f4de85df78f1bfd36136aa8905e15835c9e1941176f71e3aa5b1b131843d40479735e23e182a2bd71f66f6149dccb7ed8c16469079dc8590bbf165374951785f4531f7e7361de62f936cfb23a2b5bdf186632e7042a0dd451fdc9b7208f923f3a5f250ae590ec348c63a16c3aacaf7379f53b5dd4152dcd40d23e683e2156e64c592ffc07e2cd6bbeebef4dd590b2f6b2bcbf08fcd111c079f5c4033adb6c17574f8756ecd87be27eff1d7c8e8d0324438d59ae171d5a17128fbcb5533d921bd044a2038a5046b33", + "4e3e533d5bcb15793d1b9d0468aaee801f32fdb486b11027183553a09ddbee8213924296f2815dc61577297459e834bf1c7a53f87d43782209e589b8295219ba7073a8fff18ad647fdb474fa39e1faa69911bf83438d5f64fe52f38ce6a991f25812c8f548de7bf2fdea7e9b4782beb4011d3567184c817521a2ba0ebad75b892f7f8e35d68b099827a1b08a84ec5e8125651d6f260295684d0ab1011a9209d2bdeb75128bf5364774d7df91e0746b7b08bda9185035f4f226e7d0a1946fcaa9c607a66b185d8546aac2800e85b74e67", + "b5d89fa2d94531093365d1259cc6fe8827fea48e6374c8b9a8c4d2209c280fa5c44958a1847222a692a59e6aa2696e6cdc8a543dd89b0ce03bc293b4e78d6ef48e1839694ccd5c65661143095c705b07e3ced84a0f5959114dd89deb956ab3fac8130eb4a878278205b801ae41a29e34146192308c4e759b374757b0c3b00319bce92a1b95a4d2ee179fd6714ff96155d26f693a5bc973f84ac8b3b91e3926276297532d98b46992a3f104c08100bf1671c43134bac280c617da711e90a0100137525375ebb12802a428885ae7fce6514a", + "40e3d8048fc10650cb8a7fc2e7113e26dec34f9ca2d5129cd10a8e8e44d113d61ee48c7d003e19fd307fc6debd70feb30243f298c510ccc4418355ce143066f067ad7c6de7288c3080e7ad46a23c8d34deb55a43e652fe90444ad3c57d3ec1e1c489d63ef915a24bc74a7925a0a7b1e1523f21ca8fee78df24e3d0a68d0013423db97c280799a0618229c0f2c167289a891e5c8d6661ab21285951c31710e3b5fe55f6347fe16d9b40507948a59252efeb616df83e5c098b07d0a7247cd371daff0e50491c582503fd89f79ba94d6af9ed76", + "1fa444de01dd3901e2b4684e3d7a799ffa02d85afd35fb30fe4c9d672837bee6dd8a3b8608b4bb5e589220ad5a854f46b46e41c6d57ad124a46beab4169ff69fee7e3838a6165e19dad8eb5d7bf53d4edd3cd2769daf219510a02fdd2afe0c0e1da3cd30fcd1aa88b68965586f07a25a1720fbd90a096ea30fc8e945e3637d7857c8a9c0ab4154ffb2000e57b5f9adfa4e4eaf8065bc3c2b2e75f495963325588785a6ce417dcddffd299873b15dcccca128d63cd4eeeadb64cda28099a9ad7c80d34844901f26b88b00b9aafeb2f90286d29d", + "fde0a0d9d813983bd1f55cf778a003a2023b34a555322ab280584537bc6bdd844d22a7d6066c18da83ec09f3d8d5a1aab4be0d5ce19b436052f6e259a4b49017a1f47f1fe2bf115d5bc8599fb216351c60dd6b1bedb2e6f4dcadf424b833501b6f099cbfad9e2290680fb69c25032b42a6274f7cb9b5c5950401354838a45f7cb77b95bf54718e2f3d3d9fb91eb2311903980277396398d9736d8e92fd838594ac8a537c6c529db5a8a4f89290e6ba6f20ac0e5ed6fef40901d0e0e8e3e502990811f9acaae555dd54eb1bcd96b513e2fe751bec", + "9f8e0caec87858599f5ab29bff86da78a841a918a023a111098687ecdf2747612d3f3809d9ca400b878bd4f92c43a1004f1c17c7f19a3cd1ce449bd2b23aff551623c37dd8c0be56bf3fd857b500c2b9f9ccea62481944090a3cf3b6ee81d9af8eeb60f65ef150f9fa4d3ed6ce4762d3d4f174ee8ccd460c25cafac0ea5ec8a6a4b2f9e8c0520cb7061155e532cb65f188b01e4b9086db951f504b060c296b326b3fc1c590498ecce594f828f4a10ea416675720ae505295d38a791bd0e93f428448a8f4c1fc0af53604a9e8255384d29ae5c334e2", + "33d1e683a4c97ee6bbaa5f9df1a88cb53b7f3c157b6045d70a56fda0ccbd3a1fa1f049cd564da072b53f415bf5fb843771c1d2551fd075d33377362b2f7c0645f9723123d11975991db8a2b518f02e2c7c30342a044754290bae2c77496d755e5981f12e6b0a0174280b958bf11ed628a9062775993ced04bf752ea8d165e3ac2177d7cd1b9371c44efa98f0b3e68602a839d384eec007979f46429dafb138cbc231ad928a9f65f7d66fac77416395e8f1debaaf76ec2e4e03e8674102cd26f614739f3ec9f949033df1fb97e87c2326d65aef94ed5f", + "180048f09d0b480887af7fd548a85abf605440c1ddde6afe4c30c30670233f7bf928f43b4681f59279ebbda5e8f8f2a1abefdee129e18ac60f9224e90b38b0aabd01308e0a27f41b6fb2ee07ee176ec9048c5fe33c3f7c791469c81f30e28170585b9f3e7e3c8c2e9d74370cb4518f13bf2dee048cbd98ffa32d85e43bcc64a626b40efb51ce712925fdd6fee006dc68b88004a81549d2121986dd1966084cd654a7c6686b3bae32afbd9625e09344e85cf9611ea08dfce835a2e5b3726e69ae8a76a97db60fcc539944ba4b1e8449e4d9802ae99fae86", + "13c0bc2f5eb887cd90eae426143764cf82b3545998c386007cca871890912217aa143ac4ed4ddb5a7495b704aa4de18419b8664b15bc26cfc6596a4d2ae408f98b47a566476d5802d594ba84c2f538def9d016661f6404bb2337a3932a24f6e30073a6c9c274b940c62c727242e24466084a3ea336365d71ea8fa6499c0ea8d59eea505f1126b99c795023c4963aa0d99323d0391e8701110edf551b2d3799e1063ca443f1add162156e445502ca1a052fe70c289838593b58839fc63de128a03e2bbf389e22ae0cf957fd03315ee407b096cc1cfd92dee6", + "6f1eb607d679efef065df08987a1174aab41bdac8aece7726dfa65805d6fff5b3d17a672d96b770dc32165f144f0f7324822a5c87563b7cd9e37a742ae83ef245d09006d91576f435a03476f509ea2936636232f66aa7f6cdf1ac187bbd1fcb8e20f8791866e60ed96c73374c12ac16795e999b891c64507d2dbd97e5fc29fac750ad27f2937cbcd29fdafccf27ab22453834d475f6186eaf975a36fad5c8bd61c21da554e1ded46c4c39765dcf5c8f5ccfb49b6a4dc562c919d0c7d8940ec536ab2448ec3c9a9c8b0e8fd4870cad9de2577c7b0c38563f355", + "dcdd993c94d3acbc555f464871a32c5da6f13b3d5bbc3e34429705e8ad2e76393fdd96a69a94acb652f5dc3c120d41187e9aa919669f727c4868013b0cb6acc165c1b7706c52248e15c3bf81eb6c147619467945c7c48fa14a73e7c3d5bec91706c567145342a026c9d97eff97ec672c5debb9df1a998083b0b0081d65c517b3e5634c95e347e781aa30ca1c8af815e2e494d844e847fdcb41622894a518dc36571123a40bfdbe8c4f4cff44d83c61dd9dcd24c464c53b395edb31efee9f3aa080e87cdc3d22d613ae84a53c9249c32c96f9a3bc4629bb126a70", + "49971f9823e63c3a72574d977953329e813b22a8387cd13f56d8ea77a5d1a8a20012632d1d8732bbcb9f756b9675aab5db927beacab7ca263e5718b8dfa7b2eed9a91bf5ed163b16139d45f7b8cc7e3f7bdda6202106f67dfb23b7c315ee3e17a09d466b1e6b13e7c7428184a979f5358667b4fa8bd40bcc8ea46058db44587a85377ac46bf155136c09ac58cb6c27f28e17028c91e7e8f74d5b500e56293b316974f02b9d9ea205d9b6ac4cfb74eb8eb0c944577fd2f41316368307beab3e327bf7dbaa0a4428836ec4e895dea635234abeaf113ceeadac33c7a3", + "c57a9cc958cee983599b04fe694f15fb470fcbc53e4bfcc00a27351b12d5d2434444253ad4184e87b81b738922ffd7ff1dc1e54f39c5518b49fb8fe50d63e3935f99e4bd125e8dc0ba8a17fd62de709339a43fabe15cf86d96a54010112170c340cfac4132182eed7301402bc7c8276089dec38488af145cb6222525894658f03501204b7a66aba0be1b557b28a2f652d66f7313ed825ecc4d8596c1be7420d4425b86a1a90a5b7f30d0f24e0d1aae0eb619ca457a71699e44be612a4011c597ee80b94d5507e429d7fc6af22579cd6ad642723b05ef169fade526fb", + "0568a672cd1ecbaa947045b712e2ac27995392fbef8f9488f79803cbee561c212287f080eca95adb5ba42739d78e3ba667f06045d87850d3a0499358649caa257ad29f1a9c511e7054db20554d15cbb55ff854afa45cae475c729cea72ede953522031865bc02b95589ed4d9841c552a8cc94904a93ed09ed77222f6c178195056be59bc4e96a815adf534e6b466fb47e262ff79c803c157a21b6e2269c2e0abeb494113cd868d8466e82d4b2f6a28b73645853d96bc9242515d803e33294848d3fe42fdff68da53c03491636beede47ff1399dd3d54a5e914d55d7adf", + "3f19f61a4cd085796731ac9f85a75a8bce77031932c31762d87d8b8d07b8bd19ff78d6b7d1bd1e87f3a4f41aad03b6c4d17a6cbc86be55f7c8b88ada047bb04f8d49f1c34bcf81cc0f3389ad01a758fc7eeb0072aa9ad1481992bfdde82e438e75590a4423832dfbe3756e2229ea873bc3606e6d72174cb2163bf40b5d49c81009dab85ecc03e311351bbf96e32c030a2b276a7698cb25bc2c967acb3213161a1fdde7d912cd6a804490f8056c47da1333f6e35c41e749c2c23919cb9af5eec5652e6e072b034fb1682e9aaa194a9c0bd456ea0b008d14dbce37967a7a8e", + "705f98f632d99d3651793825c38dc4deda56c59eac539da6a0159c83131cf8ab6f2ee0c3b74111fde351f7aa1a8c500a0cecab17c212d2c58ca09eae608c8eefc922b9902ef8d6832f799ba48c3c28aa702b3242107edeba01daafe424406a3822965056cfe8783455a671e93b1e2eae2321364f1871471c82124df33bc09e1b52882bd7e1c4c7d0b2f3dd4a28c2a002a43246768af0700f9659de99d62167be93177aabf19d678e79e9c726ac510d94e74873eda99620a3961930cd91937c88a06d8153d64fd60da7ca38cf26d1d4f04a0df273f52127c53fdc593f0f8df9", + "ea6f8e977c954657b45f25480ff42c36c7a10c77caa26eb1c907062e24fbca5aebc65cacca0de10abea8c78322f08672e13d8ac16996eca1aa17402eaea4c1cc6c800b22dc18cb8d620192d74bac02c07b5cfa61e513c7f28b7e29b9700e0e442720bf4c669d4995da19d19f841d9eb68cc74153592591e3bf059ef616b95305aa453b32fe99a91afb35bd482cf2b7aa42702837a53be3c38883d2963020e347556f841254ec6b85854485fe8c520b05f2ea67a9bf3981555c20991e2bacd4db5b418228b6002d8d41c025cb472bf5443aaa885974a408ea7f2e3f932c600deb", + "408190134ed06556811b1af808ab2d986aff152a28de2c41a2207c0ccc18125ac20f48384de89ea7c80cda1da14e60cc1599943646b4c0082bbcda2d9fa55a13e9df2934edf15eb4fd41f25fa3dd706ab6de522ed351b106321e494e7a27d5f7caf44ec6fadf1122d227eefc0f57aefc140d2c63d07dcbfd65790b1099745ed042cfd1548242076b98e616b76ff0d53db5179df8dd62c06a36a8b9e95a671e2a9b9dd3fb187a31ae5828d218ec5851913e0b52e2532bd4bf9e7b349f32de2b6d5d3cdf9f372d49617b6220c93c05962327e99a0480488443349f0fd54c1860f7c8", + "5f9e5c6f38573a85010a9d84d33f29c057003b2645e3ea6f72cbc7af95d197ce6a06b13fea81722853e6991791b8b15091cd066f5ed913592ed3d3af5370d39ba22beeb2a582a414b16824b77e194a094c2afdcc09aa73ce36f4943cca5ae32c5017dc398801dd92a47382d9327c9f6cffd38ca4167cd836f7855fc5ff048d8efba378cdde224905a0425e6b1de061fc951c5e624a5153b008ad41160a710b3ff2081748d5e02deb9f841f4fc6cf4a15153dd4fe874fd447482696283e79ee0e6bc8c1c0409baa5ab02c5209c319e3169b2476149c0c6e541c6197ca46e004eef533", + "218c6b3508aec69574f2b5039b30b942b72a8349d05f48ff945bbbe5c8957d5a6199492a6bf54bab821c9377e2edfa4c908384664d2c80112d5e805d66e0a551b941021be17dd20bd825bea9a3b6afb1b8c605805b3bda58750f03ea5c953a698494b425d8980c69f34d1c3f6b5866e8717031152a127215c256e08873c21b0f5cc85875d0f7c94601659150c04cd5fe5d381ba29983a2d94fcd3a65a94c53c7279cd000dddd4253d8cff8d7f6ace10247fe3bc30d63ba4bb54f557b3d22a3924369430d71ab37b701e9500bda70b5a643704858beed4726a889b6c9c91584194c68f1", + "dac26aa7273fc25d6e044c79fc2bfa46e59892a42bbca59a86826c91e76ab03e4bd9f7c0b5f08d1931d88b36ea77d94f7ba67cd4f1d3086e529427201119096ae066ae6f170940830ed7900de7bb9d66e09788287403a4ecc93c6da975d2fb08e918840a236c15f5d3a8f7375c2eeebbf6f01a6e7f29ca2b8d42df158414c320777433663c59fdcd1f39ca68e3473db721be7ce8c6dba5fddc024f94fedb286b0477581d451313ca8c737484daf60d67f9b2d56d4bcc271f7e9ae958c7f258efbc74d25753e0516f28282461941bf2dcc7dd8c7df6173b89760cefcac07190243ff863fb", + "c46e6512e6797cc7a54254a1b26b2de29aa83d6c4b1ea5a2786fbcec388270625b12635eae39e1fba013f8a65219421bca8b52a8ddfd431cda60299bdf160734d5a7450ec79620058522702174ae451b9bfa7c4a455fbbee3e1d048c7d4bac5131018228f137c8e130440c7059b4f15eaa34ce872a851a16ce86f982df78a00be4d564da2003a450ddee9ab43ea876b8b4b65c84f0b39265fd5456417afb5bc54997c986e66fc222f2123ba5e719c4d6b9a177b188277df384f1125821cf19d5248cef0be183ccdc84ac194506f740ed2188b2689ea4c9236a9e9e3a2fff85b6af4e9b49a3", + "1ccd4d278d67b65cf2564ecd4de1b55fe07adc80e1f735fe2f08ea53fd3977323689122c29c798957abaff6aba09bdcbf661d77f4dc8913ab1fe2bef38846166e3834785e7105d746484eff8c656af5d8c7854abc1c62b7fadb65521dc6f793d978bda9838eb3800417d32e8a24d8c8cb1d18a5de6ca79d9e1b0ff9aa25e6218fe944cf18666fecc1e31334b390260dbe0997539e1b02f6366b2aea4f4a21efe04f4b97568fcb39e59919d5ebac6543d5d0f48fc66b923c34aac377dc95c20329b837b6ed5e8d9a3d2089cd0d8f025658006ff41cbdaccca618822ca590ab155253f8bc1c7f5", + "9875209588395ee3c9fdd793fd48717cc84c8c3ea622b2ccc4a1be4448e6034b7810569855255031f10be5ffd714b05f9ce01972d712d40abf03d4d0ce175813a7a668f761324996093fc2aa5912f7fc2abdadd8775d2b4d9ad492216293381460ed8f6db3d641d1525f4242c348bbfe504c704f215dc461de51b5c75c1aae967936963848f16c673eca5e78dfd47eb19001d52d1bcf96c98956dad5ddf594a5da757e7ca35f2f69803b784e66ac5a58b75c228b8266ec592505e5d1ca87d81225738855f15bc0914677e81593fd409e77d159f8a908f67788de9eb06c5561547aada96c47c535", + "40c90e375e366f3756d89091eb3eed9fe0fbfc5638700af4617d358812bac53124a2205dd6756456787d49cd6a35e302479a0992288f47532e4ea7ab62fc5ad5adc690a5d9a446f7e035ad4641bd8dae83946aee3338ec984ccb5cc633e1409f2531eeffe05532a8b0062ba99454c9aeabf8ecb94db195af7032bfebc22912f49d39330add47ff8fa5720612d697f0b602738930e060a1bb214efc5e292224cf34e29deaea6b1b1ff847e94ecc997325ac38df61db45d82bf0e74a664d2fe085c20b04c39e90d6a170b68d2f1d373f00c731c524456ada73d659aaac9df3191a7a3865083343fc13", + "e8800d82e072210ca6d7fa2472028974780b76aad4bcb9ad362422dd05ae3232668251d164daa375a43b26a38cce28dbeb3dee1a4a579f70d0fe7febb29b5ece8aa836e050fb3d188c63aa9c3c0da6c717d86458a6096b5effceb964efdec7035960c09ccd10dea3c5f1c7f9f478d5887ebbe2e15c5ff85dbacbc444bb951c4eec7abecb89ed80187e409e2972ffe1a5f01562af109f2cf09471cf72cf83a3bb8f4e2ef38ed0e326b698296394e5b2718a5000c01425708e8ad0461e62462d8819c2377f13ab1be2c7c9f33dc06fe23cad27b87569f2ce2e56e4b2c60c7b1b3d370841d89ebdc1f192", + "796d6d1447d5b7e8c55cd8b2f8b7010db39f27565f907e3fc0e464ea2d4bb52b37f10e7c6dcfc59231b9cdee12c32aeb4adbc42b86e86eb6defb5b69e6ca75e1f4d0dae3e124e5a1b8b6697f7e10b0403f1f0a5ff848eef3752837a9ba17780f16a9a709188a8d5b89a2fa74adb2e651163b1c2b3d261e225c9158dcd9eb7ac3d6704cee290cdff6bcb3cb90cee030aa0d19d4693655c3c30ac6fc06d2ae37787c47126d57ed9a6bef5f8a6c56859aefc08755739a95aac57a4dd916a92ba9f3afbf969df8085949615033365c751a9a3e1a18cee98a69d22e64009bebf8307169b6c61de0617ecfafdf", + "4f9057183566153cf337b07c3f5556006de54c56b2a1e5326c07aaeabd1886ec6f1641358925db232b2f0dbf75229c796a7395b2f934c1f99090bec1123f3c841b1cb3c5b1ec42ed5408f2940f0c48a9470b852c46d6557853d459cecd2c32bbcd8ee21fa11e385eef0857cba4d8545a61b52a484cdd779db4739fbc7aa9860dcabe0488b98fa0b60c3f7d6153db279000a52ffb573dab37d2ab1896a90e5deb7ac6bbe56239085c325d83a917dc6e8a448425b718c2356b9f3066163555ec444f372e184e02c8c4c69b1c1c2ae2b51e45b98f73d933d18750968945ca85d6bbb22014b4c4015262e3c40d", + "79dcca7d8b81a61359e4aece21f3df7b99518ce70bd2f57a18bab5e7114af2add0a0cea7f319d69f231f060e0a539d9a23fb3e95451ce8c6340cfb09edf931df84203a39226dd9eb278f11b691ef612585b973daab373e65d11325898badf6732100371fd759960fa8fec373268421d28bffdb9b12a430b92fe4b07566ca0c89e616e49f8fc75ccd9cdc66db820d7c02e109aa5ed86b89770262918a518f90a2292f6b68d68ae03992e4259a17a23c84ec2a417f082b5abf3a26e44d2278ecb8ba9456965303a75f25394d1aaf5544590e74b14d8a4cc4050be2b0ebcfe4d2db6b12a02c68a3bcdda70301f3", + "848755dc31e25e9a42f9ec12d847d19f292c14c162c9aba49e972cb123b58b8e57bb263a923929833373858594ff52dbc298dbbc078599194e4c07b0e5fc1e10808bbacdb6e93c72b333685cf961f28eb0d5a395c63266b01f130d25db384b356e5da6d01042fc2359581b89c63b3bb2d1ce897fbc9e83fe85d9666cb60e6a8c657f70caad5387b8a045bf91095606802c8424ea8ac52ef29386dc46183378a5fcb2cb927428b8c070f1c42aafd3bc70ca25437807696a46873cfeb7b80ba2ebc3c4272443d445e46343a1465253a9eebd532a0d1d2c18264b91ff45159f245404ae9335f2af55c802772426b4", + "ecaa6e999ef355a0768730edb835db411829a3764f79d764bb5682af6d00f51b313e017b83fffe2e332cd4a3de0a81d6a52084d5748346a1f81eb9b183ff6d93d05edc00e938d001c90872dfe234e8dd085f639af168af4a07e18f1c56ca6c7c1addffc4a70eb4660666dda0321636c3f83479ad3b64e23d749620413a2ecdcc52ad4e6e63f2b817ce99c15b5d2da3792721d7158297cce65e0c04fe810d7e2434b969e4c7892b3840623e153576356e9a696fd9e7a801c25de621a7849da3f99158d3d09bf039f43c510c8ffb00fa3e9a3c12d2c8062dd25b8dabe53d8581e30427e81c3dfc2d455352487e1255", + "23a3fe80e3636313fdf922a1359514d9f31775e1adf24285e8001c04dbce866df055edf25b506e18953492a173ba5aa0c1ec758123406a97025ba9b6b7a97eb14734424d1a7841ec0eaeba0051d6e9734263bea1af9895a3b8c83d8c854da2ae7832bdd7c285b73f8113c3821cced38b3656b4e6369a9f8327cd368f04128f1d78b6b4260f55995277feffa15e34532cd0306c1f47354667c17018ee012a791af2dbbc7afc92c388008c601740cccbbe66f1eb06ea657e9d478066c2bd2093ab62cd94abadc002722f50968e8acf361658fc64f50685a5b1b004888b3b4f64a4ddb67bec7e4ac64c9ee8deeda896b9", + "758f3567cd992228386a1c01930f7c52a9dcce28fdc1aaa54b0fed97d9a54f1df805f31bac12d559e90a2063cd7df8311a148f6904f78c5440f75e49877c0c0855d59c7f7ee52837e6ef3e54a568a7b38a0d5b896e298c8e46a56d24d8cabda8aeff85a622a3e7c87483ba921f34156defd185f608e2241224286e38121a162c2ba7604f68484717196f6628861a948180e8f06c6cc1ec66d032cf8d16da039cd74277cde31e535bc1692a44046e16881c954af3cd91dc49b443a3680e4bc42a954a46ebd1368b1398edd7580f935514b15c7fbfa9b40048a35122283af731f5e460aa85b66e65f49a9d158699bd2870", + "fe511e86971cea2b6af91b2afa898d9b067fa71780790bb409189f5debe719f405e16acf7c4306a6e6ac5cd535290efe088943b9e6c5d25bfc508023c1b105d20d57252fee8cdbddb4d34a6ec2f72e8d55be55afcafd2e922ab8c31888bec4e816d04f0b2cd23df6e04720969c5152b3563c6da37e4608554cc7b8715bc10aba6a2e3b6fbcd35408df0dd73a9076bfad32b741fcdb0edfb563b3f753508b9b26f0a91673255f9bcda2b9a120f6bfa0632b6551ca517d846a747b66ebda1b2170891ece94c19ce8bf682cc94afdf0053fba4e4f0530935c07cdd6f879c999a8c4328ef6d3e0a37974a230ada83910604337", + "a6024f5b959698c0de45f4f29e1803f99dc8112989c536e5a1337e281bc856ff721e986de183d7b0ea9eb61166830ae5d6d6bc857dc833ff189b52889b8e2bd3f35b4937624d9b36dc5f19db44f0772508029784c7dac9568d28609058bc437e2f79f95b12307d8a8fb042d7fd6ee910a9e8df609ede3283f958ba918a9925a0b1d0f9f9f232062315f28a52cbd60e71c09d83e0f6600f508f0ae8ad7642c080ffc618fcd2314e26f67f1529342569f6df37017f7e3b2dac32ad88d56d175ab22205ee7e3ee94720d76933a21132e110fefbb0689a3adbaa4c685f43652136d09b3a359b5c671e38f11915cb5612db2ae294", + "af6de0e227bd78494acb559ddf34d8a7d55a03912384831be21c38376f39cda8a864aff7a48aed758f6bdf777779a669068a75ce82a06f6b3325c855ed83daf5513a078a61f7dc6c1622a633367e5f3a33e765c8ec5d8d54f48494006fdbf8922063e5340013e312871b7f8f8e5ea439c0d4cb78e2f19dd11f010729b692c65dd0d347f0ce53de9d849224666ea2f6487f1c6f953e8f9dbfd3d6de291c3e9d045e633cfd83c89d2f2327d0b2f31f72ac1604a3db1febc5f22cad08153278047210cc2894582c251a014c652e3951593e70e52a5d7451be8924b64f85c8247dab6268d24710b39fc1c07b4ac829fbda34ed79b5", + "d7314e8b1ff82100b8f5870da62b61c31ab37ace9e6a7b6f7d294571523783c1fdedcbc00dd487dd6f848c34aab493507d07071b5eb59d1a2346068c7f356755fbde3d2cab67514f8c3a12d6ff9f96a977a9ac9263491bd33122a904da5386b943d35a6ba383932df07f259b6b45f69e9b27b4ca124fb3ae143d709853eed86690bc2754d5f8865c355a44b5279d8eb31cdc00f7407fb5f5b34edc57fc7ace943565da2222dc80632ccf42f2f125ceb19714ea964c2e50603c9f8960c3f27c2ed0e18a559931c4352bd7422109a28c5e145003f55c9b7c664fdc985168868950396eaf6fefc7b73d815c1aca721d7c67da632925", + "2928b55c0e4d0f5cb4b60af59e9a702e3d616a8cf427c8bb03981fb8c29026d8f7d89161f36c11654f9a5e8ccb703595a58d671ecdc22c6a784abe363158682be4643002a7da5c9d268a30ea9a8d4cc24f562ab59f55c2b43af7dbcecc7e5ebe7494e82d74145a1e7d442125eb0431c5ea0939b27afa47f8ca97849f341f707660c7fbe49b7a0712fbcb6f7562ae2961425f27c7779c7534ecdeb8047ff3cb89a25159f3e1cefe42f9ef16426241f2c4d62c11d7ac43c4500dfcd184436bb4ef33260366f875230f26d81613c334dbda4736ba9d1d2966502914ec01bbe72d885606ec11da7a2cb01b29d35eebedbb0ecc73ed6c35", + "fd993f50e8a68c7b2c7f87511ce65b93c0aa94dcbdf2c9cca93816f0f3b2ab34c62c586fc507b4900a34cf9d0517e0fe10a89d154c5419c1f5e38de00e8834fe3dc1032abdeb10729a81655a69a12856a78ca6e12110580de879b086fd6608726541cfa9616326bdd36064bc0d1e5f9c93b41278bff6a13b2494b81e238c0c45aea1b07d855e8f3fe1478e373bd9d3957cf8a5e5b9003386793d994c7c575cff2322e2428cbbaa4f47560316ae3354a7478842ff7cc5dcbacb6e871e72b36f06d63a9aaeb9044cfb7974afdc238a5816f537dcf33ee40b4e1a5eb3cff2402b46d548264e133008d284f11b7e4e450bc3c5ff9f79b9c4", + "8df21892f5fc303b0de4adef1970186db6fe71bb3ea3094922e13afcfabf1d0be009f36d6f6310c5f9fda51f1a946507a055b645c296370440e5e83d8e906a2fb51f2b42de8856a81a4f28a73a8825c68ea08e5e366730bce8047011cb7d6d9be8c6f4211308fad21856284d5bc47d199988e0abf5badf8693ceeed0a2d98e8ae94b7775a42925edb1f697ffbd8e806af23145054a85e071819cca4cd48875290ca65e5ee72a9a54ff9f19c10ef4adaf8d04c9a9afcc73853fc128bbebc61f78702787c966ca6e1b1a0e4dab646acdfcd3c6bf3e5cfbec5ebe3e06c8abaa1de56e48421d87c46b5c78030afcafd91f27e7d7c85eb4872b", + "48ec6ec520f8e593d7b3f653eb15553de246723b81a6d0c3221aaa42a37420fba98a23796338dff5f845dce6d5a449be5ecc1887356619270461087e08d05fb60433a83d7bd00c002b09ea210b428965124b9b27d9105a71c826c1a2491cfd60e4cfa86c2da0c7100a8dc1c3f2f94b280d54e01e043acf0e966200d9fa8a41daf3b9382820786c75cadbb8841a1b2be5b6cbeb64878e4a231ae063a99b4e2308960ef0c8e2a16bb3545cc43bdf171493fb89a84f47e7973dc60cf75aeeca71e0a7ebe17d161d4fb9fe009941cc438f16a5bae6c99fcad08cac486eb2a48060b023d8730bf1d82fe60a2f036e6f52a5bff95f43bbe088933f", + "f4d84ed3e564c102600a795eaa9b1eaf4ad12f1a4deca1d042a0a2750ddf6201db03073d8bf553cb9dde48a1b0083827a609f7242b86584cc180964ae794b12ce55661e00e36a6ba4dbc389e6a5a85f1b45df9af7ead1b0a54db56e68639b9d438a91504e82c35d40c7bc7e048a53ac0b04accd0dadf4ac9884b0ca0e3cb5ba4336e3581be4c4760a553823ffa283a1120d4e145af56a59f2533903650f0b9e9ad9fe2e8a3c3c3dd03a1fcb709032c8835324839c735b0c051d0cbd8b5d867617c11023432e4bd275d3d0eb98a0b6cf58071a5b712922f2bc751ac7c2588c447444cde2f37a8ea5ec126425bf517e0d17c9e2999f52fee14b3", + "2ccea21bac9c2b70d3923309cbf2d7cb7abd1fcc8b8b002688870a80029c62397350c3c898194e5deea360bb963d26d485cb7963f8167586976ec0556950b2e86135f4a2800991ce8473bfd44a3c5e937a48b5e355ba5141bccf2131a83988d9d2a9e8e7635a956105b3512c05ef708139ced51d7a4e204c12d8a49a21e8dc6de2629a2fd092326885d9f218745fe09f6d91fb6afce250a30a63689534b6be1f26899ffa3767d835cf586aa47776700f94241bc999b1e3deefe188f37ff734f5f16ee6a00914323dc7b8a143c9137cdcc5cd08ae9566f04bb2941532674c97dff6ffa5ce3405ef8e5d27ec403114253dd6394c0167d72a0044c5", + "2b681c6398aee63bf862770341648bbcd31d7de7903c5903fe3d9469311320bb24d914f2af0cdca199c97214c7c679dc32a2800ba484a03c010ea6be3bb9f2c87e30a98b606050b8a3f297f12b8f92caaeceb3e844652115934874e0a1ab093a73d759b53f6a6c3096940dd22c2bb96ce6820a7b9c6d71a208de9892aa6a7209b0fff56a0cafea52b952cdd6f5752cff3309d448800b4e4c878aa595595b56b12b83fcd6ca89520c7da664e449d7b4438fc455888aad5de0fad9a06eed14afd3513b5ebbffe01775549b701181bd26370764f56eba52fdb24286ad1ac0f5418a7c429f7dfc7f3168437fa8eed7a2ed7c723a485e4c3ed14dea2e07", + "aadfd505a89f4aade2c3018258a7e039401b1fc6a7f3d87910dddbb880d372ec8a13c70d92245de5b8e5f9a285c33b99dc82fa2b22decee72b93a72211656ad7a52696c8e570f78be28c0e427a371dafde856e8d5ed24f83b0660b51e7fac05d93a8666dfde6def59af863f80f3e5f6801182c87422203df390dcb736b8f830052a8832eeeb0b4e27e732aaf793d166b5a3ec7745aeef3766937c2b75a276bddd145f6010c29d035e343e267cb2d828436876ec3a7ebe3b6347d4172f7a99d6821ce152e039e53deb33340b324c7f068ffb94b3cde35a8eaa12d15c3806a7ad0acec3e8c7078c1d32a28fd3eec9f32cb86e4c22166ff69e83785e851", + "1605b8cce529a9d6262fd4390d9e4ae5e14e0adc0ec89b028ef68dd0f373ea259aaa96f2967091dd0874c0105385e9e6da9ca68297c31afa44ef834535fb302ce5b4e49edacbbdf359fe1228a8172495b3e57014c27edd58b685110980056c50c398a64f4923f2d720b4df16d75cb36b4233660694182099c35028a972519c24764fc94e18e582b24deb3491535fc06b83837c7958522800e822201d694af0bd0aa3834e17d4b1ba36f470905ae5f8bbeeb6c4c8604d8af02baa347b07086d6989867ddd5e8e8ed7740c3469bfa2810519c55c6add1332c4c54ee9097961d6741cb12a09713a0d07645f784f42f5ad94b48b836b34263130b0483f15e3", + "ff9c6125b2f60bfd6c2427b279df070e430075096647599bdc68c531152c58e13858b82385d78c856092d6c74106e87ccf51ac7e673936332d9b223444eaa0e762ee258d8a733d3a515ec68ed73285e5ca183ae3278b4820b0ab2797feb1e7d8cc864df585dfb5ebe02a993325a9ad5e2d7d49d3132cf66013898351d044e0fe908ccdfeeebf651983601e3673a1f92d36510c0cc19b2e75856db8e4a41f92a51efa66d6cc22e414944c2c34a5a89ccde0be76f51410824e330d8e7c613194338c93732e8aea651fca18bcf1ac1824340c5553aff1e58d4ab8d7c8842b4712021e517cd6c140f6743c69c7bee05b10a8f24050a8caa4f96d1664909c5a06", + "6e85c2f8e1fdc3aaeb969da1258cb504bbf0070cd03d23b3fb5ee08feea5ee2e0ee1c71a5d0f4f701b351f4e4b4d74cb1e2ae6184814f77b62d2f08134b7236ebf6b67d8a6c9f01b4248b30667c555f5d8646dbfe291151b23c9c9857e33a4d5c847be29a5ee7b402e03bac02d1a4319acc0dd8f25e9c7a266f5e5c896cc11b5b238df96a0963ae806cb277abc515c298a3e61a3036b177acf87a56ca4478c4c6d0d468913de602ec891318bbaf52c97a77c35c5b7d164816cf24e4c4b0b5f45853882f716d61eb947a45ce2efa78f1c70a918512af1ad536cbe6148083385b34e207f5f690d7a954021e4b5f4258a385fd8a87809a481f34202af4caccb82", + "1e9b2c454e9de3a2d723d850331037dbf54133dbe27488ff757dd255833a27d8eb8a128ad12d0978b6884e25737086a704fb289aaaccf930d5b582ab4df1f55f0c429b6875edec3fe45464fa74164be056a55e243c4222c586bec5b18f39036aa903d98180f24f83d09a454dfa1e03a60e6a3ba4613e99c35f874d790174ee48a557f4f021ade4d1b278d7997ef094569b37b3db0505951e9ee8400adaea275c6db51b325ee730c69df97745b556ae41cd98741e28aa3a49544541eeb3da1b1e8fa4e8e9100d66dd0c7f5e2c271b1ecc077de79c462b9fe4c273543ecd82a5bea63c5acc01eca5fb780c7d7c8c9fe208ae8bd50cad1769693d92c6c8649d20d8", +} diff --git a/vendor/golang.org/x/crypto/blake2b/blake2x.go b/vendor/golang.org/x/crypto/blake2b/blake2x.go new file mode 100644 index 0000000..c814496 --- /dev/null +++ b/vendor/golang.org/x/crypto/blake2b/blake2x.go @@ -0,0 +1,177 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package blake2b + +import ( + "encoding/binary" + "errors" + "io" +) + +// XOF defines the interface to hash functions that +// support arbitrary-length output. +type XOF interface { + // Write absorbs more data into the hash's state. It panics if called + // after Read. + io.Writer + + // Read reads more output from the hash. It returns io.EOF if the limit + // has been reached. + io.Reader + + // Clone returns a copy of the XOF in its current state. + Clone() XOF + + // Reset resets the XOF to its initial state. + Reset() +} + +// OutputLengthUnknown can be used as the size argument to NewXOF to indicate +// the the length of the output is not known in advance. +const OutputLengthUnknown = 0 + +// magicUnknownOutputLength is a magic value for the output size that indicates +// an unknown number of output bytes. +const magicUnknownOutputLength = (1 << 32) - 1 + +// maxOutputLength is the absolute maximum number of bytes to produce when the +// number of output bytes is unknown. +const maxOutputLength = (1 << 32) * 64 + +// NewXOF creates a new variable-output-length hash. The hash either produce a +// known number of bytes (1 <= size < 2**32-1), or an unknown number of bytes +// (size == OutputLengthUnknown). In the latter case, an absolute limit of +// 256GiB applies. +// +// A non-nil key turns the hash into a MAC. The key must between +// zero and 32 bytes long. +func NewXOF(size uint32, key []byte) (XOF, error) { + if len(key) > Size { + return nil, errKeySize + } + if size == magicUnknownOutputLength { + // 2^32-1 indicates an unknown number of bytes and thus isn't a + // valid length. + return nil, errors.New("blake2b: XOF length too large") + } + if size == OutputLengthUnknown { + size = magicUnknownOutputLength + } + x := &xof{ + d: digest{ + size: Size, + keyLen: len(key), + }, + length: size, + } + copy(x.d.key[:], key) + x.Reset() + return x, nil +} + +type xof struct { + d digest + length uint32 + remaining uint64 + cfg, root, block [Size]byte + offset int + nodeOffset uint32 + readMode bool +} + +func (x *xof) Write(p []byte) (n int, err error) { + if x.readMode { + panic("blake2b: write to XOF after read") + } + return x.d.Write(p) +} + +func (x *xof) Clone() XOF { + clone := *x + return &clone +} + +func (x *xof) Reset() { + x.cfg[0] = byte(Size) + binary.LittleEndian.PutUint32(x.cfg[4:], uint32(Size)) // leaf length + binary.LittleEndian.PutUint32(x.cfg[12:], x.length) // XOF length + x.cfg[17] = byte(Size) // inner hash size + + x.d.Reset() + x.d.h[1] ^= uint64(x.length) << 32 + + x.remaining = uint64(x.length) + if x.remaining == magicUnknownOutputLength { + x.remaining = maxOutputLength + } + x.offset, x.nodeOffset = 0, 0 + x.readMode = false +} + +func (x *xof) Read(p []byte) (n int, err error) { + if !x.readMode { + x.d.finalize(&x.root) + x.readMode = true + } + + if x.remaining == 0 { + return 0, io.EOF + } + + n = len(p) + if uint64(n) > x.remaining { + n = int(x.remaining) + p = p[:n] + } + + if x.offset > 0 { + blockRemaining := Size - x.offset + if n < blockRemaining { + x.offset += copy(p, x.block[x.offset:]) + x.remaining -= uint64(n) + return + } + copy(p, x.block[x.offset:]) + p = p[blockRemaining:] + x.offset = 0 + x.remaining -= uint64(blockRemaining) + } + + for len(p) >= Size { + binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset) + x.nodeOffset++ + + x.d.initConfig(&x.cfg) + x.d.Write(x.root[:]) + x.d.finalize(&x.block) + + copy(p, x.block[:]) + p = p[Size:] + x.remaining -= uint64(Size) + } + + if todo := len(p); todo > 0 { + if x.remaining < uint64(Size) { + x.cfg[0] = byte(x.remaining) + } + binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset) + x.nodeOffset++ + + x.d.initConfig(&x.cfg) + x.d.Write(x.root[:]) + x.d.finalize(&x.block) + + x.offset = copy(p, x.block[:todo]) + x.remaining -= uint64(todo) + } + return +} + +func (d *digest) initConfig(cfg *[Size]byte) { + d.offset, d.c[0], d.c[1] = 0, 0, 0 + for i := range d.h { + d.h[i] = iv[i] ^ binary.LittleEndian.Uint64(cfg[i*8:]) + } +} diff --git a/vendor/golang.org/x/crypto/ocsp/ocsp.go b/vendor/golang.org/x/crypto/ocsp/ocsp.go index 6bd347e..ae8d63e 100644 --- a/vendor/golang.org/x/crypto/ocsp/ocsp.go +++ b/vendor/golang.org/x/crypto/ocsp/ocsp.go @@ -502,7 +502,7 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon } else { match := false for _, resp := range basicResp.TBSResponseData.Responses { - if cert == nil || cert.SerialNumber.Cmp(resp.CertID.SerialNumber) == 0 { + if cert.SerialNumber.Cmp(resp.CertID.SerialNumber) == 0 { singleResp = resp match = true break diff --git a/vendor/golang.org/x/crypto/ssh/client_auth_test.go b/vendor/golang.org/x/crypto/ssh/client_auth_test.go index 89e0d92..145b57a 100644 --- a/vendor/golang.org/x/crypto/ssh/client_auth_test.go +++ b/vendor/golang.org/x/crypto/ssh/client_auth_test.go @@ -622,7 +622,7 @@ func TestClientAuthErrorList(t *testing.T) { t.Fatalf("errors: got %v, want %v", e, publicKeyErr) } default: - t.Fatal("errors: got %v, expected 2 errors", authErrs.Errors) + t.Fatalf("errors: got %v, expected 2 errors", authErrs.Errors) } } } diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index efdacb7..5cb4d8b 100755 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -173,6 +173,7 @@ struct ltchars { #include #include #include +#include #include #include #include diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index 4723162..a6b3b5f 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -1413,6 +1413,16 @@ const ( SIOCADDMULTI = 0x8931 SIOCADDRT = 0x890b SIOCATMARK = 0x8905 + SIOCBONDCHANGEACTIVE = 0x8995 + SIOCBONDENSLAVE = 0x8990 + SIOCBONDINFOQUERY = 0x8994 + SIOCBONDRELEASE = 0x8991 + SIOCBONDSETHWADDR = 0x8992 + SIOCBONDSLAVEINFOQUERY = 0x8993 + SIOCBRADDBR = 0x89a0 + SIOCBRADDIF = 0x89a2 + SIOCBRDELBR = 0x89a1 + SIOCBRDELIF = 0x89a3 SIOCDARP = 0x8953 SIOCDELDLCI = 0x8981 SIOCDELMULTI = 0x8932 @@ -1420,7 +1430,9 @@ const ( SIOCDEVPRIVATE = 0x89f0 SIOCDIFADDR = 0x8936 SIOCDRARP = 0x8960 + SIOCETHTOOL = 0x8946 SIOCGARP = 0x8954 + SIOCGHWTSTAMP = 0x89b1 SIOCGIFADDR = 0x8915 SIOCGIFBR = 0x8940 SIOCGIFBRDADDR = 0x8919 @@ -1440,13 +1452,21 @@ const ( SIOCGIFPFLAGS = 0x8935 SIOCGIFSLAVE = 0x8929 SIOCGIFTXQLEN = 0x8942 + SIOCGIFVLAN = 0x8982 + SIOCGMIIPHY = 0x8947 + SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x8904 SIOCGRARP = 0x8961 + SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 SIOCGSTAMPNS = 0x8907 + SIOCINQ = 0x541b + SIOCOUTQ = 0x5411 + SIOCOUTQNSD = 0x894b SIOCPROTOPRIVATE = 0x89e0 SIOCRTMSG = 0x890d SIOCSARP = 0x8955 + SIOCSHWTSTAMP = 0x89b0 SIOCSIFADDR = 0x8916 SIOCSIFBR = 0x8941 SIOCSIFBRDADDR = 0x891a @@ -1465,11 +1485,15 @@ const ( SIOCSIFPFLAGS = 0x8934 SIOCSIFSLAVE = 0x8930 SIOCSIFTXQLEN = 0x8943 + SIOCSIFVLAN = 0x8983 + SIOCSMIIREG = 0x8949 SIOCSPGRP = 0x8902 SIOCSRARP = 0x8962 + SIOCWANDEV = 0x894a SOCK_CLOEXEC = 0x80000 SOCK_DCCP = 0x6 SOCK_DGRAM = 0x2 + SOCK_IOC_TYPE = 0x89 SOCK_NONBLOCK = 0x800 SOCK_PACKET = 0xa SOCK_RAW = 0x3 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index b5c978e..4ffc8d2 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -1414,6 +1414,16 @@ const ( SIOCADDMULTI = 0x8931 SIOCADDRT = 0x890b SIOCATMARK = 0x8905 + SIOCBONDCHANGEACTIVE = 0x8995 + SIOCBONDENSLAVE = 0x8990 + SIOCBONDINFOQUERY = 0x8994 + SIOCBONDRELEASE = 0x8991 + SIOCBONDSETHWADDR = 0x8992 + SIOCBONDSLAVEINFOQUERY = 0x8993 + SIOCBRADDBR = 0x89a0 + SIOCBRADDIF = 0x89a2 + SIOCBRDELBR = 0x89a1 + SIOCBRDELIF = 0x89a3 SIOCDARP = 0x8953 SIOCDELDLCI = 0x8981 SIOCDELMULTI = 0x8932 @@ -1421,7 +1431,9 @@ const ( SIOCDEVPRIVATE = 0x89f0 SIOCDIFADDR = 0x8936 SIOCDRARP = 0x8960 + SIOCETHTOOL = 0x8946 SIOCGARP = 0x8954 + SIOCGHWTSTAMP = 0x89b1 SIOCGIFADDR = 0x8915 SIOCGIFBR = 0x8940 SIOCGIFBRDADDR = 0x8919 @@ -1441,13 +1453,21 @@ const ( SIOCGIFPFLAGS = 0x8935 SIOCGIFSLAVE = 0x8929 SIOCGIFTXQLEN = 0x8942 + SIOCGIFVLAN = 0x8982 + SIOCGMIIPHY = 0x8947 + SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x8904 SIOCGRARP = 0x8961 + SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 SIOCGSTAMPNS = 0x8907 + SIOCINQ = 0x541b + SIOCOUTQ = 0x5411 + SIOCOUTQNSD = 0x894b SIOCPROTOPRIVATE = 0x89e0 SIOCRTMSG = 0x890d SIOCSARP = 0x8955 + SIOCSHWTSTAMP = 0x89b0 SIOCSIFADDR = 0x8916 SIOCSIFBR = 0x8941 SIOCSIFBRDADDR = 0x891a @@ -1466,11 +1486,15 @@ const ( SIOCSIFPFLAGS = 0x8934 SIOCSIFSLAVE = 0x8930 SIOCSIFTXQLEN = 0x8943 + SIOCSIFVLAN = 0x8983 + SIOCSMIIREG = 0x8949 SIOCSPGRP = 0x8902 SIOCSRARP = 0x8962 + SIOCWANDEV = 0x894a SOCK_CLOEXEC = 0x80000 SOCK_DCCP = 0x6 SOCK_DGRAM = 0x2 + SOCK_IOC_TYPE = 0x89 SOCK_NONBLOCK = 0x800 SOCK_PACKET = 0xa SOCK_RAW = 0x3 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go index 0ae0e8c..f4b178e 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go @@ -1418,6 +1418,16 @@ const ( SIOCADDMULTI = 0x8931 SIOCADDRT = 0x890b SIOCATMARK = 0x8905 + SIOCBONDCHANGEACTIVE = 0x8995 + SIOCBONDENSLAVE = 0x8990 + SIOCBONDINFOQUERY = 0x8994 + SIOCBONDRELEASE = 0x8991 + SIOCBONDSETHWADDR = 0x8992 + SIOCBONDSLAVEINFOQUERY = 0x8993 + SIOCBRADDBR = 0x89a0 + SIOCBRADDIF = 0x89a2 + SIOCBRDELBR = 0x89a1 + SIOCBRDELIF = 0x89a3 SIOCDARP = 0x8953 SIOCDELDLCI = 0x8981 SIOCDELMULTI = 0x8932 @@ -1425,7 +1435,9 @@ const ( SIOCDEVPRIVATE = 0x89f0 SIOCDIFADDR = 0x8936 SIOCDRARP = 0x8960 + SIOCETHTOOL = 0x8946 SIOCGARP = 0x8954 + SIOCGHWTSTAMP = 0x89b1 SIOCGIFADDR = 0x8915 SIOCGIFBR = 0x8940 SIOCGIFBRDADDR = 0x8919 @@ -1445,13 +1457,21 @@ const ( SIOCGIFPFLAGS = 0x8935 SIOCGIFSLAVE = 0x8929 SIOCGIFTXQLEN = 0x8942 + SIOCGIFVLAN = 0x8982 + SIOCGMIIPHY = 0x8947 + SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x8904 SIOCGRARP = 0x8961 + SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 SIOCGSTAMPNS = 0x8907 + SIOCINQ = 0x541b + SIOCOUTQ = 0x5411 + SIOCOUTQNSD = 0x894b SIOCPROTOPRIVATE = 0x89e0 SIOCRTMSG = 0x890d SIOCSARP = 0x8955 + SIOCSHWTSTAMP = 0x89b0 SIOCSIFADDR = 0x8916 SIOCSIFBR = 0x8941 SIOCSIFBRDADDR = 0x891a @@ -1470,11 +1490,15 @@ const ( SIOCSIFPFLAGS = 0x8934 SIOCSIFSLAVE = 0x8930 SIOCSIFTXQLEN = 0x8943 + SIOCSIFVLAN = 0x8983 + SIOCSMIIREG = 0x8949 SIOCSPGRP = 0x8902 SIOCSRARP = 0x8962 + SIOCWANDEV = 0x894a SOCK_CLOEXEC = 0x80000 SOCK_DCCP = 0x6 SOCK_DGRAM = 0x2 + SOCK_IOC_TYPE = 0x89 SOCK_NONBLOCK = 0x800 SOCK_PACKET = 0xa SOCK_RAW = 0x3 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index 3c53a84..495f13b 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -1403,6 +1403,16 @@ const ( SIOCADDMULTI = 0x8931 SIOCADDRT = 0x890b SIOCATMARK = 0x8905 + SIOCBONDCHANGEACTIVE = 0x8995 + SIOCBONDENSLAVE = 0x8990 + SIOCBONDINFOQUERY = 0x8994 + SIOCBONDRELEASE = 0x8991 + SIOCBONDSETHWADDR = 0x8992 + SIOCBONDSLAVEINFOQUERY = 0x8993 + SIOCBRADDBR = 0x89a0 + SIOCBRADDIF = 0x89a2 + SIOCBRDELBR = 0x89a1 + SIOCBRDELIF = 0x89a3 SIOCDARP = 0x8953 SIOCDELDLCI = 0x8981 SIOCDELMULTI = 0x8932 @@ -1410,7 +1420,9 @@ const ( SIOCDEVPRIVATE = 0x89f0 SIOCDIFADDR = 0x8936 SIOCDRARP = 0x8960 + SIOCETHTOOL = 0x8946 SIOCGARP = 0x8954 + SIOCGHWTSTAMP = 0x89b1 SIOCGIFADDR = 0x8915 SIOCGIFBR = 0x8940 SIOCGIFBRDADDR = 0x8919 @@ -1430,13 +1442,21 @@ const ( SIOCGIFPFLAGS = 0x8935 SIOCGIFSLAVE = 0x8929 SIOCGIFTXQLEN = 0x8942 + SIOCGIFVLAN = 0x8982 + SIOCGMIIPHY = 0x8947 + SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x8904 SIOCGRARP = 0x8961 + SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 SIOCGSTAMPNS = 0x8907 + SIOCINQ = 0x541b + SIOCOUTQ = 0x5411 + SIOCOUTQNSD = 0x894b SIOCPROTOPRIVATE = 0x89e0 SIOCRTMSG = 0x890d SIOCSARP = 0x8955 + SIOCSHWTSTAMP = 0x89b0 SIOCSIFADDR = 0x8916 SIOCSIFBR = 0x8941 SIOCSIFBRDADDR = 0x891a @@ -1455,11 +1475,15 @@ const ( SIOCSIFPFLAGS = 0x8934 SIOCSIFSLAVE = 0x8930 SIOCSIFTXQLEN = 0x8943 + SIOCSIFVLAN = 0x8983 + SIOCSMIIREG = 0x8949 SIOCSPGRP = 0x8902 SIOCSRARP = 0x8962 + SIOCWANDEV = 0x894a SOCK_CLOEXEC = 0x80000 SOCK_DCCP = 0x6 SOCK_DGRAM = 0x2 + SOCK_IOC_TYPE = 0x89 SOCK_NONBLOCK = 0x800 SOCK_PACKET = 0xa SOCK_RAW = 0x3 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go index 23e845e..59651e4 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go @@ -1415,6 +1415,16 @@ const ( SIOCADDMULTI = 0x8931 SIOCADDRT = 0x890b SIOCATMARK = 0x40047307 + SIOCBONDCHANGEACTIVE = 0x8995 + SIOCBONDENSLAVE = 0x8990 + SIOCBONDINFOQUERY = 0x8994 + SIOCBONDRELEASE = 0x8991 + SIOCBONDSETHWADDR = 0x8992 + SIOCBONDSLAVEINFOQUERY = 0x8993 + SIOCBRADDBR = 0x89a0 + SIOCBRADDIF = 0x89a2 + SIOCBRDELBR = 0x89a1 + SIOCBRDELIF = 0x89a3 SIOCDARP = 0x8953 SIOCDELDLCI = 0x8981 SIOCDELMULTI = 0x8932 @@ -1422,7 +1432,9 @@ const ( SIOCDEVPRIVATE = 0x89f0 SIOCDIFADDR = 0x8936 SIOCDRARP = 0x8960 + SIOCETHTOOL = 0x8946 SIOCGARP = 0x8954 + SIOCGHWTSTAMP = 0x89b1 SIOCGIFADDR = 0x8915 SIOCGIFBR = 0x8940 SIOCGIFBRDADDR = 0x8919 @@ -1442,13 +1454,21 @@ const ( SIOCGIFPFLAGS = 0x8935 SIOCGIFSLAVE = 0x8929 SIOCGIFTXQLEN = 0x8942 + SIOCGIFVLAN = 0x8982 + SIOCGMIIPHY = 0x8947 + SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x40047309 SIOCGRARP = 0x8961 + SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 SIOCGSTAMPNS = 0x8907 + SIOCINQ = 0x467f + SIOCOUTQ = 0x7472 + SIOCOUTQNSD = 0x894b SIOCPROTOPRIVATE = 0x89e0 SIOCRTMSG = 0x890d SIOCSARP = 0x8955 + SIOCSHWTSTAMP = 0x89b0 SIOCSIFADDR = 0x8916 SIOCSIFBR = 0x8941 SIOCSIFBRDADDR = 0x891a @@ -1467,11 +1487,15 @@ const ( SIOCSIFPFLAGS = 0x8934 SIOCSIFSLAVE = 0x8930 SIOCSIFTXQLEN = 0x8943 + SIOCSIFVLAN = 0x8983 + SIOCSMIIREG = 0x8949 SIOCSPGRP = 0x80047308 SIOCSRARP = 0x8962 + SIOCWANDEV = 0x894a SOCK_CLOEXEC = 0x80000 SOCK_DCCP = 0x6 SOCK_DGRAM = 0x1 + SOCK_IOC_TYPE = 0x89 SOCK_NONBLOCK = 0x80 SOCK_PACKET = 0xa SOCK_RAW = 0x3 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go index d27b373..a09bf9b 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go @@ -1415,6 +1415,16 @@ const ( SIOCADDMULTI = 0x8931 SIOCADDRT = 0x890b SIOCATMARK = 0x40047307 + SIOCBONDCHANGEACTIVE = 0x8995 + SIOCBONDENSLAVE = 0x8990 + SIOCBONDINFOQUERY = 0x8994 + SIOCBONDRELEASE = 0x8991 + SIOCBONDSETHWADDR = 0x8992 + SIOCBONDSLAVEINFOQUERY = 0x8993 + SIOCBRADDBR = 0x89a0 + SIOCBRADDIF = 0x89a2 + SIOCBRDELBR = 0x89a1 + SIOCBRDELIF = 0x89a3 SIOCDARP = 0x8953 SIOCDELDLCI = 0x8981 SIOCDELMULTI = 0x8932 @@ -1422,7 +1432,9 @@ const ( SIOCDEVPRIVATE = 0x89f0 SIOCDIFADDR = 0x8936 SIOCDRARP = 0x8960 + SIOCETHTOOL = 0x8946 SIOCGARP = 0x8954 + SIOCGHWTSTAMP = 0x89b1 SIOCGIFADDR = 0x8915 SIOCGIFBR = 0x8940 SIOCGIFBRDADDR = 0x8919 @@ -1442,13 +1454,21 @@ const ( SIOCGIFPFLAGS = 0x8935 SIOCGIFSLAVE = 0x8929 SIOCGIFTXQLEN = 0x8942 + SIOCGIFVLAN = 0x8982 + SIOCGMIIPHY = 0x8947 + SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x40047309 SIOCGRARP = 0x8961 + SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 SIOCGSTAMPNS = 0x8907 + SIOCINQ = 0x467f + SIOCOUTQ = 0x7472 + SIOCOUTQNSD = 0x894b SIOCPROTOPRIVATE = 0x89e0 SIOCRTMSG = 0x890d SIOCSARP = 0x8955 + SIOCSHWTSTAMP = 0x89b0 SIOCSIFADDR = 0x8916 SIOCSIFBR = 0x8941 SIOCSIFBRDADDR = 0x891a @@ -1467,11 +1487,15 @@ const ( SIOCSIFPFLAGS = 0x8934 SIOCSIFSLAVE = 0x8930 SIOCSIFTXQLEN = 0x8943 + SIOCSIFVLAN = 0x8983 + SIOCSMIIREG = 0x8949 SIOCSPGRP = 0x80047308 SIOCSRARP = 0x8962 + SIOCWANDEV = 0x894a SOCK_CLOEXEC = 0x80000 SOCK_DCCP = 0x6 SOCK_DGRAM = 0x1 + SOCK_IOC_TYPE = 0x89 SOCK_NONBLOCK = 0x80 SOCK_PACKET = 0xa SOCK_RAW = 0x3 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go index b314601..72a0083 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go @@ -1415,6 +1415,16 @@ const ( SIOCADDMULTI = 0x8931 SIOCADDRT = 0x890b SIOCATMARK = 0x40047307 + SIOCBONDCHANGEACTIVE = 0x8995 + SIOCBONDENSLAVE = 0x8990 + SIOCBONDINFOQUERY = 0x8994 + SIOCBONDRELEASE = 0x8991 + SIOCBONDSETHWADDR = 0x8992 + SIOCBONDSLAVEINFOQUERY = 0x8993 + SIOCBRADDBR = 0x89a0 + SIOCBRADDIF = 0x89a2 + SIOCBRDELBR = 0x89a1 + SIOCBRDELIF = 0x89a3 SIOCDARP = 0x8953 SIOCDELDLCI = 0x8981 SIOCDELMULTI = 0x8932 @@ -1422,7 +1432,9 @@ const ( SIOCDEVPRIVATE = 0x89f0 SIOCDIFADDR = 0x8936 SIOCDRARP = 0x8960 + SIOCETHTOOL = 0x8946 SIOCGARP = 0x8954 + SIOCGHWTSTAMP = 0x89b1 SIOCGIFADDR = 0x8915 SIOCGIFBR = 0x8940 SIOCGIFBRDADDR = 0x8919 @@ -1442,13 +1454,21 @@ const ( SIOCGIFPFLAGS = 0x8935 SIOCGIFSLAVE = 0x8929 SIOCGIFTXQLEN = 0x8942 + SIOCGIFVLAN = 0x8982 + SIOCGMIIPHY = 0x8947 + SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x40047309 SIOCGRARP = 0x8961 + SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 SIOCGSTAMPNS = 0x8907 + SIOCINQ = 0x467f + SIOCOUTQ = 0x7472 + SIOCOUTQNSD = 0x894b SIOCPROTOPRIVATE = 0x89e0 SIOCRTMSG = 0x890d SIOCSARP = 0x8955 + SIOCSHWTSTAMP = 0x89b0 SIOCSIFADDR = 0x8916 SIOCSIFBR = 0x8941 SIOCSIFBRDADDR = 0x891a @@ -1467,11 +1487,15 @@ const ( SIOCSIFPFLAGS = 0x8934 SIOCSIFSLAVE = 0x8930 SIOCSIFTXQLEN = 0x8943 + SIOCSIFVLAN = 0x8983 + SIOCSMIIREG = 0x8949 SIOCSPGRP = 0x80047308 SIOCSRARP = 0x8962 + SIOCWANDEV = 0x894a SOCK_CLOEXEC = 0x80000 SOCK_DCCP = 0x6 SOCK_DGRAM = 0x1 + SOCK_IOC_TYPE = 0x89 SOCK_NONBLOCK = 0x80 SOCK_PACKET = 0xa SOCK_RAW = 0x3 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go index aa69fe6..84c0e3c 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go @@ -1415,6 +1415,16 @@ const ( SIOCADDMULTI = 0x8931 SIOCADDRT = 0x890b SIOCATMARK = 0x40047307 + SIOCBONDCHANGEACTIVE = 0x8995 + SIOCBONDENSLAVE = 0x8990 + SIOCBONDINFOQUERY = 0x8994 + SIOCBONDRELEASE = 0x8991 + SIOCBONDSETHWADDR = 0x8992 + SIOCBONDSLAVEINFOQUERY = 0x8993 + SIOCBRADDBR = 0x89a0 + SIOCBRADDIF = 0x89a2 + SIOCBRDELBR = 0x89a1 + SIOCBRDELIF = 0x89a3 SIOCDARP = 0x8953 SIOCDELDLCI = 0x8981 SIOCDELMULTI = 0x8932 @@ -1422,7 +1432,9 @@ const ( SIOCDEVPRIVATE = 0x89f0 SIOCDIFADDR = 0x8936 SIOCDRARP = 0x8960 + SIOCETHTOOL = 0x8946 SIOCGARP = 0x8954 + SIOCGHWTSTAMP = 0x89b1 SIOCGIFADDR = 0x8915 SIOCGIFBR = 0x8940 SIOCGIFBRDADDR = 0x8919 @@ -1442,13 +1454,21 @@ const ( SIOCGIFPFLAGS = 0x8935 SIOCGIFSLAVE = 0x8929 SIOCGIFTXQLEN = 0x8942 + SIOCGIFVLAN = 0x8982 + SIOCGMIIPHY = 0x8947 + SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x40047309 SIOCGRARP = 0x8961 + SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 SIOCGSTAMPNS = 0x8907 + SIOCINQ = 0x467f + SIOCOUTQ = 0x7472 + SIOCOUTQNSD = 0x894b SIOCPROTOPRIVATE = 0x89e0 SIOCRTMSG = 0x890d SIOCSARP = 0x8955 + SIOCSHWTSTAMP = 0x89b0 SIOCSIFADDR = 0x8916 SIOCSIFBR = 0x8941 SIOCSIFBRDADDR = 0x891a @@ -1467,11 +1487,15 @@ const ( SIOCSIFPFLAGS = 0x8934 SIOCSIFSLAVE = 0x8930 SIOCSIFTXQLEN = 0x8943 + SIOCSIFVLAN = 0x8983 + SIOCSMIIREG = 0x8949 SIOCSPGRP = 0x80047308 SIOCSRARP = 0x8962 + SIOCWANDEV = 0x894a SOCK_CLOEXEC = 0x80000 SOCK_DCCP = 0x6 SOCK_DGRAM = 0x1 + SOCK_IOC_TYPE = 0x89 SOCK_NONBLOCK = 0x80 SOCK_PACKET = 0xa SOCK_RAW = 0x3 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go index 6438fc8..8e4606e 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go @@ -1471,6 +1471,16 @@ const ( SIOCADDMULTI = 0x8931 SIOCADDRT = 0x890b SIOCATMARK = 0x8905 + SIOCBONDCHANGEACTIVE = 0x8995 + SIOCBONDENSLAVE = 0x8990 + SIOCBONDINFOQUERY = 0x8994 + SIOCBONDRELEASE = 0x8991 + SIOCBONDSETHWADDR = 0x8992 + SIOCBONDSLAVEINFOQUERY = 0x8993 + SIOCBRADDBR = 0x89a0 + SIOCBRADDIF = 0x89a2 + SIOCBRDELBR = 0x89a1 + SIOCBRDELIF = 0x89a3 SIOCDARP = 0x8953 SIOCDELDLCI = 0x8981 SIOCDELMULTI = 0x8932 @@ -1478,7 +1488,9 @@ const ( SIOCDEVPRIVATE = 0x89f0 SIOCDIFADDR = 0x8936 SIOCDRARP = 0x8960 + SIOCETHTOOL = 0x8946 SIOCGARP = 0x8954 + SIOCGHWTSTAMP = 0x89b1 SIOCGIFADDR = 0x8915 SIOCGIFBR = 0x8940 SIOCGIFBRDADDR = 0x8919 @@ -1498,13 +1510,21 @@ const ( SIOCGIFPFLAGS = 0x8935 SIOCGIFSLAVE = 0x8929 SIOCGIFTXQLEN = 0x8942 + SIOCGIFVLAN = 0x8982 + SIOCGMIIPHY = 0x8947 + SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x8904 SIOCGRARP = 0x8961 + SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 SIOCGSTAMPNS = 0x8907 + SIOCINQ = 0x4004667f + SIOCOUTQ = 0x40047473 + SIOCOUTQNSD = 0x894b SIOCPROTOPRIVATE = 0x89e0 SIOCRTMSG = 0x890d SIOCSARP = 0x8955 + SIOCSHWTSTAMP = 0x89b0 SIOCSIFADDR = 0x8916 SIOCSIFBR = 0x8941 SIOCSIFBRDADDR = 0x891a @@ -1523,11 +1543,15 @@ const ( SIOCSIFPFLAGS = 0x8934 SIOCSIFSLAVE = 0x8930 SIOCSIFTXQLEN = 0x8943 + SIOCSIFVLAN = 0x8983 + SIOCSMIIREG = 0x8949 SIOCSPGRP = 0x8902 SIOCSRARP = 0x8962 + SIOCWANDEV = 0x894a SOCK_CLOEXEC = 0x80000 SOCK_DCCP = 0x6 SOCK_DGRAM = 0x2 + SOCK_IOC_TYPE = 0x89 SOCK_NONBLOCK = 0x800 SOCK_PACKET = 0xa SOCK_RAW = 0x3 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go index 00c9942..16ed193 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go @@ -1471,6 +1471,16 @@ const ( SIOCADDMULTI = 0x8931 SIOCADDRT = 0x890b SIOCATMARK = 0x8905 + SIOCBONDCHANGEACTIVE = 0x8995 + SIOCBONDENSLAVE = 0x8990 + SIOCBONDINFOQUERY = 0x8994 + SIOCBONDRELEASE = 0x8991 + SIOCBONDSETHWADDR = 0x8992 + SIOCBONDSLAVEINFOQUERY = 0x8993 + SIOCBRADDBR = 0x89a0 + SIOCBRADDIF = 0x89a2 + SIOCBRDELBR = 0x89a1 + SIOCBRDELIF = 0x89a3 SIOCDARP = 0x8953 SIOCDELDLCI = 0x8981 SIOCDELMULTI = 0x8932 @@ -1478,7 +1488,9 @@ const ( SIOCDEVPRIVATE = 0x89f0 SIOCDIFADDR = 0x8936 SIOCDRARP = 0x8960 + SIOCETHTOOL = 0x8946 SIOCGARP = 0x8954 + SIOCGHWTSTAMP = 0x89b1 SIOCGIFADDR = 0x8915 SIOCGIFBR = 0x8940 SIOCGIFBRDADDR = 0x8919 @@ -1498,13 +1510,21 @@ const ( SIOCGIFPFLAGS = 0x8935 SIOCGIFSLAVE = 0x8929 SIOCGIFTXQLEN = 0x8942 + SIOCGIFVLAN = 0x8982 + SIOCGMIIPHY = 0x8947 + SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x8904 SIOCGRARP = 0x8961 + SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 SIOCGSTAMPNS = 0x8907 + SIOCINQ = 0x4004667f + SIOCOUTQ = 0x40047473 + SIOCOUTQNSD = 0x894b SIOCPROTOPRIVATE = 0x89e0 SIOCRTMSG = 0x890d SIOCSARP = 0x8955 + SIOCSHWTSTAMP = 0x89b0 SIOCSIFADDR = 0x8916 SIOCSIFBR = 0x8941 SIOCSIFBRDADDR = 0x891a @@ -1523,11 +1543,15 @@ const ( SIOCSIFPFLAGS = 0x8934 SIOCSIFSLAVE = 0x8930 SIOCSIFTXQLEN = 0x8943 + SIOCSIFVLAN = 0x8983 + SIOCSMIIREG = 0x8949 SIOCSPGRP = 0x8902 SIOCSRARP = 0x8962 + SIOCWANDEV = 0x894a SOCK_CLOEXEC = 0x80000 SOCK_DCCP = 0x6 SOCK_DGRAM = 0x2 + SOCK_IOC_TYPE = 0x89 SOCK_NONBLOCK = 0x800 SOCK_PACKET = 0xa SOCK_RAW = 0x3 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go index 89674f3..bd385f8 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go @@ -1475,6 +1475,16 @@ const ( SIOCADDMULTI = 0x8931 SIOCADDRT = 0x890b SIOCATMARK = 0x8905 + SIOCBONDCHANGEACTIVE = 0x8995 + SIOCBONDENSLAVE = 0x8990 + SIOCBONDINFOQUERY = 0x8994 + SIOCBONDRELEASE = 0x8991 + SIOCBONDSETHWADDR = 0x8992 + SIOCBONDSLAVEINFOQUERY = 0x8993 + SIOCBRADDBR = 0x89a0 + SIOCBRADDIF = 0x89a2 + SIOCBRDELBR = 0x89a1 + SIOCBRDELIF = 0x89a3 SIOCDARP = 0x8953 SIOCDELDLCI = 0x8981 SIOCDELMULTI = 0x8932 @@ -1482,7 +1492,9 @@ const ( SIOCDEVPRIVATE = 0x89f0 SIOCDIFADDR = 0x8936 SIOCDRARP = 0x8960 + SIOCETHTOOL = 0x8946 SIOCGARP = 0x8954 + SIOCGHWTSTAMP = 0x89b1 SIOCGIFADDR = 0x8915 SIOCGIFBR = 0x8940 SIOCGIFBRDADDR = 0x8919 @@ -1502,13 +1514,21 @@ const ( SIOCGIFPFLAGS = 0x8935 SIOCGIFSLAVE = 0x8929 SIOCGIFTXQLEN = 0x8942 + SIOCGIFVLAN = 0x8982 + SIOCGMIIPHY = 0x8947 + SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x8904 SIOCGRARP = 0x8961 + SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 SIOCGSTAMPNS = 0x8907 + SIOCINQ = 0x541b + SIOCOUTQ = 0x5411 + SIOCOUTQNSD = 0x894b SIOCPROTOPRIVATE = 0x89e0 SIOCRTMSG = 0x890d SIOCSARP = 0x8955 + SIOCSHWTSTAMP = 0x89b0 SIOCSIFADDR = 0x8916 SIOCSIFBR = 0x8941 SIOCSIFBRDADDR = 0x891a @@ -1527,11 +1547,15 @@ const ( SIOCSIFPFLAGS = 0x8934 SIOCSIFSLAVE = 0x8930 SIOCSIFTXQLEN = 0x8943 + SIOCSIFVLAN = 0x8983 + SIOCSMIIREG = 0x8949 SIOCSPGRP = 0x8902 SIOCSRARP = 0x8962 + SIOCWANDEV = 0x894a SOCK_CLOEXEC = 0x80000 SOCK_DCCP = 0x6 SOCK_DGRAM = 0x2 + SOCK_IOC_TYPE = 0x89 SOCK_NONBLOCK = 0x800 SOCK_PACKET = 0xa SOCK_RAW = 0x3 diff --git a/vendor/qiniupkg.com/api.v7/.travis.yml b/vendor/qiniupkg.com/api.v7/.travis.yml index eefb670..d2e2d07 100644 --- a/vendor/qiniupkg.com/api.v7/.travis.yml +++ b/vendor/qiniupkg.com/api.v7/.travis.yml @@ -1,16 +1,19 @@ language: go -before_script: - - export QINIU_KODO_TEST=1 - - export QINIU_ACCESS_KEY="QWYn5TFQsLLU1pL5MFEmX3s5DmHdUThav9WyOWOm" - - export QINIU_SECRET_KEY="Bxckh6FA-Fbs9Yt3i3cbKVK22UPBmAOHJcL95pGz" - - export QINIU_TEST_BUCKET="gosdk" - - export QINIU_TEST_DOMAIN="gosdk.qiniudn.com" +go: + - 1.7 + - 1.8 + - master +env: + global: + - QINIU_KODO_TEST=1 + - QINIU_ACCESS_KEY="QWYn5TFQsLLU1pL5MFEmX3s5DmHdUThav9WyOWOm" + - QINIU_SECRET_KEY="Bxckh6FA-Fbs9Yt3i3cbKVK22UPBmAOHJcL95pGz" + - QINIU_TEST_BUCKET="gosdk" + - QINIU_TEST_DOMAIN="gosdk.qiniudn.com" + install: - export QINIU_SRC=$HOME/gopath/src - - mkdir -p $QINIU_SRC/qiniupkg.com - - mv $QINIU_SRC/github.com/qiniu/api.v7 $QINIU_SRC/qiniupkg.com/api.v7 - - git clone --depth=50 git://github.com/qiniu/x.git $QINIU_SRC/qiniupkg.com/x - - export TRAVIS_BUILD_DIR=$QINIU_SRC/qiniupkg.com/api.v7 + - mkdir -p $QINIU_SRC/github.com/qiniu + - export TRAVIS_BUILD_DIR=$QINIU_SRC/github.com/qiniu/api.v7 - cd $TRAVIS_BUILD_DIR - - go get golang.org/x/net/context - + - go get github.com/qiniu/x \ No newline at end of file diff --git a/vendor/qiniupkg.com/api.v7/README.md b/vendor/qiniupkg.com/api.v7/README.md index def2682..4c9bbd5 100644 --- a/vendor/qiniupkg.com/api.v7/README.md +++ b/vendor/qiniupkg.com/api.v7/README.md @@ -1,14 +1,14 @@ -qiniupkg.com/api.v7 (Qiniu Go SDK v7.x) +github.com/qiniu/api.v7 (Qiniu Go SDK v7.x) =============== -[![Build Status](https://travis-ci.org/qiniu/api.v7.svg?branch=develop)](https://travis-ci.org/qiniu/api.v7) [![GoDoc](https://godoc.org/qiniupkg.com/api.v7?status.svg)](https://godoc.org/qiniupkg.com/api.v7) +[![Build Status](https://travis-ci.org/qiniu/api.v7.svg?branch=develop)](https://travis-ci.org/qiniu/api.v7) [![GoDoc](https://godoc.org/github.com/qiniu/api.v7?status.svg)](https://godoc.org/github.com/qiniu/api.v7) [![Qiniu Logo](http://open.qiniudn.com/logo.png)](http://qiniu.com/) # 下载 ``` -go get -u qiniupkg.com/api.v7 +go get -u github.com/qiniu/api.v7 ``` 如果碰到golang.org/x/net/context 不能下载,请把 http://devtools.qiniu.com/golang.org.x.net.context.tgz 下载到代码目录下并解压到src目录,或者直接下载全部 http://devtools.qiniu.com/qiniu_api_v7.tgz。 @@ -16,10 +16,10 @@ go get -u qiniupkg.com/api.v7 ## KODO Blob Storage (七牛对象存储) -* [qiniupkg.com/api.v7/kodo](http://godoc.org/qiniupkg.com/api.v7/kodo) -* [qiniupkg.com/api.v7/kodocli](http://godoc.org/qiniupkg.com/api.v7/kodocli) +* [github.com/qiniu/api.v7/kodo](http://godoc.org/github.com/qiniu/api.v7/kodo) +* [github.com/qiniu/api.v7/kodocli](http://godoc.org/github.com/qiniu/api.v7/kodocli) -如果您是在业务服务器(服务器端)调用七牛云存储的服务,请使用 [qiniupkg.com/api.v7/kodo](http://godoc.org/qiniupkg.com/api.v7/kodo)。 +如果您是在业务服务器(服务器端)调用七牛云存储的服务,请使用 [github.com/qiniu/api.v7/kodo](http://godoc.org/github.com/qiniu/api.v7/kodo)。 -如果您是在客户端(比如:Android/iOS 设备、Windows/Mac/Linux 桌面环境)调用七牛云存储的服务,请使用 [qiniupkg.com/api.v7/kodocli](http://godoc.org/qiniupkg.com/api.v7/kodocli)。注意,在这种场合下您不应该在任何地方配置 AccessKey/SecretKey。泄露 AccessKey/SecretKey 如同泄露您的用户名/密码一样十分危险,会影响您的数据安全。 +如果您是在客户端(比如:Android/iOS 设备、Windows/Mac/Linux 桌面环境)调用七牛云存储的服务,请使用 [github.com/qiniu/api.v7/kodocli](http://godoc.org/github.com/qiniu/api.v7/kodocli)。注意,在这种场合下您不应该在任何地方配置 AccessKey/SecretKey。泄露 AccessKey/SecretKey 如同泄露您的用户名/密码一样十分危险,会影响您的数据安全。 diff --git a/vendor/qiniupkg.com/api.v7/api/api.go b/vendor/qiniupkg.com/api.v7/api/api.go index d2cedf4..4943d45 100644 --- a/vendor/qiniupkg.com/api.v7/api/api.go +++ b/vendor/qiniupkg.com/api.v7/api/api.go @@ -1,13 +1,11 @@ package api import ( + . "context" + "sync" "time" - "qiniupkg.com/x/rpc.v7" - - "sync" - - . "golang.org/x/net/context" + "github.com/qiniu/x/rpc.v7" ) const DefaultApiHost string = "http://uc.qbox.me" diff --git a/vendor/qiniupkg.com/api.v7/auth/qbox/qbox_auth.go b/vendor/qiniupkg.com/api.v7/auth/qbox/qbox_auth.go index 5f54a48..e8aa481 100644 --- a/vendor/qiniupkg.com/api.v7/auth/qbox/qbox_auth.go +++ b/vendor/qiniupkg.com/api.v7/auth/qbox/qbox_auth.go @@ -7,8 +7,8 @@ import ( "io" "net/http" - . "qiniupkg.com/api.v7/conf" - "qiniupkg.com/x/bytes.v7/seekable" + . "github.com/qiniu/api.v7/conf" + "github.com/qiniu/x/bytes.v7/seekable" ) // ---------------------------------------------------------- diff --git a/vendor/qiniupkg.com/api.v7/cdn/api.go b/vendor/qiniupkg.com/api.v7/cdn/api.go index b1d0c03..6ea5d35 100644 --- a/vendor/qiniupkg.com/api.v7/cdn/api.go +++ b/vendor/qiniupkg.com/api.v7/cdn/api.go @@ -8,8 +8,8 @@ import ( "net/http" "strings" - "qiniupkg.com/api.v7/auth/qbox" - . "qiniupkg.com/api.v7/conf" + "github.com/qiniu/api.v7/auth/qbox" + . "github.com/qiniu/api.v7/conf" ) var ( diff --git a/vendor/qiniupkg.com/api.v7/cdn/api_test.go b/vendor/qiniupkg.com/api.v7/cdn/api_test.go index d917823..ecd23a9 100644 --- a/vendor/qiniupkg.com/api.v7/cdn/api_test.go +++ b/vendor/qiniupkg.com/api.v7/cdn/api_test.go @@ -9,7 +9,7 @@ import ( "time" - "qiniupkg.com/api.v7/kodo" + "github.com/qiniu/api.v7/kodo" ) var ( diff --git a/vendor/qiniupkg.com/api.v7/cdn/doc.go b/vendor/qiniupkg.com/api.v7/cdn/doc.go index 1cd1934..ece1769 100644 --- a/vendor/qiniupkg.com/api.v7/cdn/doc.go +++ b/vendor/qiniupkg.com/api.v7/cdn/doc.go @@ -1,21 +1,21 @@ /* -包qiniupkg.com/api.v7/fusion提供了七牛CDN的API功能 +包 github.com/qiniu/api.v7/cdn 提供了七牛CDN的API功能 首先,我们要配置下 AccessKey/SecretKey, - import "qiniupkg.com/api.v7/kodo" + import "github.com/qiniu/api.v7/kodo" kodo.SetMac("ak", "sk") 设置了AccessKey/SecretKey 就可以使用cdn的各类功能 比如我们要生成一个带时间戳防盗链的链接: q :=url.Values{}// url.Values 请求参数 - link, err := cdn.CreateTimestampAntiLeechUrl("http://www.qiniu.com", "abc/bcc/aa-s.mp4", nil, "encryptedkey", 20) + link, err := cdn.CreateTimestampAntileechURL(""http://www.qiniu.com/abc/bcc/aa-s.mp4?x=2&y=3", "encryptedkey", 20) if err != nil { fmt.Println(err) } fmt.Println(link) 又或者我们要列出CDN日志及其下载地址: - resp, err := cdn.GetLogOfDomain("2016-12-26", "x-mas.com") + resp, err := cdn.GetCdnLogList("2016-12-26", "x-mas.com") if err != nil { fmt.Println(err) } diff --git a/vendor/qiniupkg.com/api.v7/cdn/logs.go b/vendor/qiniupkg.com/api.v7/cdn/logs.go index 92a743e..1b640f3 100644 --- a/vendor/qiniupkg.com/api.v7/cdn/logs.go +++ b/vendor/qiniupkg.com/api.v7/cdn/logs.go @@ -7,7 +7,7 @@ import ( "net/http" "strings" - "qiniupkg.com/api.v7/auth/qbox" + "github.com/qiniu/api.v7/auth/qbox" ) const ( diff --git a/vendor/qiniupkg.com/api.v7/cdn/logs_test.go b/vendor/qiniupkg.com/api.v7/cdn/logs_test.go index 5c4cdca..58870d7 100644 --- a/vendor/qiniupkg.com/api.v7/cdn/logs_test.go +++ b/vendor/qiniupkg.com/api.v7/cdn/logs_test.go @@ -5,7 +5,7 @@ import ( "reflect" "testing" - "qiniupkg.com/api.v7/kodo" + "github.com/qiniu/api.v7/kodo" ) func init() { diff --git a/vendor/qiniupkg.com/api.v7/conf/conf.go b/vendor/qiniupkg.com/api.v7/conf/conf.go index fd81d95..d08a8d0 100644 --- a/vendor/qiniupkg.com/api.v7/conf/conf.go +++ b/vendor/qiniupkg.com/api.v7/conf/conf.go @@ -5,8 +5,8 @@ import ( "runtime" "syscall" - "qiniupkg.com/x/ctype.v7" - "qiniupkg.com/x/rpc.v7" + "github.com/qiniu/x/ctype.v7" + "github.com/qiniu/x/rpc.v7" ) var version = "7.1.0" @@ -36,4 +36,3 @@ func init() { } // ---------------------------------------------------------- - diff --git a/vendor/qiniupkg.com/api.v7/conf/conf_test.go b/vendor/qiniupkg.com/api.v7/conf/conf_test.go index 1fafbec..a68c0ec 100644 --- a/vendor/qiniupkg.com/api.v7/conf/conf_test.go +++ b/vendor/qiniupkg.com/api.v7/conf/conf_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "qiniupkg.com/x/rpc.v7" + "github.com/qiniu/x/rpc.v7" ) func TestUA(t *testing.T) { diff --git a/vendor/qiniupkg.com/api.v7/doc.go b/vendor/qiniupkg.com/api.v7/doc.go index 0403228..6f0d9bd 100644 --- a/vendor/qiniupkg.com/api.v7/doc.go +++ b/vendor/qiniupkg.com/api.v7/doc.go @@ -1,23 +1,23 @@ /* -包 qiniupkg.com/api.v7 是七牛 Go 语言 SDK v7.x 版本 +包 github.com/qiniu/api.v7 是七牛 Go 语言 SDK v7.x 版本 七牛对象存储,我们取了一个好听的名字,叫 KODO Blob Storage。要使用它,你主要和以下两个包打交道: - import "qiniupkg.com/api.v7/kodo" - import "qiniupkg.com/api.v7/kodocli" + import "github.com/qiniu/api.v7/kodo" + import "github.com/qiniu/api.v7/kodocli" -如果您是在业务服务器(服务器端)调用七牛云存储的服务,请使用 qiniupkg.com/api.v7/kodo。 +如果您是在业务服务器(服务器端)调用七牛云存储的服务,请使用 github.com/qiniu/api.v7/kodo。 -如果您是在客户端(比如:Android/iOS 设备、Windows/Mac/Linux 桌面环境)调用七牛云存储的服务,请使用 qiniupkg.com/api.v7/kodocli。 +如果您是在客户端(比如:Android/iOS 设备、Windows/Mac/Linux 桌面环境)调用七牛云存储的服务,请使用 github.com/qiniu/api.v7/kodocli。 注意,在这种场合下您不应该在任何地方配置 AccessKey/SecretKey。泄露 AccessKey/SecretKey 如同泄露您的用户名/密码一样十分危险, 会影响您的数据安全。 */ package api import ( - _ "qiniupkg.com/api.v7/auth/qbox" - _ "qiniupkg.com/api.v7/conf" - _ "qiniupkg.com/api.v7/kodo" - _ "qiniupkg.com/api.v7/kodocli" + _ "github.com/qiniu/api.v7/auth/qbox" + _ "github.com/qiniu/api.v7/cdn" + _ "github.com/qiniu/api.v7/conf" + _ "github.com/qiniu/api.v7/kodo" + _ "github.com/qiniu/api.v7/kodocli" ) - diff --git a/vendor/qiniupkg.com/api.v7/kodo/bucket.go b/vendor/qiniupkg.com/api.v7/kodo/bucket.go index c90bfa0..9692d4f 100644 --- a/vendor/qiniupkg.com/api.v7/kodo/bucket.go +++ b/vendor/qiniupkg.com/api.v7/kodo/bucket.go @@ -1,20 +1,20 @@ package kodo import ( + . "context" "encoding/base64" "fmt" "io" "net/url" "strconv" - . "golang.org/x/net/context" - "qiniupkg.com/api.v7/api" - "qiniupkg.com/x/log.v7" + "github.com/qiniu/api.v7/api" + "github.com/qiniu/x/log.v7" ) // ---------------------------------------------------------- -// 批量操作。 +// Batch 批量操作。 // func (p *Client) Batch(ctx Context, ret interface{}, op []string) (err error) { @@ -29,7 +29,20 @@ type Bucket struct { Name string } -// 取七牛空间(bucket)的对象实例。 +// Buckets 获取所有地区的所有空间(bucket) +// +// shared 是否获取所有授权获得空间,true为包含授权空间 +// +func (p *Client) Buckets(ctx Context, shared bool) (buckets []string, err error) { + if shared { + err = p.Call(ctx, &buckets, "POST", p.RSHost+"/buckets?shared=trye") + } else { + err = p.Call(ctx, &buckets, "POST", p.RSHost+"/buckets") + } + return +} + +// Bucket 取七牛空间(bucket)的对象实例。 // // name 是创建该七牛空间(bucket)时采用的名称。 // @@ -41,6 +54,7 @@ func (p *Client) Bucket(name string) Bucket { return b } +// BucketWithSafe 确认空间存在并获取七牛空间(bucket)的对象实例。 func (p *Client) BucketWithSafe(name string) (Bucket, error) { var info api.BucketInfo if len(p.UpHosts) == 0 { @@ -56,6 +70,7 @@ func (p *Client) BucketWithSafe(name string) (Bucket, error) { return Bucket{info, p, name}, nil } +// Entry 资源元信息 type Entry struct { Hash string `json:"hash"` Fsize int64 `json:"fsize"` @@ -65,7 +80,7 @@ type Entry struct { EndUser string `json:"endUser"` } -// 取文件属性。 +// Stat 取文件属性。 // // ctx 是请求的上下文。 // key 是要访问的文件的访问路径。 @@ -75,7 +90,7 @@ func (p Bucket) Stat(ctx Context, key string) (entry Entry, err error) { return } -// 删除一个文件。 +// Delete 删除一个文件。 // // ctx 是请求的上下文。 // key 是要删除的文件的访问路径。 @@ -84,7 +99,7 @@ func (p Bucket) Delete(ctx Context, key string) (err error) { return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIDelete(p.Name, key)) } -// 移动一个文件。 +// Move 移动一个文件。 // // ctx 是请求的上下文。 // keySrc 是要移动的文件的旧路径。 @@ -94,7 +109,7 @@ func (p Bucket) Move(ctx Context, keySrc, keyDest string) (err error) { return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIMove(p.Name, keySrc, p.Name, keyDest)) } -// 跨空间(bucket)移动一个文件。 +// MoveEx 跨空间(bucket)移动一个文件。 // // ctx 是请求的上下文。 // keySrc 是要移动的文件的旧路径。 @@ -105,7 +120,7 @@ func (p Bucket) MoveEx(ctx Context, keySrc, bucketDest, keyDest string) (err err return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIMove(p.Name, keySrc, bucketDest, keyDest)) } -// 复制一个文件。 +// Copy 复制一个文件。 // // ctx 是请求的上下文。 // keySrc 是要复制的文件的源路径。 @@ -115,7 +130,7 @@ func (p Bucket) Copy(ctx Context, keySrc, keyDest string) (err error) { return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URICopy(p.Name, keySrc, p.Name, keyDest)) } -// 修改文件的MIME类型。 +// ChangeMime 修改文件的MIME类型。 // // ctx 是请求的上下文。 // key 是要修改的文件的访问路径。 @@ -125,17 +140,17 @@ func (p Bucket) ChangeMime(ctx Context, key, mime string) (err error) { return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIChangeMime(p.Name, key, mime)) } -// 修改文件的存储类型。 +// ChangeType 修改文件的存储类型。 // // ctx 是请求的上下文。 // key 是要修改的文件的访问路径。 -// fileType 是要设置的新存储类型。 +// fileType 是要设置的新存储类型。0 表示标准存储;1 表示低频存储。 // func (p Bucket) ChangeType(ctx Context, key string, fileType int) (err error) { return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIChangeType(p.Name, key, fileType)) } -// 从网上抓取一个资源并存储到七牛空间(bucket)中。 +// Fetch 从网上抓取一个资源并存储到七牛空间(bucket)中。 // // ctx 是请求的上下文。 // key 是要存储的文件的访问路径。如果文件已经存在则覆盖。 @@ -145,7 +160,7 @@ func (p Bucket) Fetch(ctx Context, key string, url string) (err error) { return p.Conn.Call(ctx, nil, "POST", p.IoHost+uriFetch(p.Name, key, url)) } -// 更新文件生命周期 +// DeleteAfterDays 更新文件生命周期 // // ctx 是请求的上下文。 // key 是要更新的文件的访问路径。 @@ -155,8 +170,104 @@ func (p Bucket) DeleteAfterDays(ctx Context, key string, days int) (err error) { return p.Conn.Call(ctx, nil, "POST", p.Conn.RSHost+URIDeleteAfterDays(p.Name, key, days)) } +// Image 设置镜像源 +// +// srcSiteURL 镜像源的访问域名。必须设置为形如 `http://source.com/` 或 `http://114.114.114.114/` 的字符串 +// host 回源时使用的 Host 头部值 +// +// 镜像源地址支持两种格式: +// 格式 1:`http(s)://绑定域名/源站资源相对路径` +// 格式 2:`http(s)://绑定 IP/源站资源相对路径` +// +func (p Bucket) Image(ctx Context, srcSiteURL, host string) (err error) { + return p.Conn.Call(ctx, nil, "POST", "http://pu.qbox.me:10200"+URIImage(p.Name, srcSiteURL, host)) +} + +// UnImage 取消镜像源 +// +func (p Bucket) UnImage(ctx Context) (err error) { + return p.Conn.Call(ctx, nil, "POST", "http://pu.qbox.me:10200"+URIUnImage(p.Name)) +} + +// Prefetch 镜像资源更新 +// +// key 被抓取资源名称 +// +func (p Bucket) Prefetch(ctx Context, key string) (err error) { + return p.Conn.Call(ctx, nil, "POST", p.Conn.IoHost+URIPrefetch(p.Name, key)) +} + +// PfopResult pfop返回信息 +type PfopResult struct { + PersistentID string `json:"persistentId,omitempty"` +} + +// FopRet 持久化云处理结果 +type FopRet struct { + ID string `json:"id"` + Code int `json:"code"` + Desc string `json:"desc"` + InputBucket string `json:"inputBucket,omitempty"` + InputKey string `json:"inputKey,omitempty"` + Pipeline string `json:"pipeline,omitempty"` + Reqid string `json:"reqid,omitempty"` + Items []FopResult +} + +// FopResult 云处理操作列表,包含每个云处理操作的状态信息 +type FopResult struct { + Cmd string `json:"cmd"` + Code int `json:"code"` + Desc string `json:"desc"` + Error string `json:"error,omitempty"` + Hash string `json:"hash,omitempty"` + Key string `json:"key,omitempty"` + Keys []string `json:"keys,omitempty"` +} + +// Pfop 持久化数据处理 +// +// bucket 资源空间 +// key 源资源名 +// fops 云处理操作列表,用`;``分隔,如:`avthumb/flv;saveas/cWJ1Y2tldDpxa2V5`,是将上传的视频文件转码成flv格式后存储为 qbucket:qkey ,其中 cWJ1Y2tldDpxa2V5 是 qbucket:qkey 的URL安全的Base64编码结果。 +// notifyURL 处理结果通知接收 URL,七牛将会向你设置的 URL 发起 Content-Type: application/json 的 POST 请求。 +// pipeline 为空则表示使用公用队列,处理速度比较慢。建议指定私有队列,转码的时候使用独立的计算资源。 +// force 强制执行数据处理。当服务端发现 fops 指定的数据处理结果已经存在,那就认为已经处理成功,避免重复处理浪费资源。本字段设为 `true`,则可强制执行数据处理并覆盖原结果。 +// +func (p *Client) Pfop(ctx Context, bucket, key, fops, notifyURL, pipeline string, force bool) (persistentID string, err error) { + pfopParams := map[string][]string{ + "bucket": []string{bucket}, + "key": []string{key}, + "fops": []string{fops}, + } + if notifyURL != "" { + pfopParams["notifyURL"] = []string{notifyURL} + } + if pipeline != "" { + pfopParams["pipeline"] = []string{pipeline} + } + if force { + pfopParams["force"] = []string{"1"} + } + var ret PfopResult + err = p.CallWithForm(ctx, &ret, "POST", "http://api.qiniu.com/pfop/", pfopParams) + if err != nil { + return + } + + persistentID = ret.PersistentID + return +} + +// Prefop 持久化处理状态查询 +func (p *Client) Prefop(ctx Context, persistentID string) (ret FopRet, err error) { + err = p.Call(ctx, &ret, "GET", "http://api.qiniu.com/status/get/prefop?id="+persistentID) + return +} + // ---------------------------------------------------------- +// ListItem List借口返回结果 type ListItem struct { Key string `json:"key"` Hash string `json:"hash"` @@ -166,7 +277,7 @@ type ListItem struct { EndUser string `json:"endUser"` } -// 首次请求,请将 marker 设置为 ""。 +// List 首次请求,请将 marker 设置为 ""。 // 无论 err 值如何,均应该先看 entries 是否有内容。 // 如果后续没有更多数据,err 返回 EOF,markerOut 返回 ""(但不通过该特征来判断是否结束)。 // @@ -217,6 +328,7 @@ type BatchStatItemRet struct { Code int `json:"code"` } +// BatchStat 批量取文件属性 func (p Bucket) BatchStat(ctx Context, keys ...string) (ret []BatchStatItemRet, err error) { b := make([]string, len(keys)) @@ -232,6 +344,7 @@ type BatchItemRet struct { Code int `json:"code"` } +// BatchDelete 批量删除 func (p Bucket) BatchDelete(ctx Context, keys ...string) (ret []BatchItemRet, err error) { b := make([]string, len(keys)) @@ -247,6 +360,7 @@ type KeyPair struct { Dest string } +// BatchMove 批量移动文件 func (p Bucket) BatchMove(ctx Context, entries ...KeyPair) (ret []BatchItemRet, err error) { b := make([]string, len(entries)) @@ -257,6 +371,7 @@ func (p Bucket) BatchMove(ctx Context, entries ...KeyPair) (ret []BatchItemRet, return } +// BatchCopy 批量复制文件 func (p Bucket) BatchCopy(ctx Context, entries ...KeyPair) (ret []BatchItemRet, err error) { b := make([]string, len(entries)) @@ -305,4 +420,16 @@ func URIDeleteAfterDays(bucket, key string, days int) string { return fmt.Sprintf("/deleteAfterDays/%s/%d", encodeURI(bucket+":"+key), days) } +func URIImage(bucket, srcSiteURL, host string) string { + return fmt.Sprintf("/image/%s/from/%s/host/%s", bucket, encodeURI(srcSiteURL), encodeURI(host)) +} + +func URIUnImage(bucket string) string { + return fmt.Sprintf("/unimage/%s", bucket) +} + +func URIPrefetch(bucket, key string) string { + return fmt.Sprintf("/prefetch/%s", encodeURI(bucket+":"+key)) +} + // ---------------------------------------------------------- diff --git a/vendor/qiniupkg.com/api.v7/kodo/doc.go b/vendor/qiniupkg.com/api.v7/kodo/doc.go index 6c9914d..a18e7de 100644 --- a/vendor/qiniupkg.com/api.v7/kodo/doc.go +++ b/vendor/qiniupkg.com/api.v7/kodo/doc.go @@ -1,5 +1,5 @@ /* -包 qiniupkg.com/api.v7/kodo 提供了在您的业务服务器(服务端)调用七牛云存储服务的能力 +包 github.com/qiniu/api.v7/kodo 提供了在您的业务服务器(服务端)调用七牛云存储服务的能力 首先,我们要配置下 AccessKey/SecretKey,这可以在七牛 Portal 中查到: @@ -7,7 +7,7 @@ 然后我们创建一个 Client 对象: - zone := 0 // 您空间(Bucket)所在的区域 + zone := kodo.ZoneZ0 // 您空间(Bucket)所在的区域 c := kodo.New(zone, nil) // 用默认配置创建 Client 有了 Client,你就可以操作您的空间(Bucket)了,比如我们要上传一个文件: diff --git a/vendor/qiniupkg.com/api.v7/kodo/main.go b/vendor/qiniupkg.com/api.v7/kodo/main.go index 9358449..dc09200 100644 --- a/vendor/qiniupkg.com/api.v7/kodo/main.go +++ b/vendor/qiniupkg.com/api.v7/kodo/main.go @@ -3,10 +3,10 @@ package kodo import ( "net/http" - "qiniupkg.com/api.v7/api" - "qiniupkg.com/api.v7/auth/qbox" - "qiniupkg.com/api.v7/conf" - "qiniupkg.com/x/rpc.v7" + "github.com/qiniu/api.v7/api" + "github.com/qiniu/api.v7/auth/qbox" + "github.com/qiniu/api.v7/conf" + "github.com/qiniu/x/rpc.v7" ) // ---------------------------------------------------------- @@ -16,6 +16,17 @@ type zoneConfig struct { UpHosts []string } +const ( + // ZoneZ0 华东机房 + ZoneZ0 = iota + // ZoneZ1 华北机房 + ZoneZ1 + // ZoneZ2 华南机房 + ZoneZ2 + // ZoneNa0 北美机房 + ZoneNa0 +) + var zones = []zoneConfig{ // z0 华东机房: { diff --git a/vendor/qiniupkg.com/api.v7/kodo/main_test.go b/vendor/qiniupkg.com/api.v7/kodo/main_test.go index b025d98..3172a31 100644 --- a/vendor/qiniupkg.com/api.v7/kodo/main_test.go +++ b/vendor/qiniupkg.com/api.v7/kodo/main_test.go @@ -40,7 +40,7 @@ func newBucket() (bucket Bucket) { QINIU_KODO_TEST = os.Getenv("QINIU_KODO_TEST") if skipTest() { - println("[INFO] QINIU_KODO_TEST: skipping to test qiniupkg.com/api.v7") + println("[INFO] QINIU_KODO_TEST: skipping to test github.com/qiniu/api.v7") return } diff --git a/vendor/qiniupkg.com/api.v7/kodo/token.go b/vendor/qiniupkg.com/api.v7/kodo/token.go index 7d82fcd..7cae521 100644 --- a/vendor/qiniupkg.com/api.v7/kodo/token.go +++ b/vendor/qiniupkg.com/api.v7/kodo/token.go @@ -7,9 +7,9 @@ import ( "strings" "time" - "qiniupkg.com/api.v7/api" - "qiniupkg.com/api.v7/auth/qbox" - "qiniupkg.com/x/url.v7" + "github.com/qiniu/api.v7/api" + "github.com/qiniu/api.v7/auth/qbox" + "github.com/qiniu/x/url.v7" ) // ---------------------------------------------------------- diff --git a/vendor/qiniupkg.com/api.v7/kodo/upload.go b/vendor/qiniupkg.com/api.v7/kodo/upload.go index ce8cfd1..eda8922 100644 --- a/vendor/qiniupkg.com/api.v7/kodo/upload.go +++ b/vendor/qiniupkg.com/api.v7/kodo/upload.go @@ -1,13 +1,12 @@ package kodo import ( + . "context" "io" "net/http" - "qiniupkg.com/api.v7/kodocli" - "qiniupkg.com/x/rpc.v7" - - . "golang.org/x/net/context" + "github.com/qiniu/api.v7/kodocli" + "github.com/qiniu/x/rpc.v7" ) type PutExtra kodocli.PutExtra @@ -44,7 +43,7 @@ func (p Bucket) makeUploader() kodocli.Uploader { // ---------------------------------------------------------- -// 上传一个文件。 +// Put 上传一个文件。 // // ctx 是请求的上下文。 // ret 是上传成功后返回的数据。返回的是 PutRet 结构。可选,可以传 nil 表示不感兴趣。 @@ -61,7 +60,7 @@ func (p Bucket) Put( return uploader.Put(ctx, ret, uptoken, key, data, size, (*kodocli.PutExtra)(extra)) } -// 上传一个文件。自动以文件的 hash 作为文件的访问路径(key)。 +// PutWithoutKey 上传一个文件。自动以文件的 hash 作为文件的访问路径(key)。 // // ctx 是请求的上下文。 // ret 是上传成功后返回的数据。返回的是 PutRet 结构。可选,可以传 nil 表示不感兴趣。 @@ -77,7 +76,7 @@ func (p Bucket) PutWithoutKey( return uploader.PutWithoutKey(ctx, ret, uptoken, data, size, (*kodocli.PutExtra)(extra)) } -// 上传一个文件。 +// PutFile 上传一个文件。 // 和 Put 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.Reader 来访问。 // // ctx 是请求的上下文。 @@ -93,7 +92,7 @@ func (p Bucket) PutFile( return uploader.PutFile(ctx, ret, uptoken, key, localFile, (*kodocli.PutExtra)(extra)) } -// 上传一个文件。自动以文件的 hash 作为文件的访问路径(key)。 +// PutFileWithoutKey 上传一个文件。自动以文件的 hash 作为文件的访问路径(key)。 // 和 PutWithoutKey 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.Reader 来访问。 // // ctx 是请求的上下文。 @@ -111,7 +110,7 @@ func (p Bucket) PutFileWithoutKey( // ---------------------------------------------------------- -// 上传一个文件,支持断点续传和分块上传。 +// Rput 上传一个文件,支持断点续传和分块上传。 // // ctx 是请求的上下文。 // ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody,那么返回的数据结构是 PutRet 结构。 @@ -128,7 +127,7 @@ func (p Bucket) Rput( return uploader.Rput(ctx, ret, uptoken, key, data, size, (*kodocli.RputExtra)(extra)) } -// 上传一个文件,支持断点续传和分块上传。自动以文件的 hash 作为文件的访问路径(key)。 +// RputWithoutKey 上传一个文件,支持断点续传和分块上传。自动以文件的 hash 作为文件的访问路径(key)。 // // ctx 是请求的上下文。 // ret 是上传成功后返回的数据。如果 uptoken 中没有设置 CallbackUrl 或 ReturnBody,那么返回的数据结构是 PutRet 结构。 @@ -144,7 +143,7 @@ func (p Bucket) RputWithoutKey( return uploader.RputWithoutKey(ctx, ret, uptoken, data, size, (*kodocli.RputExtra)(extra)) } -// 上传一个文件,支持断点续传和分块上传。 +// RputFile 上传一个文件,支持断点续传和分块上传。 // 和 Rput 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.ReaderAt 来访问。 // // ctx 是请求的上下文。 @@ -161,7 +160,7 @@ func (p Bucket) RputFile( return uploader.RputFile(ctx, ret, uptoken, key, localFile, (*kodocli.RputExtra)(extra)) } -// 上传一个文件,支持断点续传和分块上传。自动以文件的 hash 作为文件的访问路径(key)。 +// RputFileWithoutKey 上传一个文件,支持断点续传和分块上传。自动以文件的 hash 作为文件的访问路径(key)。 // 和 RputWithoutKey 不同的只是一个通过提供文件路径来访问文件内容,一个通过 io.ReaderAt 来访问。 // // ctx 是请求的上下文。 diff --git a/vendor/qiniupkg.com/api.v7/kodocli/doc.go b/vendor/qiniupkg.com/api.v7/kodocli/doc.go index 688c490..5a4eb1e 100644 --- a/vendor/qiniupkg.com/api.v7/kodocli/doc.go +++ b/vendor/qiniupkg.com/api.v7/kodocli/doc.go @@ -1,5 +1,5 @@ /* -包 qiniupkg.com/api.v7/kodocli 提供了在客户端调用七牛云存储部分服务的能力 +包 github.com/qiniu/api.v7/kodocli 提供了在客户端调用七牛云存储部分服务的能力 客户端,严谨说是非可信环境,主要是指在用户端执行的环境,比如:Android/iOS 设备、Windows/Mac/Linux 桌面环境、也包括浏览器(如果浏览器能够执行 Go 语言代码的话)。 @@ -7,11 +7,11 @@ 第一个问题是如何上传文件。因为是在非可信环境,所以我们首先是要授予它有上传文件的能力。答案是给它颁发上传凭证。假设服务端也是 Go 语言,那么: - import "qiniupkg.com/api.v7/kodo" + import "github.com/qiniu/api.v7/kodo" kodo.SetMac("your-access-key", "your-secret-key") // 配置 AccessKey/SecretKey - zone := 0 + zone := kodo.ZoneZ0 c := kodo.New(zone, nil) // 创建一个 Client 对象 bucket := "your-bucket-name" diff --git a/vendor/qiniupkg.com/api.v7/kodocli/main.go b/vendor/qiniupkg.com/api.v7/kodocli/main.go index d703980..b880567 100644 --- a/vendor/qiniupkg.com/api.v7/kodocli/main.go +++ b/vendor/qiniupkg.com/api.v7/kodocli/main.go @@ -3,10 +3,10 @@ package kodocli import ( "net/http" - "qiniupkg.com/api.v7/api" - "qiniupkg.com/api.v7/conf" - "qiniupkg.com/x/rpc.v7" - "qiniupkg.com/x/url.v7" + "github.com/qiniu/api.v7/api" + "github.com/qiniu/api.v7/conf" + "github.com/qiniu/x/rpc.v7" + "github.com/qiniu/x/url.v7" ) // ---------------------------------------------------------- diff --git a/vendor/qiniupkg.com/api.v7/kodocli/resumable.go b/vendor/qiniupkg.com/api.v7/kodocli/resumable.go index c1c0b33..1f56a1c 100644 --- a/vendor/qiniupkg.com/api.v7/kodocli/resumable.go +++ b/vendor/qiniupkg.com/api.v7/kodocli/resumable.go @@ -1,6 +1,7 @@ package kodocli import ( + . "context" "encoding/base64" "encoding/json" "errors" @@ -9,9 +10,7 @@ import ( "strings" "sync" - "qiniupkg.com/x/xlog.v7" - - . "golang.org/x/net/context" + "github.com/qiniu/x/xlog.v7" ) // ---------------------------------------------------------- diff --git a/vendor/qiniupkg.com/api.v7/kodocli/resumable_base.go b/vendor/qiniupkg.com/api.v7/kodocli/resumable_base.go index 723d71d..7b84b66 100644 --- a/vendor/qiniupkg.com/api.v7/kodocli/resumable_base.go +++ b/vendor/qiniupkg.com/api.v7/kodocli/resumable_base.go @@ -1,6 +1,7 @@ package kodocli import ( + . "context" "encoding/base64" "fmt" "hash/crc32" @@ -8,11 +9,9 @@ import ( "net/http" "strconv" - "qiniupkg.com/x/bytes.v7" - "qiniupkg.com/x/rpc.v7" - "qiniupkg.com/x/xlog.v7" - - . "golang.org/x/net/context" + "github.com/qiniu/x/bytes.v7" + "github.com/qiniu/x/rpc.v7" + "github.com/qiniu/x/xlog.v7" ) // ---------------------------------------------------------- diff --git a/vendor/qiniupkg.com/api.v7/kodocli/upload.go b/vendor/qiniupkg.com/api.v7/kodocli/upload.go index 8c1c9c4..02eab4e 100644 --- a/vendor/qiniupkg.com/api.v7/kodocli/upload.go +++ b/vendor/qiniupkg.com/api.v7/kodocli/upload.go @@ -2,6 +2,7 @@ package kodocli import ( "bytes" + . "context" "fmt" "hash/crc32" "io" @@ -12,8 +13,6 @@ import ( "path/filepath" "strconv" "strings" - - . "golang.org/x/net/context" ) // ----------------------------------------------------------