github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/resty/response.go (about)

     1  // Copyright (c) 2015-2021 Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
     2  // resty source code and usage is governed by a MIT style
     3  // license that can be found in the LICENSE file.
     4  
     5  package resty
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  	"io"
    11  	"net/http"
    12  	"strings"
    13  	"time"
    14  )
    15  
    16  //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
    17  // Response struct and methods
    18  //_______________________________________________________________________
    19  
    20  // Response struct holds response values of executed request.
    21  type Response struct {
    22  	Request     *Request
    23  	RawResponse *http.Response
    24  
    25  	body       []byte
    26  	size       int64
    27  	receivedAt time.Time
    28  }
    29  
    30  // Body method returns HTTP response as []byte array for the executed request.
    31  //
    32  // Note: `Response.Body` might be nil, if `Request.SetOutput` is used.
    33  func (r *Response) Body() []byte {
    34  	if r.RawResponse == nil {
    35  		return []byte{}
    36  	}
    37  	return r.body
    38  }
    39  
    40  // Status method returns the HTTP status string for the executed request.
    41  //
    42  //	Example: 200 OK
    43  func (r *Response) Status() string {
    44  	if r.RawResponse == nil {
    45  		return ""
    46  	}
    47  	return r.RawResponse.Status
    48  }
    49  
    50  // StatusCode method returns the HTTP status code for the executed request.
    51  //
    52  //	Example: 200
    53  func (r *Response) StatusCode() int {
    54  	if r.RawResponse == nil {
    55  		return 0
    56  	}
    57  	return r.RawResponse.StatusCode
    58  }
    59  
    60  // Proto method returns the HTTP response protocol used for the request.
    61  func (r *Response) Proto() string {
    62  	if r.RawResponse == nil {
    63  		return ""
    64  	}
    65  	return r.RawResponse.Proto
    66  }
    67  
    68  // Result method returns the response value as an object if it has one
    69  func (r *Response) Result() interface{} { return r.Request.Result }
    70  
    71  // Error method returns the error object if it has one
    72  func (r *Response) Error() interface{} { return r.Request.Error }
    73  
    74  // Header method returns the response headers
    75  func (r *Response) Header() http.Header {
    76  	if r.RawResponse == nil {
    77  		return http.Header{}
    78  	}
    79  	return r.RawResponse.Header
    80  }
    81  
    82  // Cookies method to access all the response cookies
    83  func (r *Response) Cookies() []*http.Cookie {
    84  	if r.RawResponse == nil {
    85  		return make([]*http.Cookie, 0)
    86  	}
    87  	return r.RawResponse.Cookies()
    88  }
    89  
    90  // String method returns the body of the server response as String.
    91  func (r *Response) String() string {
    92  	if r.body == nil {
    93  		return ""
    94  	}
    95  	return strings.TrimSpace(string(r.body))
    96  }
    97  
    98  // Time method returns the time of HTTP response time that from request we sent and received a request.
    99  //
   100  // See `Response.ReceivedAt` to know when client received response and see `Response.Request.Time` to know
   101  // when client sent a request.
   102  func (r *Response) Time() time.Duration {
   103  	if r.Request.clientTrace != nil {
   104  		return r.Request.TraceInfo().TotalTime
   105  	}
   106  	return r.receivedAt.Sub(r.Request.Time)
   107  }
   108  
   109  // ReceivedAt method returns when response got received from server for the request.
   110  func (r *Response) ReceivedAt() time.Time { return r.receivedAt }
   111  
   112  // Size method returns the HTTP response size in bytes. Ya, you can relay on HTTP `Content-Length` header,
   113  // however it won't be good for chucked transfer/compressed response. Since Resty calculates response size
   114  // at the client end. You will get actual size of the http response.
   115  func (r *Response) Size() int64 { return r.size }
   116  
   117  // RawBody method exposes the HTTP raw response body. Use this method in-conjunction with `SetDoNotParseResponse`
   118  // option otherwise you get an error as `read err: http: read on closed response body`.
   119  //
   120  // Do not forget to close the body, otherwise you might get into connection leaks, no connection reuse.
   121  // Basically you have taken over the control of response parsing from `Resty`.
   122  func (r *Response) RawBody() io.ReadCloser {
   123  	if r.RawResponse == nil {
   124  		return nil
   125  	}
   126  	return r.RawResponse.Body
   127  }
   128  
   129  // IsSuccess method returns true if HTTP status `code >= 200 and <= 299` otherwise false.
   130  func (r *Response) IsSuccess() bool { return r.StatusCode() > 199 && r.StatusCode() < 300 }
   131  
   132  // IsError method returns true if HTTP status `code >= 400` otherwise false.
   133  func (r *Response) IsError() bool { return r.StatusCode() > 399 }
   134  
   135  //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
   136  // Response Unexported methods
   137  //_______________________________________________________________________
   138  
   139  func (r *Response) setReceivedAt() {
   140  	r.receivedAt = time.Now()
   141  	if r.Request.clientTrace != nil {
   142  		r.Request.clientTrace.endTime = r.receivedAt
   143  	}
   144  }
   145  
   146  func (r *Response) fmtBodyString(sl int64) string {
   147  	if r.body != nil {
   148  		if int64(len(r.body)) > sl {
   149  			return fmt.Sprintf("***** RESPONSE TOO LARGE (size - %d) *****", len(r.body))
   150  		}
   151  		ct := r.Header().Get(hdrContentTypeKey)
   152  		if IsJSONType(ct) {
   153  			out := acquireBuffer()
   154  			defer releaseBuffer(out)
   155  			err := json.Indent(out, r.body, "", "   ")
   156  			if err != nil {
   157  				return fmt.Sprintf("*** Error: Unable to format response body - \"%s\" ***\n\nLog Body as-is:\n%s", err, r.String())
   158  			}
   159  			return out.String()
   160  		}
   161  		return r.String()
   162  	}
   163  
   164  	return "***** NO CONTENT *****"
   165  }