Imported Upstream version 4.8.1
[platform/upstream/gcc48.git] / libgo / go / net / http / request.go
index f5bc6eb..217f35b 100644 (file)
@@ -19,6 +19,7 @@ import (
        "mime/multipart"
        "net/textproto"
        "net/url"
+       "strconv"
        "strings"
 )
 
@@ -70,7 +71,13 @@ var reqWriteExcludeHeader = map[string]bool{
 // or to be sent by a client.
 type Request struct {
        Method string // GET, POST, PUT, etc.
-       URL    *url.URL
+
+       // URL is created 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)
+       URL *url.URL
 
        // The protocol version for incoming requests.
        // Outgoing requests always use HTTP/1.1.
@@ -123,6 +130,7 @@ type Request struct {
        // 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".
        Host string
 
        // Form contains the parsed form data, including both the URL
@@ -131,6 +139,12 @@ type Request struct {
        // The HTTP client ignores Form and uses Body instead.
        Form url.Values
 
+       // PostForm contains the parsed form data from POST or PUT
+       // body parameters.
+       // This field is only available after ParseForm is called.
+       // The HTTP client ignores PostForm and uses Body instead.
+       PostForm url.Values
+
        // MultipartForm is the parsed multipart form, including file uploads.
        // This field is only available after ParseMultipartForm is called.
        // The HTTP client ignores MultipartForm and uses Body instead.
@@ -317,11 +331,20 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err
        }
        // TODO(bradfitz): escape at least newlines in ruri?
 
-       bw := bufio.NewWriter(w)
-       fmt.Fprintf(bw, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), ruri)
+       // Wrap the writer in a bufio Writer if it's not already buffered.
+       // Don't always call NewWriter, as that forces a bytes.Buffer
+       // and other small bufio Writers to have a minimum 4k buffer
+       // size.
+       var bw *bufio.Writer
+       if _, ok := w.(io.ByteWriter); !ok {
+               bw = bufio.NewWriter(w)
+               w = bw
+       }
+
+       fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), ruri)
 
        // Header lines
-       fmt.Fprintf(bw, "Host: %s\r\n", host)
+       fmt.Fprintf(w, "Host: %s\r\n", host)
 
        // Use the defaultUserAgent unless the Header contains one, which
        // may be blank to not send the header.
@@ -332,7 +355,7 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err
                }
        }
        if userAgent != "" {
-               fmt.Fprintf(bw, "User-Agent: %s\r\n", userAgent)
+               fmt.Fprintf(w, "User-Agent: %s\r\n", userAgent)
        }
 
        // Process Body,ContentLength,Close,Trailer
@@ -340,65 +363,61 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err
        if err != nil {
                return err
        }
-       err = tw.WriteHeader(bw)
+       err = tw.WriteHeader(w)
        if err != nil {
                return err
        }
 
        // TODO: split long values?  (If so, should share code with Conn.Write)
-       err = req.Header.WriteSubset(bw, reqWriteExcludeHeader)
+       err = req.Header.WriteSubset(w, reqWriteExcludeHeader)
        if err != nil {
                return err
        }
 
        if extraHeaders != nil {
-               err = extraHeaders.Write(bw)
+               err = extraHeaders.Write(w)
                if err != nil {
                        return err
                }
        }
 
-       io.WriteString(bw, "\r\n")
+       io.WriteString(w, "\r\n")
 
        // Write body and trailer
-       err = tw.WriteBody(bw)
+       err = tw.WriteBody(w)
        if err != nil {
                return err
        }
 
-       return bw.Flush()
-}
-
-// Convert decimal at s[i:len(s)] to integer,
-// returning value, string position where the digits stopped,
-// and whether there was a valid number (digits, not too big).
-func atoi(s string, i int) (n, i1 int, ok bool) {
-       const Big = 1000000
-       if i >= len(s) || s[i] < '0' || s[i] > '9' {
-               return 0, 0, false
-       }
-       n = 0
-       for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
-               n = n*10 + int(s[i]-'0')
-               if n > Big {
-                       return 0, 0, false
-               }
+       if bw != nil {
+               return bw.Flush()
        }
-       return n, i, true
+       return nil
 }
 
 // ParseHTTPVersion parses a HTTP version string.
 // "HTTP/1.0" returns (1, 0, true).
 func ParseHTTPVersion(vers string) (major, minor int, ok bool) {
-       if len(vers) < 5 || vers[0:5] != "HTTP/" {
+       const Big = 1000000 // arbitrary upper bound
+       switch vers {
+       case "HTTP/1.1":
+               return 1, 1, true
+       case "HTTP/1.0":
+               return 1, 0, true
+       }
+       if !strings.HasPrefix(vers, "HTTP/") {
                return 0, 0, false
        }
-       major, i, ok := atoi(vers, 5)
-       if !ok || i >= len(vers) || vers[i] != '.' {
+       dot := strings.Index(vers, ".")
+       if dot < 0 {
                return 0, 0, false
        }
-       minor, i, ok = atoi(vers, i+1)
-       if !ok || i != len(vers) {
+       major, err := strconv.Atoi(vers[5:dot])
+       if err != nil || major < 0 || major > Big {
+               return 0, 0, false
+       }
+       minor, err = strconv.Atoi(vers[dot+1:])
+       if err != nil || minor < 0 || minor > Big {
                return 0, 0, false
        }
        return major, minor, true
@@ -426,10 +445,12 @@ func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
        }
        if body != nil {
                switch v := body.(type) {
-               case *strings.Reader:
-                       req.ContentLength = int64(v.Len())
                case *bytes.Buffer:
                        req.ContentLength = int64(v.Len())
+               case *bytes.Reader:
+                       req.ContentLength = int64(v.Len())
+               case *strings.Reader:
+                       req.ContentLength = int64(v.Len())
                }
        }
 
@@ -513,9 +534,9 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) {
        // the same.  In the second case, any Host line is ignored.
        req.Host = req.URL.Host
        if req.Host == "" {
-               req.Host = req.Header.Get("Host")
+               req.Host = req.Header.get("Host")
        }
-       req.Header.Del("Host")
+       delete(req.Header, "Host")
 
        fixPragmaCacheControl(req.Header)
 
@@ -594,66 +615,97 @@ func (l *maxBytesReader) Close() error {
        return l.r.Close()
 }
 
-// ParseForm parses the raw query from the URL.
+func copyValues(dst, src url.Values) {
+       for k, vs := range src {
+               for _, value := range vs {
+                       dst.Add(k, value)
+               }
+       }
+}
+
+func parsePostForm(r *Request) (vs url.Values, err error) {
+       if r.Body == nil {
+               err = errors.New("missing form body")
+               return
+       }
+       ct := r.Header.Get("Content-Type")
+       ct, _, err = mime.ParseMediaType(ct)
+       switch {
+       case ct == "application/x-www-form-urlencoded":
+               var reader io.Reader = r.Body
+               maxFormSize := int64(1<<63 - 1)
+               if _, ok := r.Body.(*maxBytesReader); !ok {
+                       maxFormSize = int64(10 << 20) // 10 MB is a lot of text.
+                       reader = io.LimitReader(r.Body, maxFormSize+1)
+               }
+               b, e := ioutil.ReadAll(reader)
+               if e != nil {
+                       if err == nil {
+                               err = e
+                       }
+                       break
+               }
+               if int64(len(b)) > maxFormSize {
+                       err = errors.New("http: POST too large")
+                       return
+               }
+               vs, e = url.ParseQuery(string(b))
+               if err == nil {
+                       err = e
+               }
+       case ct == "multipart/form-data":
+               // handled by ParseMultipartForm (which is calling us, or should be)
+               // TODO(bradfitz): there are too many possible
+               // orders to call too many functions here.
+               // Clean this up and write more tests.
+               // request_test.go contains the start of this,
+               // in TestRequestMultipartCallOrder.
+       }
+       return
+}
+
+// ParseForm parses the raw query from the URL and updates r.Form.
+//
+// For POST or PUT requests, it also parses the request body as a form and
+// put the results into both r.PostForm and r.Form.
+// POST and PUT body parameters take precedence over URL query string values
+// in r.Form.
 //
-// For POST or PUT requests, it also parses the request body as a form.
 // If the request Body's size has not already been limited by MaxBytesReader,
 // the size is capped at 10MB.
 //
 // ParseMultipartForm calls ParseForm automatically.
 // It is idempotent.
-func (r *Request) ParseForm() (err error) {
-       if r.Form != nil {
-               return
-       }
-       if r.URL != nil {
-               r.Form, err = url.ParseQuery(r.URL.RawQuery)
+func (r *Request) ParseForm() error {
+       var err error
+       if r.PostForm == nil {
+               if r.Method == "POST" || r.Method == "PUT" {
+                       r.PostForm, err = parsePostForm(r)
+               }
+               if r.PostForm == nil {
+                       r.PostForm = make(url.Values)
+               }
        }
-       if r.Method == "POST" || r.Method == "PUT" {
-               if r.Body == nil {
-                       return errors.New("missing form body")
+       if r.Form == nil {
+               if len(r.PostForm) > 0 {
+                       r.Form = make(url.Values)
+                       copyValues(r.Form, r.PostForm)
                }
-               ct := r.Header.Get("Content-Type")
-               ct, _, err = mime.ParseMediaType(ct)
-               switch {
-               case ct == "application/x-www-form-urlencoded":
-                       var reader io.Reader = r.Body
-                       maxFormSize := int64(1<<63 - 1)
-                       if _, ok := r.Body.(*maxBytesReader); !ok {
-                               maxFormSize = int64(10 << 20) // 10 MB is a lot of text.
-                               reader = io.LimitReader(r.Body, maxFormSize+1)
-                       }
-                       b, e := ioutil.ReadAll(reader)
-                       if e != nil {
-                               if err == nil {
-                                       err = e
-                               }
-                               break
-                       }
-                       if int64(len(b)) > maxFormSize {
-                               return errors.New("http: POST too large")
-                       }
-                       var newValues url.Values
-                       newValues, e = url.ParseQuery(string(b))
+               var newValues url.Values
+               if r.URL != nil {
+                       var e error
+                       newValues, e = url.ParseQuery(r.URL.RawQuery)
                        if err == nil {
                                err = e
                        }
-                       if r.Form == nil {
-                               r.Form = make(url.Values)
-                       }
-                       // Copy values into r.Form. TODO: make this smoother.
-                       for k, vs := range newValues {
-                               for _, value := range vs {
-                                       r.Form.Add(k, value)
-                               }
-                       }
-               case ct == "multipart/form-data":
-                       // handled by ParseMultipartForm (which is calling us, or should be)
-                       // TODO(bradfitz): there are too many possible
-                       // orders to call too many functions here.
-                       // Clean this up and write more tests.
-                       // request_test.go contains the start of this,
-                       // in TestRequestMultipartCallOrder.
+               }
+               if newValues == nil {
+                       newValues = make(url.Values)
+               }
+               if r.Form == nil {
+                       r.Form = newValues
+               } else {
+                       copyValues(r.Form, newValues)
                }
        }
        return err
@@ -699,7 +751,9 @@ 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.
 func (r *Request) FormValue(key string) string {
        if r.Form == nil {
                r.ParseMultipartForm(defaultMaxMemory)
@@ -710,6 +764,19 @@ func (r *Request) FormValue(key string) string {
        return ""
 }
 
+// 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.
+func (r *Request) PostFormValue(key string) string {
+       if r.PostForm == nil {
+               r.ParseMultipartForm(defaultMaxMemory)
+       }
+       if vs := r.PostForm[key]; len(vs) > 0 {
+               return vs[0]
+       }
+       return ""
+}
+
 // FormFile returns the first file for the provided form key.
 // FormFile calls ParseMultipartForm and ParseForm if necessary.
 func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {
@@ -732,12 +799,16 @@ func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, e
 }
 
 func (r *Request) expectsContinue() bool {
-       return strings.ToLower(r.Header.Get("Expect")) == "100-continue"
+       return hasToken(r.Header.get("Expect"), "100-continue")
 }
 
 func (r *Request) wantsHttp10KeepAlive() bool {
        if r.ProtoMajor != 1 || r.ProtoMinor != 0 {
                return false
        }
-       return strings.Contains(strings.ToLower(r.Header.Get("Connection")), "keep-alive")
+       return hasToken(r.Header.get("Connection"), "keep-alive")
+}
+
+func (r *Request) wantsClose() bool {
+       return hasToken(r.Header.get("Connection"), "close")
 }