mirror of
https://github.com/golang/go
synced 2024-11-22 01:14:40 -07:00
http: have client set Content-Length when possible
Also some cleanup, removing redundant code. Make more things use NewRequest. Add some tests, docs. R=golang-dev, adg, rsc CC=golang-dev https://golang.org/cl/4561047
This commit is contained in:
parent
50effb654c
commit
219805066e
@ -11,9 +11,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -228,23 +226,12 @@ func Post(url string, bodyType string, body io.Reader) (r *Response, err os.Erro
|
|||||||
//
|
//
|
||||||
// Caller should close r.Body when done reading from it.
|
// Caller should close r.Body when done reading from it.
|
||||||
func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err os.Error) {
|
func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err os.Error) {
|
||||||
var req Request
|
req, err := NewRequest("POST", url, body)
|
||||||
req.Method = "POST"
|
|
||||||
req.ProtoMajor = 1
|
|
||||||
req.ProtoMinor = 1
|
|
||||||
req.Close = true
|
|
||||||
req.Body = ioutil.NopCloser(body)
|
|
||||||
req.Header = Header{
|
|
||||||
"Content-Type": {bodyType},
|
|
||||||
}
|
|
||||||
req.TransferEncoding = []string{"chunked"}
|
|
||||||
|
|
||||||
req.URL, err = ParseURL(url)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
req.Header.Set("Content-Type", bodyType)
|
||||||
return send(&req, c.Transport)
|
return send(req, c.Transport)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostForm issues a POST to the specified URL,
|
// PostForm issues a POST to the specified URL,
|
||||||
@ -262,25 +249,7 @@ func PostForm(url string, data map[string]string) (r *Response, err os.Error) {
|
|||||||
//
|
//
|
||||||
// Caller should close r.Body when done reading from it.
|
// Caller should close r.Body when done reading from it.
|
||||||
func (c *Client) PostForm(url string, data map[string]string) (r *Response, err os.Error) {
|
func (c *Client) PostForm(url string, data map[string]string) (r *Response, err os.Error) {
|
||||||
var req Request
|
return c.Post(url, "application/x-www-form-urlencoded", urlencode(data))
|
||||||
req.Method = "POST"
|
|
||||||
req.ProtoMajor = 1
|
|
||||||
req.ProtoMinor = 1
|
|
||||||
req.Close = true
|
|
||||||
body := urlencode(data)
|
|
||||||
req.Body = ioutil.NopCloser(body)
|
|
||||||
req.Header = Header{
|
|
||||||
"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, c.Transport)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove this function when PostForm takes a multimap.
|
// TODO: remove this function when PostForm takes a multimap.
|
||||||
|
@ -78,6 +78,61 @@ func TestGetRequestFormat(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPostRequestFormat(t *testing.T) {
|
||||||
|
tr := &recordingTransport{}
|
||||||
|
client := &Client{Transport: tr}
|
||||||
|
|
||||||
|
url := "http://dummy.faketld/"
|
||||||
|
json := `{"key":"value"}`
|
||||||
|
b := strings.NewReader(json)
|
||||||
|
client.Post(url, "application/json", b) // Note: doesn't hit network
|
||||||
|
|
||||||
|
if tr.req.Method != "POST" {
|
||||||
|
t.Errorf("got method %q, want %q", tr.req.Method, "POST")
|
||||||
|
}
|
||||||
|
if tr.req.URL.String() != url {
|
||||||
|
t.Errorf("got URL %q, want %q", tr.req.URL.String(), url)
|
||||||
|
}
|
||||||
|
if tr.req.Header == nil {
|
||||||
|
t.Fatalf("expected non-nil request Header")
|
||||||
|
}
|
||||||
|
if tr.req.Close {
|
||||||
|
t.Error("got Close true, want false")
|
||||||
|
}
|
||||||
|
if g, e := tr.req.ContentLength, int64(len(json)); g != e {
|
||||||
|
t.Errorf("got ContentLength %d, want %d", g, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPostFormRequestFormat(t *testing.T) {
|
||||||
|
tr := &recordingTransport{}
|
||||||
|
client := &Client{Transport: tr}
|
||||||
|
|
||||||
|
url := "http://dummy.faketld/"
|
||||||
|
form := map[string]string{"foo": "bar"}
|
||||||
|
client.PostForm(url, form) // Note: doesn't hit network
|
||||||
|
|
||||||
|
if tr.req.Method != "POST" {
|
||||||
|
t.Errorf("got method %q, want %q", tr.req.Method, "POST")
|
||||||
|
}
|
||||||
|
if tr.req.URL.String() != url {
|
||||||
|
t.Errorf("got URL %q, want %q", tr.req.URL.String(), url)
|
||||||
|
}
|
||||||
|
if tr.req.Header == nil {
|
||||||
|
t.Fatalf("expected non-nil request Header")
|
||||||
|
}
|
||||||
|
if g, e := tr.req.Header.Get("Content-Type"), "application/x-www-form-urlencoded"; g != e {
|
||||||
|
t.Errorf("got Content-Type %q, want %q", g, e)
|
||||||
|
}
|
||||||
|
if tr.req.Close {
|
||||||
|
t.Error("got Close true, want false")
|
||||||
|
}
|
||||||
|
if g, e := tr.req.ContentLength, int64(len("foo=bar")); g != e {
|
||||||
|
t.Errorf("got ContentLength %d, want %d", g, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func TestRedirects(t *testing.T) {
|
func TestRedirects(t *testing.T) {
|
||||||
var ts *httptest.Server
|
var ts *httptest.Server
|
||||||
ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
||||||
|
@ -10,6 +10,7 @@ package http
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"container/vector"
|
"container/vector"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
@ -231,7 +232,7 @@ const defaultUserAgent = "Go http package"
|
|||||||
// Method (defaults to "GET")
|
// Method (defaults to "GET")
|
||||||
// UserAgent (defaults to defaultUserAgent)
|
// UserAgent (defaults to defaultUserAgent)
|
||||||
// Referer
|
// Referer
|
||||||
// Header
|
// Header (only keys not already in this list)
|
||||||
// Cookie
|
// Cookie
|
||||||
// ContentLength
|
// ContentLength
|
||||||
// TransferEncoding
|
// TransferEncoding
|
||||||
@ -256,6 +257,9 @@ func (req *Request) WriteProxy(w io.Writer) os.Error {
|
|||||||
func (req *Request) write(w io.Writer, usingProxy bool) os.Error {
|
func (req *Request) write(w io.Writer, usingProxy bool) os.Error {
|
||||||
host := req.Host
|
host := req.Host
|
||||||
if 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
|
host = req.URL.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -475,6 +479,17 @@ func NewRequest(method, url string, body io.Reader) (*Request, os.Error) {
|
|||||||
Body: rc,
|
Body: rc,
|
||||||
Host: u.Host,
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,6 +175,35 @@ var reqWriteTests = []reqWriteTest{
|
|||||||
"abcdef",
|
"abcdef",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// HTTP/1.1 POST with Content-Length in headers
|
||||||
|
{
|
||||||
|
Request{
|
||||||
|
Method: "POST",
|
||||||
|
RawURL: "http://example.com/",
|
||||||
|
Host: "example.com",
|
||||||
|
Header: Header{
|
||||||
|
"Content-Length": []string{"10"}, // ignored
|
||||||
|
},
|
||||||
|
ContentLength: 6,
|
||||||
|
},
|
||||||
|
|
||||||
|
[]byte("abcdef"),
|
||||||
|
|
||||||
|
"POST http://example.com/ HTTP/1.1\r\n" +
|
||||||
|
"Host: example.com\r\n" +
|
||||||
|
"User-Agent: Go http package\r\n" +
|
||||||
|
"Content-Length: 6\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"abcdef",
|
||||||
|
|
||||||
|
"POST http://example.com/ HTTP/1.1\r\n" +
|
||||||
|
"Host: example.com\r\n" +
|
||||||
|
"User-Agent: Go http package\r\n" +
|
||||||
|
"Content-Length: 6\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"abcdef",
|
||||||
|
},
|
||||||
|
|
||||||
// default to HTTP/1.1
|
// default to HTTP/1.1
|
||||||
{
|
{
|
||||||
Request{
|
Request{
|
||||||
|
@ -17,6 +17,12 @@ type Reader struct {
|
|||||||
prevRune int // index of previous rune; or < 0
|
prevRune int // index of previous rune; or < 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Len returns the number of bytes of the unread portion of the
|
||||||
|
// string.
|
||||||
|
func (r *Reader) Len() int {
|
||||||
|
return len(r.s) - r.i
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Reader) Read(b []byte) (n int, err os.Error) {
|
func (r *Reader) Read(b []byte) (n int, err os.Error) {
|
||||||
if r.i >= len(r.s) {
|
if r.i >= len(r.s) {
|
||||||
return 0, os.EOF
|
return 0, os.EOF
|
||||||
|
Loading…
Reference in New Issue
Block a user