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  }