diff options
Diffstat (limited to 'src/pkg/http/client.go')
-rw-r--r-- | src/pkg/http/client.go | 70 |
1 files changed, 63 insertions, 7 deletions
diff --git a/src/pkg/http/client.go b/src/pkg/http/client.go index 54487dac2..29678ee32 100644 --- a/src/pkg/http/client.go +++ b/src/pkg/http/client.go @@ -8,11 +8,14 @@ package http import ( "bufio" + "bytes" + "crypto/tls" "encoding/base64" "fmt" "io" "net" "os" + "strconv" "strings" ) @@ -21,7 +24,7 @@ import ( func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") } // Used in Send to implement io.ReadCloser by bundling together the -// io.BufReader through which we read the response, and the underlying +// bufio.Reader through which we read the response, and the underlying // network connection. type readClose struct { io.Reader @@ -34,15 +37,15 @@ type readClose struct { // send() method is nonpublic because, when we refactor the code for persistent // connections, it may no longer make sense to have a method with this signature. func send(req *Request) (resp *Response, err os.Error) { - if req.URL.Scheme != "http" { + if req.URL.Scheme != "http" && req.URL.Scheme != "https" { return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme} } addr := req.URL.Host if !hasPort(addr) { - addr += ":http" + addr += ":" + req.URL.Scheme } - info := req.URL.Userinfo + info := req.URL.RawUserinfo if len(info) > 0 { enc := base64.URLEncoding encoded := make([]byte, enc.EncodedLen(len(info))) @@ -52,9 +55,25 @@ func send(req *Request) (resp *Response, err os.Error) { } req.Header["Authorization"] = "Basic " + string(encoded) } - conn, err := net.Dial("tcp", "", addr) - if err != nil { - return nil, err + + var conn io.ReadWriteCloser + if req.URL.Scheme == "http" { + conn, err = net.Dial("tcp", "", addr) + if err != nil { + return nil, err + } + } else { // https + conn, err = tls.Dial("tcp", "", addr, nil) + if err != nil { + return nil, err + } + h := req.URL.Host + if hasPort(h) { + h = h[0:strings.LastIndex(h, ":")] + } + if err := conn.(*tls.Conn).VerifyHostname(h); err != nil { + return nil, err + } } err = req.Write(conn) @@ -111,6 +130,7 @@ func Get(url string) (r *Response, finalURL string, err os.Error) { if req.URL, err = ParseURL(url); err != nil { break } + url = req.URL.String() if r, err = send(&req); err != nil { break } @@ -153,6 +173,41 @@ func Post(url string, bodyType string, body io.Reader) (r *Response, err os.Erro return send(&req) } +// PostForm issues a POST to the specified URL, +// with data's keys and values urlencoded as the request body. +// +// Caller should close r.Body when done reading it. +func PostForm(url string, data map[string]string) (r *Response, err os.Error) { + var req Request + req.Method = "POST" + req.ProtoMajor = 1 + req.ProtoMinor = 1 + req.Close = true + body := urlencode(data) + req.Body = nopCloser{body} + req.Header = map[string]string{ + "Content-Type": "application/x-www-form-urlencoded", + "Content-Length": strconv.Itoa(body.Len()), + } + req.ContentLength = int64(body.Len()) + + req.URL, err = ParseURL(url) + if err != nil { + return nil, err + } + + return send(&req) +} + +// TODO: remove this function when PostForm takes a multimap. +func urlencode(data map[string]string) (b *bytes.Buffer) { + m := make(map[string][]string, len(data)) + for k, v := range data { + m[k] = []string{v} + } + return bytes.NewBuffer([]byte(EncodeQuery(m))) +} + // Head issues a HEAD to the specified URL. func Head(url string) (r *Response, err os.Error) { var req Request @@ -160,6 +215,7 @@ func Head(url string) (r *Response, err os.Error) { if req.URL, err = ParseURL(url); err != nil { return } + url = req.URL.String() if r, err = send(&req); err != nil { return } |