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 }