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