diff options
Diffstat (limited to 'libgo/go/net/http/request.go')
-rw-r--r-- | libgo/go/net/http/request.go | 199 |
1 files changed, 129 insertions, 70 deletions
diff --git a/libgo/go/net/http/request.go b/libgo/go/net/http/request.go index 57b5d09484..487eebcb84 100644 --- a/libgo/go/net/http/request.go +++ b/libgo/go/net/http/request.go @@ -10,6 +10,7 @@ import ( "bufio" "bytes" "crypto/tls" + "encoding/base64" "errors" "fmt" "io" @@ -20,6 +21,7 @@ import ( "net/url" "strconv" "strings" + "sync" ) const ( @@ -68,18 +70,31 @@ var reqWriteExcludeHeader = map[string]bool{ // A Request represents an HTTP request received by a server // or to be sent by a client. +// +// The field semantics differ slightly between client and server +// usage. In addition to the notes on the fields below, see the +// documentation for Request.Write and RoundTripper. type Request struct { - Method string // GET, POST, PUT, etc. + // Method specifies the HTTP method (GET, POST, PUT, etc.). + // For client requests an empty string means GET. + Method string - // URL is created from the URI supplied on the Request-Line - // as stored in RequestURI. + // URL specifies either the URI being requested (for server + // requests) or the URL to access (for client requests). + // + // For server requests the URL is parsed from the URI + // supplied on the Request-Line as stored in RequestURI. For + // most requests, fields other than Path and RawQuery will be + // empty. (See RFC 2616, Section 5.1.2) // - // For most requests, fields other than Path and RawQuery - // will be empty. (See RFC 2616, Section 5.1.2) + // For client requests, the URL's Host specifies the server to + // connect to, while the Request's Host field optionally + // specifies the Host header value to send in the HTTP + // request. URL *url.URL // The protocol version for incoming requests. - // Outgoing requests always use HTTP/1.1. + // Client requests always use HTTP/1.1. Proto string // "HTTP/1.0" ProtoMajor int // 1 ProtoMinor int // 0 @@ -103,15 +118,20 @@ type Request struct { // The request parser implements this by canonicalizing the // name, making the first character and any characters // following a hyphen uppercase and the rest lowercase. + // + // For client requests certain headers are automatically + // added and may override values in Header. + // + // See the documentation for the Request.Write method. Header Header // Body is the request's body. // - // For client requests, a nil body means the request has no + // For client requests a nil body means the request has no // body, such as a GET request. The HTTP Client's Transport // is responsible for calling the Close method. // - // For server requests, the Request Body is always non-nil + // For server requests the Request Body is always non-nil // but will return EOF immediately when no body is present. // The Server will close the request body. The ServeHTTP // Handler does not need to. @@ -121,7 +141,7 @@ type Request struct { // The value -1 indicates that the length is unknown. // Values >= 0 indicate that the given number of bytes may // be read from Body. - // For outgoing requests, a value of 0 means unknown if Body is not nil. + // For client requests, a value of 0 means unknown if Body is not nil. ContentLength int64 // TransferEncoding lists the transfer encodings from outermost to @@ -132,13 +152,18 @@ type Request struct { TransferEncoding []string // Close indicates whether to close the connection after - // replying to this request. + // replying to this request (for servers) or after sending + // the request (for clients). Close bool - // The host on which the URL is sought. - // Per RFC 2616, this is either the value of the Host: header - // or the host name given in the URL itself. + // For server requests Host specifies the host on which the + // URL is sought. Per RFC 2616, this is either the value of + // the "Host" header or the host name given in the URL itself. // It may be of the form "host:port". + // + // For client requests Host optionally overrides the Host + // header to send. If empty, the Request.Write method uses + // the value of URL.Host. Host string // Form contains the parsed form data, including both the URL @@ -158,12 +183,24 @@ type Request struct { // The HTTP client ignores MultipartForm and uses Body instead. MultipartForm *multipart.Form - // Trailer maps trailer keys to values. Like for Header, if the - // response has multiple trailer lines with the same key, they will be - // concatenated, delimited by commas. - // For server requests, Trailer is only populated after Body has been - // closed or fully consumed. - // Trailer support is only partially complete. + // Trailer specifies additional headers that are sent after the request + // body. + // + // For server requests the Trailer map initially contains only the + // trailer keys, with nil values. (The client declares which trailers it + // will later send.) While the handler is reading from Body, it must + // not reference Trailer. After reading from Body returns EOF, Trailer + // can be read again and will contain non-nil values, if they were sent + // by the client. + // + // For client requests Trailer must be initialized to a map containing + // the trailer keys to later send. The values may be nil or their final + // values. The ContentLength must be 0 or -1, to send a chunked request. + // After the HTTP request is sent the map values can be updated while + // the request body is read. Once the body returns EOF, the caller must + // not mutate Trailer. + // + // Few HTTP clients, servers, or proxies support HTTP trailers. Trailer Header // RemoteAddr allows HTTP servers and other software to record @@ -354,10 +391,16 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err w = bw } - fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), ruri) + _, err := fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), ruri) + if err != nil { + return err + } // Header lines - fmt.Fprintf(w, "Host: %s\r\n", host) + _, err = fmt.Fprintf(w, "Host: %s\r\n", host) + if err != nil { + return err + } // Use the defaultUserAgent unless the Header contains one, which // may be blank to not send the header. @@ -368,7 +411,10 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err } } if userAgent != "" { - fmt.Fprintf(w, "User-Agent: %s\r\n", userAgent) + _, err = fmt.Fprintf(w, "User-Agent: %s\r\n", userAgent) + if err != nil { + return err + } } // Process Body,ContentLength,Close,Trailer @@ -381,7 +427,6 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err return err } - // TODO: split long values? (If so, should share code with Conn.Write) err = req.Header.WriteSubset(w, reqWriteExcludeHeader) if err != nil { return err @@ -394,7 +439,10 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err } } - io.WriteString(w, "\r\n") + _, err = io.WriteString(w, "\r\n") + if err != nil { + return err + } // Write body and trailer err = tw.WriteBody(w) @@ -474,6 +522,35 @@ func NewRequest(method, urlStr string, body io.Reader) (*Request, error) { return req, nil } +// BasicAuth returns the username and password provided in the request's +// Authorization header, if the request uses HTTP Basic Authentication. +// See RFC 2617, Section 2. +func (r *Request) BasicAuth() (username, password string, ok bool) { + auth := r.Header.Get("Authorization") + if auth == "" { + return + } + return parseBasicAuth(auth) +} + +// parseBasicAuth parses an HTTP Basic Authentication string. +// "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true). +func parseBasicAuth(auth string) (username, password string, ok bool) { + if !strings.HasPrefix(auth, "Basic ") { + return + } + c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(auth, "Basic ")) + if err != nil { + return + } + cs := string(c) + s := strings.IndexByte(cs, ':') + if s < 0 { + return + } + return cs[:s], cs[s+1:], true +} + // SetBasicAuth sets the request's Authorization header to use HTTP // Basic Authentication with the provided username and password. // @@ -494,25 +571,20 @@ func parseRequestLine(line string) (method, requestURI, proto string, ok bool) { return line[:s1], line[s1+1 : s2], line[s2+1:], true } -// TODO(bradfitz): use a sync.Cache when available -var textprotoReaderCache = make(chan *textproto.Reader, 4) +var textprotoReaderPool sync.Pool func newTextprotoReader(br *bufio.Reader) *textproto.Reader { - select { - case r := <-textprotoReaderCache: - r.R = br - return r - default: - return textproto.NewReader(br) + if v := textprotoReaderPool.Get(); v != nil { + tr := v.(*textproto.Reader) + tr.R = br + return tr } + return textproto.NewReader(br) } func putTextprotoReader(r *textproto.Reader) { r.R = nil - select { - case textprotoReaderCache <- r: - default: - } + textprotoReaderPool.Put(r) } // ReadRequest reads and parses a request from b. @@ -588,37 +660,12 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) { fixPragmaCacheControl(req.Header) - // TODO: Parse specific header values: - // Accept - // Accept-Encoding - // Accept-Language - // Authorization - // Cache-Control - // Connection - // Date - // Expect - // From - // If-Match - // If-Modified-Since - // If-None-Match - // If-Range - // If-Unmodified-Since - // Max-Forwards - // Proxy-Authorization - // Referer [sic] - // TE (transfer-codings) - // Trailer - // Transfer-Encoding - // Upgrade - // User-Agent - // Via - // Warning - err = readTransfer(req, b) if err != nil { return nil, err } + req.Close = shouldClose(req.ProtoMajor, req.ProtoMinor, req.Header, false) return req, nil } @@ -677,6 +724,11 @@ func parsePostForm(r *Request) (vs url.Values, err error) { return } ct := r.Header.Get("Content-Type") + // RFC 2616, section 7.2.1 - empty type + // SHOULD be treated as application/octet-stream + if ct == "" { + ct = "application/octet-stream" + } ct, _, err = mime.ParseMediaType(ct) switch { case ct == "application/x-www-form-urlencoded": @@ -707,7 +759,7 @@ func parsePostForm(r *Request) (vs url.Values, err error) { // orders to call too many functions here. // Clean this up and write more tests. // request_test.go contains the start of this, - // in TestRequestMultipartCallOrder. + // in TestParseMultipartFormOrder and others. } return } @@ -727,7 +779,7 @@ func parsePostForm(r *Request) (vs url.Values, err error) { func (r *Request) ParseForm() error { var err error if r.PostForm == nil { - if r.Method == "POST" || r.Method == "PUT" { + if r.Method == "POST" || r.Method == "PUT" || r.Method == "PATCH" { r.PostForm, err = parsePostForm(r) } if r.PostForm == nil { @@ -780,9 +832,7 @@ func (r *Request) ParseMultipartForm(maxMemory int64) error { } mr, err := r.multipartReader() - if err == ErrNotMultipart { - return nil - } else if err != nil { + if err != nil { return err } @@ -800,8 +850,10 @@ func (r *Request) ParseMultipartForm(maxMemory int64) error { // FormValue returns the first value for the named component of the query. // POST and PUT body parameters take precedence over URL query string values. -// FormValue calls ParseMultipartForm and ParseForm if necessary. -// To access multiple values of the same key use ParseForm. +// FormValue calls ParseMultipartForm and ParseForm if necessary and ignores +// any errors returned by these functions. +// To access multiple values of the same key, call ParseForm and +// then inspect Request.Form directly. func (r *Request) FormValue(key string) string { if r.Form == nil { r.ParseMultipartForm(defaultMaxMemory) @@ -814,7 +866,8 @@ func (r *Request) FormValue(key string) string { // PostFormValue returns the first value for the named component of the POST // or PUT request body. URL query parameters are ignored. -// PostFormValue calls ParseMultipartForm and ParseForm if necessary. +// PostFormValue calls ParseMultipartForm and ParseForm if necessary and ignores +// any errors returned by these functions. func (r *Request) PostFormValue(key string) string { if r.PostForm == nil { r.ParseMultipartForm(defaultMaxMemory) @@ -860,3 +913,9 @@ func (r *Request) wantsHttp10KeepAlive() bool { func (r *Request) wantsClose() bool { return hasToken(r.Header.get("Connection"), "close") } + +func (r *Request) closeBody() { + if r.Body != nil { + r.Body.Close() + } +} |