1// Copyright (c) 2015-2019 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 5package resty 6 7import ( 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. 21type 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. 33func (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// Example: 200 OK 42func (r *Response) Status() string { 43 if r.RawResponse == nil { 44 return "" 45 } 46 return r.RawResponse.Status 47} 48 49// StatusCode method returns the HTTP status code for the executed request. 50// Example: 200 51func (r *Response) StatusCode() int { 52 if r.RawResponse == nil { 53 return 0 54 } 55 return r.RawResponse.StatusCode 56} 57 58// Result method returns the response value as an object if it has one 59func (r *Response) Result() interface{} { 60 return r.Request.Result 61} 62 63// Error method returns the error object if it has one 64func (r *Response) Error() interface{} { 65 return r.Request.Error 66} 67 68// Header method returns the response headers 69func (r *Response) Header() http.Header { 70 if r.RawResponse == nil { 71 return http.Header{} 72 } 73 return r.RawResponse.Header 74} 75 76// Cookies method to access all the response cookies 77func (r *Response) Cookies() []*http.Cookie { 78 if r.RawResponse == nil { 79 return make([]*http.Cookie, 0) 80 } 81 return r.RawResponse.Cookies() 82} 83 84// String method returns the body of the server response as String. 85func (r *Response) String() string { 86 if r.body == nil { 87 return "" 88 } 89 return strings.TrimSpace(string(r.body)) 90} 91 92// Time method returns the time of HTTP response time that from request we sent and received a request. 93// 94// See `Response.ReceivedAt` to know when client recevied response and see `Response.Request.Time` to know 95// when client sent a request. 96func (r *Response) Time() time.Duration { 97 if r.Request.clientTrace != nil { 98 return r.receivedAt.Sub(r.Request.clientTrace.getConn) 99 } 100 return r.receivedAt.Sub(r.Request.Time) 101} 102 103// ReceivedAt method returns when response got recevied from server for the request. 104func (r *Response) ReceivedAt() time.Time { 105 return r.receivedAt 106} 107 108// Size method returns the HTTP response size in bytes. Ya, you can relay on HTTP `Content-Length` header, 109// however it won't be good for chucked transfer/compressed response. Since Resty calculates response size 110// at the client end. You will get actual size of the http response. 111func (r *Response) Size() int64 { 112 return r.size 113} 114 115// RawBody method exposes the HTTP raw response body. Use this method in-conjunction with `SetDoNotParseResponse` 116// option otherwise you get an error as `read err: http: read on closed response body`. 117// 118// Do not forget to close the body, otherwise you might get into connection leaks, no connection reuse. 119// Basically you have taken over the control of response parsing from `Resty`. 120func (r *Response) RawBody() io.ReadCloser { 121 if r.RawResponse == nil { 122 return nil 123 } 124 return r.RawResponse.Body 125} 126 127// IsSuccess method returns true if HTTP status `code >= 200 and <= 299` otherwise false. 128func (r *Response) IsSuccess() bool { 129 return r.StatusCode() > 199 && r.StatusCode() < 300 130} 131 132// IsError method returns true if HTTP status `code >= 400` otherwise false. 133func (r *Response) IsError() bool { 134 return r.StatusCode() > 399 135} 136 137//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ 138// Response Unexported methods 139//_______________________________________________________________________ 140 141func (r *Response) fmtBodyString(sl int64) string { 142 if r.body != nil { 143 if int64(len(r.body)) > sl { 144 return fmt.Sprintf("***** RESPONSE TOO LARGE (size - %d) *****", len(r.body)) 145 } 146 ct := r.Header().Get(hdrContentTypeKey) 147 if IsJSONType(ct) { 148 out := acquireBuffer() 149 defer releaseBuffer(out) 150 err := json.Indent(out, r.body, "", " ") 151 if err != nil { 152 return fmt.Sprintf("*** Error: Unable to format response body - \"%s\" ***\n\nLog Body as-is:\n%s", err, r.String()) 153 } 154 return out.String() 155 } 156 return r.String() 157 } 158 159 return "***** NO CONTENT *****" 160} 161