github.com/qiniu/go-sdk/v7@v7.27.0/storagev2/http_client/http_client.go (about) 1 package http_client 2 3 import ( 4 "context" 5 "errors" 6 "io" 7 "net" 8 "net/http" 9 "net/url" 10 "strconv" 11 "strings" 12 "time" 13 14 "github.com/qiniu/go-sdk/v7/auth" 15 clientv1 "github.com/qiniu/go-sdk/v7/client" 16 "github.com/qiniu/go-sdk/v7/internal/clientv2" 17 "github.com/qiniu/go-sdk/v7/internal/hostprovider" 18 compatible_io "github.com/qiniu/go-sdk/v7/internal/io" 19 "github.com/qiniu/go-sdk/v7/storagev2/chooser" 20 "github.com/qiniu/go-sdk/v7/storagev2/credentials" 21 "github.com/qiniu/go-sdk/v7/storagev2/defaults" 22 "github.com/qiniu/go-sdk/v7/storagev2/region" 23 "github.com/qiniu/go-sdk/v7/storagev2/resolver" 24 "github.com/qiniu/go-sdk/v7/storagev2/retrier" 25 "github.com/qiniu/go-sdk/v7/storagev2/uptoken" 26 ) 27 28 var ( 29 ErrNoRegion = errors.New("no region from provider") 30 ErrNoEndpointsConfigured = errors.New("no endpoints configured") 31 ) 32 33 type ( 34 InterceptorPriority = clientv2.InterceptorPriority 35 Interceptor = clientv2.Interceptor 36 BasicHTTPClient = clientv2.Client 37 GetRequestBody = clientv2.GetRequestBody 38 RetryConfig = clientv2.RetryConfig 39 Handler = clientv2.Handler 40 41 // Client 提供了对七牛 HTTP 客户端 42 Client struct { 43 useHttps bool 44 accelerateUploading bool 45 basicHTTPClient BasicHTTPClient 46 bucketQuery region.BucketRegionsQuery 47 allRegions region.RegionsProvider 48 regions region.RegionsProvider 49 credentials credentials.CredentialsProvider 50 resolver resolver.Resolver 51 chooser chooser.Chooser 52 hostRetryConfig *RetryConfig 53 hostsRetryConfig *RetryConfig 54 hostFreezeDuration time.Duration 55 shouldFreezeHost func(req *http.Request, resp *http.Response, err error) bool 56 beforeSign func(req *http.Request) 57 afterSign func(req *http.Request) 58 signError func(req *http.Request, err error) 59 beforeResolve func(*http.Request) 60 afterResolve func(*http.Request, []net.IP) 61 resolveError func(*http.Request, error) 62 beforeBackoff func(*http.Request, *retrier.RetrierOptions, time.Duration) 63 afterBackoff func(*http.Request, *retrier.RetrierOptions, time.Duration) 64 beforeRequest func(*http.Request, *retrier.RetrierOptions) 65 afterResponse func(*http.Response, *retrier.RetrierOptions, error) 66 } 67 68 // Options 为构建 Client 提供了可选参数 69 Options struct { 70 // 基础 HTTP 客户端 71 BasicHTTPClient BasicHTTPClient 72 73 // 空间区域查询器 74 BucketQuery region.BucketRegionsQuery 75 76 // 所有区域提供者 77 AllRegions region.RegionsProvider 78 79 // 区域提供者 80 Regions region.RegionsProvider 81 82 // 凭证信息提供者 83 Credentials credentials.CredentialsProvider 84 85 // 拦截器 86 Interceptors []Interceptor 87 88 // 是否使用 HTTP 协议 89 UseInsecureProtocol bool 90 91 // 域名解析器 92 Resolver resolver.Resolver 93 94 // 域名选择器 95 Chooser chooser.Chooser 96 97 // 单域名重试配置 98 HostRetryConfig *RetryConfig 99 100 // 主备域名重试配置 101 HostsRetryConfig *RetryConfig 102 103 // 主备域名冻结时间 104 HostFreezeDuration time.Duration 105 106 // 主备域名冻结判断函数 107 ShouldFreezeHost func(*http.Request, *http.Response, error) bool 108 109 // 签名前回调函数 110 BeforeSign func(*http.Request) 111 112 // 签名后回调函数 113 AfterSign func(*http.Request) 114 115 // 签名错误回调函数 116 SignError func(*http.Request, error) 117 118 // 域名解析前回调函数 119 BeforeResolve func(*http.Request) 120 121 // 域名解析后回调函数 122 AfterResolve func(*http.Request, []net.IP) 123 124 // 域名解析错误回调函数 125 ResolveError func(*http.Request, error) 126 127 // 退避前回调函数 128 BeforeBackoff func(*http.Request, *retrier.RetrierOptions, time.Duration) 129 130 // 退避后回调函数 131 AfterBackoff func(*http.Request, *retrier.RetrierOptions, time.Duration) 132 133 // 请求前回调函数 134 BeforeRequest func(*http.Request, *retrier.RetrierOptions) 135 136 // 请求后回调函数 137 AfterResponse func(*http.Response, *retrier.RetrierOptions, error) 138 139 // 是否加速上传 140 AccelerateUploading bool 141 } 142 143 // Request 包含一个具体的 HTTP 请求的参数 144 Request struct { 145 // 请求方法 146 Method string 147 148 // 请求服务名 149 // 注:应该是考虑同一个 API 有多个服务提供 150 ServiceNames []region.ServiceName 151 152 // 服务地址提供者 153 Endpoints region.EndpointsProvider 154 155 // 区域提供者 156 // 注:如果有多个只会使用第一个 157 Region region.RegionsProvider 158 159 // 请求路径 160 Path string 161 162 // 原始请求查询参数 163 RawQuery string 164 165 // 请求查询参数 166 Query url.Values 167 168 // 请求头 169 Header http.Header 170 171 // 请求 Body 获取函数 172 RequestBody GetRequestBody 173 174 // 凭证信息提供者 175 Credentials credentials.CredentialsProvider 176 177 // 授权类型 178 AuthType auth.TokenType 179 180 // 上传凭证接口 181 UpToken uptoken.UpTokenProvider 182 183 // 是否缓存响应 184 BufferResponse bool 185 186 // 拦截器追加列表 187 Interceptors []Interceptor 188 189 // 请求进度回调函数 190 OnRequestProgress func(uint64, uint64) 191 } 192 ) 193 194 // NewClient 用来构建一个新的七牛 HTTP 客户端 195 func NewClient(options *Options) *Client { 196 if options == nil { 197 options = &Options{} 198 if isDisabled, err := defaults.DisableSecureProtocol(); err == nil { 199 options.UseInsecureProtocol = isDisabled 200 } 201 } 202 hostFreezeDuration := options.HostFreezeDuration 203 if hostFreezeDuration < time.Millisecond { 204 hostFreezeDuration = 600 * time.Second 205 } 206 shouldFreezeHost := options.ShouldFreezeHost 207 if shouldFreezeHost == nil { 208 shouldFreezeHost = defaultShouldFreezeHost 209 } 210 creds := options.Credentials 211 if creds == nil { 212 if defaultCreds := credentials.Default(); defaultCreds != nil { 213 creds = defaultCreds 214 } 215 } 216 217 return &Client{ 218 useHttps: !options.UseInsecureProtocol, 219 accelerateUploading: options.AccelerateUploading, 220 basicHTTPClient: clientv2.NewClient(options.BasicHTTPClient, options.Interceptors...), 221 bucketQuery: options.BucketQuery, 222 allRegions: options.AllRegions, 223 regions: options.Regions, 224 credentials: creds, 225 resolver: options.Resolver, 226 chooser: options.Chooser, 227 hostRetryConfig: options.HostRetryConfig, 228 hostsRetryConfig: options.HostsRetryConfig, 229 hostFreezeDuration: hostFreezeDuration, 230 shouldFreezeHost: shouldFreezeHost, 231 beforeSign: options.BeforeSign, 232 afterSign: options.AfterSign, 233 signError: options.SignError, 234 beforeResolve: options.BeforeResolve, 235 afterResolve: options.AfterResolve, 236 resolveError: options.ResolveError, 237 beforeBackoff: options.BeforeBackoff, 238 afterBackoff: options.AfterBackoff, 239 beforeRequest: options.BeforeRequest, 240 afterResponse: options.AfterResponse, 241 } 242 } 243 244 // Do 发送 HTTP 请求 245 func (httpClient *Client) Do(ctx context.Context, request *Request) (*http.Response, error) { 246 req, err := httpClient.makeReq(ctx, request) 247 if err != nil { 248 return nil, err 249 } 250 req = clientv2.WithInterceptors(req, clientv2.NewAntiHijackingInterceptor()) 251 if !isSignatureDisabled(ctx) { 252 if upTokenProvider := request.UpToken; upTokenProvider != nil { 253 req = clientv2.WithInterceptors(req, clientv2.NewUpTokenInterceptor(clientv2.UpTokenConfig{ 254 UpToken: upTokenProvider, 255 })) 256 } else { 257 credentialsProvider := request.Credentials 258 if credentialsProvider == nil { 259 credentialsProvider = httpClient.credentials 260 } 261 if credentialsProvider != nil { 262 req = clientv2.WithInterceptors(req, clientv2.NewAuthInterceptor(clientv2.AuthConfig{ 263 Credentials: credentialsProvider, 264 TokenType: request.AuthType, 265 BeforeSign: httpClient.beforeSign, 266 AfterSign: httpClient.afterSign, 267 SignError: httpClient.signError, 268 })) 269 } 270 } 271 } 272 if len(request.Interceptors) > 0 { 273 req = clientv2.WithInterceptors(req, request.Interceptors...) 274 } 275 return httpClient.basicHTTPClient.Do(req) 276 } 277 278 // DoAndAcceptJSON 发送 HTTP 请求并接收 JSON 响应 279 func (httpClient *Client) DoAndAcceptJSON(ctx context.Context, request *Request, ret interface{}) error { 280 if resp, err := httpClient.Do(ctx, request); err != nil { 281 return err 282 } else { 283 return clientv1.DecodeJsonFromReader(resp.Body, ret) 284 } 285 } 286 287 func (httpClient *Client) AccelerateUploadingEnabled() bool { 288 return httpClient.accelerateUploading 289 } 290 291 func (httpClient *Client) GetBucketQuery() region.BucketRegionsQuery { 292 return httpClient.bucketQuery 293 } 294 295 func (httpClient *Client) GetCredentials() credentials.CredentialsProvider { 296 return httpClient.credentials 297 } 298 299 func (httpClient *Client) GetAllRegions() region.RegionsProvider { 300 return httpClient.allRegions 301 } 302 303 func (httpClient *Client) GetRegions() region.RegionsProvider { 304 return httpClient.regions 305 } 306 307 func (httpClient *Client) GetClient() BasicHTTPClient { 308 return httpClient.basicHTTPClient 309 } 310 311 func (httpClient *Client) UseInsecureProtocol() bool { 312 return !httpClient.useHttps 313 } 314 315 func (httpClient *Client) GetHostFreezeDuration() time.Duration { 316 return httpClient.hostFreezeDuration 317 } 318 319 func (httpClient *Client) GetHostRetryConfig() *RetryConfig { 320 return httpClient.hostRetryConfig 321 } 322 323 func (httpClient *Client) GetHostsRetryConfig() *RetryConfig { 324 return httpClient.hostsRetryConfig 325 } 326 327 func (httpClient *Client) GetResolver() resolver.Resolver { 328 return httpClient.resolver 329 } 330 331 func (httpClient *Client) GetChooser() chooser.Chooser { 332 return httpClient.chooser 333 } 334 335 func (httpClient *Client) GetBeforeSignCallback() func(*http.Request) { 336 return httpClient.beforeSign 337 } 338 339 func (httpClient *Client) GetAfterSignCallback() func(*http.Request) { 340 return httpClient.afterSign 341 } 342 343 func (httpClient *Client) GetSignErrorCallback() func(*http.Request, error) { 344 return httpClient.signError 345 } 346 347 func (httpClient *Client) GetBeforeResolveCallback() func(*http.Request) { 348 return httpClient.beforeResolve 349 } 350 351 func (httpClient *Client) GetAfterResolveCallback() func(*http.Request, []net.IP) { 352 return httpClient.afterResolve 353 } 354 355 func (httpClient *Client) GetResolveErrorCallback() func(*http.Request, error) { 356 return httpClient.resolveError 357 } 358 359 func (httpClient *Client) GetBeforeBackoffCallback() func(*http.Request, *retrier.RetrierOptions, time.Duration) { 360 return httpClient.beforeBackoff 361 } 362 363 func (httpClient *Client) GetAfterBackoffCallback() func(*http.Request, *retrier.RetrierOptions, time.Duration) { 364 return httpClient.afterBackoff 365 } 366 367 func (httpClient *Client) GetBeforeRequestCallback() func(*http.Request, *retrier.RetrierOptions) { 368 return httpClient.beforeRequest 369 } 370 371 func (httpClient *Client) GetAfterResponseCallback() func(*http.Response, *retrier.RetrierOptions, error) { 372 return httpClient.afterResponse 373 } 374 375 func (httpClient *Client) getEndpoints(ctx context.Context, request *Request) (region.Endpoints, error) { 376 getEndpointsFromEndpointsProvider := func(ctx context.Context, endpoints region.EndpointsProvider) (region.Endpoints, error) { 377 return endpoints.GetEndpoints(ctx) 378 } 379 getEndpointsFromRegionsProvider := func(ctx context.Context, regions region.RegionsProvider, serviceNames []region.ServiceName) (region.Endpoints, error) { 380 rs, err := regions.GetRegions(ctx) 381 if err != nil { 382 return region.Endpoints{}, err 383 } else if len(rs) == 0 { 384 return region.Endpoints{}, ErrNoRegion 385 } 386 r := rs[0] 387 return r.Endpoints(serviceNames) 388 } 389 if request.Endpoints != nil { 390 return getEndpointsFromEndpointsProvider(ctx, request.Endpoints) 391 } else if request.Region != nil && len(request.ServiceNames) > 0 { 392 return getEndpointsFromRegionsProvider(ctx, request.Region, request.ServiceNames) 393 } else if httpClient.regions != nil && len(request.ServiceNames) > 0 { 394 return getEndpointsFromRegionsProvider(ctx, httpClient.regions, request.ServiceNames) 395 } 396 return region.Endpoints{}, ErrNoEndpointsConfigured 397 } 398 399 func (httpClient *Client) makeReq(ctx context.Context, request *Request) (*http.Request, error) { 400 endpoints, err := httpClient.getEndpoints(ctx, request) 401 if err != nil { 402 return nil, err 403 } 404 hostProvider := endpoints.ToHostProvider() 405 url, err := httpClient.generateUrl(request, hostProvider) 406 if err != nil { 407 return nil, err 408 } 409 410 interceptors := make([]Interceptor, 0, 3) 411 412 var hostsRetryConfig, hostRetryConfig clientv2.RetryConfig 413 if httpClient.hostsRetryConfig != nil { 414 hostsRetryConfig = *httpClient.hostsRetryConfig 415 } 416 if hostsRetryConfig.RetryMax <= 0 { 417 hostsRetryConfig.RetryMax = endpoints.HostsLength() 418 } 419 if hostsRetryConfig.Retrier == nil { 420 hostsRetryConfig.Retrier = retrier.NewErrorRetrier() 421 } 422 423 if httpClient.hostRetryConfig != nil { 424 hostRetryConfig = *httpClient.hostRetryConfig 425 } 426 if hostRetryConfig.RetryMax <= 0 { 427 hostRetryConfig.RetryMax = 3 428 } 429 if hostRetryConfig.Retrier == nil { 430 hostRetryConfig.Retrier = retrier.NewErrorRetrier() 431 } 432 433 interceptors = append(interceptors, clientv2.NewBufferResponseInterceptor()) 434 interceptors = append(interceptors, clientv2.NewHostsRetryInterceptor(clientv2.HostsRetryConfig{ 435 RetryMax: hostsRetryConfig.RetryMax, 436 ShouldRetry: hostsRetryConfig.ShouldRetry, 437 Retrier: hostsRetryConfig.Retrier, 438 HostFreezeDuration: httpClient.hostFreezeDuration, 439 ShouldFreezeHost: httpClient.shouldFreezeHost, 440 HostProvider: hostProvider, 441 })) 442 interceptors = append(interceptors, clientv2.NewSimpleRetryInterceptor( 443 clientv2.SimpleRetryConfig{ 444 RetryMax: hostRetryConfig.RetryMax, 445 RetryInterval: hostRetryConfig.RetryInterval, 446 Backoff: hostRetryConfig.Backoff, 447 ShouldRetry: hostRetryConfig.ShouldRetry, 448 Retrier: hostRetryConfig.Retrier, 449 Resolver: httpClient.resolver, 450 Chooser: httpClient.chooser, 451 BeforeResolve: httpClient.beforeResolve, 452 AfterResolve: httpClient.afterResolve, 453 ResolveError: httpClient.resolveError, 454 BeforeBackoff: httpClient.beforeBackoff, 455 AfterBackoff: httpClient.afterBackoff, 456 BeforeRequest: httpClient.beforeRequest, 457 AfterResponse: httpClient.afterResponse, 458 }, 459 )) 460 req, err := clientv2.NewRequest(clientv2.RequestParams{ 461 Context: ctx, 462 Method: request.Method, 463 Url: url, 464 Header: request.Header, 465 GetBody: request.RequestBody, 466 BufferResponse: request.BufferResponse, 467 OnRequestProgress: request.OnRequestProgress, 468 }) 469 if err != nil { 470 return nil, err 471 } 472 return clientv2.WithInterceptors(req, interceptors...), nil 473 } 474 475 func (httpClient *Client) generateUrl(request *Request, hostProvider hostprovider.HostProvider) (string, error) { 476 var url string 477 host, err := hostProvider.Provider() 478 if err != nil { 479 return "", err 480 } 481 if strings.Contains(host, "://") { 482 url = host 483 } else { 484 if httpClient.useHttps { 485 url = "https://" 486 } else { 487 url = "http://" 488 } 489 url += host 490 } 491 if !strings.HasPrefix(request.Path, "/") { 492 url += "/" 493 } 494 url += request.Path 495 if request.RawQuery != "" || request.Query != nil { 496 url += "?" 497 var rawQuery string 498 if request.RawQuery != "" { 499 rawQuery = request.RawQuery 500 } 501 if request.Query != nil { 502 if rawQuery != "" { 503 rawQuery += "&" 504 } 505 rawQuery += request.Query.Encode() 506 } 507 url += rawQuery 508 } 509 return url, nil 510 } 511 512 func (options *Options) SetBucketHosts(bucketHosts region.Endpoints) (err error) { 513 options.BucketQuery, err = region.NewBucketRegionsQuery(bucketHosts, nil) 514 return 515 } 516 517 // GetFormRequestBody 将数据通过 Form 作为请求 Body 发送 518 func GetFormRequestBody(info map[string][]string) GetRequestBody { 519 return clientv2.GetFormRequestBody(info) 520 } 521 522 // GetJsonRequestBody 将数据通过 JSON 作为请求 Body 发送 523 func GetJsonRequestBody(object interface{}) (GetRequestBody, error) { 524 return clientv2.GetJsonRequestBody(object) 525 } 526 527 // MultipartForm 用来构建 Multipart 表单 528 type MultipartForm = clientv2.MultipartForm 529 530 // GetMultipartFormRequestBody 将数据通过 Multipart 表单作为请求 Body 发送 531 func GetMultipartFormRequestBody(info *MultipartForm) GetRequestBody { 532 return clientv2.GetMultipartFormRequestBody(info) 533 } 534 535 // GetMultipartFormRequestBody 将二进制数据请求 Body 发送 536 func GetRequestBodyFromReadSeekCloser(r compatible_io.ReadSeekCloser) GetRequestBody { 537 return func(params *clientv2.RequestParams) (io.ReadCloser, error) { 538 params.Header.Set("Content-Type", "application/octet-stream") 539 totalSize, err := r.Seek(0, io.SeekEnd) 540 if err != nil { 541 return r, err 542 } 543 params.Header.Set("Content-Length", strconv.FormatInt(totalSize, 10)) 544 _, err = r.Seek(0, io.SeekStart) 545 return r, err 546 } 547 } 548 549 var defaultBucketHosts = region.Endpoints{ 550 Preferred: []string{"uc.qiniuapi.com", "kodo-config.qiniuapi.com"}, 551 Alternative: []string{"uc.qbox.me"}, 552 } 553 554 func init() { 555 if bucketUrls, err := defaults.BucketURLs(); err == nil && len(bucketUrls) > 0 { 556 defaultBucketHosts = region.Endpoints{Preferred: bucketUrls} 557 } 558 } 559 560 // DefaultBucketHosts 默认的 Bucket 域名列表 561 func DefaultBucketHosts() region.Endpoints { 562 return defaultBucketHosts.Clone() 563 } 564 565 func defaultShouldFreezeHost(*http.Request, *http.Response, error) bool { 566 return true 567 }