summaryrefslogtreecommitdiff
path: root/libgo/go/net/http/request.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/net/http/request.go')
-rw-r--r--libgo/go/net/http/request.go212
1 files changed, 185 insertions, 27 deletions
diff --git a/libgo/go/net/http/request.go b/libgo/go/net/http/request.go
index dc5559282d0..fb6bb0aab58 100644
--- a/libgo/go/net/http/request.go
+++ b/libgo/go/net/http/request.go
@@ -18,12 +18,17 @@ import (
"io/ioutil"
"mime"
"mime/multipart"
+ "net"
"net/http/httptrace"
"net/textproto"
"net/url"
"strconv"
"strings"
"sync"
+
+ "golang_org/x/net/idna"
+ "golang_org/x/text/unicode/norm"
+ "golang_org/x/text/width"
)
const (
@@ -34,21 +39,40 @@ const (
// is either not present in the request or not a file field.
var ErrMissingFile = errors.New("http: no such file")
-// HTTP request parsing errors.
+// ProtocolError represents an HTTP protocol error.
+//
+// Deprecated: Not all errors in the http package related to protocol errors
+// are of type ProtocolError.
type ProtocolError struct {
ErrorString string
}
-func (err *ProtocolError) Error() string { return err.ErrorString }
+func (pe *ProtocolError) Error() string { return pe.ErrorString }
var (
- ErrHeaderTooLong = &ProtocolError{"header too long"}
- ErrShortBody = &ProtocolError{"entity body too short"}
- ErrNotSupported = &ProtocolError{"feature not supported"}
- ErrUnexpectedTrailer = &ProtocolError{"trailer header without chunked transfer encoding"}
+ // ErrNotSupported is returned by the Push method of Pusher
+ // implementations to indicate that HTTP/2 Push support is not
+ // available.
+ ErrNotSupported = &ProtocolError{"feature not supported"}
+
+ // ErrUnexpectedTrailer is returned by the Transport when a server
+ // replies with a Trailer header, but without a chunked reply.
+ ErrUnexpectedTrailer = &ProtocolError{"trailer header without chunked transfer encoding"}
+
+ // ErrMissingBoundary is returned by Request.MultipartReader when the
+ // request's Content-Type does not include a "boundary" parameter.
+ ErrMissingBoundary = &ProtocolError{"no multipart boundary param in Content-Type"}
+
+ // ErrNotMultipart is returned by Request.MultipartReader when the
+ // request's Content-Type is not multipart/form-data.
+ ErrNotMultipart = &ProtocolError{"request Content-Type isn't multipart/form-data"}
+
+ // Deprecated: ErrHeaderTooLong is not used.
+ ErrHeaderTooLong = &ProtocolError{"header too long"}
+ // Deprecated: ErrShortBody is not used.
+ ErrShortBody = &ProtocolError{"entity body too short"}
+ // Deprecated: ErrMissingContentLength is not used.
ErrMissingContentLength = &ProtocolError{"missing ContentLength in HEAD response"}
- ErrNotMultipart = &ProtocolError{"request Content-Type isn't multipart/form-data"}
- ErrMissingBoundary = &ProtocolError{"no multipart boundary param in Content-Type"}
)
type badStringError struct {
@@ -146,11 +170,20 @@ type Request struct {
// Handler does not need to.
Body io.ReadCloser
+ // GetBody defines an optional func to return a new copy of
+ // Body. It is used for client requests when a redirect requires
+ // reading the body more than once. Use of GetBody still
+ // requires setting Body.
+ //
+ // For server requests it is unused.
+ GetBody func() (io.ReadCloser, error)
+
// 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.
- // For client requests, a value of 0 means unknown if Body is not nil.
+ // For client requests, a value of 0 with a non-nil Body is
+ // also treated as unknown.
ContentLength int64
// TransferEncoding lists the transfer encodings from outermost to
@@ -175,11 +208,15 @@ type Request struct {
// 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".
+ // It may be of the form "host:port". For international domain
+ // names, Host may be in Punycode or Unicode form. Use
+ // golang.org/x/net/idna to convert it to either format if
+ // needed.
//
// For client requests Host optionally overrides the Host
// header to send. If empty, the Request.Write method uses
- // the value of URL.Host.
+ // the value of URL.Host. Host may contain an international
+ // domain name.
Host string
// Form contains the parsed form data, including both the URL
@@ -276,8 +313,8 @@ type Request struct {
// For outgoing client requests, the context controls cancelation.
//
// For incoming server requests, the context is canceled when the
-// ServeHTTP method returns. For its associated values, see
-// ServerContextKey and LocalAddrContextKey.
+// client's connection closes, the request is canceled (with HTTP/2),
+// or when the ServeHTTP method returns.
func (r *Request) Context() context.Context {
if r.ctx != nil {
return r.ctx
@@ -304,6 +341,18 @@ func (r *Request) ProtoAtLeast(major, minor int) bool {
r.ProtoMajor == major && r.ProtoMinor >= minor
}
+// protoAtLeastOutgoing is like ProtoAtLeast, but is for outgoing
+// requests (see issue 18407) where these fields aren't supposed to
+// matter. As a minor fix for Go 1.8, at least treat (0, 0) as
+// matching HTTP/1.1 or HTTP/1.0. Only HTTP/1.1 is used.
+// TODO(bradfitz): ideally remove this whole method. It shouldn't be used.
+func (r *Request) protoAtLeastOutgoing(major, minor int) bool {
+ if r.ProtoMajor == 0 && r.ProtoMinor == 0 && major == 1 && minor <= 1 {
+ return true
+ }
+ return r.ProtoAtLeast(major, minor)
+}
+
// UserAgent returns the client's User-Agent, if sent in the request.
func (r *Request) UserAgent() string {
return r.Header.Get("User-Agent")
@@ -319,6 +368,8 @@ var ErrNoCookie = errors.New("http: named cookie not present")
// Cookie returns the named cookie provided in the request or
// ErrNoCookie if not found.
+// If multiple cookies match the given name, only one cookie will
+// be returned.
func (r *Request) Cookie(name string) (*Cookie, error) {
for _, c := range readCookies(r.Header, name) {
return c, nil
@@ -561,6 +612,12 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, wai
}
}
+ if bw, ok := w.(*bufio.Writer); ok && tw.FlushHeaders {
+ if err := bw.Flush(); err != nil {
+ return err
+ }
+ }
+
// Write body and trailer
err = tw.WriteBody(w)
if err != nil {
@@ -573,7 +630,24 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, wai
return nil
}
-// cleanHost strips anything after '/' or ' '.
+func idnaASCII(v string) (string, error) {
+ if isASCII(v) {
+ return v, nil
+ }
+ // The idna package doesn't do everything from
+ // https://tools.ietf.org/html/rfc5895 so we do it here.
+ // TODO(bradfitz): should the idna package do this instead?
+ v = strings.ToLower(v)
+ v = width.Fold.String(v)
+ v = norm.NFC.String(v)
+ return idna.ToASCII(v)
+}
+
+// cleanHost cleans up the host sent in request's Host header.
+//
+// It both strips anything after '/' or ' ', and puts the value
+// into Punycode form, if necessary.
+//
// Ideally we'd clean the Host header according to the spec:
// https://tools.ietf.org/html/rfc7230#section-5.4 (Host = uri-host [ ":" port ]")
// https://tools.ietf.org/html/rfc7230#section-2.7 (uri-host -> rfc3986's host)
@@ -584,9 +658,21 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, wai
// first offending character.
func cleanHost(in string) string {
if i := strings.IndexAny(in, " /"); i != -1 {
- return in[:i]
+ in = in[:i]
+ }
+ host, port, err := net.SplitHostPort(in)
+ if err != nil { // input was just a host
+ a, err := idnaASCII(in)
+ if err != nil {
+ return in // garbage in, garbage out
+ }
+ return a
}
- return in
+ a, err := idnaASCII(host)
+ if err != nil {
+ return in // garbage in, garbage out
+ }
+ return net.JoinHostPort(a, port)
}
// removeZone removes IPv6 zone identifier from host.
@@ -658,11 +744,17 @@ func validMethod(method string) bool {
// methods Do, Post, and PostForm, and Transport.RoundTrip.
//
// NewRequest returns a Request suitable for use with Client.Do or
-// Transport.RoundTrip.
-// To create a request for use with testing a Server Handler use either
-// ReadRequest or manually update the Request fields. See the Request
-// type's documentation for the difference between inbound and outbound
-// request fields.
+// Transport.RoundTrip. To create a request for use with testing a
+// Server Handler, either use the NewRequest function in the
+// net/http/httptest package, use ReadRequest, or manually update the
+// Request fields. See the Request type's documentation for the
+// difference between inbound and outbound request fields.
+//
+// If body is of type *bytes.Buffer, *bytes.Reader, or
+// *strings.Reader, the returned request's ContentLength is set to its
+// exact value (instead of -1), GetBody is populated (so 307 and 308
+// redirects can replay the body), and Body is set to NoBody if the
+// ContentLength is 0.
func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
if method == "" {
// We document that "" means "GET" for Request.Method, and people have
@@ -697,10 +789,43 @@ func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
switch v := body.(type) {
case *bytes.Buffer:
req.ContentLength = int64(v.Len())
+ buf := v.Bytes()
+ req.GetBody = func() (io.ReadCloser, error) {
+ r := bytes.NewReader(buf)
+ return ioutil.NopCloser(r), nil
+ }
case *bytes.Reader:
req.ContentLength = int64(v.Len())
+ snapshot := *v
+ req.GetBody = func() (io.ReadCloser, error) {
+ r := snapshot
+ return ioutil.NopCloser(&r), nil
+ }
case *strings.Reader:
req.ContentLength = int64(v.Len())
+ snapshot := *v
+ req.GetBody = func() (io.ReadCloser, error) {
+ r := snapshot
+ return ioutil.NopCloser(&r), nil
+ }
+ default:
+ // This is where we'd set it to -1 (at least
+ // if body != NoBody) to mean unknown, but
+ // that broke people during the Go 1.8 testing
+ // period. People depend on it being 0 I
+ // guess. Maybe retry later. See Issue 18117.
+ }
+ // For client requests, Request.ContentLength of 0
+ // means either actually 0, or unknown. The only way
+ // to explicitly say that the ContentLength is zero is
+ // to set the Body to nil. But turns out too much code
+ // depends on NewRequest returning a non-nil Body,
+ // so we use a well-known ReadCloser variable instead
+ // and have the http package also treat that sentinel
+ // variable to mean explicitly zero.
+ if req.GetBody != nil && req.ContentLength == 0 {
+ req.Body = NoBody
+ req.GetBody = func() (io.ReadCloser, error) { return NoBody, nil }
}
}
@@ -1000,18 +1125,24 @@ func parsePostForm(r *Request) (vs url.Values, err error) {
return
}
-// ParseForm parses the raw query from the URL and updates r.Form.
+// ParseForm populates r.Form and r.PostForm.
+//
+// For all requests, ParseForm parses the raw query from the URL and updates
+// r.Form.
+//
+// For POST, PUT, and PATCH requests, it also parses the request body as a form
+// and puts the results into both r.PostForm and r.Form. Request 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 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 other HTTP methods, or when the Content-Type is not
+// application/x-www-form-urlencoded, the request Body is not read, and
+// r.PostForm is initialized to a non-nil, empty value.
//
// 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.
+// ParseForm is idempotent.
func (r *Request) ParseForm() error {
var err error
if r.PostForm == nil {
@@ -1174,3 +1305,30 @@ func (r *Request) isReplayable() bool {
}
return false
}
+
+// outgoingLength reports the Content-Length of this outgoing (Client) request.
+// It maps 0 into -1 (unknown) when the Body is non-nil.
+func (r *Request) outgoingLength() int64 {
+ if r.Body == nil || r.Body == NoBody {
+ return 0
+ }
+ if r.ContentLength != 0 {
+ return r.ContentLength
+ }
+ return -1
+}
+
+// requestMethodUsuallyLacksBody reports whether the given request
+// method is one that typically does not involve a request body.
+// This is used by the Transport (via
+// transferWriter.shouldSendChunkedRequestBody) to determine whether
+// we try to test-read a byte from a non-nil Request.Body when
+// Request.outgoingLength() returns -1. See the comments in
+// shouldSendChunkedRequestBody.
+func requestMethodUsuallyLacksBody(method string) bool {
+ switch method {
+ case "GET", "HEAD", "DELETE", "OPTIONS", "PROPFIND", "SEARCH":
+ return true
+ }
+ return false
+}