summaryrefslogtreecommitdiff
path: root/src/pkg/http/request.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/http/request.go')
-rw-r--r--src/pkg/http/request.go101
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)
}