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