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