github.com/Ingenico-ePayments/connect-sdk-go@v0.0.0-20240318153750-1f8cd329b9c9/communicator/Communicator.go (about)

     1  package communicator
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"net/url"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/Ingenico-ePayments/connect-sdk-go/communicator/communication"
    14  	sdkErrors "github.com/Ingenico-ePayments/connect-sdk-go/errors"
    15  	"github.com/Ingenico-ePayments/connect-sdk-go/logging"
    16  	"github.com/Ingenico-ePayments/connect-sdk-go/logging/obfuscation"
    17  )
    18  
    19  var (
    20  	// ErrNilSession occurs when the given session is nil
    21  	ErrNilSession = errors.New("session is nil")
    22  	// ErrNilMarshaller occurs when the given marshaller is nil
    23  	ErrNilMarshaller = errors.New("marshaller is nil")
    24  )
    25  
    26  // A Communicator is used to communicate with the Ingenico ePayments platform web services.
    27  // It contains all the logic to transform a request object to a HTTP request and a HTTP response to a response object.
    28  // It is also thread safe.
    29  type Communicator struct {
    30  	session    *Session
    31  	marshaller Marshaller
    32  }
    33  
    34  // Session returns the session of this Communicator
    35  func (c *Communicator) Session() *Session {
    36  	return c.session
    37  }
    38  
    39  // Marshaller returns the marshaller of this Communicator
    40  func (c *Communicator) Marshaller() Marshaller {
    41  	return c.marshaller
    42  }
    43  
    44  // Close closes the connection of the Communicator
    45  func (c *Communicator) Close() error {
    46  	return c.session.Connection().Close()
    47  }
    48  
    49  // SetBodyObfuscator sets the body obfuscator to use.
    50  func (c *Communicator) SetBodyObfuscator(bodyObfuscator obfuscation.BodyObfuscator) {
    51  	if connection, ok := c.session.connection.(obfuscation.Capable); ok {
    52  		connection.SetBodyObfuscator(bodyObfuscator)
    53  	}
    54  }
    55  
    56  // SetHeaderObfuscator sets the header obfuscator to use.
    57  func (c *Communicator) SetHeaderObfuscator(headerObfuscator obfuscation.HeaderObfuscator) {
    58  	if connection, ok := c.session.connection.(obfuscation.Capable); ok {
    59  		connection.SetHeaderObfuscator(headerObfuscator)
    60  	}
    61  }
    62  
    63  // EnableLogging turns on logging using the given communicator logger.
    64  func (c *Communicator) EnableLogging(logger logging.CommunicatorLogger) {
    65  	c.session.Connection().EnableLogging(logger)
    66  }
    67  
    68  // DisableLogging turns off logging.
    69  func (c *Communicator) DisableLogging() {
    70  	c.session.Connection().DisableLogging()
    71  }
    72  
    73  // Get corresponds to the HTTP Get method
    74  //
    75  // relativePath is the Path to call, relative to the base URI
    76  // requestHeaders is an optimal list of request headers
    77  // requestParameters is an optional set of request parameters. If not used set to nil
    78  // context is an optional Call context which can be used
    79  // expectedObject is a reference to the expected response object
    80  //
    81  // Possibly returns an error
    82  func (c *Communicator) Get(relativePath string, requestHeaders []communication.Header, requestParameters ParamRequest, context communication.CallContext, expectedObject interface{}) error {
    83  	connection := c.session.Connection()
    84  	var requestParameterList []RequestParam
    85  	var err error
    86  	if requestParameters != nil {
    87  		requestParameterList = requestParameters.ToRequestParameters()
    88  	}
    89  	uri, err := c.toAbsoluteURI(relativePath, requestParameterList)
    90  	if err != nil {
    91  		return err
    92  	}
    93  	if requestHeaders == nil {
    94  		requestHeaders = []communication.Header{}
    95  	}
    96  	requestHeaders, err = c.addGenericHeaders(http.MethodGet, uri, requestHeaders, context)
    97  	if err != nil {
    98  		return err
    99  	}
   100  
   101  	_, err = connection.Get(uri, requestHeaders, communication.ResponseHandlerFunc(func(statusCode int, headers []communication.Header, reader io.Reader) (interface{}, error) {
   102  		return nil, c.processResponse(statusCode, reader, headers, relativePath, context, expectedObject)
   103  	}))
   104  
   105  	return err
   106  }
   107  
   108  // GetWithHandler corresponds to the HTTP Get method
   109  //
   110  // relativePath is the Path to call, relative to the base URI
   111  // requestHeaders is an optimal list of request headers
   112  // requestParameters is an optional set of request parameters. If not used set to nil
   113  // context is an optional Call context which can be used
   114  // expectedObject is a reference to the expected response object
   115  // bodyHandler is a BodyHandler that handles the body stream
   116  //
   117  // Possibly returns an error
   118  func (c *Communicator) GetWithHandler(relativePath string, requestHeaders []communication.Header, requestParameters ParamRequest, context communication.CallContext, bodyHandler BodyHandler) error {
   119  	connection := c.session.Connection()
   120  	var requestParameterList []RequestParam
   121  	var err error
   122  	if requestParameters != nil {
   123  		requestParameterList = requestParameters.ToRequestParameters()
   124  	}
   125  	uri, err := c.toAbsoluteURI(relativePath, requestParameterList)
   126  	if err != nil {
   127  		return err
   128  	}
   129  	if requestHeaders == nil {
   130  		requestHeaders = []communication.Header{}
   131  	}
   132  	requestHeaders, err = c.addGenericHeaders(http.MethodGet, uri, requestHeaders, context)
   133  	if err != nil {
   134  		return err
   135  	}
   136  
   137  	_, err = connection.Get(uri, requestHeaders, communication.ResponseHandlerFunc(func(statusCode int, headers []communication.Header, reader io.Reader) (interface{}, error) {
   138  		return nil, c.processResponseWithHandler(statusCode, reader, headers, relativePath, context, bodyHandler)
   139  	}))
   140  
   141  	return err
   142  }
   143  
   144  // Delete corresponds to the HTTP Delete method
   145  //
   146  // relativePath is the Path to call, relative to the base URI
   147  // requestHeaders is an optimal list of request headers
   148  // requestParameters is an optional set of request parameters. If not used set to nil
   149  // context is an optional Call context which can be used
   150  // expectedObject is a reference to the expected response object
   151  //
   152  // Possibly returns an error
   153  func (c *Communicator) Delete(relativePath string, requestHeaders []communication.Header, requestParameters ParamRequest, context communication.CallContext, expectedObject interface{}) error {
   154  	connection := c.session.Connection()
   155  	var requestParameterList []RequestParam
   156  	var err error
   157  	if requestParameters != nil {
   158  		requestParameterList = requestParameters.ToRequestParameters()
   159  	}
   160  	uri, err := c.toAbsoluteURI(relativePath, requestParameterList)
   161  	if err != nil {
   162  		return err
   163  	}
   164  	if requestHeaders == nil {
   165  		requestHeaders = []communication.Header{}
   166  	}
   167  	requestHeaders, err = c.addGenericHeaders(http.MethodDelete, uri, requestHeaders, context)
   168  	if err != nil {
   169  		return err
   170  	}
   171  
   172  	_, err = connection.Delete(uri, requestHeaders, communication.ResponseHandlerFunc(func(statusCode int, headers []communication.Header, reader io.Reader) (interface{}, error) {
   173  		return nil, c.processResponse(statusCode, reader, headers, relativePath, context, expectedObject)
   174  	}))
   175  
   176  	return err
   177  }
   178  
   179  // DeleteWithHandler corresponds to the HTTP Delete method
   180  //
   181  // relativePath is the Path to call, relative to the base URI
   182  // requestHeaders is an optimal list of request headers
   183  // requestParameters is an optional set of request parameters. If not used set to nil
   184  // context is an optional Call context which can be used
   185  // expectedObject is a reference to the expected response object
   186  // bodyHandler is a BodyHandler that handles the body stream
   187  //
   188  // Possibly returns an error
   189  func (c *Communicator) DeleteWithHandler(relativePath string, requestHeaders []communication.Header, requestParameters ParamRequest, context communication.CallContext, bodyHandler BodyHandler) error {
   190  	connection := c.session.Connection()
   191  	var requestParameterList []RequestParam
   192  	var err error
   193  	if requestParameters != nil {
   194  		requestParameterList = requestParameters.ToRequestParameters()
   195  	}
   196  	uri, err := c.toAbsoluteURI(relativePath, requestParameterList)
   197  	if err != nil {
   198  		return err
   199  	}
   200  	if requestHeaders == nil {
   201  		requestHeaders = []communication.Header{}
   202  	}
   203  	requestHeaders, err = c.addGenericHeaders(http.MethodDelete, uri, requestHeaders, context)
   204  	if err != nil {
   205  		return err
   206  	}
   207  
   208  	_, err = connection.Delete(uri, requestHeaders, communication.ResponseHandlerFunc(func(statusCode int, headers []communication.Header, reader io.Reader) (interface{}, error) {
   209  		return nil, c.processResponseWithHandler(statusCode, reader, headers, relativePath, context, bodyHandler)
   210  	}))
   211  
   212  	return err
   213  }
   214  
   215  // Post corresponds to the HTTP Post method
   216  //
   217  // relativePath is the Path to call, relative to the base URI
   218  // requestHeaders is an optimal list of request headers
   219  // requestParameters is an optional set of request parameters. If not used set it to nil
   220  // requestBody is the body of the request. If not used set to nil
   221  // context is an optional Call context which can be used. If not used set it to nil
   222  // expectedObject is a reference to the expected response object
   223  //
   224  // Possibly returns an error
   225  func (c *Communicator) Post(relativePath string, requestHeaders []communication.Header, requestParameters ParamRequest, requestBody interface{}, context communication.CallContext, expectedResponse interface{}) error {
   226  	if multipartObject, ok := requestBody.(communication.MultipartFormDataObject); ok {
   227  		return c.postMultipart(relativePath, requestHeaders, requestParameters, &multipartObject, context, expectedResponse)
   228  	}
   229  	if multipartObject, ok := requestBody.(*communication.MultipartFormDataObject); ok {
   230  		return c.postMultipart(relativePath, requestHeaders, requestParameters, multipartObject, context, expectedResponse)
   231  	}
   232  	if multipartRequest, ok := requestBody.(MultipartFormDataRequest); ok {
   233  		return c.postMultipart(relativePath, requestHeaders, requestParameters, multipartRequest.ToMultipartFormDataObject(), context, expectedResponse)
   234  	}
   235  
   236  	connection := c.session.Connection()
   237  	var requestParameterList []RequestParam
   238  	if requestParameters != nil {
   239  		requestParameterList = requestParameters.ToRequestParameters()
   240  	}
   241  	uri, err := c.toAbsoluteURI(relativePath, requestParameterList)
   242  	if err != nil {
   243  		return err
   244  	}
   245  	if requestHeaders == nil {
   246  		requestHeaders = []communication.Header{}
   247  	}
   248  
   249  	var requestJSON string
   250  	if requestBody != nil {
   251  		jsonHeader, _ := communication.NewHeader("Content-Type", "application/json")
   252  		requestHeaders = append(requestHeaders, *jsonHeader)
   253  
   254  		requestJSON, err = c.marshaller.Marshal(requestBody)
   255  
   256  		if err != nil {
   257  			return err
   258  		}
   259  	}
   260  
   261  	requestHeaders, err = c.addGenericHeaders(http.MethodPost, uri, requestHeaders, context)
   262  	if err != nil {
   263  		return err
   264  	}
   265  	_, err = connection.Post(uri, requestHeaders, requestJSON, communication.ResponseHandlerFunc(func(statusCode int, headers []communication.Header, reader io.Reader) (interface{}, error) {
   266  		return nil, c.processResponse(statusCode, reader, headers, relativePath, context, expectedResponse)
   267  	}))
   268  
   269  	return err
   270  }
   271  
   272  func (c *Communicator) postMultipart(relativePath string, requestHeaders []communication.Header, requestParameters ParamRequest, requestBody *communication.MultipartFormDataObject, context communication.CallContext, expectedResponse interface{}) error {
   273  	connection := c.session.Connection()
   274  	var requestParameterList []RequestParam
   275  	if requestParameters != nil {
   276  		requestParameterList = requestParameters.ToRequestParameters()
   277  	}
   278  	uri, err := c.toAbsoluteURI(relativePath, requestParameterList)
   279  	if err != nil {
   280  		return err
   281  	}
   282  	if requestHeaders == nil {
   283  		requestHeaders = []communication.Header{}
   284  	}
   285  
   286  	multipartHeader, _ := communication.NewHeader("Content-Type", requestBody.GetContentType())
   287  	requestHeaders = append(requestHeaders, *multipartHeader)
   288  
   289  	requestHeaders, err = c.addGenericHeaders(http.MethodPost, uri, requestHeaders, context)
   290  	if err != nil {
   291  		return err
   292  	}
   293  	_, err = connection.PostMultipart(uri, requestHeaders, requestBody, communication.ResponseHandlerFunc(func(statusCode int, headers []communication.Header, reader io.Reader) (interface{}, error) {
   294  		return nil, c.processResponse(statusCode, reader, headers, relativePath, context, expectedResponse)
   295  	}))
   296  
   297  	return err
   298  }
   299  
   300  // PostWithHandler corresponds to the HTTP Post method
   301  //
   302  // relativePath is the Path to call, relative to the base URI
   303  // requestHeaders is an optimal list of request headers
   304  // requestParameters is an optional set of request parameters. If not used set it to nil
   305  // requestBody is the body of the request. If not used set to nil
   306  // context is an optional Call context which can be used. If not used set it to nil
   307  // expectedObject is a reference to the expected response object
   308  // bodyHandler is a BodybodyHandler that handles the body stream
   309  //
   310  // Possibly returns an error
   311  func (c *Communicator) PostWithHandler(relativePath string, requestHeaders []communication.Header, requestParameters ParamRequest, requestBody interface{}, context communication.CallContext, bodyHandler BodyHandler) error {
   312  	if multipartObject, ok := requestBody.(communication.MultipartFormDataObject); ok {
   313  		return c.postMultipartWithHandler(relativePath, requestHeaders, requestParameters, &multipartObject, context, bodyHandler)
   314  	}
   315  	if multipartObject, ok := requestBody.(*communication.MultipartFormDataObject); ok {
   316  		return c.postMultipartWithHandler(relativePath, requestHeaders, requestParameters, multipartObject, context, bodyHandler)
   317  	}
   318  	if multipartRequest, ok := requestBody.(MultipartFormDataRequest); ok {
   319  		return c.postMultipartWithHandler(relativePath, requestHeaders, requestParameters, multipartRequest.ToMultipartFormDataObject(), context, bodyHandler)
   320  	}
   321  
   322  	connection := c.session.Connection()
   323  	var requestParameterList []RequestParam
   324  	if requestParameters != nil {
   325  		requestParameterList = requestParameters.ToRequestParameters()
   326  	}
   327  	uri, err := c.toAbsoluteURI(relativePath, requestParameterList)
   328  	if err != nil {
   329  		return err
   330  	}
   331  	if requestHeaders == nil {
   332  		requestHeaders = []communication.Header{}
   333  	}
   334  
   335  	var requestJSON string
   336  	if requestBody != nil {
   337  		jsonHeader, _ := communication.NewHeader("Content-Type", "application/json")
   338  		requestHeaders = append(requestHeaders, *jsonHeader)
   339  
   340  		requestJSON, err = c.marshaller.Marshal(requestBody)
   341  
   342  		if err != nil {
   343  			return err
   344  		}
   345  	}
   346  
   347  	requestHeaders, err = c.addGenericHeaders(http.MethodPost, uri, requestHeaders, context)
   348  	if err != nil {
   349  		return err
   350  	}
   351  
   352  	_, err = connection.Post(uri, requestHeaders, requestJSON, communication.ResponseHandlerFunc(func(statusCode int, headers []communication.Header, reader io.Reader) (interface{}, error) {
   353  		return nil, c.processResponseWithHandler(statusCode, reader, headers, relativePath, context, bodyHandler)
   354  	}))
   355  
   356  	return err
   357  }
   358  
   359  func (c *Communicator) postMultipartWithHandler(relativePath string, requestHeaders []communication.Header, requestParameters ParamRequest, requestBody *communication.MultipartFormDataObject, context communication.CallContext, bodyHandler BodyHandler) error {
   360  	connection := c.session.Connection()
   361  	var requestParameterList []RequestParam
   362  	if requestParameters != nil {
   363  		requestParameterList = requestParameters.ToRequestParameters()
   364  	}
   365  	uri, err := c.toAbsoluteURI(relativePath, requestParameterList)
   366  	if err != nil {
   367  		return err
   368  	}
   369  	if requestHeaders == nil {
   370  		requestHeaders = []communication.Header{}
   371  	}
   372  
   373  	multipartHeader, _ := communication.NewHeader("Content-Type", requestBody.GetContentType())
   374  	requestHeaders = append(requestHeaders, *multipartHeader)
   375  
   376  	requestHeaders, err = c.addGenericHeaders(http.MethodPost, uri, requestHeaders, context)
   377  	if err != nil {
   378  		return err
   379  	}
   380  
   381  	_, err = connection.PostMultipart(uri, requestHeaders, requestBody, communication.ResponseHandlerFunc(func(statusCode int, headers []communication.Header, reader io.Reader) (interface{}, error) {
   382  		return nil, c.processResponseWithHandler(statusCode, reader, headers, relativePath, context, bodyHandler)
   383  	}))
   384  
   385  	return err
   386  }
   387  
   388  // Put corresponds to the HTTP Put method
   389  //
   390  // relativePath is the Path to call, relative to the base URI
   391  // requestHeaders is an optimal list of request headers
   392  // requestParameters is an optional set of request parameters. If not used set it to nil
   393  // requestBody is the body of the request. If not used set to nil
   394  // context is an optional Call context which can be used. If not used set it to nil
   395  // expectedObject is a reference to the expected response object
   396  //
   397  // Possibly returns an error
   398  func (c *Communicator) Put(relativePath string, requestHeaders []communication.Header, requestParameters ParamRequest, requestBody interface{}, context communication.CallContext, expectedObject interface{}) error {
   399  	if multipartObject, ok := requestBody.(communication.MultipartFormDataObject); ok {
   400  		return c.putMultipart(relativePath, requestHeaders, requestParameters, &multipartObject, context, expectedObject)
   401  	}
   402  	if multipartObject, ok := requestBody.(*communication.MultipartFormDataObject); ok {
   403  		return c.putMultipart(relativePath, requestHeaders, requestParameters, multipartObject, context, expectedObject)
   404  	}
   405  	if multipartRequest, ok := requestBody.(MultipartFormDataRequest); ok {
   406  		return c.putMultipart(relativePath, requestHeaders, requestParameters, multipartRequest.ToMultipartFormDataObject(), context, expectedObject)
   407  	}
   408  
   409  	connection := c.session.Connection()
   410  	var requestParameterList []RequestParam
   411  	var err error
   412  	if requestParameters != nil {
   413  		requestParameterList = requestParameters.ToRequestParameters()
   414  	}
   415  	uri, err := c.toAbsoluteURI(relativePath, requestParameterList)
   416  	if err != nil {
   417  		return err
   418  	}
   419  	if requestHeaders == nil {
   420  		requestHeaders = []communication.Header{}
   421  	}
   422  
   423  	var requestJSON string
   424  	if requestBody != nil {
   425  		jsonHeader, _ := communication.NewHeader("Content-Type", "application/json")
   426  		requestHeaders = append(requestHeaders, *jsonHeader)
   427  		requestJSON, err = c.marshaller.Marshal(requestBody)
   428  
   429  		if err != nil {
   430  			return err
   431  		}
   432  	}
   433  
   434  	requestHeaders, err = c.addGenericHeaders(http.MethodPut, uri, requestHeaders, context)
   435  	if err != nil {
   436  		return err
   437  	}
   438  	_, err = connection.Put(uri, requestHeaders, requestJSON, communication.ResponseHandlerFunc(func(statusCode int, headers []communication.Header, reader io.Reader) (interface{}, error) {
   439  		return nil, c.processResponse(statusCode, reader, headers, relativePath, context, expectedObject)
   440  	}))
   441  
   442  	return err
   443  }
   444  
   445  func (c *Communicator) putMultipart(relativePath string, requestHeaders []communication.Header, requestParameters ParamRequest, requestBody *communication.MultipartFormDataObject, context communication.CallContext, expectedResponse interface{}) error {
   446  	connection := c.session.Connection()
   447  	var requestParameterList []RequestParam
   448  	if requestParameters != nil {
   449  		requestParameterList = requestParameters.ToRequestParameters()
   450  	}
   451  	uri, err := c.toAbsoluteURI(relativePath, requestParameterList)
   452  	if err != nil {
   453  		return err
   454  	}
   455  	if requestHeaders == nil {
   456  		requestHeaders = []communication.Header{}
   457  	}
   458  
   459  	jsonHeader, _ := communication.NewHeader("Content-Type", requestBody.GetContentType())
   460  	requestHeaders = append(requestHeaders, *jsonHeader)
   461  
   462  	requestHeaders, err = c.addGenericHeaders(http.MethodPost, uri, requestHeaders, context)
   463  	if err != nil {
   464  		return err
   465  	}
   466  	_, err = connection.PutMultipart(uri, requestHeaders, requestBody, communication.ResponseHandlerFunc(func(statusCode int, headers []communication.Header, reader io.Reader) (interface{}, error) {
   467  		return nil, c.processResponse(statusCode, reader, headers, relativePath, context, expectedResponse)
   468  	}))
   469  
   470  	return err
   471  }
   472  
   473  // PutWithHandler corresponds to the HTTP Put method
   474  //
   475  // relativePath is the Path to call, relative to the base URI
   476  // requestHeaders is an optimal list of request headers
   477  // requestParameters is an optional set of request parameters. If not used set it to nil
   478  // requestBody is the body of the request. If not used set to nil
   479  // context is an optional Call context which can be used. If not used set it to nil
   480  // expectedObject is a reference to the expected response object
   481  // bodyHandler is a BodyHandler that handles the body stream
   482  //
   483  // Possibly returns an error
   484  func (c *Communicator) PutWithHandler(relativePath string, requestHeaders []communication.Header, requestParameters ParamRequest, requestBody interface{}, context communication.CallContext, bodyHandler BodyHandler) error {
   485  	if multipartObject, ok := requestBody.(communication.MultipartFormDataObject); ok {
   486  		return c.putMultipartWithHandler(relativePath, requestHeaders, requestParameters, &multipartObject, context, bodyHandler)
   487  	}
   488  	if multipartObject, ok := requestBody.(*communication.MultipartFormDataObject); ok {
   489  		return c.putMultipartWithHandler(relativePath, requestHeaders, requestParameters, multipartObject, context, bodyHandler)
   490  	}
   491  	if multipartRequest, ok := requestBody.(MultipartFormDataRequest); ok {
   492  		return c.putMultipartWithHandler(relativePath, requestHeaders, requestParameters, multipartRequest.ToMultipartFormDataObject(), context, bodyHandler)
   493  	}
   494  
   495  	connection := c.session.Connection()
   496  	var requestParameterList []RequestParam
   497  	var err error
   498  	if requestParameters != nil {
   499  		requestParameterList = requestParameters.ToRequestParameters()
   500  	}
   501  	uri, err := c.toAbsoluteURI(relativePath, requestParameterList)
   502  	if err != nil {
   503  		return err
   504  	}
   505  	if requestHeaders == nil {
   506  		requestHeaders = []communication.Header{}
   507  	}
   508  
   509  	var requestJSON string
   510  	if requestBody != nil {
   511  		jsonHeader, _ := communication.NewHeader("Content-Type", "application/json")
   512  		requestHeaders = append(requestHeaders, *jsonHeader)
   513  		requestJSON, err = c.marshaller.Marshal(requestBody)
   514  
   515  		if err != nil {
   516  			return err
   517  		}
   518  	}
   519  
   520  	requestHeaders, err = c.addGenericHeaders(http.MethodPut, uri, requestHeaders, context)
   521  	if err != nil {
   522  		return err
   523  	}
   524  
   525  	_, err = connection.Put(uri, requestHeaders, requestJSON, communication.ResponseHandlerFunc(func(statusCode int, headers []communication.Header, reader io.Reader) (interface{}, error) {
   526  		return nil, c.processResponseWithHandler(statusCode, reader, headers, relativePath, context, bodyHandler)
   527  	}))
   528  
   529  	return err
   530  }
   531  
   532  func (c *Communicator) putMultipartWithHandler(relativePath string, requestHeaders []communication.Header, requestParameters ParamRequest, requestBody *communication.MultipartFormDataObject, context communication.CallContext, bodyHandler BodyHandler) error {
   533  	connection := c.session.Connection()
   534  	var requestParameterList []RequestParam
   535  	if requestParameters != nil {
   536  		requestParameterList = requestParameters.ToRequestParameters()
   537  	}
   538  	uri, err := c.toAbsoluteURI(relativePath, requestParameterList)
   539  	if err != nil {
   540  		return err
   541  	}
   542  	if requestHeaders == nil {
   543  		requestHeaders = []communication.Header{}
   544  	}
   545  
   546  	multipartHeader, _ := communication.NewHeader("Content-Type", requestBody.GetContentType())
   547  	requestHeaders = append(requestHeaders, *multipartHeader)
   548  
   549  	requestHeaders, err = c.addGenericHeaders(http.MethodPost, uri, requestHeaders, context)
   550  	if err != nil {
   551  		return err
   552  	}
   553  
   554  	_, err = connection.PutMultipart(uri, requestHeaders, requestBody, communication.ResponseHandlerFunc(func(statusCode int, headers []communication.Header, reader io.Reader) (interface{}, error) {
   555  		return nil, c.processResponseWithHandler(statusCode, reader, headers, relativePath, context, bodyHandler)
   556  	}))
   557  
   558  	return err
   559  }
   560  
   561  // CloseExpiredConnections is a utility method that delegates the call to this communicator's session's connection.
   562  // Also see Connection.CloseExpiredConnections
   563  func (c *Communicator) CloseExpiredConnections() {
   564  	c.session.Connection().CloseExpiredConnections()
   565  }
   566  
   567  // CloseIdleConnections is a utility method that delegates the call to this communicator's session's connection.
   568  // The duration argument is a specification of how long the connection has to be Idle.
   569  // Also see Connection.CloseIdleConnections
   570  func (c *Communicator) CloseIdleConnections(duration time.Duration) {
   571  	c.session.Connection().CloseIdleConnections(duration)
   572  }
   573  
   574  func (c *Communicator) toAbsoluteURI(relativePath string, requestParameters []RequestParam) (url.URL, error) {
   575  	apiEndpoint := c.session.APIEndpoint()
   576  
   577  	if apiEndpoint.Path != "" {
   578  		return url.URL{}, ErrPathEndpoint
   579  	}
   580  	if apiEndpoint.User != nil || apiEndpoint.Query().Encode() != "" || apiEndpoint.Fragment != "" {
   581  		return url.URL{}, ErrUserInfo
   582  	}
   583  
   584  	var absolutePath string
   585  	if strings.Index(relativePath, "/") == 0 {
   586  		absolutePath = relativePath
   587  	} else {
   588  		absolutePath = "/" + relativePath
   589  	}
   590  
   591  	var rawQuery = ""
   592  	for index, element := range requestParameters {
   593  		if index != 0 {
   594  			rawQuery += "&"
   595  		}
   596  		id := url.QueryEscape(element.Name())
   597  		value := url.QueryEscape(element.Value())
   598  		rawQuery += id + "=" + value
   599  	}
   600  
   601  	var url = url.URL{Scheme: apiEndpoint.Scheme,
   602  		Host:     apiEndpoint.Host,
   603  		Path:     absolutePath,
   604  		RawQuery: rawQuery}
   605  
   606  	return url, nil
   607  }
   608  
   609  // Adds the necessary headers to the given list of headers. This includes the authorization header,
   610  // which uses other headers, so when you need to override this method,
   611  // make sure to call AddGenericHeaders at the end of your overridden method.
   612  func (c *Communicator) addGenericHeaders(httpMethod string, url url.URL, requestHeaders []communication.Header, context communication.CallContext) ([]communication.Header, error) {
   613  	//add server meta info headers
   614  	requestHeaders = append(requestHeaders, c.session.MetaDataProvider().MetaDataHeaders()...)
   615  
   616  	//add date header
   617  	header, err := communication.NewHeader("Date", getHeaderDateString())
   618  	if err != nil {
   619  		return nil, err
   620  	}
   621  	requestHeaders = append(requestHeaders, *header)
   622  
   623  	//add content specific headers
   624  	if context != nil && context.GetIdempotenceKey() != "" {
   625  		header, err = communication.NewHeader("X-GCS-Idempotence-Key", context.GetIdempotenceKey())
   626  		if err != nil {
   627  			return nil, err
   628  		}
   629  		requestHeaders = append(requestHeaders, *header)
   630  	}
   631  
   632  	//add signature
   633  	authenticator := c.session.Authenticator()
   634  	authenticationSignature, err := authenticator.CreateSimpleAuthenticationSignature(httpMethod, url, requestHeaders)
   635  	if err != nil {
   636  		return nil, err
   637  	}
   638  
   639  	header, err = communication.NewHeader("Authorization", authenticationSignature)
   640  	if err != nil {
   641  		return nil, err
   642  	}
   643  	requestHeaders = append(requestHeaders, *header)
   644  	return requestHeaders, nil
   645  }
   646  
   647  // Gets the date in the preferred format for the HTTP date header (RFC1123).
   648  func getHeaderDateString() string {
   649  	return time.Now().UTC().Format(http.TimeFormat)
   650  }
   651  
   652  // Checks the Response for errors and creates an error if necessary.
   653  func (c *Communicator) createErrorIfNecessary(statusCode int, reader io.Reader, headers []communication.Header, requestPath string) error {
   654  	if statusCode < http.StatusOK || statusCode >= http.StatusMultipleChoices {
   655  		bodyBuff, err := ioutil.ReadAll(reader)
   656  		if err != nil {
   657  			return err
   658  		}
   659  		body := string(bodyBuff)
   660  		if body != "" && !isJSON(headers) {
   661  			var cause = sdkErrors.NewResponseError(statusCode, body, headers)
   662  			if statusCode == http.StatusNotFound {
   663  				err, _ := sdkErrors.NewNotFoundErrorVerbose("The requested resource was not found; invalid path: "+requestPath, cause)
   664  				return err
   665  			}
   666  			err, _ := sdkErrors.NewCommunicationError(cause)
   667  			return err
   668  		}
   669  		return sdkErrors.NewResponseError(statusCode, body, headers)
   670  	}
   671  	return nil
   672  }
   673  
   674  func (c *Communicator) processResponse(statusCode int, reader io.Reader, headers []communication.Header, requestPath string, context communication.CallContext, expectedObject interface{}) error {
   675  	if context != nil {
   676  		timestamp, err := getIdempotenceTimestamp(headers)
   677  		if err != nil {
   678  			return err
   679  		}
   680  		context.SetIdempotenceRequestTimestamp(timestamp)
   681  	}
   682  
   683  	err := c.createErrorIfNecessary(statusCode, reader, headers, requestPath)
   684  	if err != nil {
   685  		return err
   686  	}
   687  
   688  	return c.marshaller.UnmarshalFromReader(reader, expectedObject)
   689  }
   690  
   691  func (c *Communicator) processResponseWithHandler(statusCode int, reader io.Reader, headers []communication.Header, requestPath string, context communication.CallContext, bodyHandler BodyHandler) error {
   692  	if context != nil {
   693  		timestamp, err := getIdempotenceTimestamp(headers)
   694  		if err != nil {
   695  			return err
   696  		}
   697  		context.SetIdempotenceRequestTimestamp(timestamp)
   698  	}
   699  
   700  	err := c.createErrorIfNecessary(statusCode, reader, headers, requestPath)
   701  	if err != nil {
   702  		return err
   703  	}
   704  
   705  	return bodyHandler.Handle(headers, reader)
   706  }
   707  
   708  func getIdempotenceTimestamp(headers []communication.Header) (*int64, error) {
   709  	header := communication.Headers(headers).GetHeader("X-GCS-Idempotence-Request-Timestamp")
   710  	if header == nil {
   711  		return nil, nil
   712  	}
   713  
   714  	retValue, err := strconv.ParseInt(header.Value(), 10, 64)
   715  	return &retValue, err
   716  }
   717  
   718  func isJSON(headers []communication.Header) bool {
   719  	header := communication.Headers(headers).GetHeader("Content-Type")
   720  	if header == nil {
   721  		return true
   722  	}
   723  
   724  	contentType := strings.ToLower(header.Value())
   725  
   726  	if contentType == "application/json" {
   727  		return true
   728  	}
   729  
   730  	return strings.HasPrefix(contentType, "application/json")
   731  }
   732  
   733  // NewCommunicator creates a communicator with the given session and marshaller
   734  func NewCommunicator(session *Session, marshaller Marshaller) (*Communicator, error) {
   735  	if session == nil {
   736  		return nil, ErrNilSession
   737  	}
   738  	if marshaller == nil {
   739  		return nil, ErrNilMarshaller
   740  	}
   741  
   742  	return &Communicator{session, marshaller}, nil
   743  }