github.com/qiniu/go-sdk/v7@v7.27.0/internal/clientv2/request.go (about)

     1  package clientv2
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"net/http"
    10  	"net/url"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/qiniu/go-sdk/v7/conf"
    15  	internal_io "github.com/qiniu/go-sdk/v7/internal/io"
    16  )
    17  
    18  const (
    19  	RequestMethodGet    = http.MethodGet
    20  	RequestMethodPut    = http.MethodPut
    21  	RequestMethodPost   = http.MethodPost
    22  	RequestMethodHead   = http.MethodHead
    23  	RequestMethodDelete = http.MethodDelete
    24  )
    25  
    26  type GetRequestBody func(options *RequestParams) (io.ReadCloser, error)
    27  
    28  func GetJsonRequestBody(object interface{}) (GetRequestBody, error) {
    29  	reqBody, err := json.Marshal(object)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  	return func(o *RequestParams) (io.ReadCloser, error) {
    34  		o.Header.Set("Content-Type", conf.CONTENT_TYPE_JSON)
    35  		o.Header.Set("Content-Length", strconv.Itoa(len(reqBody)))
    36  		return internal_io.NewReadSeekableNopCloser(bytes.NewReader(reqBody)), nil
    37  	}, nil
    38  }
    39  
    40  func GetFormRequestBody(info map[string][]string) GetRequestBody {
    41  	body := formStringInfo(info)
    42  	return func(o *RequestParams) (io.ReadCloser, error) {
    43  		o.Header.Set("Content-Type", conf.CONTENT_TYPE_FORM)
    44  		o.Header.Set("Content-Length", strconv.Itoa(len(body)))
    45  		return internal_io.NewReadSeekableNopCloser(strings.NewReader(body)), nil
    46  	}
    47  }
    48  
    49  func formStringInfo(info map[string][]string) string {
    50  	if len(info) == 0 {
    51  		return ""
    52  	}
    53  	return url.Values(info).Encode()
    54  }
    55  
    56  type RequestParams struct {
    57  	Context           context.Context
    58  	Method            string
    59  	Url               string
    60  	Header            http.Header
    61  	GetBody           GetRequestBody
    62  	BufferResponse    bool
    63  	OnRequestProgress RequestBodyProgress
    64  }
    65  
    66  func (o *RequestParams) init() {
    67  	if o.Context == nil {
    68  		o.Context = context.Background()
    69  	}
    70  
    71  	if len(o.Method) == 0 {
    72  		o.Method = RequestMethodGet
    73  	}
    74  
    75  	if o.Header == nil {
    76  		o.Header = http.Header{}
    77  	}
    78  
    79  	if o.GetBody == nil {
    80  		o.GetBody = func(options *RequestParams) (io.ReadCloser, error) {
    81  			return nil, nil
    82  		}
    83  	}
    84  }
    85  
    86  func NewRequest(options RequestParams) (req *http.Request, err error) {
    87  	var (
    88  		bodyWrapper   *requestBodyWrapperWithProgress = nil
    89  		contentLength int64                           = 0
    90  	)
    91  
    92  	options.init()
    93  
    94  	body, err := options.GetBody(&options)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	if body != nil {
   100  		if contentLengthHeaderValue := options.Header.Get("Content-Length"); contentLengthHeaderValue != "" {
   101  			contentLength, err = strconv.ParseInt(contentLengthHeaderValue, 10, 64)
   102  			if err != nil {
   103  				return nil, fmt.Errorf("invalid Content-Length header value: %s, %s", contentLengthHeaderValue, err)
   104  			}
   105  		}
   106  		if options.OnRequestProgress != nil {
   107  			bodyWrapper = &requestBodyWrapperWithProgress{body: body, expectedSize: uint64(contentLength), callback: options.OnRequestProgress}
   108  		}
   109  	}
   110  	if bodyWrapper != nil {
   111  		req, err = http.NewRequest(options.Method, options.Url, bodyWrapper)
   112  	} else {
   113  		req, err = http.NewRequest(options.Method, options.Url, body)
   114  	}
   115  	if err != nil {
   116  		return
   117  	}
   118  	if options.Context != nil {
   119  		req = req.WithContext(options.Context)
   120  	}
   121  	if options.BufferResponse {
   122  		req = req.WithContext(context.WithValue(options.Context, bufferResponseContextKey{}, struct{}{}))
   123  	}
   124  	req.Header = options.Header
   125  	if options.GetBody != nil && body != nil && body != http.NoBody {
   126  		req.GetBody = func() (io.ReadCloser, error) {
   127  			reqBody, err := options.GetBody(&options)
   128  			if err != nil {
   129  				return nil, err
   130  			}
   131  			if bodyWrapper != nil {
   132  				return &requestBodyWrapperWithProgress{
   133  					body:         reqBody,
   134  					expectedSize: uint64(contentLength),
   135  					callback:     options.OnRequestProgress,
   136  				}, nil
   137  			} else {
   138  				return reqBody, nil
   139  			}
   140  		}
   141  	}
   142  	if contentLength > 0 {
   143  		req.ContentLength = contentLength
   144  	}
   145  	return
   146  }
   147  
   148  type (
   149  	RequestBodyProgress            func(uint64, uint64)
   150  	requestBodyWrapperWithProgress struct {
   151  		body                       io.ReadCloser
   152  		haveReadSize, expectedSize uint64
   153  		callback                   RequestBodyProgress
   154  	}
   155  )
   156  
   157  func (wrapper *requestBodyWrapperWithProgress) Read(p []byte) (n int, err error) {
   158  	n, err = wrapper.body.Read(p)
   159  	if callback := wrapper.callback; callback != nil && n > 0 {
   160  		wrapper.haveReadSize += uint64(n)
   161  		callback(wrapper.haveReadSize, wrapper.expectedSize)
   162  	}
   163  	return
   164  }
   165  
   166  func (wrapper *requestBodyWrapperWithProgress) Close() error {
   167  	return wrapper.body.Close()
   168  }