diff options
Diffstat (limited to 'src/pkg/http/request.go')
| -rw-r--r-- | src/pkg/http/request.go | 101 | 
1 files changed, 84 insertions, 17 deletions
| diff --git a/src/pkg/http/request.go b/src/pkg/http/request.go index 4852ca3e1..bdc3a7e4f 100644 --- a/src/pkg/http/request.go +++ b/src/pkg/http/request.go @@ -10,8 +10,10 @@ package http  import (  	"bufio" +	"bytes"  	"crypto/tls"  	"container/vector" +	"encoding/base64"  	"fmt"  	"io"  	"io/ioutil" @@ -88,10 +90,10 @@ type Request struct {  	//  	// then  	// -	//	Header = map[string]string{ -	//		"Accept-Encoding": "gzip, deflate", -	//		"Accept-Language": "en-us", -	//		"Connection": "keep-alive", +	//	Header = map[string][]string{ +	//		"Accept-Encoding": {"gzip, deflate"}, +	//		"Accept-Language": {"en-us"}, +	//		"Connection": {"keep-alive"},  	//	}  	//  	// HTTP defines that header names are case-insensitive. @@ -139,7 +141,7 @@ type Request struct {  	UserAgent string  	// The parsed form. Only available after ParseForm is called. -	Form map[string][]string +	Form Values  	// The parsed multipart form, including file uploads.  	// Only available after ParseMultipartForm is called. @@ -230,15 +232,15 @@ const defaultUserAgent = "Go http package"  //	Method (defaults to "GET")  //	UserAgent (defaults to defaultUserAgent)  //	Referer -//	Header +//	Header (only keys not already in this list)  //	Cookie  //	ContentLength  //	TransferEncoding  //	Body  // -// If Body is present but Content-Length is <= 0, Write adds -// "Transfer-Encoding: chunked" to the header. Body is closed after -// it is sent. +// 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.  func (req *Request) Write(w io.Writer) os.Error {  	return req.write(w, false)  } @@ -255,6 +257,9 @@ func (req *Request) WriteProxy(w io.Writer) os.Error {  func (req *Request) write(w io.Writer, usingProxy bool) os.Error {  	host := req.Host  	if host == "" { +		if req.URL == nil { +			return os.NewError("http: Request.Write on Request with no Host or URL set") +		}  		host = req.URL.Host  	} @@ -275,9 +280,7 @@ func (req *Request) write(w io.Writer, usingProxy bool) os.Error {  	fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), uri)  	// Header lines -	if !usingProxy { -		fmt.Fprintf(w, "Host: %s\r\n", host) -	} +	fmt.Fprintf(w, "Host: %s\r\n", host)  	fmt.Fprintf(w, "User-Agent: %s\r\n", valueOrDefault(req.UserAgent, defaultUserAgent))  	if req.Referer != "" {  		fmt.Fprintf(w, "Referer: %s\r\n", req.Referer) @@ -300,7 +303,7 @@ func (req *Request) write(w io.Writer, usingProxy bool) os.Error {  	// from Request, and introduce Request methods along the lines of  	// Response.{GetHeader,AddHeader} and string constants for "Host",  	// "User-Agent" and "Referer". -	err = writeSortedHeader(w, req.Header, reqExcludeHeader) +	err = req.Header.WriteSubset(w, reqExcludeHeader)  	if err != nil {  		return err  	} @@ -476,9 +479,35 @@ func NewRequest(method, url string, body io.Reader) (*Request, os.Error) {  		Body:       rc,  		Host:       u.Host,  	} +	if body != nil { +		switch v := body.(type) { +		case *strings.Reader: +			req.ContentLength = int64(v.Len()) +		case *bytes.Buffer: +			req.ContentLength = int64(v.Len()) +		default: +			req.ContentLength = -1 // chunked +		} +		if req.ContentLength == 0 { +			// To prevent chunking and disambiguate this +			// from the default ContentLength zero value. +			req.TransferEncoding = []string{"identity"} +		} +	} +  	return req, nil  } +// 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 +	r.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(s))) +} +  // ReadRequest reads and parses a request from b.  func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { @@ -573,18 +602,56 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) {  	return req, nil  } +// Values maps a string key to a list of values. +// It is typically used for query parameters and form values. +// Unlike in the Header map, the keys in a Values map +// are case-sensitive. +type Values map[string][]string + +// Get gets the first value associated with the given key. +// If there are no values associated with the key, Get returns +// the empty string. To access multiple values, use the map +// directly. +func (v Values) Get(key string) string { +	if v == nil { +		return "" +	} +	vs, ok := v[key] +	if !ok || len(vs) == 0 { +		return "" +	} +	return vs[0] +} + +// Set sets the key to value. It replaces any existing +// values. +func (v Values) Set(key, value string) { +	v[key] = []string{value} +} + +// Add adds the key to value. It appends to any existing +// values associated with key. +func (v Values) Add(key, value string) { +	v[key] = append(v[key], value) +} + +// Del deletes the values associated with key. +func (v Values) Del(key string) { +	v[key] = nil, false +} +  // ParseQuery parses the URL-encoded query string and returns  // a map listing the values specified for each key.  // ParseQuery always returns a non-nil map containing all the  // valid query parameters found; err describes the first decoding error  // encountered, if any. -func ParseQuery(query string) (m map[string][]string, err os.Error) { -	m = make(map[string][]string) +func ParseQuery(query string) (m Values, err os.Error) { +	m = make(Values)  	err = parseQuery(m, query)  	return  } -func parseQuery(m map[string][]string, query string) (err os.Error) { +func parseQuery(m Values, query string) (err os.Error) {  	for _, kv := range strings.Split(query, "&", -1) {  		if len(kv) == 0 {  			continue @@ -617,7 +684,7 @@ func (r *Request) ParseForm() (err os.Error) {  		return  	} -	r.Form = make(map[string][]string) +	r.Form = make(Values)  	if r.URL != nil {  		err = parseQuery(r.Form, r.URL.RawQuery)  	} | 
