summaryrefslogtreecommitdiff
path: root/libgo/go/net/http/transfer.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/net/http/transfer.go')
-rw-r--r--libgo/go/net/http/transfer.go101
1 files changed, 72 insertions, 29 deletions
diff --git a/libgo/go/net/http/transfer.go b/libgo/go/net/http/transfer.go
index 2eec9d9abc4..7f63686528a 100644
--- a/libgo/go/net/http/transfer.go
+++ b/libgo/go/net/http/transfer.go
@@ -12,11 +12,20 @@ import (
"io"
"io/ioutil"
"net/textproto"
+ "sort"
"strconv"
"strings"
"sync"
)
+type errorReader struct {
+ err error
+}
+
+func (r *errorReader) Read(p []byte) (n int, err error) {
+ return 0, r.err
+}
+
// transferWriter inspects the fields of a user-supplied Request or Response,
// sanitizes them without changing the user object and provides methods for
// writing the respective header, body and trailer in wire format.
@@ -53,8 +62,11 @@ func newTransferWriter(r interface{}) (t *transferWriter, err error) {
if t.ContentLength == 0 {
// Test to see if it's actually zero or just unset.
var buf [1]byte
- n, _ := io.ReadFull(t.Body, buf[:])
- if n == 1 {
+ n, rerr := io.ReadFull(t.Body, buf[:])
+ if rerr != nil && rerr != io.EOF {
+ t.ContentLength = -1
+ t.Body = &errorReader{rerr}
+ } else if n == 1 {
// Oh, guess there is data in this Body Reader after all.
// The ContentLength field just wasn't set.
// Stich the Body back together again, re-attaching our
@@ -132,11 +144,10 @@ func (t *transferWriter) shouldSendContentLength() bool {
return false
}
-func (t *transferWriter) WriteHeader(w io.Writer) (err error) {
+func (t *transferWriter) WriteHeader(w io.Writer) error {
if t.Close {
- _, err = io.WriteString(w, "Connection: close\r\n")
- if err != nil {
- return
+ if _, err := io.WriteString(w, "Connection: close\r\n"); err != nil {
+ return err
}
}
@@ -144,43 +155,44 @@ func (t *transferWriter) WriteHeader(w io.Writer) (err error) {
// function of the sanitized field triple (Body, ContentLength,
// TransferEncoding)
if t.shouldSendContentLength() {
- io.WriteString(w, "Content-Length: ")
- _, err = io.WriteString(w, strconv.FormatInt(t.ContentLength, 10)+"\r\n")
- if err != nil {
- return
+ if _, err := io.WriteString(w, "Content-Length: "); err != nil {
+ return err
+ }
+ if _, err := io.WriteString(w, strconv.FormatInt(t.ContentLength, 10)+"\r\n"); err != nil {
+ return err
}
} else if chunked(t.TransferEncoding) {
- _, err = io.WriteString(w, "Transfer-Encoding: chunked\r\n")
- if err != nil {
- return
+ if _, err := io.WriteString(w, "Transfer-Encoding: chunked\r\n"); err != nil {
+ return err
}
}
// Write Trailer header
if t.Trailer != nil {
- // TODO: At some point, there should be a generic mechanism for
- // writing long headers, using HTTP line splitting
- io.WriteString(w, "Trailer: ")
- needComma := false
+ keys := make([]string, 0, len(t.Trailer))
for k := range t.Trailer {
k = CanonicalHeaderKey(k)
switch k {
case "Transfer-Encoding", "Trailer", "Content-Length":
return &badStringError{"invalid Trailer key", k}
}
- if needComma {
- io.WriteString(w, ",")
+ keys = append(keys, k)
+ }
+ if len(keys) > 0 {
+ sort.Strings(keys)
+ // TODO: could do better allocation-wise here, but trailers are rare,
+ // so being lazy for now.
+ if _, err := io.WriteString(w, "Trailer: "+strings.Join(keys, ",")+"\r\n"); err != nil {
+ return err
}
- io.WriteString(w, k)
- needComma = true
}
- _, err = io.WriteString(w, "\r\n")
}
- return
+ return nil
}
-func (t *transferWriter) WriteBody(w io.Writer) (err error) {
+func (t *transferWriter) WriteBody(w io.Writer) error {
+ var err error
var ncopy int64
// Write body
@@ -217,11 +229,16 @@ func (t *transferWriter) WriteBody(w io.Writer) (err error) {
// TODO(petar): Place trailer writer code here.
if chunked(t.TransferEncoding) {
+ // Write Trailer header
+ if t.Trailer != nil {
+ if err := t.Trailer.Write(w); err != nil {
+ return err
+ }
+ }
// Last chunk, empty trailer
_, err = io.WriteString(w, "\r\n")
}
-
- return
+ return err
}
type transferReader struct {
@@ -253,6 +270,22 @@ func bodyAllowedForStatus(status int) bool {
return true
}
+var (
+ suppressedHeaders304 = []string{"Content-Type", "Content-Length", "Transfer-Encoding"}
+ suppressedHeadersNoBody = []string{"Content-Length", "Transfer-Encoding"}
+)
+
+func suppressedHeaders(status int) []string {
+ switch {
+ case status == 304:
+ // RFC 2616 section 10.3.5: "the response MUST NOT include other entity-headers"
+ return suppressedHeaders304
+ case !bodyAllowedForStatus(status):
+ return suppressedHeadersNoBody
+ }
+ return nil
+}
+
// msg is *Request or *Response.
func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
t := &transferReader{RequestMethod: "GET"}
@@ -499,7 +532,7 @@ func fixTrailer(header Header, te []string) (Header, error) {
case "Transfer-Encoding", "Trailer", "Content-Length":
return nil, &badStringError{"bad trailer key", key}
}
- trailer.Del(key)
+ trailer[key] = nil
}
if len(trailer) == 0 {
return nil, nil
@@ -631,13 +664,23 @@ func (b *body) readTrailer() error {
}
switch rr := b.hdr.(type) {
case *Request:
- rr.Trailer = Header(hdr)
+ mergeSetHeader(&rr.Trailer, Header(hdr))
case *Response:
- rr.Trailer = Header(hdr)
+ mergeSetHeader(&rr.Trailer, Header(hdr))
}
return nil
}
+func mergeSetHeader(dst *Header, src Header) {
+ if *dst == nil {
+ *dst = src
+ return
+ }
+ for k, vv := range src {
+ (*dst)[k] = vv
+ }
+}
+
func (b *body) Close() error {
b.mu.Lock()
defer b.mu.Unlock()