Commit f319e1df authored by Evan Shaw's avatar Evan Shaw Committed by Brad Fitzpatrick

http: add Header.Write method

R=golang-dev, bradfitz, dsymonds
CC=golang-dev
https://golang.org/cl/4426069
parent d2d50f85
......@@ -168,17 +168,8 @@ func (r *response) WriteHeader(code int) {
r.header.Add("Content-Type", "text/html; charset=utf-8")
}
// TODO: add a method on http.Header to write itself to an io.Writer?
// This is duplicated code.
for k, vv := range r.header {
for _, v := range vv {
v = strings.Replace(v, "\n", "", -1)
v = strings.Replace(v, "\r", "", -1)
v = strings.TrimSpace(v)
fmt.Fprintf(r.bufw, "%s: %s\r\n", k, v)
}
}
r.bufw.Write([]byte("\r\n"))
r.header.Write(r.bufw)
r.bufw.WriteString("\r\n")
r.bufw.Flush()
}
......
......@@ -169,15 +169,7 @@ func (r *response) WriteHeader(code int) {
}
fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code))
// TODO(eds): this is duplicated in http and http/cgi
for k, vv := range r.header {
for _, v := range vv {
v = strings.Replace(v, "\n", "", -1)
v = strings.Replace(v, "\r", "", -1)
v = strings.TrimSpace(v)
fmt.Fprintf(r.w, "%s: %s\r\n", k, v)
}
}
r.header.Write(r.w)
r.w.WriteString("\r\n")
}
......
......@@ -4,7 +4,14 @@
package http
import "net/textproto"
import (
"fmt"
"io"
"net/textproto"
"os"
"sort"
"strings"
)
// A Header represents the key-value pairs in an HTTP header.
type Header map[string][]string
......@@ -35,6 +42,37 @@ func (h Header) Del(key string) {
textproto.MIMEHeader(h).Del(key)
}
// Write writes a header in wire format.
func (h Header) Write(w io.Writer) os.Error {
return h.WriteSubset(w, nil)
}
// WriteSubset writes a header in wire format.
// If exclude is not nil, keys where exclude[key] == true are not written.
func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) os.Error {
keys := make([]string, 0, len(h))
for k := range h {
if exclude == nil || !exclude[k] {
keys = append(keys, k)
}
}
sort.SortStrings(keys)
for _, k := range keys {
for _, v := range h[k] {
v = strings.Replace(v, "\n", " ", -1)
v = strings.Replace(v, "\r", " ", -1)
v = strings.TrimSpace(v)
if v == "" {
continue
}
if _, err := fmt.Fprintf(w, "%s: %s\r\n", k, v); err != nil {
return err
}
}
}
return nil
}
// CanonicalHeaderKey returns the canonical format of the
// header key s. The canonicalization converts the first
// letter and any letter following a hyphen to upper case;
......
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package http
import (
"bytes"
"testing"
)
var headerWriteTests = []struct {
h Header
exclude map[string]bool
expected string
}{
{Header{}, nil, ""},
{
Header{
"Content-Type": {"text/html; charset=UTF-8"},
"Content-Length": {"0"},
},
nil,
"Content-Length: 0\r\nContent-Type: text/html; charset=UTF-8\r\n",
},
{
Header{
"Content-Length": {"0", "1", "2"},
},
nil,
"Content-Length: 0\r\nContent-Length: 1\r\nContent-Length: 2\r\n",
},
{
Header{
"Expires": {"-1"},
"Content-Length": {"0"},
"Content-Encoding": {"gzip"},
},
map[string]bool{"Content-Length": true},
"Content-Encoding: gzip\r\nExpires: -1\r\n",
},
{
Header{
"Expires": {"-1"},
"Content-Length": {"0", "1", "2"},
"Content-Encoding": {"gzip"},
},
map[string]bool{"Content-Length": true},
"Content-Encoding: gzip\r\nExpires: -1\r\n",
},
{
Header{
"Expires": {"-1"},
"Content-Length": {"0"},
"Content-Encoding": {"gzip"},
},
map[string]bool{"Content-Length": true, "Expires": true, "Content-Encoding": true},
"",
},
}
func TestHeaderWrite(t *testing.T) {
var buf bytes.Buffer
for i, test := range headerWriteTests {
test.h.WriteSubset(&buf, test.exclude)
if buf.String() != test.expected {
t.Errorf("#%d:\n got: %q\nwant: %q", i, buf.String(), test.expected)
}
buf.Reset()
}
}
......@@ -300,7 +300,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
}
......
......@@ -8,11 +8,9 @@ package http
import (
"bufio"
"fmt"
"io"
"net/textproto"
"os"
"sort"
"strconv"
"strings"
)
......@@ -192,7 +190,7 @@ func (resp *Response) Write(w io.Writer) os.Error {
}
// Rest of header
err = writeSortedHeader(w, resp.Header, respExcludeHeader)
err = resp.Header.WriteSubset(w, respExcludeHeader)
if err != nil {
return err
}
......@@ -213,27 +211,3 @@ func (resp *Response) Write(w io.Writer) os.Error {
// Success
return nil
}
func writeSortedHeader(w io.Writer, h Header, exclude map[string]bool) os.Error {
keys := make([]string, 0, len(h))
for k := range h {
if exclude == nil || !exclude[k] {
keys = append(keys, k)
}
}
sort.SortStrings(keys)
for _, k := range keys {
for _, v := range h[k] {
v = strings.Replace(v, "\n", " ", -1)
v = strings.Replace(v, "\r", " ", -1)
v = strings.TrimSpace(v)
if v == "" {
continue
}
if _, err := fmt.Fprintf(w, "%s: %s\r\n", k, v); err != nil {
return err
}
}
}
return nil
}
......@@ -309,7 +309,7 @@ func (w *response) WriteHeader(code int) {
text = "status code " + codestring
}
io.WriteString(w.conn.buf, proto+" "+codestring+" "+text+"\r\n")
writeSortedHeader(w.conn.buf, w.header, nil)
w.header.Write(w.conn.buf)
io.WriteString(w.conn.buf, "\r\n")
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment