request.go raw

   1  package session
   2  
   3  import (
   4  	"bytes"
   5  	"encoding/json"
   6  	"errors"
   7  	"fmt"
   8  	"io"
   9  	"net/http"
  10  	"net/http/httputil"
  11  )
  12  
  13  var (
  14  	// ErrInvalidArgument is returned when invalid number of arguments were supplied to a function
  15  	ErrInvalidArgument = errors.New("invalid arguments provided")
  16  	// ErrMarshaling represents marshaling error
  17  	ErrMarshaling = errors.New("marshaling input")
  18  	// ErrUnmarshaling represents unmarshaling error
  19  	ErrUnmarshaling = errors.New("unmarshaling output")
  20  )
  21  
  22  // Exec will sign and execute the request using the client edgegrid.Config
  23  func (s *session) Exec(r *http.Request, out interface{}, in ...interface{}) (*http.Response, error) {
  24  	if len(in) > 1 {
  25  		return nil, fmt.Errorf("%w: %s", ErrInvalidArgument, "'in' argument must have 0 or 1 value")
  26  	}
  27  	log := s.Log(r.Context())
  28  
  29  	// Apply any context header overrides
  30  	if o, ok := r.Context().Value(contextOptionKey).(*contextOptions); ok {
  31  		for k, v := range o.header {
  32  			r.Header[k] = v
  33  		}
  34  	}
  35  
  36  	r.URL.RawQuery = r.URL.Query().Encode()
  37  	if r.UserAgent() == "" {
  38  		r.Header.Set("User-Agent", s.userAgent)
  39  	}
  40  
  41  	if r.Header.Get("Content-Type") == "" {
  42  		r.Header.Set("Content-Type", "application/json")
  43  	}
  44  
  45  	if r.Header.Get("Accept") == "" {
  46  		r.Header.Set("Accept", "application/json")
  47  	}
  48  
  49  	if r.URL.Scheme == "" {
  50  		r.URL.Scheme = "https"
  51  	}
  52  
  53  	if len(in) > 0 {
  54  		data, err := json.Marshal(in[0])
  55  		if err != nil {
  56  			return nil, fmt.Errorf("%w: %s", ErrMarshaling, err)
  57  		}
  58  
  59  		r.Body = io.NopCloser(bytes.NewBuffer(data))
  60  		r.ContentLength = int64(len(data))
  61  	}
  62  
  63  	s.client.CheckRedirect = func(req *http.Request, _ []*http.Request) error {
  64  		return s.Sign(req)
  65  	}
  66  
  67  	if err := s.Sign(r); err != nil {
  68  		return nil, err
  69  	}
  70  
  71  	if s.trace {
  72  		data, err := httputil.DumpRequestOut(r, true)
  73  		if err != nil {
  74  			log.Error("Failed to dump request", "error", err)
  75  		} else {
  76  			log.Debug(string(data))
  77  		}
  78  	}
  79  
  80  	resp, err := s.client.Do(r)
  81  	if err != nil {
  82  		return nil, err
  83  	}
  84  
  85  	if s.trace {
  86  		data, err := httputil.DumpResponse(resp, true)
  87  		if err != nil {
  88  			log.Error("Failed to dump response", "error", err)
  89  		} else {
  90  			log.Debug(string(data))
  91  		}
  92  	}
  93  
  94  	if out != nil &&
  95  		resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices &&
  96  		resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusResetContent {
  97  		data, err := io.ReadAll(resp.Body)
  98  		if err != nil {
  99  			return nil, err
 100  		}
 101  		resp.Body = io.NopCloser(bytes.NewBuffer(data))
 102  
 103  		if err := json.Unmarshal(data, out); err != nil {
 104  			return nil, fmt.Errorf("%w: %s", ErrUnmarshaling, err)
 105  		}
 106  	}
 107  
 108  	return resp, nil
 109  }
 110  
 111  // Sign will only sign a request
 112  func (s *session) Sign(r *http.Request) error {
 113  	s.signer.SignRequest(r)
 114  
 115  	if s.requestLimit != 0 {
 116  		s.signer.CheckRequestLimit(s.requestLimit)
 117  	}
 118  	return nil
 119  }
 120