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  }