github.com/opentelekomcloud/gophertelekomcloud@v0.9.3/openstack/obs/http.go (about) 1 package obs 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 "math/rand" 9 "net" 10 "net/http" 11 "net/url" 12 "os" 13 "strings" 14 "time" 15 ) 16 17 func prepareHeaders(headers map[string][]string, meta bool, isObs bool) map[string][]string { 18 _headers := make(map[string][]string, len(headers)) 19 20 for key, value := range headers { 21 key = strings.TrimSpace(key) 22 if key == "" { 23 continue 24 } 25 _key := strings.ToLower(key) 26 if _, ok := allowedRequestHttpHeaderMetadataNames[_key]; !ok && !strings.HasPrefix(key, HEADER_PREFIX) && !strings.HasPrefix(key, HEADER_PREFIX_OBS) { 27 if !meta { 28 continue 29 } 30 if !isObs { 31 _key = HEADER_PREFIX_META + _key 32 } else { 33 _key = HEADER_PREFIX_META_OBS + _key 34 } 35 } else { 36 _key = key 37 } 38 _headers[_key] = value 39 } 40 41 return _headers 42 } 43 44 func (obsClient ObsClient) doActionWithoutBucket(action, method string, input ISerializable, output IBaseModel) error { 45 return obsClient.doAction(action, method, "", "", input, output, true, true) 46 } 47 48 func (obsClient ObsClient) doActionWithBucketV2(action, method, bucketName string, input ISerializable, output IBaseModel) error { 49 if strings.TrimSpace(bucketName) == "" && !obsClient.conf.cname { 50 return errors.New("Bucket is empty") 51 } 52 return obsClient.doAction(action, method, bucketName, "", input, output, false, true) 53 } 54 55 func (obsClient ObsClient) doActionWithBucket(action, method, bucketName string, input ISerializable, output IBaseModel) error { 56 if strings.TrimSpace(bucketName) == "" && !obsClient.conf.cname { 57 return errors.New("Bucket is empty") 58 } 59 return obsClient.doAction(action, method, bucketName, "", input, output, true, true) 60 } 61 62 func (obsClient ObsClient) doActionWithBucketAndKey(action, method, bucketName, objectKey string, input ISerializable, output IBaseModel) error { 63 return obsClient._doActionWithBucketAndKey(action, method, bucketName, objectKey, input, output, true) 64 } 65 66 func (obsClient ObsClient) doActionWithBucketAndKeyUnRepeatable(action, method, bucketName, objectKey string, input ISerializable, output IBaseModel) error { 67 return obsClient._doActionWithBucketAndKey(action, method, bucketName, objectKey, input, output, false) 68 } 69 70 func (obsClient ObsClient) _doActionWithBucketAndKey(action, method, bucketName, objectKey string, input ISerializable, output IBaseModel, repeatable bool) error { 71 if strings.TrimSpace(bucketName) == "" && !obsClient.conf.cname { 72 return errors.New("Bucket is empty") 73 } 74 if strings.TrimSpace(objectKey) == "" { 75 return errors.New("Key is empty") 76 } 77 return obsClient.doAction(action, method, bucketName, objectKey, input, output, true, repeatable) 78 } 79 80 func (obsClient ObsClient) doAction(action, method, bucketName, objectKey string, input ISerializable, output IBaseModel, xmlResult bool, repeatable bool) error { 81 82 var resp *http.Response 83 var respError error 84 doLog(LEVEL_INFO, "Enter method %s...", action) 85 start := GetCurrentTimestamp() 86 87 params, headers, data, err := input.trans(obsClient.conf.signature == SignatureObs) 88 if err != nil { 89 return err 90 } 91 if params == nil { 92 params = make(map[string]string) 93 } 94 95 if headers == nil { 96 headers = make(map[string][]string) 97 } 98 99 switch method { 100 case HTTP_GET: 101 resp, respError = obsClient.doHttpGet(bucketName, objectKey, params, headers, data, repeatable) 102 case HTTP_POST: 103 resp, respError = obsClient.doHttpPost(bucketName, objectKey, params, headers, data, repeatable) 104 case HTTP_PUT: 105 resp, respError = obsClient.doHttpPut(bucketName, objectKey, params, headers, data, repeatable) 106 case HTTP_DELETE: 107 resp, respError = obsClient.doHttpDelete(bucketName, objectKey, params, headers, data, repeatable) 108 case HTTP_HEAD: 109 resp, respError = obsClient.doHttpHead(bucketName, objectKey, params, headers, data, repeatable) 110 case HTTP_OPTIONS: 111 resp, respError = obsClient.doHttpOptions(bucketName, objectKey, params, headers, data, repeatable) 112 default: 113 respError = errors.New("Unexpect http method error") 114 } 115 if respError == nil && output != nil { 116 respError = ParseResponseToBaseModel(resp, output, xmlResult, obsClient.conf.signature == SignatureObs) 117 if respError != nil { 118 doLog(LEVEL_WARN, "Parse response to BaseModel with error: %v", respError) 119 } 120 } else { 121 doLog(LEVEL_WARN, "Do http request with error: %v", respError) 122 } 123 124 if isDebugLogEnabled() { 125 doLog(LEVEL_DEBUG, "End method %s, obsclient cost %d ms", action, GetCurrentTimestamp()-start) 126 } 127 128 return respError 129 } 130 131 func (obsClient ObsClient) doHttpGet(bucketName, objectKey string, params map[string]string, 132 headers map[string][]string, data interface{}, repeatable bool) (*http.Response, error) { 133 return obsClient.doHttp(HTTP_GET, bucketName, objectKey, params, prepareHeaders(headers, false, obsClient.conf.signature == SignatureObs), data, repeatable) 134 } 135 136 func (obsClient ObsClient) doHttpHead(bucketName, objectKey string, params map[string]string, 137 headers map[string][]string, data interface{}, repeatable bool) (*http.Response, error) { 138 return obsClient.doHttp(HTTP_HEAD, bucketName, objectKey, params, prepareHeaders(headers, false, obsClient.conf.signature == SignatureObs), data, repeatable) 139 } 140 141 func (obsClient ObsClient) doHttpOptions(bucketName, objectKey string, params map[string]string, 142 headers map[string][]string, data interface{}, repeatable bool) (*http.Response, error) { 143 return obsClient.doHttp(HTTP_OPTIONS, bucketName, objectKey, params, prepareHeaders(headers, false, obsClient.conf.signature == SignatureObs), data, repeatable) 144 } 145 146 func (obsClient ObsClient) doHttpDelete(bucketName, objectKey string, params map[string]string, 147 headers map[string][]string, data interface{}, repeatable bool) (*http.Response, error) { 148 return obsClient.doHttp(HTTP_DELETE, bucketName, objectKey, params, prepareHeaders(headers, false, obsClient.conf.signature == SignatureObs), data, repeatable) 149 } 150 151 func (obsClient ObsClient) doHttpPut(bucketName, objectKey string, params map[string]string, 152 headers map[string][]string, data interface{}, repeatable bool) (*http.Response, error) { 153 return obsClient.doHttp(HTTP_PUT, bucketName, objectKey, params, prepareHeaders(headers, true, obsClient.conf.signature == SignatureObs), data, repeatable) 154 } 155 156 func (obsClient ObsClient) doHttpPost(bucketName, objectKey string, params map[string]string, 157 headers map[string][]string, data interface{}, repeatable bool) (*http.Response, error) { 158 return obsClient.doHttp(HTTP_POST, bucketName, objectKey, params, prepareHeaders(headers, true, obsClient.conf.signature == SignatureObs), data, repeatable) 159 } 160 161 func (obsClient ObsClient) doHttpWithSignedUrl(action, method string, signedUrl string, actualSignedRequestHeaders http.Header, data io.Reader, output IBaseModel, xmlResult bool) (respError error) { 162 req, err := http.NewRequest(method, signedUrl, data) 163 if err != nil { 164 return err 165 } 166 if obsClient.conf.ctx != nil { 167 req = req.WithContext(obsClient.conf.ctx) 168 } 169 var resp *http.Response 170 171 doLog(LEVEL_INFO, "Do %s with signedUrl %s...", action, signedUrl) 172 173 req.Header = actualSignedRequestHeaders 174 if value, ok := req.Header[HEADER_HOST_CAMEL]; ok { 175 req.Host = value[0] 176 delete(req.Header, HEADER_HOST_CAMEL) 177 } else if value, ok := req.Header[HEADER_HOST]; ok { 178 req.Host = value[0] 179 delete(req.Header, HEADER_HOST) 180 } 181 182 if value, ok := req.Header[HEADER_CONTENT_LENGTH_CAMEL]; ok { 183 req.ContentLength = StringToInt64(value[0], -1) 184 delete(req.Header, HEADER_CONTENT_LENGTH_CAMEL) 185 } else if value, ok := req.Header[HEADER_CONTENT_LENGTH]; ok { 186 req.ContentLength = StringToInt64(value[0], -1) 187 delete(req.Header, HEADER_CONTENT_LENGTH) 188 } 189 190 req.Header[HEADER_USER_AGENT_CAMEL] = []string{USER_AGENT} 191 start := GetCurrentTimestamp() 192 resp, err = obsClient.httpClient.Do(req) 193 if isInfoLogEnabled() { 194 doLog(LEVEL_INFO, "Do http request cost %d ms", GetCurrentTimestamp()-start) 195 } 196 197 var msg interface{} 198 if err != nil { 199 respError = err 200 resp = nil 201 } else { 202 doLog(LEVEL_DEBUG, "Response headers: %v", resp.Header) 203 if resp.StatusCode >= 300 { 204 respError = ParseResponseToObsError(resp, obsClient.conf.signature == SignatureObs) 205 msg = resp.Status 206 resp = nil 207 } else { 208 if output != nil { 209 respError = ParseResponseToBaseModel(resp, output, xmlResult, obsClient.conf.signature == SignatureObs) 210 } 211 if respError != nil { 212 doLog(LEVEL_WARN, "Parse response to BaseModel with error: %v", respError) 213 } 214 } 215 } 216 217 if msg != nil { 218 doLog(LEVEL_ERROR, "Failed to send request with reason:%v", msg) 219 } 220 221 if isDebugLogEnabled() { 222 doLog(LEVEL_DEBUG, "End method %s, obsclient cost %d ms", action, GetCurrentTimestamp()-start) 223 } 224 225 return 226 } 227 228 func (obsClient ObsClient) doHttp(method, bucketName, objectKey string, params map[string]string, 229 headers map[string][]string, data interface{}, repeatable bool) (resp *http.Response, respError error) { 230 231 bucketName = strings.TrimSpace(bucketName) 232 233 method = strings.ToUpper(method) 234 235 var redirectUrl string 236 var requestUrl string 237 maxRetryCount := obsClient.conf.maxRetryCount 238 maxRedirectCount := obsClient.conf.maxRedirectCount 239 240 var _data io.Reader 241 if data != nil { 242 if dataStr, ok := data.(string); ok { 243 doLog(LEVEL_DEBUG, "Do http request with string: %s", dataStr) 244 headers["Content-Length"] = []string{IntToString(len(dataStr))} 245 _data = strings.NewReader(dataStr) 246 } else if dataByte, ok := data.([]byte); ok { 247 doLog(LEVEL_DEBUG, "Do http request with byte array") 248 headers["Content-Length"] = []string{IntToString(len(dataByte))} 249 _data = bytes.NewReader(dataByte) 250 } else if dataReader, ok := data.(io.Reader); ok { 251 _data = dataReader 252 } else { 253 doLog(LEVEL_WARN, "Data is not a valid io.Reader") 254 return nil, errors.New("Data is not a valid io.Reader") 255 } 256 } 257 258 redirectFlag := false 259 for i, redirectCount := 0, 0; i <= maxRetryCount; i++ { 260 if redirectUrl != "" { 261 if !redirectFlag { 262 parsedRedirectUrl, err := url.Parse(redirectUrl) 263 if err != nil { 264 return nil, err 265 } 266 requestUrl, _ = obsClient.doAuth(method, bucketName, objectKey, params, headers, parsedRedirectUrl.Host) 267 if parsedRequestUrl, err := url.Parse(requestUrl); err != nil { 268 return nil, err 269 } else if parsedRequestUrl.RawQuery != "" && parsedRedirectUrl.RawQuery == "" { 270 redirectUrl += "?" + parsedRequestUrl.RawQuery 271 } 272 } 273 requestUrl = redirectUrl 274 } else { 275 var err error 276 requestUrl, err = obsClient.doAuth(method, bucketName, objectKey, params, headers, "") 277 if err != nil { 278 return nil, err 279 } 280 } 281 282 req, err := http.NewRequest(method, requestUrl, _data) 283 if err != nil { 284 return nil, fmt.Errorf("failed to build a request: %s", err) 285 } 286 if obsClient.conf.ctx != nil { 287 req = req.WithContext(obsClient.conf.ctx) 288 } 289 doLog(LEVEL_DEBUG, "Do request with url [%s] and method [%s]", requestUrl, method) 290 291 if isDebugLogEnabled() { 292 auth := headers[HEADER_AUTH_CAMEL] 293 delete(headers, HEADER_AUTH_CAMEL) 294 doLog(LEVEL_DEBUG, "Request headers: %v", headers) 295 headers[HEADER_AUTH_CAMEL] = auth 296 } 297 298 if req == nil { 299 return nil, fmt.Errorf("error building a requiest, `req` is nil") 300 } 301 302 if req.Header == nil { 303 req.Header = make(http.Header) 304 } 305 for key, value := range headers { 306 if key == HEADER_HOST_CAMEL { 307 req.Host = value[0] 308 delete(headers, key) 309 } else if key == HEADER_CONTENT_LENGTH_CAMEL { 310 req.ContentLength = StringToInt64(value[0], -1) 311 delete(headers, key) 312 } else { 313 req.Header[key] = value 314 } 315 } 316 317 req.Header[HEADER_USER_AGENT_CAMEL] = []string{USER_AGENT} 318 319 start := GetCurrentTimestamp() 320 resp, err = obsClient.httpClient.Do(req) 321 if isInfoLogEnabled() { 322 doLog(LEVEL_INFO, "Do http request cost %d ms", GetCurrentTimestamp()-start) 323 } 324 325 var msg interface{} 326 if err != nil { 327 msg = err 328 respError = err 329 resp = nil 330 if !repeatable { 331 break 332 } 333 } else { 334 doLog(LEVEL_DEBUG, "Response headers: %v", resp.Header) 335 if resp.StatusCode < 300 { 336 break 337 } else if !repeatable || (resp.StatusCode >= 400 && resp.StatusCode < 500) || resp.StatusCode == 304 { 338 respError = ParseResponseToObsError(resp, obsClient.conf.signature == SignatureObs) 339 resp = nil 340 break 341 } else if resp.StatusCode >= 300 && resp.StatusCode < 400 { 342 if location := resp.Header.Get(HEADER_LOCATION_CAMEL); location != "" && redirectCount < maxRedirectCount { 343 redirectUrl = location 344 doLog(LEVEL_WARN, "Redirect request to %s", redirectUrl) 345 msg = resp.Status 346 maxRetryCount++ 347 redirectCount++ 348 if resp.StatusCode == 302 && method == HTTP_GET { 349 redirectFlag = true 350 } else { 351 redirectFlag = false 352 } 353 } else { 354 respError = ParseResponseToObsError(resp, obsClient.conf.signature == SignatureObs) 355 resp = nil 356 break 357 } 358 } else { 359 msg = resp.Status 360 } 361 } 362 if i != maxRetryCount { 363 if resp != nil { 364 _err := resp.Body.Close() 365 if _err != nil { 366 doLog(LEVEL_WARN, "Failed to close resp body with reason: %v", _err) 367 } 368 resp = nil 369 } 370 delete(headers, HEADER_AUTH_CAMEL) 371 doLog(LEVEL_WARN, "Failed to send request with reason:%v, will try again", msg) 372 if r, ok := _data.(*strings.Reader); ok { 373 _, err := r.Seek(0, 0) 374 if err != nil { 375 return nil, err 376 } 377 } else if r, ok := _data.(*bytes.Reader); ok { 378 _, err := r.Seek(0, 0) 379 if err != nil { 380 return nil, err 381 } 382 } else if r, ok := _data.(*fileReaderWrapper); ok { 383 fd, err := os.Open(r.filePath) 384 if err != nil { 385 return nil, err 386 } 387 defer fd.Close() 388 fileReaderWrapper := &fileReaderWrapper{filePath: r.filePath} 389 fileReaderWrapper.mark = r.mark 390 fileReaderWrapper.reader = fd 391 fileReaderWrapper.totalCount = r.totalCount 392 _data = fileReaderWrapper 393 _, err = fd.Seek(r.mark, 0) 394 if err != nil { 395 return nil, err 396 } 397 } else if r, ok := _data.(*readerWrapper); ok { 398 _, err := r.seek(0, 0) 399 if err != nil { 400 return nil, err 401 } 402 } 403 time.Sleep(time.Duration(float64(i+2) * rand.Float64() * float64(time.Second))) 404 } else { 405 doLog(LEVEL_ERROR, "Failed to send request with reason:%v", msg) 406 if resp != nil { 407 respError = ParseResponseToObsError(resp, obsClient.conf.signature == SignatureObs) 408 resp = nil 409 } 410 } 411 } 412 return 413 } 414 415 type connDelegate struct { 416 conn net.Conn 417 socketTimeout time.Duration 418 finalTimeout time.Duration 419 } 420 421 func getConnDelegate(conn net.Conn, socketTimeout int, finalTimeout int) *connDelegate { 422 return &connDelegate{ 423 conn: conn, 424 socketTimeout: time.Second * time.Duration(socketTimeout), 425 finalTimeout: time.Second * time.Duration(finalTimeout), 426 } 427 } 428 429 func (delegate *connDelegate) Read(b []byte) (n int, err error) { 430 setReadDeadlineErr := delegate.SetReadDeadline(time.Now().Add(delegate.socketTimeout)) 431 flag := isDebugLogEnabled() 432 433 if setReadDeadlineErr != nil && flag { 434 doLog(LEVEL_DEBUG, "Failed to set read deadline with reason: %v, but it's ok", setReadDeadlineErr) 435 } 436 437 n, err = delegate.conn.Read(b) 438 setReadDeadlineErr = delegate.SetReadDeadline(time.Now().Add(delegate.finalTimeout)) 439 if setReadDeadlineErr != nil && flag { 440 doLog(LEVEL_DEBUG, "Failed to set read deadline with reason: %v, but it's ok", setReadDeadlineErr) 441 } 442 return n, err 443 } 444 445 func (delegate *connDelegate) Write(b []byte) (n int, err error) { 446 setWriteDeadlineErr := delegate.SetWriteDeadline(time.Now().Add(delegate.socketTimeout)) 447 flag := isDebugLogEnabled() 448 if setWriteDeadlineErr != nil && flag { 449 doLog(LEVEL_DEBUG, "Failed to set write deadline with reason: %v, but it's ok", setWriteDeadlineErr) 450 } 451 452 n, err = delegate.conn.Write(b) 453 finalTimeout := time.Now().Add(delegate.finalTimeout) 454 setWriteDeadlineErr = delegate.SetWriteDeadline(finalTimeout) 455 if setWriteDeadlineErr != nil && flag { 456 doLog(LEVEL_DEBUG, "Failed to set write deadline with reason: %v, but it's ok", setWriteDeadlineErr) 457 } 458 setReadDeadlineErr := delegate.SetReadDeadline(finalTimeout) 459 if setReadDeadlineErr != nil && flag { 460 doLog(LEVEL_DEBUG, "Failed to set read deadline with reason: %v, but it's ok", setReadDeadlineErr) 461 } 462 return n, err 463 } 464 465 func (delegate *connDelegate) Close() error { 466 return delegate.conn.Close() 467 } 468 469 func (delegate *connDelegate) LocalAddr() net.Addr { 470 return delegate.conn.LocalAddr() 471 } 472 473 func (delegate *connDelegate) RemoteAddr() net.Addr { 474 return delegate.conn.RemoteAddr() 475 } 476 477 func (delegate *connDelegate) SetDeadline(t time.Time) error { 478 return delegate.conn.SetDeadline(t) 479 } 480 481 func (delegate *connDelegate) SetReadDeadline(t time.Time) error { 482 return delegate.conn.SetReadDeadline(t) 483 } 484 485 func (delegate *connDelegate) SetWriteDeadline(t time.Time) error { 486 return delegate.conn.SetWriteDeadline(t) 487 }