-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathresponse.go
139 lines (110 loc) · 2.58 KB
/
response.go
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
package rawhttp
import (
"bufio"
"io"
"io/ioutil"
"strconv"
"strings"
)
// A Response wraps the HTTP response from the server
type Response struct {
rawStatus string
headers []string
body []byte
}
// Header finds and returns the value of a header on the response.
// An empty string is returned if no match is found.
func (r Response) Header(search string) string {
search = strings.ToLower(search)
for _, header := range r.headers {
p := strings.SplitN(header, ":", 2)
if len(p) != 2 {
continue
}
if strings.ToLower(p[0]) == search {
return strings.TrimSpace(p[1])
}
}
return ""
}
// ParseLocation parses the Location header of a response,
// using the initial request for context on relative URLs
func (r Response) ParseLocation(req *Request) string {
loc := r.Header("Location")
if loc == "" {
return ""
}
// Relative locations need the context of the request
if len(loc) > 2 && loc[:2] == "//" {
return req.Scheme + ":" + loc
}
if len(loc) > 0 && loc[0] == '/' {
return req.Scheme + "://" + req.Hostname + loc
}
return loc
}
// StatusLine returns the HTTP status line from the response
func (r Response) StatusLine() string {
return r.rawStatus
}
// StatusCode returns the HTTP status code as a string; e.g. 200
func (r Response) StatusCode() string {
parts := strings.SplitN(r.rawStatus, " ", 3)
if len(parts) != 3 {
return ""
}
return parts[1]
}
// Headers returns the response headers
func (r Response) Headers() []string {
return r.headers
}
// Body returns the response body
func (r Response) Body() []byte {
return r.body
}
// addHeader adds a header to the *Response
func (r *Response) addHeader(header string) {
r.headers = append(r.headers, header)
}
// newResponse accepts an io.Reader, reads the response
// headers and body and returns a new *Response and any
// error that occured.
func newResponse(conn io.Reader) (*Response, error) {
r := bufio.NewReader(conn)
resp := &Response{}
s, err := r.ReadString('\n')
if err != nil {
return nil, err
}
resp.rawStatus = strings.TrimSpace(s)
for {
line, err := r.ReadString('\n')
line = strings.TrimSpace(line)
if err != nil || line == "" {
break
}
resp.addHeader(line)
}
if cl := resp.Header("Content-Length"); cl != "" {
length, err := strconv.Atoi(cl)
if err != nil {
return nil, err
}
if length > 0 {
b := make([]byte, length)
_, err = io.ReadAtLeast(r, b, length)
if err != nil {
return nil, err
}
resp.body = b
}
} else {
b, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
resp.body = b
}
return resp, nil
}