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// Response is an object represents executed request and its values.
17type Response struct {
18	Request     *Request
19	RawResponse *http.Response
20
21	body       []byte
22	size       int64
23	receivedAt time.Time
24}
25
26// Body method returns HTTP response as []byte array for the executed request.
27// Note: `Response.Body` might be nil, if `Request.SetOutput` is used.
28func (r *Response) Body() []byte {
29	if r.RawResponse == nil {
30		return []byte{}
31	}
32	return r.body
33}
34
35// Status method returns the HTTP status string for the executed request.
36//	Example: 200 OK
37func (r *Response) Status() string {
38	if r.RawResponse == nil {
39		return ""
40	}
41
42	return r.RawResponse.Status
43}
44
45// StatusCode method returns the HTTP status code for the executed request.
46//	Example: 200
47func (r *Response) StatusCode() int {
48	if r.RawResponse == nil {
49		return 0
50	}
51
52	return r.RawResponse.StatusCode
53}
54
55// Result method returns the response value as an object if it has one
56func (r *Response) Result() interface{} {
57	return r.Request.Result
58}
59
60// Error method returns the error object if it has one
61func (r *Response) Error() interface{} {
62	return r.Request.Error
63}
64
65// Header method returns the response headers
66func (r *Response) Header() http.Header {
67	if r.RawResponse == nil {
68		return http.Header{}
69	}
70
71	return r.RawResponse.Header
72}
73
74// Cookies method to access all the response cookies
75func (r *Response) Cookies() []*http.Cookie {
76	if r.RawResponse == nil {
77		return make([]*http.Cookie, 0)
78	}
79
80	return r.RawResponse.Cookies()
81}
82
83// String method returns the body of the server response as String.
84func (r *Response) String() string {
85	if r.body == nil {
86		return ""
87	}
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// See `response.ReceivedAt` to know when client recevied response and see `response.Request.Time` to know
94// when client sent a request.
95func (r *Response) Time() time.Duration {
96	return r.receivedAt.Sub(r.Request.Time)
97}
98
99// ReceivedAt method returns when response got recevied from server for the request.
100func (r *Response) ReceivedAt() time.Time {
101	return r.receivedAt
102}
103
104// Size method returns the HTTP response size in bytes. Ya, you can relay on HTTP `Content-Length` header,
105// however it won't be good for chucked transfer/compressed response. Since Resty calculates response size
106// at the client end. You will get actual size of the http response.
107func (r *Response) Size() int64 {
108	return r.size
109}
110
111// RawBody method exposes the HTTP raw response body. Use this method in-conjunction with `SetDoNotParseResponse`
112// option otherwise you get an error as `read err: http: read on closed response body`.
113//
114// Do not forget to close the body, otherwise you might get into connection leaks, no connection reuse.
115// Basically you have taken over the control of response parsing from `Resty`.
116func (r *Response) RawBody() io.ReadCloser {
117	if r.RawResponse == nil {
118		return nil
119	}
120	return r.RawResponse.Body
121}
122
123// IsSuccess method returns true if HTTP status code >= 200 and <= 299 otherwise false.
124func (r *Response) IsSuccess() bool {
125	return r.StatusCode() > 199 && r.StatusCode() < 300
126}
127
128// IsError method returns true if HTTP status code >= 400 otherwise false.
129func (r *Response) IsError() bool {
130	return r.StatusCode() > 399
131}
132
133func (r *Response) fmtBodyString(sl int64) string {
134	if r.body != nil {
135		if int64(len(r.body)) > sl {
136			return fmt.Sprintf("***** RESPONSE TOO LARGE (size - %d) *****", len(r.body))
137		}
138		ct := r.Header().Get(hdrContentTypeKey)
139		if IsJSONType(ct) {
140			out := acquireBuffer()
141			defer releaseBuffer(out)
142			if err := json.Indent(out, r.body, "", "   "); err == nil {
143				return out.String()
144			}
145		}
146		return r.String()
147	}
148
149	return "***** NO CONTENT *****"
150}
151