Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(43)

Delta Between Two Patch Sets: src/pkg/http/request.go

Issue 4607052: code review 4607052: os.Error API: don't export os.ErrorString, use os.NewEr... (Closed)
Left Patch Set: diff -r 3a5f42c956e0 https://go.googlecode.com/hg/ Created 13 years, 9 months ago
Right Patch Set: diff -r 6e3e06fb2dc3 https://go.googlecode.com/hg/ Created 13 years, 9 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « src/pkg/http/fs.go ('k') | src/pkg/http/transport.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 // Copyright 2009 The Go Authors. All rights reserved. 1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 // HTTP Request reading and parsing. 5 // HTTP Request reading and parsing.
6 6
7 // Package http implements parsing of HTTP requests, replies, and URLs and 7 // Package http implements parsing of HTTP requests, replies, and URLs and
8 // provides an extensible HTTP server and a basic HTTP client. 8 // provides an extensible HTTP server and a basic HTTP client.
9 package http 9 package http
10 10
(...skipping 21 matching lines...) Expand all
32 chunkSize = 4 << 10 // 4 KB chunks 32 chunkSize = 4 << 10 // 4 KB chunks
33 defaultMaxMemory = 32 << 20 // 32 MB 33 defaultMaxMemory = 32 << 20 // 32 MB
34 ) 34 )
35 35
36 // ErrMissingFile is returned by FormFile when the provided file field name 36 // ErrMissingFile is returned by FormFile when the provided file field name
37 // is either not present in the request or not a file field. 37 // is either not present in the request or not a file field.
38 var ErrMissingFile = os.NewError("http: no such file") 38 var ErrMissingFile = os.NewError("http: no such file")
39 39
40 // HTTP request parsing errors. 40 // HTTP request parsing errors.
41 type ProtocolError struct { 41 type ProtocolError struct {
42 » os.Error 42 » ErrorString string
43 } 43 }
44
45 func (err *ProtocolError) String() string { return err.ErrorString }
44 46
45 var ( 47 var (
46 » ErrLineTooLong = &ProtocolError{os.NewError("header line too lo ng")} 48 » ErrLineTooLong = &ProtocolError{"header line too long"}
47 » ErrHeaderTooLong = &ProtocolError{os.NewError("header too long")} 49 » ErrHeaderTooLong = &ProtocolError{"header too long"}
48 » ErrShortBody = &ProtocolError{os.NewError("entity body too sh ort")} 50 » ErrShortBody = &ProtocolError{"entity body too short"}
49 » ErrNotSupported = &ProtocolError{os.NewError("feature not suppor ted")} 51 » ErrNotSupported = &ProtocolError{"feature not supported"}
50 » ErrUnexpectedTrailer = &ProtocolError{os.NewError("trailer header wit hout chunked transfer encoding")} 52 » ErrUnexpectedTrailer = &ProtocolError{"trailer header without chunked transfer encoding"}
51 » ErrMissingContentLength = &ProtocolError{os.NewError("missing ContentLen gth in HEAD response")} 53 » ErrMissingContentLength = &ProtocolError{"missing ContentLength in HEAD response"}
52 » ErrNotMultipart = &ProtocolError{os.NewError("request Content-Ty pe isn't multipart/form-data")} 54 » ErrNotMultipart = &ProtocolError{"request Content-Type isn't mul tipart/form-data"}
53 » ErrMissingBoundary = &ProtocolError{os.NewError("no multipart bound ary param Content-Type")} 55 » ErrMissingBoundary = &ProtocolError{"no multipart boundary param Co ntent-Type"}
54 ) 56 )
55 57
56 type badStringError struct { 58 type badStringError struct {
57 what string 59 what string
58 str string 60 str string
59 } 61 }
60 62
61 func (e *badStringError) String() string { return fmt.Sprintf("%s %q", e.what, e .str) } 63 func (e *badStringError) String() string { return fmt.Sprintf("%s %q", e.what, e .str) }
62 64
63 var reqExcludeHeader = map[string]bool{ 65 // Headers that Request.Write handles itself and should be skipped.
66 var reqWriteExcludeHeader = map[string]bool{
64 "Host": true, 67 "Host": true,
65 "User-Agent": true, 68 "User-Agent": true,
66 "Referer": true,
67 "Content-Length": true, 69 "Content-Length": true,
68 "Transfer-Encoding": true, 70 "Transfer-Encoding": true,
69 "Trailer": true, 71 "Trailer": true,
70 } 72 }
71 73
72 // A Request represents a parsed HTTP request header. 74 // A Request represents a parsed HTTP request header.
73 type Request struct { 75 type Request struct {
74 Method string // GET, POST, PUT, etc. 76 Method string // GET, POST, PUT, etc.
75 RawURL string // The raw URL given in the request. 77 RawURL string // The raw URL given in the request.
76 URL *URL // Parsed URL. 78 URL *URL // Parsed URL.
(...skipping 18 matching lines...) Expand all
95 // "Accept-Language": {"en-us"}, 97 // "Accept-Language": {"en-us"},
96 // "Connection": {"keep-alive"}, 98 // "Connection": {"keep-alive"},
97 // } 99 // }
98 // 100 //
99 // HTTP defines that header names are case-insensitive. 101 // HTTP defines that header names are case-insensitive.
100 // The request parser implements this by canonicalizing the 102 // The request parser implements this by canonicalizing the
101 // name, making the first character and any characters 103 // name, making the first character and any characters
102 // following a hyphen uppercase and the rest lowercase. 104 // following a hyphen uppercase and the rest lowercase.
103 Header Header 105 Header Header
104 106
105 // Cookie records the HTTP cookies sent with the request.
106 Cookie []*Cookie
107
108 // The message body. 107 // The message body.
109 Body io.ReadCloser 108 Body io.ReadCloser
110 109
111 // ContentLength records the length of the associated content. 110 // ContentLength records the length of the associated content.
112 // The value -1 indicates that the length is unknown. 111 // The value -1 indicates that the length is unknown.
113 // Values >= 0 indicate that the given number of bytes may be read from Body. 112 // Values >= 0 indicate that the given number of bytes may be read from Body.
114 ContentLength int64 113 ContentLength int64
115 114
116 // TransferEncoding lists the transfer encodings from outermost to inner most. 115 // TransferEncoding lists the transfer encodings from outermost to inner most.
117 // An empty list denotes the "identity" encoding. 116 // An empty list denotes the "identity" encoding.
118 TransferEncoding []string 117 TransferEncoding []string
119 118
120 // Whether to close the connection after replying to this request. 119 // Whether to close the connection after replying to this request.
121 Close bool 120 Close bool
122 121
123 // The host on which the URL is sought. 122 // The host on which the URL is sought.
124 // Per RFC 2616, this is either the value of the Host: header 123 // Per RFC 2616, this is either the value of the Host: header
125 // or the host name given in the URL itself. 124 // or the host name given in the URL itself.
126 Host string 125 Host string
127
128 // The referring URL, if sent in the request.
129 //
130 // Referer is misspelled as in the request itself,
131 // a mistake from the earliest days of HTTP.
132 // This value can also be fetched from the Header map
133 // as Header["Referer"]; the benefit of making it
134 // available as a structure field is that the compiler
135 // can diagnose programs that use the alternate
136 // (correct English) spelling req.Referrer but cannot
137 // diagnose programs that use Header["Referrer"].
138 Referer string
139
140 // The User-Agent: header string, if sent in the request.
141 UserAgent string
142 126
143 // The parsed form. Only available after ParseForm is called. 127 // The parsed form. Only available after ParseForm is called.
144 Form Values 128 Form Values
145 129
146 // The parsed multipart form, including file uploads. 130 // The parsed multipart form, including file uploads.
147 // Only available after ParseMultipartForm is called. 131 // Only available after ParseMultipartForm is called.
148 MultipartForm *multipart.Form 132 MultipartForm *multipart.Form
149 133
150 // Trailer maps trailer keys to values. Like for Header, if the 134 // Trailer maps trailer keys to values. Like for Header, if the
151 // response has multiple trailer lines with the same key, they will be 135 // response has multiple trailer lines with the same key, they will be
(...skipping 17 matching lines...) Expand all
169 TLS *tls.ConnectionState 153 TLS *tls.ConnectionState
170 } 154 }
171 155
172 // ProtoAtLeast returns whether the HTTP protocol used 156 // ProtoAtLeast returns whether the HTTP protocol used
173 // in the request is at least major.minor. 157 // in the request is at least major.minor.
174 func (r *Request) ProtoAtLeast(major, minor int) bool { 158 func (r *Request) ProtoAtLeast(major, minor int) bool {
175 return r.ProtoMajor > major || 159 return r.ProtoMajor > major ||
176 r.ProtoMajor == major && r.ProtoMinor >= minor 160 r.ProtoMajor == major && r.ProtoMinor >= minor
177 } 161 }
178 162
163 // UserAgent returns the client's User-Agent, if sent in the request.
164 func (r *Request) UserAgent() string {
165 return r.Header.Get("User-Agent")
166 }
167
168 // Cookies parses and returns the HTTP cookies sent with the request.
169 func (r *Request) Cookies() []*Cookie {
170 return readCookies(r.Header, "")
171 }
172
173 var ErrNoCookie = os.NewError("http: named cookied not present")
174
175 // Cookie returns the named cookie provided in the request or
176 // ErrNoCookie if not found.
177 func (r *Request) Cookie(name string) (*Cookie, os.Error) {
178 for _, c := range readCookies(r.Header, name) {
179 return c, nil
180 }
181 return nil, ErrNoCookie
182 }
183
184 // AddCookie adds a cookie to the request. Per RFC 6265 section 5.4,
185 // AddCookie does not attach more than one Cookie header field. That
186 // means all cookies, if any, are written into the same line,
187 // separated by semicolon.
188 func (r *Request) AddCookie(c *Cookie) {
189 s := fmt.Sprintf("%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value))
190 if c := r.Header.Get("Cookie"); c != "" {
191 r.Header.Set("Cookie", c+"; "+s)
192 } else {
193 r.Header.Set("Cookie", s)
194 }
195 }
196
197 // Referer returns the referring URL, if sent in the request.
198 //
199 // Referer is misspelled as in the request itself, a mistake from the
200 // earliest days of HTTP. This value can also be fetched from the
201 // Header map as Header["Referer"]; the benefit of making it available
202 // as a method is that the compiler can diagnose programs that use the
203 // alternate (correct English) spelling req.Referrer() but cannot
204 // diagnose programs that use Header["Referrer"].
205 func (r *Request) Referer() string {
206 return r.Header.Get("Referer")
207 }
208
179 // multipartByReader is a sentinel value. 209 // multipartByReader is a sentinel value.
180 // Its presence in Request.MultipartForm indicates that parsing of the request 210 // Its presence in Request.MultipartForm indicates that parsing of the request
181 // body has been handed off to a MultipartReader instead of ParseMultipartFrom. 211 // body has been handed off to a MultipartReader instead of ParseMultipartFrom.
182 var multipartByReader = &multipart.Form{ 212 var multipartByReader = &multipart.Form{
183 Value: make(map[string][]string), 213 Value: make(map[string][]string),
184 File: make(map[string][]*multipart.FileHeader), 214 File: make(map[string][]*multipart.FileHeader),
185 } 215 }
186 216
187 // MultipartReader returns a MIME multipart reader if this is a 217 // MultipartReader returns a MIME multipart reader if this is a
188 // multipart/form-data POST request, else returns nil and an error. 218 // multipart/form-data POST request, else returns nil and an error.
189 // Use this function instead of ParseMultipartForm to 219 // Use this function instead of ParseMultipartForm to
190 // process the request body as a stream. 220 // process the request body as a stream.
191 func (r *Request) MultipartReader() (multipart.Reader, os.Error) { 221 func (r *Request) MultipartReader() (*multipart.Reader, os.Error) {
192 if r.MultipartForm == multipartByReader { 222 if r.MultipartForm == multipartByReader {
193 return nil, os.NewError("http: MultipartReader called twice") 223 return nil, os.NewError("http: MultipartReader called twice")
194 } 224 }
195 if r.MultipartForm != nil { 225 if r.MultipartForm != nil {
196 return nil, os.NewError("http: multipart handled by ParseMultipa rtForm") 226 return nil, os.NewError("http: multipart handled by ParseMultipa rtForm")
197 } 227 }
198 r.MultipartForm = multipartByReader 228 r.MultipartForm = multipartByReader
199 return r.multipartReader() 229 return r.multipartReader()
200 } 230 }
201 231
202 func (r *Request) multipartReader() (multipart.Reader, os.Error) { 232 func (r *Request) multipartReader() (*multipart.Reader, os.Error) {
203 v := r.Header.Get("Content-Type") 233 v := r.Header.Get("Content-Type")
204 if v == "" { 234 if v == "" {
205 return nil, ErrNotMultipart 235 return nil, ErrNotMultipart
206 } 236 }
207 d, params := mime.ParseMediaType(v) 237 d, params := mime.ParseMediaType(v)
208 if d != "multipart/form-data" { 238 if d != "multipart/form-data" {
209 return nil, ErrNotMultipart 239 return nil, ErrNotMultipart
210 } 240 }
211 boundary, ok := params["boundary"] 241 boundary, ok := params["boundary"]
212 if !ok { 242 if !ok {
(...skipping 10 matching lines...) Expand all
223 return def 253 return def
224 } 254 }
225 255
226 const defaultUserAgent = "Go http package" 256 const defaultUserAgent = "Go http package"
227 257
228 // Write writes an HTTP/1.1 request -- header and body -- in wire format. 258 // Write writes an HTTP/1.1 request -- header and body -- in wire format.
229 // This method consults the following fields of req: 259 // This method consults the following fields of req:
230 // Host 260 // Host
231 // RawURL, if non-empty, or else URL 261 // RawURL, if non-empty, or else URL
232 // Method (defaults to "GET") 262 // Method (defaults to "GET")
233 //» UserAgent (defaults to defaultUserAgent) 263 //» Header
234 //» Referer
235 //» Header (only keys not already in this list)
236 //» Cookie
237 // ContentLength 264 // ContentLength
238 // TransferEncoding 265 // TransferEncoding
239 // Body 266 // Body
240 // 267 //
241 // If Body is present, Content-Length is <= 0 and TransferEncoding 268 // If Body is present, Content-Length is <= 0 and TransferEncoding
242 // hasn't been set to "identity", Write adds "Transfer-Encoding: 269 // hasn't been set to "identity", Write adds "Transfer-Encoding:
243 // chunked" to the header. Body is closed after it is sent. 270 // chunked" to the header. Body is closed after it is sent.
244 func (req *Request) Write(w io.Writer) os.Error { 271 func (req *Request) Write(w io.Writer) os.Error {
245 return req.write(w, false) 272 return req.write(w, false)
246 } 273 }
(...skipping 27 matching lines...) Expand all
274 uri = "/" + uri 301 uri = "/" + uri
275 } 302 }
276 uri = req.URL.Scheme + "://" + host + uri 303 uri = req.URL.Scheme + "://" + host + uri
277 } 304 }
278 } 305 }
279 306
280 fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), uri) 307 fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), uri)
281 308
282 // Header lines 309 // Header lines
283 fmt.Fprintf(w, "Host: %s\r\n", host) 310 fmt.Fprintf(w, "Host: %s\r\n", host)
284 » fmt.Fprintf(w, "User-Agent: %s\r\n", valueOrDefault(req.UserAgent, defau ltUserAgent)) 311
285 » if req.Referer != "" { 312 » // Use the defaultUserAgent unless the Header contains one, which
286 » » fmt.Fprintf(w, "Referer: %s\r\n", req.Referer) 313 » // may be blank to not send the header.
314 » userAgent := defaultUserAgent
315 » if req.Header != nil {
316 » » if ua := req.Header["User-Agent"]; len(ua) > 0 {
317 » » » userAgent = ua[0]
318 » » }
319 » }
320 » if userAgent != "" {
321 » » fmt.Fprintf(w, "User-Agent: %s\r\n", userAgent)
287 } 322 }
288 323
289 // Process Body,ContentLength,Close,Trailer 324 // Process Body,ContentLength,Close,Trailer
290 tw, err := newTransferWriter(req) 325 tw, err := newTransferWriter(req)
291 if err != nil { 326 if err != nil {
292 return err 327 return err
293 } 328 }
294 err = tw.WriteHeader(w) 329 err = tw.WriteHeader(w)
295 if err != nil { 330 if err != nil {
296 return err 331 return err
297 } 332 }
298 333
299 // TODO: split long values? (If so, should share code with Conn.Write) 334 // TODO: split long values? (If so, should share code with Conn.Write)
300 » // TODO: if Header includes values for Host, User-Agent, or Referer, thi s 335 » err = req.Header.WriteSubset(w, reqWriteExcludeHeader)
301 » // may conflict with the User-Agent or Referer headers we add manually.
302 » // One solution would be to remove the Host, UserAgent, and Referer fiel ds
303 » // from Request, and introduce Request methods along the lines of
304 » // Response.{GetHeader,AddHeader} and string constants for "Host",
305 » // "User-Agent" and "Referer".
306 » err = req.Header.WriteSubset(w, reqExcludeHeader)
307 if err != nil { 336 if err != nil {
308 return err
309 }
310
311 if err = writeCookies(w, req.Cookie); err != nil {
312 return err 337 return err
313 } 338 }
314 339
315 io.WriteString(w, "\r\n") 340 io.WriteString(w, "\r\n")
316 341
317 // Write body and trailer 342 // Write body and trailer
318 err = tw.WriteBody(w) 343 err = tw.WriteBody(w)
319 if err != nil { 344 if err != nil {
320 return err 345 return err
321 } 346 }
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after
551 // GET http://www.google.com/index.html HTTP/1.1 576 // GET http://www.google.com/index.html HTTP/1.1
552 // Host: doesntmatter 577 // Host: doesntmatter
553 // the same. In the second case, any Host line is ignored. 578 // the same. In the second case, any Host line is ignored.
554 req.Host = req.URL.Host 579 req.Host = req.URL.Host
555 if req.Host == "" { 580 if req.Host == "" {
556 req.Host = req.Header.Get("Host") 581 req.Host = req.Header.Get("Host")
557 } 582 }
558 req.Header.Del("Host") 583 req.Header.Del("Host")
559 584
560 fixPragmaCacheControl(req.Header) 585 fixPragmaCacheControl(req.Header)
561
562 // Pull out useful fields as a convenience to clients.
563 req.Referer = req.Header.Get("Referer")
564 req.Header.Del("Referer")
565
566 req.UserAgent = req.Header.Get("User-Agent")
567 req.Header.Del("User-Agent")
568 586
569 // TODO: Parse specific header values: 587 // TODO: Parse specific header values:
570 // Accept 588 // Accept
571 // Accept-Encoding 589 // Accept-Encoding
572 // Accept-Language 590 // Accept-Language
573 // Authorization 591 // Authorization
574 // Cache-Control 592 // Cache-Control
575 // Connection 593 // Connection
576 // Date 594 // Date
577 // Expect 595 // Expect
(...skipping 12 matching lines...) Expand all
590 // Upgrade 608 // Upgrade
591 // User-Agent 609 // User-Agent
592 // Via 610 // Via
593 // Warning 611 // Warning
594 612
595 err = readTransfer(req, b) 613 err = readTransfer(req, b)
596 if err != nil { 614 if err != nil {
597 return nil, err 615 return nil, err
598 } 616 }
599 617
600 req.Cookie = readCookies(req.Header)
601
602 return req, nil 618 return req, nil
603 } 619 }
604 620
605 // Values maps a string key to a list of values. 621 // Values maps a string key to a list of values.
606 // It is typically used for query parameters and form values. 622 // It is typically used for query parameters and form values.
607 // Unlike in the Header map, the keys in a Values map 623 // Unlike in the Header map, the keys in a Values map
608 // are case-sensitive. 624 // are case-sensitive.
609 type Values map[string][]string 625 type Values map[string][]string
610 626
611 // Get gets the first value associated with the given key. 627 // Get gets the first value associated with the given key.
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
794 func (r *Request) expectsContinue() bool { 810 func (r *Request) expectsContinue() bool {
795 return strings.ToLower(r.Header.Get("Expect")) == "100-continue" 811 return strings.ToLower(r.Header.Get("Expect")) == "100-continue"
796 } 812 }
797 813
798 func (r *Request) wantsHttp10KeepAlive() bool { 814 func (r *Request) wantsHttp10KeepAlive() bool {
799 if r.ProtoMajor != 1 || r.ProtoMinor != 0 { 815 if r.ProtoMajor != 1 || r.ProtoMinor != 0 {
800 return false 816 return false
801 } 817 }
802 return strings.Contains(strings.ToLower(r.Header.Get("Connection")), "ke ep-alive") 818 return strings.Contains(strings.ToLower(r.Header.Get("Connection")), "ke ep-alive")
803 } 819 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b