// Copyright (c) 2015-2021 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 } // 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 r.body == nil { 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 r.body != nil { 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 *****" }