Source file src/pkg/net/http/response.go
1
2
3
4
5
6
7 package http
8
9 import (
10 "bufio"
11 "errors"
12 "io"
13 "net/textproto"
14 "net/url"
15 "strconv"
16 "strings"
17 )
18
19 var respExcludeHeader = map[string]bool{
20 "Content-Length": true,
21 "Transfer-Encoding": true,
22 "Trailer": true,
23 }
24
25
26
27 type Response struct {
28 Status string
29 StatusCode int
30 Proto string
31 ProtoMajor int
32 ProtoMinor int
33
34
35
36
37
38
39
40
41
42 Header Header
43
44
45
46
47
48
49
50
51
52 Body io.ReadCloser
53
54
55
56
57
58 ContentLength int64
59
60
61
62 TransferEncoding []string
63
64
65
66
67 Close bool
68
69
70
71 Trailer Header
72
73
74
75
76 Request *Request
77 }
78
79
80 func (r *Response) Cookies() []*Cookie {
81 return readSetCookies(r.Header)
82 }
83
84 var ErrNoLocation = errors.New("http: no Location header in response")
85
86
87
88
89
90 func (r *Response) Location() (*url.URL, error) {
91 lv := r.Header.Get("Location")
92 if lv == "" {
93 return nil, ErrNoLocation
94 }
95 if r.Request != nil && r.Request.URL != nil {
96 return r.Request.URL.Parse(lv)
97 }
98 return url.Parse(lv)
99 }
100
101
102
103
104
105
106
107 func ReadResponse(r *bufio.Reader, req *Request) (resp *Response, err error) {
108
109 tp := textproto.NewReader(r)
110 resp = new(Response)
111
112 resp.Request = req
113
114
115 line, err := tp.ReadLine()
116 if err != nil {
117 if err == io.EOF {
118 err = io.ErrUnexpectedEOF
119 }
120 return nil, err
121 }
122 f := strings.SplitN(line, " ", 3)
123 if len(f) < 2 {
124 return nil, &badStringError{"malformed HTTP response", line}
125 }
126 reasonPhrase := ""
127 if len(f) > 2 {
128 reasonPhrase = f[2]
129 }
130 resp.Status = f[1] + " " + reasonPhrase
131 resp.StatusCode, err = strconv.Atoi(f[1])
132 if err != nil {
133 return nil, &badStringError{"malformed HTTP status code", f[1]}
134 }
135
136 resp.Proto = f[0]
137 var ok bool
138 if resp.ProtoMajor, resp.ProtoMinor, ok = ParseHTTPVersion(resp.Proto); !ok {
139 return nil, &badStringError{"malformed HTTP version", resp.Proto}
140 }
141
142
143 mimeHeader, err := tp.ReadMIMEHeader()
144 if err != nil {
145 return nil, err
146 }
147 resp.Header = Header(mimeHeader)
148
149 fixPragmaCacheControl(resp.Header)
150
151 err = readTransfer(resp, r)
152 if err != nil {
153 return nil, err
154 }
155
156 return resp, nil
157 }
158
159
160
161
162
163 func fixPragmaCacheControl(header Header) {
164 if hp, ok := header["Pragma"]; ok && len(hp) > 0 && hp[0] == "no-cache" {
165 if _, presentcc := header["Cache-Control"]; !presentcc {
166 header["Cache-Control"] = []string{"no-cache"}
167 }
168 }
169 }
170
171
172
173 func (r *Response) ProtoAtLeast(major, minor int) bool {
174 return r.ProtoMajor > major ||
175 r.ProtoMajor == major && r.ProtoMinor >= minor
176 }
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191 func (r *Response) Write(w io.Writer) error {
192
193
194 text := r.Status
195 if text == "" {
196 var ok bool
197 text, ok = statusText[r.StatusCode]
198 if !ok {
199 text = "status code " + strconv.Itoa(r.StatusCode)
200 }
201 }
202 protoMajor, protoMinor := strconv.Itoa(r.ProtoMajor), strconv.Itoa(r.ProtoMinor)
203 statusCode := strconv.Itoa(r.StatusCode) + " "
204 text = strings.TrimPrefix(text, statusCode)
205 io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n")
206
207
208 tw, err := newTransferWriter(r)
209 if err != nil {
210 return err
211 }
212 err = tw.WriteHeader(w)
213 if err != nil {
214 return err
215 }
216
217
218 err = r.Header.WriteSubset(w, respExcludeHeader)
219 if err != nil {
220 return err
221 }
222
223
224 io.WriteString(w, "\r\n")
225
226
227 err = tw.WriteBody(w)
228 if err != nil {
229 return err
230 }
231
232
233 return nil
234 }
View as plain text