forked from sabragirl/ufile-gosdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
request.go
191 lines (174 loc) · 6.42 KB
/
request.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
package ufsdk
import (
"bytes"
"context"
"errors"
"fmt"
"github.com/ufilesdk-dev/ufile-gosdk/utils"
"io/ioutil"
"net/http"
"net/url"
"strings"
)
//UFileRequest SDK 主要的 request 模块。本 SDK 遵从以下原则:
//
//1.接口尽可能简洁,隐藏所有复杂实现。
//
//2.本 SDK 主要的作用是封装 HTTP 请求,不做过多其他的封装(如 HTTP Body 序列化,详细的错误检查)。
//
//3.只简单封装 HTTP 请求所需要的参数,给接口使用者提供所有原生的 HTTP response header,body,status code 返回,以便排错。
//
//4.远端请求返回值统一返回一个 error,如果为 nil 表示无错。LastResponseStatus,LastResponseHeader,LastResponseBody 可以查看具体的 HTTP 返回信息()。如果你想少敲几行代码可以直接调用 DumpResponse(true) 查看详细返回。
//
type UFileRequest struct {
Auth Auth
BucketName string
Host string
Client *http.Client
Context context.Context
baseURL *url.URL
Crypto *utils.Crypto
RequestHeader http.Header
LastResponseStatus int
LastResponseHeader http.Header
LastResponseBody []byte
verifyUploadMD5 bool
lastResponse *http.Response
}
//NewFileRequest 创建一个用于管理文件的 request,管理文件的 url 与 管理 bucket 接口不一样,
//请将 bucket 和文件管理所需要的分开,NewUBucketRequest 是用来管理 bucket 的。
//Request 创建后的 instance 不是线程安全的,如果你需要做并发的操作,请创建多个 UFileRequest。
//config 参数里面包含了公私钥,以及其他必填的参数。详情见 config 相关文档。
//client 这里你可以传空,会使用默认的 http.Client。如果你需要设置超时以及一些其他相关的网络配置选项请传入一个自定义的 client。
func NewFileRequest(config *Config, client *http.Client) (*UFileRequest, error) {
config.BucketName = strings.TrimSpace(config.BucketName)
config.FileHost = strings.TrimSpace(config.FileHost)
if config.BucketName == "" || config.FileHost == "" {
return nil, errors.New("管理文件上传必须要提供 bucket 名字和所在地域的 Host 域名")
}
req := newRequest(config.PublicKey, config.PrivateKey,
config.BucketName, config.FileHost, client)
req.verifyUploadMD5 = config.VerifyUploadMD5
if req.baseURL.Scheme == "" { //用户传了非自定义域名
req.baseURL.Host = req.BucketName + "." + req.Host
req.baseURL.Scheme = "http"
}
if config.CryptoKey != "" {
cryptoKey := []byte(config.CryptoKey)
var err error
req.Crypto, err = utils.NewCrypto(cryptoKey)
if err != nil {
return nil, err
}
}
return req, nil
}
//NewFileRequestWithHeader 创建一个可携带用户自定义http请求头的 request
//Request 创建后的 instance 不是线程安全的,如果你需要做并发的操作,请创建多个 UFileRequest。
//config 参数里面包含了公私钥,以及其他必填的参数。详情见 config 相关文档。
//header 自定义http请求头
//client 这里你可以传空,会使用默认的 http.Client。如果你需要设置超时以及一些其他相关的网络配置选项请传入一个自定义的 client。
func NewFileRequestWithHeader(config *Config, header http.Header, client *http.Client) (*UFileRequest, error) {
req, err := NewFileRequest(config, client)
if err != nil {
return nil, err
}
req.RequestHeader = header
return req, nil
}
//NewBucketRequest 创建一个用于管理 bucket 的 request。
//注意:不要拿它去调用文件管理的 request,我文件管理和 bucket 管理接口放到一个 request 里面的目的就是让接口更统一,代码更清晰,简洁。
//config 参数里面包含了公私钥,以及其他必填的参数。详情见 config 相关文档。
func NewBucketRequest(config *Config, client *http.Client) (*UFileRequest, error) {
config.BucketHost = strings.TrimSpace(config.BucketHost)
if config.BucketHost == "" {
return nil, errors.New("管理 Bucket 必须要提供对应的 API host")
}
req := newRequest(config.PublicKey, config.PrivateKey, "", config.BucketHost, client)
req.verifyUploadMD5 = config.VerifyUploadMD5
if req.baseURL.Scheme == "" {
req.baseURL.Scheme = "http"
}
return req, nil
}
//DumpResponse dump 当前请求的返回结果,里面有一个 print 函数,会把 body,header,status code 直接输出到 stdout。
//如果你需要 Dump 到其他的地方,直接拿返回值即可。
func (u *UFileRequest) DumpResponse(isDumpBody bool) []byte {
var b bytes.Buffer
if u.lastResponse == nil {
return nil
}
b.WriteString(fmt.Sprintf("%s %d\n", u.lastResponse.Proto, u.LastResponseStatus))
for k, vs := range u.LastResponseHeader {
str := k + ": "
for i, v := range vs {
if i != 0 {
str += "; " + v
} else {
str += v
}
}
b.WriteString(str)
}
if isDumpBody {
b.Write(u.LastResponseBody)
}
return b.Bytes()
}
func newRequest(publicKey, privateKey, bucket, host string, client *http.Client) *UFileRequest {
req := new(UFileRequest)
req.Auth = NewAuth(publicKey, privateKey)
req.BucketName = bucket
req.Host = strings.TrimSpace(host)
req.baseURL = new(url.URL)
req.baseURL.Host = req.Host
req.baseURL.Path = "/" //for default usage.
if client == nil {
client = new(http.Client)
}
req.Client = client
req.Context = context.TODO()
return req
}
func (u *UFileRequest) responseParse(resp *http.Response) error {
resBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
defer resp.Body.Close()
u.LastResponseStatus = resp.StatusCode
u.LastResponseHeader = resp.Header
u.LastResponseBody = resBody
u.lastResponse = resp
return nil
}
func (u *UFileRequest) request(req *http.Request) error {
resp, err := u.requestWithResp(req)
if err != nil {
return err
}
err = u.responseParse(resp)
if err != nil {
return err
}
if !VerifyHTTPCode(resp.StatusCode) {
return fmt.Errorf("Remote response code is %d - %s not 2xx call DumpResponse(true) show details",
resp.StatusCode, http.StatusText(resp.StatusCode))
}
return nil
}
func (u *UFileRequest) requestWithResp(req *http.Request) (resp *http.Response, err error) {
req.Header.Set("User-Agent", "UFileGoSDK/2.02")
resp, err = u.Client.Do(req.WithContext(u.Context))
// If we got an error, and the context has been canceled,
// the context's error is probably more useful.
if err != nil {
select {
case <-u.Context.Done():
err = u.Context.Err()
default:
}
return
}
return
}