2008-09-24 16:26:55 -06:00
|
|
|
// Copyright 2009 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// HTTP Request reading and parsing.
|
|
|
|
|
2011-04-19 17:57:05 -06:00
|
|
|
// Package http implements parsing of HTTP requests, replies, and URLs and
|
|
|
|
// provides an extensible HTTP server and a basic HTTP client.
|
2008-09-24 16:26:55 -06:00
|
|
|
package http
|
|
|
|
|
|
|
|
import (
|
2009-12-15 16:35:38 -07:00
|
|
|
"bufio"
|
2011-05-31 09:47:03 -06:00
|
|
|
"bytes"
|
2011-03-10 09:17:22 -07:00
|
|
|
"crypto/tls"
|
2011-05-13 16:43:46 -06:00
|
|
|
"encoding/base64"
|
2009-12-15 16:35:38 -07:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
2010-07-14 18:26:14 -06:00
|
|
|
"mime"
|
|
|
|
"mime/multipart"
|
2011-02-22 22:39:25 -07:00
|
|
|
"net/textproto"
|
2009-12-15 16:35:38 -07:00
|
|
|
"os"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
2011-08-16 21:36:02 -06:00
|
|
|
"url"
|
2008-09-24 16:26:55 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2011-04-27 23:21:54 -06:00
|
|
|
maxLineLength = 4096 // assumed <= bufio.defaultBufSize
|
|
|
|
maxValueLength = 4096
|
|
|
|
maxHeaderLines = 1024
|
|
|
|
chunkSize = 4 << 10 // 4 KB chunks
|
|
|
|
defaultMaxMemory = 32 << 20 // 32 MB
|
2008-09-24 16:26:55 -06:00
|
|
|
)
|
|
|
|
|
2011-04-27 23:21:54 -06:00
|
|
|
// ErrMissingFile is returned by FormFile when the provided file field name
|
|
|
|
// is either not present in the request or not a file field.
|
2011-06-22 11:52:47 -06:00
|
|
|
var ErrMissingFile = os.NewError("http: no such file")
|
2011-04-27 23:21:54 -06:00
|
|
|
|
2009-03-11 13:45:53 -06:00
|
|
|
// HTTP request parsing errors.
|
2009-05-08 15:40:20 -06:00
|
|
|
type ProtocolError struct {
|
2011-06-22 11:52:47 -06:00
|
|
|
ErrorString string
|
2009-05-08 15:40:20 -06:00
|
|
|
}
|
2009-10-08 16:14:54 -06:00
|
|
|
|
2011-06-22 11:52:47 -06:00
|
|
|
func (err *ProtocolError) String() string { return err.ErrorString }
|
|
|
|
|
2009-01-20 15:40:40 -07:00
|
|
|
var (
|
2010-01-18 22:46:59 -07:00
|
|
|
ErrLineTooLong = &ProtocolError{"header line too long"}
|
|
|
|
ErrHeaderTooLong = &ProtocolError{"header too long"}
|
|
|
|
ErrShortBody = &ProtocolError{"entity body too short"}
|
|
|
|
ErrNotSupported = &ProtocolError{"feature not supported"}
|
|
|
|
ErrUnexpectedTrailer = &ProtocolError{"trailer header without chunked transfer encoding"}
|
|
|
|
ErrMissingContentLength = &ProtocolError{"missing ContentLength in HEAD response"}
|
2010-07-14 18:26:14 -06:00
|
|
|
ErrNotMultipart = &ProtocolError{"request Content-Type isn't multipart/form-data"}
|
|
|
|
ErrMissingBoundary = &ProtocolError{"no multipart boundary param Content-Type"}
|
2008-09-24 16:26:55 -06:00
|
|
|
)
|
|
|
|
|
2009-06-25 21:24:55 -06:00
|
|
|
type badStringError struct {
|
2009-12-15 16:35:38 -07:00
|
|
|
what string
|
|
|
|
str string
|
2009-06-25 21:24:55 -06:00
|
|
|
}
|
|
|
|
|
2009-12-15 16:35:38 -07:00
|
|
|
func (e *badStringError) String() string { return fmt.Sprintf("%s %q", e.what, e.str) }
|
2009-06-25 21:24:55 -06:00
|
|
|
|
2011-06-16 14:02:28 -06:00
|
|
|
// Headers that Request.Write handles itself and should be skipped.
|
|
|
|
var reqWriteExcludeHeader = map[string]bool{
|
2011-09-19 11:22:53 -06:00
|
|
|
"Host": true, // not in Header map anyway
|
2010-03-30 11:51:11 -06:00
|
|
|
"User-Agent": true,
|
|
|
|
"Content-Length": true,
|
|
|
|
"Transfer-Encoding": true,
|
|
|
|
"Trailer": true,
|
2010-02-19 09:38:40 -07:00
|
|
|
}
|
2010-02-05 19:32:02 -07:00
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
var reqWriteExcludeHeaderDump = map[string]bool{
|
|
|
|
"Host": true, // not in Header map anyway
|
|
|
|
"Content-Length": true,
|
|
|
|
"Transfer-Encoding": true,
|
|
|
|
"Trailer": true,
|
|
|
|
}
|
|
|
|
|
2009-03-11 13:45:53 -06:00
|
|
|
// A Request represents a parsed HTTP request header.
|
2009-01-20 15:40:40 -07:00
|
|
|
type Request struct {
|
2011-08-16 21:36:02 -06:00
|
|
|
Method string // GET, POST, PUT, etc.
|
|
|
|
RawURL string // The raw URL given in the request.
|
|
|
|
URL *url.URL // Parsed URL.
|
2011-04-21 20:57:19 -06:00
|
|
|
|
|
|
|
// The protocol version for incoming requests.
|
|
|
|
// Outgoing requests always use HTTP/1.1.
|
2009-12-15 16:35:38 -07:00
|
|
|
Proto string // "HTTP/1.0"
|
|
|
|
ProtoMajor int // 1
|
|
|
|
ProtoMinor int // 0
|
2009-02-02 19:01:32 -07:00
|
|
|
|
2010-08-23 16:06:28 -06:00
|
|
|
// A header maps request lines to their values.
|
2009-03-11 13:45:53 -06:00
|
|
|
// If the header says
|
|
|
|
//
|
|
|
|
// accept-encoding: gzip, deflate
|
2010-06-06 09:18:09 -06:00
|
|
|
// Accept-Language: en-us
|
2009-03-11 13:45:53 -06:00
|
|
|
// Connection: keep-alive
|
|
|
|
//
|
|
|
|
// then
|
|
|
|
//
|
2011-06-08 14:38:20 -06:00
|
|
|
// Header = map[string][]string{
|
|
|
|
// "Accept-Encoding": {"gzip, deflate"},
|
|
|
|
// "Accept-Language": {"en-us"},
|
|
|
|
// "Connection": {"keep-alive"},
|
2009-03-11 13:45:53 -06:00
|
|
|
// }
|
|
|
|
//
|
|
|
|
// HTTP defines that header names are case-insensitive.
|
|
|
|
// The request parser implements this by canonicalizing the
|
|
|
|
// name, making the first character and any characters
|
|
|
|
// following a hyphen uppercase and the rest lowercase.
|
2011-02-22 22:39:25 -07:00
|
|
|
Header Header
|
2009-02-02 19:01:32 -07:00
|
|
|
|
2009-06-06 18:30:17 -06:00
|
|
|
// The message body.
|
2010-01-25 19:49:08 -07:00
|
|
|
Body io.ReadCloser
|
2009-06-06 18:30:17 -06:00
|
|
|
|
2010-02-19 09:38:40 -07:00
|
|
|
// ContentLength records the length of the associated content.
|
|
|
|
// The value -1 indicates that the length is unknown.
|
|
|
|
// Values >= 0 indicate that the given number of bytes may be read from Body.
|
|
|
|
ContentLength int64
|
|
|
|
|
|
|
|
// TransferEncoding lists the transfer encodings from outermost to innermost.
|
|
|
|
// An empty list denotes the "identity" encoding.
|
|
|
|
TransferEncoding []string
|
|
|
|
|
2009-03-11 13:45:53 -06:00
|
|
|
// Whether to close the connection after replying to this request.
|
2009-12-15 16:35:38 -07:00
|
|
|
Close bool
|
2009-03-11 13:45:53 -06:00
|
|
|
|
|
|
|
// 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.
|
2009-12-15 16:35:38 -07:00
|
|
|
Host string
|
2009-03-11 13:45:53 -06:00
|
|
|
|
2009-06-25 22:05:44 -06:00
|
|
|
// The parsed form. Only available after ParseForm is called.
|
2011-08-16 21:36:02 -06:00
|
|
|
Form url.Values
|
2010-02-19 09:38:40 -07:00
|
|
|
|
2011-04-27 23:21:54 -06:00
|
|
|
// The parsed multipart form, including file uploads.
|
|
|
|
// Only available after ParseMultipartForm is called.
|
|
|
|
MultipartForm *multipart.Form
|
|
|
|
|
2010-02-19 09:38:40 -07:00
|
|
|
// 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.
|
2011-02-22 22:39:25 -07:00
|
|
|
Trailer Header
|
2011-03-10 09:17:22 -07:00
|
|
|
|
|
|
|
// RemoteAddr allows HTTP servers and other software to record
|
|
|
|
// the network address that sent the request, usually for
|
|
|
|
// logging. This field is not filled in by ReadRequest and
|
|
|
|
// has no defined format. The HTTP server in this package
|
|
|
|
// sets RemoteAddr to an "IP:port" address before invoking a
|
|
|
|
// handler.
|
|
|
|
RemoteAddr string
|
|
|
|
|
|
|
|
// TLS allows HTTP servers and other software to record
|
|
|
|
// information about the TLS connection on which the request
|
|
|
|
// was received. This field is not filled in by ReadRequest.
|
|
|
|
// The HTTP server in this package sets the field for
|
|
|
|
// TLS-enabled connections before invoking a handler;
|
|
|
|
// otherwise it leaves the field nil.
|
|
|
|
TLS *tls.ConnectionState
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
|
|
|
|
2009-03-11 13:45:53 -06:00
|
|
|
// ProtoAtLeast returns whether the HTTP protocol used
|
|
|
|
// in the request is at least major.minor.
|
2009-02-02 19:01:32 -07:00
|
|
|
func (r *Request) ProtoAtLeast(major, minor int) bool {
|
|
|
|
return r.ProtoMajor > major ||
|
2009-11-09 13:07:39 -07:00
|
|
|
r.ProtoMajor == major && r.ProtoMinor >= minor
|
2009-02-02 19:01:32 -07:00
|
|
|
}
|
|
|
|
|
2011-06-16 14:02:28 -06:00
|
|
|
// UserAgent returns the client's User-Agent, if sent in the request.
|
|
|
|
func (r *Request) UserAgent() string {
|
|
|
|
return r.Header.Get("User-Agent")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cookies parses and returns the HTTP cookies sent with the request.
|
|
|
|
func (r *Request) Cookies() []*Cookie {
|
|
|
|
return readCookies(r.Header, "")
|
|
|
|
}
|
|
|
|
|
|
|
|
var ErrNoCookie = os.NewError("http: named cookied not present")
|
|
|
|
|
|
|
|
// Cookie returns the named cookie provided in the request or
|
|
|
|
// ErrNoCookie if not found.
|
|
|
|
func (r *Request) Cookie(name string) (*Cookie, os.Error) {
|
|
|
|
for _, c := range readCookies(r.Header, name) {
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
return nil, ErrNoCookie
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddCookie adds a cookie to the request. Per RFC 6265 section 5.4,
|
|
|
|
// AddCookie does not attach more than one Cookie header field. That
|
|
|
|
// means all cookies, if any, are written into the same line,
|
|
|
|
// separated by semicolon.
|
|
|
|
func (r *Request) AddCookie(c *Cookie) {
|
|
|
|
s := fmt.Sprintf("%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value))
|
|
|
|
if c := r.Header.Get("Cookie"); c != "" {
|
|
|
|
r.Header.Set("Cookie", c+"; "+s)
|
|
|
|
} else {
|
|
|
|
r.Header.Set("Cookie", s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Referer returns the referring URL, if sent in the request.
|
|
|
|
//
|
|
|
|
// Referer is misspelled as in the request itself, a mistake from the
|
|
|
|
// earliest days of HTTP. This value can also be fetched from the
|
|
|
|
// Header map as Header["Referer"]; the benefit of making it available
|
|
|
|
// as a method is that the compiler can diagnose programs that use the
|
|
|
|
// alternate (correct English) spelling req.Referrer() but cannot
|
|
|
|
// diagnose programs that use Header["Referrer"].
|
|
|
|
func (r *Request) Referer() string {
|
|
|
|
return r.Header.Get("Referer")
|
|
|
|
}
|
|
|
|
|
2011-04-27 23:21:54 -06:00
|
|
|
// multipartByReader is a sentinel value.
|
|
|
|
// Its presence in Request.MultipartForm indicates that parsing of the request
|
|
|
|
// body has been handed off to a MultipartReader instead of ParseMultipartFrom.
|
|
|
|
var multipartByReader = &multipart.Form{
|
|
|
|
Value: make(map[string][]string),
|
|
|
|
File: make(map[string][]*multipart.FileHeader),
|
|
|
|
}
|
|
|
|
|
2010-07-14 18:26:14 -06:00
|
|
|
// MultipartReader returns a MIME multipart reader if this is a
|
|
|
|
// multipart/form-data POST request, else returns nil and an error.
|
2011-04-27 23:21:54 -06:00
|
|
|
// Use this function instead of ParseMultipartForm to
|
|
|
|
// process the request body as a stream.
|
2011-06-16 09:55:53 -06:00
|
|
|
func (r *Request) MultipartReader() (*multipart.Reader, os.Error) {
|
2011-04-27 23:21:54 -06:00
|
|
|
if r.MultipartForm == multipartByReader {
|
|
|
|
return nil, os.NewError("http: MultipartReader called twice")
|
|
|
|
}
|
|
|
|
if r.MultipartForm != nil {
|
|
|
|
return nil, os.NewError("http: multipart handled by ParseMultipartForm")
|
|
|
|
}
|
|
|
|
r.MultipartForm = multipartByReader
|
|
|
|
return r.multipartReader()
|
|
|
|
}
|
|
|
|
|
2011-06-16 09:55:53 -06:00
|
|
|
func (r *Request) multipartReader() (*multipart.Reader, os.Error) {
|
2011-02-22 22:39:25 -07:00
|
|
|
v := r.Header.Get("Content-Type")
|
|
|
|
if v == "" {
|
2010-07-14 18:26:14 -06:00
|
|
|
return nil, ErrNotMultipart
|
|
|
|
}
|
2011-08-18 13:51:23 -06:00
|
|
|
d, params, err := mime.ParseMediaType(v)
|
|
|
|
if err != nil || d != "multipart/form-data" {
|
2010-07-14 18:26:14 -06:00
|
|
|
return nil, ErrNotMultipart
|
|
|
|
}
|
|
|
|
boundary, ok := params["boundary"]
|
|
|
|
if !ok {
|
|
|
|
return nil, ErrMissingBoundary
|
|
|
|
}
|
|
|
|
return multipart.NewReader(r.Body, boundary), nil
|
|
|
|
}
|
|
|
|
|
2009-06-09 11:58:58 -06:00
|
|
|
// Return value if nonempty, def otherwise.
|
|
|
|
func valueOrDefault(value, def string) string {
|
|
|
|
if value != "" {
|
2009-11-09 13:07:39 -07:00
|
|
|
return value
|
2009-06-09 11:58:58 -06:00
|
|
|
}
|
2009-12-15 16:35:38 -07:00
|
|
|
return def
|
2009-06-09 11:58:58 -06:00
|
|
|
}
|
|
|
|
|
2009-11-10 10:10:08 -07:00
|
|
|
const defaultUserAgent = "Go http package"
|
2009-06-09 11:58:58 -06:00
|
|
|
|
2009-10-23 15:19:47 -06:00
|
|
|
// Write writes an HTTP/1.1 request -- header and body -- in wire format.
|
2009-06-23 19:49:47 -06:00
|
|
|
// This method consults the following fields of req:
|
2010-02-04 01:23:01 -07:00
|
|
|
// Host
|
2010-02-22 16:39:30 -07:00
|
|
|
// RawURL, if non-empty, or else URL
|
2009-06-23 19:49:47 -06:00
|
|
|
// Method (defaults to "GET")
|
2011-06-16 14:02:28 -06:00
|
|
|
// Header
|
2011-04-14 21:36:52 -06:00
|
|
|
// ContentLength
|
|
|
|
// TransferEncoding
|
2009-06-23 19:49:47 -06:00
|
|
|
// Body
|
|
|
|
//
|
http: fix handling of 0-lengthed http requests
Via Russ Ross' bug report on golang-nuts, it was not possible
to send an HTTP request with a zero length body with either a
Content-Length (it was stripped) or chunking (it wasn't set).
This means Go couldn't upload 0-length objects to Amazon S3.
(which aren't as silly as they might sound, as S3 objects can
have key/values associated with them, set in the headers)
Amazon further doesn't supported chunked uploads. (not Go's
problem, but we should be able to let users set an explicit
Content-Length, even if it's zero.)
To fix the ambiguity of an explicit zero Content-Length and
the Request struct's default zero value, users need to
explicit set TransferEncoding to []string{"identity"} to force
the Request.Write to include a Content-Length: 0. identity is
in RFC 2616 but is ignored pretty much everywhere. We don't
even then serialize it on the wire, since it's kinda useless,
except as an internal sentinel value.
The "identity" value is then documented, but most users can
ignore that because NewRequest now sets that.
And adds more tests.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/4603041
2011-06-08 16:59:23 -06:00
|
|
|
// If Body is present, Content-Length is <= 0 and TransferEncoding
|
|
|
|
// hasn't been set to "identity", Write adds "Transfer-Encoding:
|
|
|
|
// chunked" to the header. Body is closed after it is sent.
|
2009-10-23 15:19:47 -06:00
|
|
|
func (req *Request) Write(w io.Writer) os.Error {
|
2011-02-17 15:14:50 -07:00
|
|
|
return req.write(w, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
// WriteProxy is like Write but writes the request in the form
|
2011-09-15 11:28:55 -06:00
|
|
|
// expected by an HTTP proxy. In particular, WriteProxy writes the
|
|
|
|
// initial Request-URI line of the request with an absolute URI, per
|
|
|
|
// section 5.1.2 of RFC 2616, including the scheme and host. If
|
|
|
|
// req.RawURL is non-empty, WriteProxy uses it unchanged. In either
|
|
|
|
// case, WriteProxy also writes a Host header, using either req.Host
|
|
|
|
// or req.URL.Host.
|
2011-02-17 15:14:50 -07:00
|
|
|
func (req *Request) WriteProxy(w io.Writer) os.Error {
|
|
|
|
return req.write(w, true)
|
|
|
|
}
|
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
func (req *Request) dumpWrite(w io.Writer) os.Error {
|
|
|
|
urlStr := req.RawURL
|
|
|
|
if urlStr == "" {
|
|
|
|
urlStr = valueOrDefault(req.URL.EncodedPath(), "/")
|
|
|
|
if req.URL.RawQuery != "" {
|
|
|
|
urlStr += "?" + req.URL.RawQuery
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bw := bufio.NewWriter(w)
|
|
|
|
fmt.Fprintf(bw, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"), urlStr,
|
|
|
|
req.ProtoMajor, req.ProtoMinor)
|
|
|
|
|
|
|
|
host := req.Host
|
|
|
|
if host == "" && req.URL != nil {
|
|
|
|
host = req.URL.Host
|
|
|
|
}
|
|
|
|
if host != "" {
|
|
|
|
fmt.Fprintf(bw, "Host: %s\r\n", host)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process Body,ContentLength,Close,Trailer
|
|
|
|
tw, err := newTransferWriter(req)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = tw.WriteHeader(bw)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = req.Header.WriteSubset(bw, reqWriteExcludeHeaderDump)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
io.WriteString(bw, "\r\n")
|
|
|
|
|
|
|
|
// Write body and trailer
|
|
|
|
err = tw.WriteBody(bw)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
bw.Flush()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2011-02-17 15:14:50 -07:00
|
|
|
func (req *Request) write(w io.Writer, usingProxy bool) os.Error {
|
2010-01-29 17:51:42 -07:00
|
|
|
host := req.Host
|
|
|
|
if host == "" {
|
2011-05-31 09:47:03 -06:00
|
|
|
if req.URL == nil {
|
|
|
|
return os.NewError("http: Request.Write on Request with no Host or URL set")
|
|
|
|
}
|
2010-01-29 17:51:42 -07:00
|
|
|
host = req.URL.Host
|
|
|
|
}
|
|
|
|
|
2011-08-16 21:36:02 -06:00
|
|
|
urlStr := req.RawURL
|
|
|
|
if urlStr == "" {
|
|
|
|
urlStr = valueOrDefault(req.URL.EncodedPath(), "/")
|
2010-02-22 16:39:30 -07:00
|
|
|
if req.URL.RawQuery != "" {
|
2011-08-16 21:36:02 -06:00
|
|
|
urlStr += "?" + req.URL.RawQuery
|
2010-02-22 16:39:30 -07:00
|
|
|
}
|
2011-03-05 12:35:15 -07:00
|
|
|
if usingProxy {
|
2011-08-16 21:36:02 -06:00
|
|
|
if urlStr == "" || urlStr[0] != '/' {
|
|
|
|
urlStr = "/" + urlStr
|
2011-03-05 12:35:15 -07:00
|
|
|
}
|
2011-08-16 21:36:02 -06:00
|
|
|
urlStr = req.URL.Scheme + "://" + host + urlStr
|
2011-02-17 15:14:50 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-23 22:10:51 -06:00
|
|
|
bw := bufio.NewWriter(w)
|
2011-08-16 21:36:02 -06:00
|
|
|
fmt.Fprintf(bw, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), urlStr)
|
2010-02-19 09:38:40 -07:00
|
|
|
|
|
|
|
// Header lines
|
2011-06-23 22:10:51 -06:00
|
|
|
fmt.Fprintf(bw, "Host: %s\r\n", host)
|
2011-06-16 14:02:28 -06:00
|
|
|
|
|
|
|
// Use the defaultUserAgent unless the Header contains one, which
|
|
|
|
// may be blank to not send the header.
|
|
|
|
userAgent := defaultUserAgent
|
|
|
|
if req.Header != nil {
|
|
|
|
if ua := req.Header["User-Agent"]; len(ua) > 0 {
|
|
|
|
userAgent = ua[0]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if userAgent != "" {
|
2011-06-23 22:10:51 -06:00
|
|
|
fmt.Fprintf(bw, "User-Agent: %s\r\n", userAgent)
|
2009-06-09 11:58:58 -06:00
|
|
|
}
|
|
|
|
|
2010-02-19 09:38:40 -07:00
|
|
|
// Process Body,ContentLength,Close,Trailer
|
|
|
|
tw, err := newTransferWriter(req)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2011-06-23 22:10:51 -06:00
|
|
|
err = tw.WriteHeader(bw)
|
2010-02-19 09:38:40 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2009-06-23 19:49:47 -06:00
|
|
|
}
|
|
|
|
|
2009-06-09 11:58:58 -06:00
|
|
|
// TODO: split long values? (If so, should share code with Conn.Write)
|
2011-06-23 22:10:51 -06:00
|
|
|
err = req.Header.WriteSubset(bw, reqWriteExcludeHeader)
|
2010-02-09 18:42:51 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2009-06-09 11:58:58 -06:00
|
|
|
|
2011-06-23 22:10:51 -06:00
|
|
|
io.WriteString(bw, "\r\n")
|
2009-06-09 11:58:58 -06:00
|
|
|
|
2010-02-19 09:38:40 -07:00
|
|
|
// Write body and trailer
|
2011-06-23 22:10:51 -06:00
|
|
|
err = tw.WriteBody(bw)
|
2010-02-19 09:38:40 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2009-06-09 11:58:58 -06:00
|
|
|
}
|
2011-06-23 22:10:51 -06:00
|
|
|
bw.Flush()
|
2009-12-15 16:35:38 -07:00
|
|
|
return nil
|
2009-06-09 11:58:58 -06:00
|
|
|
}
|
|
|
|
|
2008-09-24 16:26:55 -06:00
|
|
|
// Read a line of bytes (up to \n) from b.
|
2009-02-02 19:01:32 -07:00
|
|
|
// Give up if the line exceeds maxLineLength.
|
2008-09-24 16:26:55 -06:00
|
|
|
// The returned bytes are a pointer into storage in
|
|
|
|
// the bufio, so they are only valid until the next bufio read.
|
2009-05-08 12:52:39 -06:00
|
|
|
func readLineBytes(b *bufio.Reader) (p []byte, err os.Error) {
|
2009-08-27 12:20:15 -06:00
|
|
|
if p, err = b.ReadSlice('\n'); err != nil {
|
2009-06-25 21:24:55 -06:00
|
|
|
// We always know when EOF is coming.
|
|
|
|
// If the caller asked for a line, there should be a line.
|
|
|
|
if err == os.EOF {
|
2009-11-09 13:07:39 -07:00
|
|
|
err = io.ErrUnexpectedEOF
|
2010-10-06 05:04:18 -06:00
|
|
|
} else if err == bufio.ErrBufferFull {
|
|
|
|
err = ErrLineTooLong
|
2009-06-25 21:24:55 -06:00
|
|
|
}
|
2009-12-15 16:35:38 -07:00
|
|
|
return nil, err
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
2009-02-02 19:01:32 -07:00
|
|
|
if len(p) >= maxLineLength {
|
2009-11-09 13:07:39 -07:00
|
|
|
return nil, ErrLineTooLong
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Chop off trailing white space.
|
2009-12-15 16:35:38 -07:00
|
|
|
var i int
|
2008-09-24 16:26:55 -06:00
|
|
|
for i = len(p); i > 0; i-- {
|
|
|
|
if c := p[i-1]; c != ' ' && c != '\r' && c != '\t' && c != '\n' {
|
2009-11-09 13:07:39 -07:00
|
|
|
break
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
|
|
|
}
|
2009-12-15 16:35:38 -07:00
|
|
|
return p[0:i], nil
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
|
|
|
|
2009-01-16 12:06:42 -07:00
|
|
|
// readLineBytes, but convert the bytes into a string.
|
2009-05-08 12:52:39 -06:00
|
|
|
func readLine(b *bufio.Reader) (s string, err os.Error) {
|
2009-12-15 16:35:38 -07:00
|
|
|
p, e := readLineBytes(b)
|
2008-09-24 16:26:55 -06:00
|
|
|
if e != nil {
|
2009-11-09 13:07:39 -07:00
|
|
|
return "", e
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
2009-12-15 16:35:38 -07:00
|
|
|
return string(p), nil
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2009-12-15 16:35:38 -07:00
|
|
|
const Big = 1000000
|
2008-09-24 16:26:55 -06:00
|
|
|
if i >= len(s) || s[i] < '0' || s[i] > '9' {
|
2009-11-09 13:07:39 -07:00
|
|
|
return 0, 0, false
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
2009-12-15 16:35:38 -07:00
|
|
|
n = 0
|
2008-09-24 16:26:55 -06:00
|
|
|
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
2009-12-15 16:35:38 -07:00
|
|
|
n = n*10 + int(s[i]-'0')
|
2008-09-24 16:26:55 -06:00
|
|
|
if n > Big {
|
2009-11-09 13:07:39 -07:00
|
|
|
return 0, 0, false
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
|
|
|
}
|
2009-12-15 16:35:38 -07:00
|
|
|
return n, i, true
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
|
|
|
|
2011-03-01 04:38:18 -07:00
|
|
|
// ParseHTTPVersion parses a HTTP version string.
|
2011-03-01 16:10:12 -07:00
|
|
|
// "HTTP/1.0" returns (1, 0, true).
|
2011-03-01 04:38:18 -07:00
|
|
|
func ParseHTTPVersion(vers string) (major, minor int, ok bool) {
|
2010-05-11 17:50:20 -06:00
|
|
|
if len(vers) < 5 || vers[0:5] != "HTTP/" {
|
2009-11-09 13:07:39 -07:00
|
|
|
return 0, 0, false
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
2009-12-15 16:35:38 -07:00
|
|
|
major, i, ok := atoi(vers, 5)
|
2008-09-24 16:26:55 -06:00
|
|
|
if !ok || i >= len(vers) || vers[i] != '.' {
|
2009-11-09 13:07:39 -07:00
|
|
|
return 0, 0, false
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
2009-12-15 16:35:38 -07:00
|
|
|
minor, i, ok = atoi(vers, i+1)
|
2008-09-24 16:26:55 -06:00
|
|
|
if !ok || i != len(vers) {
|
2009-11-09 13:07:39 -07:00
|
|
|
return 0, 0, false
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
2009-12-15 16:35:38 -07:00
|
|
|
return major, minor, true
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
|
|
|
|
2009-06-23 19:49:47 -06:00
|
|
|
type chunkedReader struct {
|
2009-12-15 16:35:38 -07:00
|
|
|
r *bufio.Reader
|
|
|
|
n uint64 // unread bytes in chunk
|
|
|
|
err os.Error
|
2009-06-23 19:49:47 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (cr *chunkedReader) beginChunk() {
|
|
|
|
// chunk-size CRLF
|
2009-12-15 16:35:38 -07:00
|
|
|
var line string
|
|
|
|
line, cr.err = readLine(cr.r)
|
2009-06-23 19:49:47 -06:00
|
|
|
if cr.err != nil {
|
2009-11-09 13:07:39 -07:00
|
|
|
return
|
2009-06-23 19:49:47 -06:00
|
|
|
}
|
2009-12-15 16:35:38 -07:00
|
|
|
cr.n, cr.err = strconv.Btoui64(line, 16)
|
2009-06-23 19:49:47 -06:00
|
|
|
if cr.err != nil {
|
2009-11-09 13:07:39 -07:00
|
|
|
return
|
2009-06-23 19:49:47 -06:00
|
|
|
}
|
|
|
|
if cr.n == 0 {
|
|
|
|
// trailer CRLF
|
|
|
|
for {
|
2009-12-15 16:35:38 -07:00
|
|
|
line, cr.err = readLine(cr.r)
|
2009-06-23 19:49:47 -06:00
|
|
|
if cr.err != nil {
|
2009-11-09 13:07:39 -07:00
|
|
|
return
|
2009-06-23 19:49:47 -06:00
|
|
|
}
|
|
|
|
if line == "" {
|
2009-11-09 13:07:39 -07:00
|
|
|
break
|
2009-06-23 19:49:47 -06:00
|
|
|
}
|
|
|
|
}
|
2009-12-15 16:35:38 -07:00
|
|
|
cr.err = os.EOF
|
2009-06-23 19:49:47 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cr *chunkedReader) Read(b []uint8) (n int, err os.Error) {
|
|
|
|
if cr.err != nil {
|
2009-11-09 13:07:39 -07:00
|
|
|
return 0, cr.err
|
2009-06-23 19:49:47 -06:00
|
|
|
}
|
|
|
|
if cr.n == 0 {
|
2009-12-15 16:35:38 -07:00
|
|
|
cr.beginChunk()
|
2009-06-23 19:49:47 -06:00
|
|
|
if cr.err != nil {
|
2009-11-09 13:07:39 -07:00
|
|
|
return 0, cr.err
|
2009-06-23 19:49:47 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if uint64(len(b)) > cr.n {
|
2009-11-09 22:23:52 -07:00
|
|
|
b = b[0:cr.n]
|
2009-06-23 19:49:47 -06:00
|
|
|
}
|
2009-12-15 16:35:38 -07:00
|
|
|
n, cr.err = cr.r.Read(b)
|
|
|
|
cr.n -= uint64(n)
|
2009-06-23 19:49:47 -06:00
|
|
|
if cr.n == 0 && cr.err == nil {
|
|
|
|
// end of chunk (CRLF)
|
2009-12-15 16:35:38 -07:00
|
|
|
b := make([]byte, 2)
|
2009-09-14 18:20:29 -06:00
|
|
|
if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil {
|
2009-06-23 19:49:47 -06:00
|
|
|
if b[0] != '\r' || b[1] != '\n' {
|
2009-11-09 13:07:39 -07:00
|
|
|
cr.err = os.NewError("malformed chunked encoding")
|
2009-06-23 19:49:47 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-12-15 16:35:38 -07:00
|
|
|
return n, cr.err
|
2009-06-23 19:49:47 -06:00
|
|
|
}
|
|
|
|
|
2011-04-14 21:36:52 -06:00
|
|
|
// NewRequest returns a new Request given a method, URL, and optional body.
|
2011-08-16 21:36:02 -06:00
|
|
|
func NewRequest(method, urlStr string, body io.Reader) (*Request, os.Error) {
|
|
|
|
u, err := url.Parse(urlStr)
|
2011-04-14 21:36:52 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
rc, ok := body.(io.ReadCloser)
|
|
|
|
if !ok && body != nil {
|
|
|
|
rc = ioutil.NopCloser(body)
|
|
|
|
}
|
|
|
|
req := &Request{
|
|
|
|
Method: method,
|
|
|
|
URL: u,
|
|
|
|
Proto: "HTTP/1.1",
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
|
|
|
Header: make(Header),
|
|
|
|
Body: rc,
|
|
|
|
Host: u.Host,
|
|
|
|
}
|
2011-05-31 09:47:03 -06:00
|
|
|
if body != nil {
|
|
|
|
switch v := body.(type) {
|
|
|
|
case *strings.Reader:
|
|
|
|
req.ContentLength = int64(v.Len())
|
|
|
|
case *bytes.Buffer:
|
|
|
|
req.ContentLength = int64(v.Len())
|
http: fix handling of 0-lengthed http requests
Via Russ Ross' bug report on golang-nuts, it was not possible
to send an HTTP request with a zero length body with either a
Content-Length (it was stripped) or chunking (it wasn't set).
This means Go couldn't upload 0-length objects to Amazon S3.
(which aren't as silly as they might sound, as S3 objects can
have key/values associated with them, set in the headers)
Amazon further doesn't supported chunked uploads. (not Go's
problem, but we should be able to let users set an explicit
Content-Length, even if it's zero.)
To fix the ambiguity of an explicit zero Content-Length and
the Request struct's default zero value, users need to
explicit set TransferEncoding to []string{"identity"} to force
the Request.Write to include a Content-Length: 0. identity is
in RFC 2616 but is ignored pretty much everywhere. We don't
even then serialize it on the wire, since it's kinda useless,
except as an internal sentinel value.
The "identity" value is then documented, but most users can
ignore that because NewRequest now sets that.
And adds more tests.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/4603041
2011-06-08 16:59:23 -06:00
|
|
|
}
|
2011-05-31 09:47:03 -06:00
|
|
|
}
|
|
|
|
|
2011-04-14 21:36:52 -06:00
|
|
|
return req, nil
|
|
|
|
}
|
|
|
|
|
2011-05-13 16:43:46 -06:00
|
|
|
// SetBasicAuth sets the request's Authorization header to use HTTP
|
|
|
|
// Basic Authentication with the provided username and password.
|
|
|
|
//
|
|
|
|
// With HTTP Basic Authentication the provided username and password
|
|
|
|
// are not encrypted.
|
|
|
|
func (r *Request) SetBasicAuth(username, password string) {
|
|
|
|
s := username + ":" + password
|
2011-05-25 13:24:36 -06:00
|
|
|
r.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
|
2011-05-13 16:43:46 -06:00
|
|
|
}
|
|
|
|
|
2009-03-11 13:45:53 -06:00
|
|
|
// ReadRequest reads and parses a request from b.
|
2009-05-08 12:52:39 -06:00
|
|
|
func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) {
|
2011-02-22 22:39:25 -07:00
|
|
|
|
|
|
|
tp := textproto.NewReader(b)
|
2009-12-15 16:35:38 -07:00
|
|
|
req = new(Request)
|
2008-09-24 16:26:55 -06:00
|
|
|
|
|
|
|
// First line: GET /index.html HTTP/1.0
|
2009-12-15 16:35:38 -07:00
|
|
|
var s string
|
2011-02-22 22:39:25 -07:00
|
|
|
if s, err = tp.ReadLine(); err != nil {
|
|
|
|
if err == os.EOF {
|
|
|
|
err = io.ErrUnexpectedEOF
|
|
|
|
}
|
2009-11-09 13:07:39 -07:00
|
|
|
return nil, err
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
|
|
|
|
2009-12-15 16:35:38 -07:00
|
|
|
var f []string
|
2011-06-27 17:43:14 -06:00
|
|
|
if f = strings.SplitN(s, " ", 3); len(f) < 3 {
|
2009-11-09 13:07:39 -07:00
|
|
|
return nil, &badStringError{"malformed HTTP request", s}
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
2009-12-15 16:35:38 -07:00
|
|
|
req.Method, req.RawURL, req.Proto = f[0], f[1], f[2]
|
|
|
|
var ok bool
|
2011-03-01 04:38:18 -07:00
|
|
|
if req.ProtoMajor, req.ProtoMinor, ok = ParseHTTPVersion(req.Proto); !ok {
|
2009-11-09 13:07:39 -07:00
|
|
|
return nil, &badStringError{"malformed HTTP version", req.Proto}
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
|
|
|
|
2011-08-16 21:36:02 -06:00
|
|
|
if req.URL, err = url.ParseRequest(req.RawURL); err != nil {
|
2011-07-21 11:25:55 -06:00
|
|
|
return nil, err
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Subsequent lines: Key: value.
|
2011-02-22 22:39:25 -07:00
|
|
|
mimeHeader, err := tp.ReadMIMEHeader()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
2011-02-22 22:39:25 -07:00
|
|
|
req.Header = Header(mimeHeader)
|
2008-09-24 16:26:55 -06:00
|
|
|
|
|
|
|
// RFC2616: Must treat
|
|
|
|
// GET /index.html HTTP/1.1
|
|
|
|
// Host: www.google.com
|
|
|
|
// and
|
|
|
|
// GET http://www.google.com/index.html HTTP/1.1
|
|
|
|
// Host: doesntmatter
|
|
|
|
// the same. In the second case, any Host line is ignored.
|
2011-07-21 11:25:55 -06:00
|
|
|
req.Host = req.URL.Host
|
2010-03-30 11:51:11 -06:00
|
|
|
if req.Host == "" {
|
2011-02-22 22:39:25 -07:00
|
|
|
req.Host = req.Header.Get("Host")
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
2011-02-22 22:39:25 -07:00
|
|
|
req.Header.Del("Host")
|
2008-09-24 16:26:55 -06:00
|
|
|
|
2010-02-19 09:38:40 -07:00
|
|
|
fixPragmaCacheControl(req.Header)
|
2008-09-24 16:26:55 -06:00
|
|
|
|
|
|
|
// 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
|
|
|
|
|
2010-02-19 09:38:40 -07:00
|
|
|
err = readTransfer(req, b)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2009-06-06 18:30:17 -06:00
|
|
|
}
|
|
|
|
|
2009-12-15 16:35:38 -07:00
|
|
|
return req, nil
|
2008-09-24 16:26:55 -06:00
|
|
|
}
|
2009-06-19 19:02:15 -06:00
|
|
|
|
2011-09-15 15:26:22 -06:00
|
|
|
// MaxBytesReader is similar to io.LimitReader but is intended for
|
2011-08-23 02:17:21 -06:00
|
|
|
// limiting the size of incoming request bodies. In contrast to
|
2011-09-15 15:26:22 -06:00
|
|
|
// io.LimitReader, MaxBytesReader's result is a ReadCloser, returns a
|
|
|
|
// non-EOF error for a Read beyond the limit, and Closes the
|
|
|
|
// underlying reader when its Close method is called.
|
|
|
|
//
|
|
|
|
// MaxBytesReader prevents clients from accidentally or maliciously
|
|
|
|
// sending a large request and wasting server resources.
|
2011-08-23 02:17:21 -06:00
|
|
|
func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser {
|
|
|
|
return &maxBytesReader{w: w, r: r, n: n}
|
|
|
|
}
|
|
|
|
|
|
|
|
type maxBytesReader struct {
|
|
|
|
w ResponseWriter
|
|
|
|
r io.ReadCloser // underlying reader
|
|
|
|
n int64 // max bytes remaining
|
|
|
|
stopped bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *maxBytesReader) Read(p []byte) (n int, err os.Error) {
|
|
|
|
if l.n <= 0 {
|
|
|
|
if !l.stopped {
|
|
|
|
l.stopped = true
|
|
|
|
if res, ok := l.w.(*response); ok {
|
|
|
|
res.requestTooLarge()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0, os.NewError("http: request body too large")
|
|
|
|
}
|
|
|
|
if int64(len(p)) > l.n {
|
|
|
|
p = p[:l.n]
|
|
|
|
}
|
|
|
|
n, err = l.r.Read(p)
|
|
|
|
l.n -= int64(n)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *maxBytesReader) Close() os.Error {
|
|
|
|
return l.r.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseForm parses the raw query from the URL.
|
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
2011-04-27 23:21:54 -06:00
|
|
|
// ParseMultipartForm calls ParseForm automatically.
|
2009-06-25 22:05:44 -06:00
|
|
|
// It is idempotent.
|
2009-06-19 19:02:15 -06:00
|
|
|
func (r *Request) ParseForm() (err os.Error) {
|
2009-06-25 22:05:44 -06:00
|
|
|
if r.Form != nil {
|
2009-11-09 13:07:39 -07:00
|
|
|
return
|
2009-06-25 22:05:44 -06:00
|
|
|
}
|
2010-08-17 18:01:50 -06:00
|
|
|
if r.URL != nil {
|
2011-08-16 21:36:02 -06:00
|
|
|
r.Form, err = url.ParseQuery(r.URL.RawQuery)
|
2010-08-17 18:01:50 -06:00
|
|
|
}
|
2011-08-23 02:17:21 -06:00
|
|
|
if r.Method == "POST" || r.Method == "PUT" {
|
2009-06-25 22:05:44 -06:00
|
|
|
if r.Body == nil {
|
2011-06-22 11:52:47 -06:00
|
|
|
return os.NewError("missing form body")
|
2009-06-25 22:05:44 -06:00
|
|
|
}
|
2011-02-22 22:39:25 -07:00
|
|
|
ct := r.Header.Get("Content-Type")
|
2011-08-18 13:51:23 -06:00
|
|
|
ct, _, err := mime.ParseMediaType(ct)
|
|
|
|
switch {
|
|
|
|
case ct == "text/plain" || ct == "application/x-www-form-urlencoded" || ct == "":
|
2011-08-23 02:17:21 -06:00
|
|
|
var reader io.Reader = r.Body
|
2011-09-15 15:26:22 -06:00
|
|
|
maxFormSize := int64(1<<63 - 1)
|
2011-08-23 02:17:21 -06:00
|
|
|
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)
|
2010-08-17 18:01:50 -06:00
|
|
|
if e != nil {
|
|
|
|
if err == nil {
|
|
|
|
err = e
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
2011-04-27 16:36:39 -06:00
|
|
|
if int64(len(b)) > maxFormSize {
|
|
|
|
return os.NewError("http: POST too large")
|
|
|
|
}
|
2011-08-16 21:36:02 -06:00
|
|
|
var newValues url.Values
|
|
|
|
newValues, e = url.ParseQuery(string(b))
|
2010-08-17 18:01:50 -06:00
|
|
|
if err == nil {
|
|
|
|
err = e
|
2009-06-25 22:05:44 -06:00
|
|
|
}
|
2011-08-17 19:01:43 -06:00
|
|
|
if r.Form == nil {
|
|
|
|
r.Form = make(url.Values)
|
|
|
|
}
|
2011-08-16 21:36:02 -06:00
|
|
|
// Copy values into r.Form. TODO: make this smoother.
|
|
|
|
for k, vs := range newValues {
|
|
|
|
for _, value := range vs {
|
|
|
|
r.Form.Add(k, value)
|
|
|
|
}
|
|
|
|
}
|
2011-08-18 13:51:23 -06:00
|
|
|
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.
|
2009-06-25 22:05:44 -06:00
|
|
|
default:
|
2009-11-09 13:07:39 -07:00
|
|
|
return &badStringError{"unknown Content-Type", ct}
|
2009-06-25 22:05:44 -06:00
|
|
|
}
|
|
|
|
}
|
2010-08-17 18:01:50 -06:00
|
|
|
return err
|
2009-06-25 22:05:44 -06:00
|
|
|
}
|
|
|
|
|
2011-04-27 23:21:54 -06:00
|
|
|
// ParseMultipartForm parses a request body as multipart/form-data.
|
|
|
|
// The whole request body is parsed and up to a total of maxMemory bytes of
|
|
|
|
// its file parts are stored in memory, with the remainder stored on
|
|
|
|
// disk in temporary files.
|
|
|
|
// ParseMultipartForm calls ParseForm if necessary.
|
|
|
|
// After one call to ParseMultipartForm, subsequent calls have no effect.
|
|
|
|
func (r *Request) ParseMultipartForm(maxMemory int64) os.Error {
|
2011-08-10 09:08:53 -06:00
|
|
|
if r.MultipartForm == multipartByReader {
|
|
|
|
return os.NewError("http: multipart handled by MultipartReader")
|
|
|
|
}
|
2011-04-27 23:21:54 -06:00
|
|
|
if r.Form == nil {
|
|
|
|
err := r.ParseForm()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if r.MultipartForm != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
mr, err := r.multipartReader()
|
|
|
|
if err == ErrNotMultipart {
|
|
|
|
return nil
|
|
|
|
} else if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := mr.ReadForm(maxMemory)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for k, v := range f.Value {
|
|
|
|
r.Form[k] = append(r.Form[k], v...)
|
|
|
|
}
|
|
|
|
r.MultipartForm = f
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2009-06-25 22:05:44 -06:00
|
|
|
// FormValue returns the first value for the named component of the query.
|
2011-04-27 23:21:54 -06:00
|
|
|
// FormValue calls ParseMultipartForm and ParseForm if necessary.
|
2009-06-25 22:05:44 -06:00
|
|
|
func (r *Request) FormValue(key string) string {
|
|
|
|
if r.Form == nil {
|
2011-04-27 23:21:54 -06:00
|
|
|
r.ParseMultipartForm(defaultMaxMemory)
|
2009-06-25 22:05:44 -06:00
|
|
|
}
|
2010-03-30 11:51:11 -06:00
|
|
|
if vs := r.Form[key]; len(vs) > 0 {
|
2009-11-09 13:07:39 -07:00
|
|
|
return vs[0]
|
2009-06-25 22:05:44 -06:00
|
|
|
}
|
2009-12-15 16:35:38 -07:00
|
|
|
return ""
|
2009-06-19 19:02:15 -06:00
|
|
|
}
|
2010-06-16 11:15:39 -06:00
|
|
|
|
2011-04-27 23:21:54 -06:00
|
|
|
// 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, os.Error) {
|
|
|
|
if r.MultipartForm == multipartByReader {
|
|
|
|
return nil, nil, os.NewError("http: multipart handled by MultipartReader")
|
|
|
|
}
|
|
|
|
if r.MultipartForm == nil {
|
|
|
|
err := r.ParseMultipartForm(defaultMaxMemory)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
2011-05-03 21:45:10 -06:00
|
|
|
if r.MultipartForm != nil && r.MultipartForm.File != nil {
|
|
|
|
if fhs := r.MultipartForm.File[key]; len(fhs) > 0 {
|
|
|
|
f, err := fhs[0].Open()
|
|
|
|
return f, fhs[0], err
|
|
|
|
}
|
2011-04-27 23:21:54 -06:00
|
|
|
}
|
|
|
|
return nil, nil, ErrMissingFile
|
|
|
|
}
|
|
|
|
|
2010-06-16 11:15:39 -06:00
|
|
|
func (r *Request) expectsContinue() bool {
|
2011-02-22 22:39:25 -07:00
|
|
|
return strings.ToLower(r.Header.Get("Expect")) == "100-continue"
|
2010-06-16 11:15:39 -06:00
|
|
|
}
|
2010-09-27 19:55:04 -06:00
|
|
|
|
|
|
|
func (r *Request) wantsHttp10KeepAlive() bool {
|
|
|
|
if r.ProtoMajor != 1 || r.ProtoMinor != 0 {
|
|
|
|
return false
|
|
|
|
}
|
2011-02-22 22:39:25 -07:00
|
|
|
return strings.Contains(strings.ToLower(r.Header.Get("Connection")), "keep-alive")
|
2010-09-27 19:55:04 -06:00
|
|
|
}
|