package session import ( "bytes" "encoding/json" "errors" "fmt" "io" "net/http" "net/http/httputil" ) var ( // ErrInvalidArgument is returned when invalid number of arguments were supplied to a function ErrInvalidArgument = errors.New("invalid arguments provided") // ErrMarshaling represents marshaling error ErrMarshaling = errors.New("marshaling input") // ErrUnmarshaling represents unmarshaling error ErrUnmarshaling = errors.New("unmarshaling output") ) // Exec will sign and execute the request using the client edgegrid.Config func (s *session) Exec(r *http.Request, out interface{}, in ...interface{}) (*http.Response, error) { if len(in) > 1 { return nil, fmt.Errorf("%w: %s", ErrInvalidArgument, "'in' argument must have 0 or 1 value") } log := s.Log(r.Context()) // Apply any context header overrides if o, ok := r.Context().Value(contextOptionKey).(*contextOptions); ok { for k, v := range o.header { r.Header[k] = v } } r.URL.RawQuery = r.URL.Query().Encode() if r.UserAgent() == "" { r.Header.Set("User-Agent", s.userAgent) } if r.Header.Get("Content-Type") == "" { r.Header.Set("Content-Type", "application/json") } if r.Header.Get("Accept") == "" { r.Header.Set("Accept", "application/json") } if r.URL.Scheme == "" { r.URL.Scheme = "https" } if len(in) > 0 { data, err := json.Marshal(in[0]) if err != nil { return nil, fmt.Errorf("%w: %s", ErrMarshaling, err) } r.Body = io.NopCloser(bytes.NewBuffer(data)) r.ContentLength = int64(len(data)) } s.client.CheckRedirect = func(req *http.Request, _ []*http.Request) error { return s.Sign(req) } if err := s.Sign(r); err != nil { return nil, err } if s.trace { data, err := httputil.DumpRequestOut(r, true) if err != nil { log.Error("Failed to dump request", "error", err) } else { log.Debug(string(data)) } } resp, err := s.client.Do(r) if err != nil { return nil, err } if s.trace { data, err := httputil.DumpResponse(resp, true) if err != nil { log.Error("Failed to dump response", "error", err) } else { log.Debug(string(data)) } } if out != nil && resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices && resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusResetContent { data, err := io.ReadAll(resp.Body) if err != nil { return nil, err } resp.Body = io.NopCloser(bytes.NewBuffer(data)) if err := json.Unmarshal(data, out); err != nil { return nil, fmt.Errorf("%w: %s", ErrUnmarshaling, err) } } return resp, nil } // Sign will only sign a request func (s *session) Sign(r *http.Request) error { s.signer.SignRequest(r) if s.requestLimit != 0 { s.signer.CheckRequestLimit(s.requestLimit) } return nil }