yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/huawei/obs/http.go (about)

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