http: change ResponseWriter.SetHeader(k,v) to Header() accessor
Caller code needs to change:
rw.SetHeader("Content-Type", "text/plain")
to:
rw.Header().Set("Content-Type", "text/plain")
This now permits returning multiple headers
with the same name using Add:
rw.Header().Add("Set-Cookie", "..")
rw.Header().Add("Set-Cookie", "..")
This patch also fixes serialization of headers, removing newline characters.
Fixes #488
Fixes #914
R=rsc
CC=gburd, golang-dev
https://golang.org/cl/4239076
diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go
index 9dce5ed..41bd37a 100644
--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -702,7 +702,7 @@
func serveText(w http.ResponseWriter, text []byte) {
- w.SetHeader("Content-Type", "text/plain; charset=utf-8")
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Write(text)
}
diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go
index 1ebb802..f32a5b9 100644
--- a/src/cmd/godoc/main.go
+++ b/src/cmd/godoc/main.go
@@ -111,7 +111,7 @@
os.Stderr.Write(buf.Bytes())
}
if rw != nil {
- rw.SetHeader("content-type", "text/plain; charset=utf-8")
+ rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
rw.Write(buf.Bytes())
}
diff --git a/src/pkg/expvar/expvar.go b/src/pkg/expvar/expvar.go
index b1f0f6c..ed6cff7 100644
--- a/src/pkg/expvar/expvar.go
+++ b/src/pkg/expvar/expvar.go
@@ -269,7 +269,7 @@
}
func expvarHandler(w http.ResponseWriter, r *http.Request) {
- w.SetHeader("content-type", "application/json; charset=utf-8")
+ w.Header().Set("Content-Type", "application/json; charset=utf-8")
fmt.Fprintf(w, "{\n")
first := true
for name, value := range vars {
diff --git a/src/pkg/http/cgi/child.go b/src/pkg/http/cgi/child.go
index 50f90e5..e410c0a 100644
--- a/src/pkg/http/cgi/child.go
+++ b/src/pkg/http/cgi/child.go
@@ -149,12 +149,8 @@
return os.Getenv("REMOTE_ADDR")
}
-func (r *response) SetHeader(k, v string) {
- if v == "" {
- r.header.Del(k)
- } else {
- r.header.Set(k, v)
- }
+func (r *response) Header() http.Header {
+ return r.header
}
func (r *response) Write(p []byte) (n int, err os.Error) {
diff --git a/src/pkg/http/cgi/host.go b/src/pkg/http/cgi/host.go
index 4a2efc7..d6c8ab2 100644
--- a/src/pkg/http/cgi/host.go
+++ b/src/pkg/http/cgi/host.go
@@ -139,7 +139,7 @@
}
linebody := line.NewReader(cmd.Stdout, 1024)
- headers := make(map[string]string)
+ headers := rw.Header()
statusCode := http.StatusOK
for {
line, isPrefix, err := linebody.ReadLine()
@@ -181,12 +181,9 @@
}
statusCode = code
default:
- headers[header] = val
+ headers.Add(header, val)
}
}
- for h, v := range headers {
- rw.SetHeader(h, v)
- }
rw.WriteHeader(statusCode)
_, err = io.Copy(rw, linebody)
diff --git a/src/pkg/http/cgi/host_test.go b/src/pkg/http/cgi/host_test.go
index 3362ae5..2db08d5 100644
--- a/src/pkg/http/cgi/host_test.go
+++ b/src/pkg/http/cgi/host_test.go
@@ -111,10 +111,10 @@
}
replay := runCgiTest(t, h, "GET /test.cgi?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
- if expected, got := "text/html", replay.Header.Get("Content-Type"); got != expected {
+ if expected, got := "text/html", replay.Header().Get("Content-Type"); got != expected {
t.Errorf("got a Content-Type of %q; expected %q", got, expected)
}
- if expected, got := "X-Test-Value", replay.Header.Get("X-Test-Header"); got != expected {
+ if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
t.Errorf("got a X-Test-Header of %q; expected %q", got, expected)
}
}
diff --git a/src/pkg/http/cgi/matryoshka_test.go b/src/pkg/http/cgi/matryoshka_test.go
index 4bf9c19..3e4a6ad 100644
--- a/src/pkg/http/cgi/matryoshka_test.go
+++ b/src/pkg/http/cgi/matryoshka_test.go
@@ -43,10 +43,10 @@
}
replay := runCgiTest(t, h, "GET /test.go?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
- if expected, got := "text/html; charset=utf-8", replay.Header.Get("Content-Type"); got != expected {
+ if expected, got := "text/html; charset=utf-8", replay.Header().Get("Content-Type"); got != expected {
t.Errorf("got a Content-Type of %q; expected %q", got, expected)
}
- if expected, got := "X-Test-Value", replay.Header.Get("X-Test-Header"); got != expected {
+ if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
t.Errorf("got a X-Test-Header of %q; expected %q", got, expected)
}
}
@@ -58,7 +58,7 @@
return
}
Serve(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
- rw.SetHeader("X-Test-Header", "X-Test-Value")
+ rw.Header().Set("X-Test-Header", "X-Test-Value")
fmt.Fprintf(rw, "test=Hello CGI-in-CGI\n")
req.ParseForm()
for k, vv := range req.Form {
diff --git a/src/pkg/http/fs.go b/src/pkg/http/fs.go
index a4cd707..4ad680c 100644
--- a/src/pkg/http/fs.go
+++ b/src/pkg/http/fs.go
@@ -108,7 +108,7 @@
w.WriteHeader(StatusNotModified)
return
}
- w.SetHeader("Last-Modified", time.SecondsToUTC(d.Mtime_ns/1e9).Format(TimeFormat))
+ w.Header().Set("Last-Modified", time.SecondsToUTC(d.Mtime_ns/1e9).Format(TimeFormat))
// use contents of index.html for directory, if present
if d.IsDirectory() {
@@ -137,16 +137,16 @@
// use extension to find content type.
ext := filepath.Ext(name)
if ctype := mime.TypeByExtension(ext); ctype != "" {
- w.SetHeader("Content-Type", ctype)
+ w.Header().Set("Content-Type", ctype)
} else {
// read first chunk to decide between utf-8 text and binary
var buf [1024]byte
n, _ := io.ReadFull(f, buf[:])
b := buf[:n]
if isText(b) {
- w.SetHeader("Content-Type", "text-plain; charset=utf-8")
+ w.Header().Set("Content-Type", "text-plain; charset=utf-8")
} else {
- w.SetHeader("Content-Type", "application/octet-stream") // generic binary
+ w.Header().Set("Content-Type", "application/octet-stream") // generic binary
}
f.Seek(0, 0) // rewind to output whole file
}
@@ -166,11 +166,11 @@
}
size = ra.length
code = StatusPartialContent
- w.SetHeader("Content-Range", fmt.Sprintf("bytes %d-%d/%d", ra.start, ra.start+ra.length-1, d.Size))
+ w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", ra.start, ra.start+ra.length-1, d.Size))
}
- w.SetHeader("Accept-Ranges", "bytes")
- w.SetHeader("Content-Length", strconv.Itoa64(size))
+ w.Header().Set("Accept-Ranges", "bytes")
+ w.Header().Set("Content-Length", strconv.Itoa64(size))
w.WriteHeader(code)
diff --git a/src/pkg/http/httptest/recorder.go b/src/pkg/http/httptest/recorder.go
index ec7bde8..22827a3 100644
--- a/src/pkg/http/httptest/recorder.go
+++ b/src/pkg/http/httptest/recorder.go
@@ -14,11 +14,10 @@
// ResponseRecorder is an implementation of http.ResponseWriter that
// records its mutations for later inspection in tests.
type ResponseRecorder struct {
- Code int // the HTTP response code from WriteHeader
- Header http.Header // if non-nil, the headers to populate
- Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
- Flushed bool
-
+ Code int // the HTTP response code from WriteHeader
+ HeaderMap http.Header // the HTTP response headers
+ Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
+ Flushed bool
FakeRemoteAddr string // the fake RemoteAddr to return, or "" for DefaultRemoteAddr
FakeUsingTLS bool // whether to return true from the UsingTLS method
}
@@ -26,8 +25,8 @@
// NewRecorder returns an initialized ResponseRecorder.
func NewRecorder() *ResponseRecorder {
return &ResponseRecorder{
- Header: http.Header(make(map[string][]string)),
- Body: new(bytes.Buffer),
+ HeaderMap: make(http.Header),
+ Body: new(bytes.Buffer),
}
}
@@ -49,15 +48,9 @@
return rw.FakeUsingTLS
}
-// SetHeader populates rw.Header, if non-nil.
-func (rw *ResponseRecorder) SetHeader(k, v string) {
- if rw.Header != nil {
- if v == "" {
- rw.Header.Del(k)
- } else {
- rw.Header.Set(k, v)
- }
- }
+// Header returns the response headers.
+func (rw *ResponseRecorder) Header() http.Header {
+ return rw.HeaderMap
}
// Write always succeeds and writes to rw.Body, if not nil.
diff --git a/src/pkg/http/pprof/pprof.go b/src/pkg/http/pprof/pprof.go
index f7db9aa..0bac266 100644
--- a/src/pkg/http/pprof/pprof.go
+++ b/src/pkg/http/pprof/pprof.go
@@ -41,14 +41,14 @@
// command line, with arguments separated by NUL bytes.
// The package initialization registers it as /debug/pprof/cmdline.
func Cmdline(w http.ResponseWriter, r *http.Request) {
- w.SetHeader("content-type", "text/plain; charset=utf-8")
+ w.Header().Set("content-type", "text/plain; charset=utf-8")
fmt.Fprintf(w, strings.Join(os.Args, "\x00"))
}
// Heap responds with the pprof-formatted heap profile.
// The package initialization registers it as /debug/pprof/heap.
func Heap(w http.ResponseWriter, r *http.Request) {
- w.SetHeader("content-type", "text/plain; charset=utf-8")
+ w.Header().Set("content-type", "text/plain; charset=utf-8")
pprof.WriteHeapProfile(w)
}
@@ -56,7 +56,7 @@
// responding with a table mapping program counters to function names.
// The package initialization registers it as /debug/pprof/symbol.
func Symbol(w http.ResponseWriter, r *http.Request) {
- w.SetHeader("content-type", "text/plain; charset=utf-8")
+ w.Header().Set("content-type", "text/plain; charset=utf-8")
// We don't know how many symbols we have, but we
// do have symbol information. Pprof only cares whether
diff --git a/src/pkg/http/response.go b/src/pkg/http/response.go
index 3d77c55..7ac7fb8 100644
--- a/src/pkg/http/response.go
+++ b/src/pkg/http/response.go
@@ -217,13 +217,16 @@
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[k] {
+ if exclude == nil || !exclude[k] {
keys = append(keys, k)
}
}
sort.SortStrings(keys)
for _, k := range keys {
for _, v := range h[k] {
+ v = strings.TrimSpace(v)
+ v = strings.Replace(v, "\n", " ", -1)
+ v = strings.Replace(v, "\r", " ", -1)
if _, err := fmt.Fprintf(w, "%s: %s\r\n", k, v); err != nil {
return err
}
diff --git a/src/pkg/http/responsewrite_test.go b/src/pkg/http/responsewrite_test.go
index 228ed5f..0ef7f04 100644
--- a/src/pkg/http/responsewrite_test.go
+++ b/src/pkg/http/responsewrite_test.go
@@ -65,6 +65,29 @@
"Transfer-Encoding: chunked\r\n\r\n" +
"6\r\nabcdef\r\n0\r\n\r\n",
},
+
+ // Header value with a newline character (Issue 914).
+ // Also tests removal of leading and trailing whitespace.
+ {
+ Response{
+ StatusCode: 204,
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ RequestMethod: "GET",
+ Header: Header{
+ "Foo": []string{" Bar\nBaz "},
+ },
+ Body: nil,
+ ContentLength: 0,
+ TransferEncoding: []string{"chunked"},
+ Close: true,
+ },
+
+ "HTTP/1.1 204 No Content\r\n" +
+ "Connection: close\r\n" +
+ "Foo: Bar Baz\r\n" +
+ "\r\n",
+ },
}
func TestResponseWrite(t *testing.T) {
@@ -78,7 +101,7 @@
}
sraw := braw.String()
if sraw != tt.Raw {
- t.Errorf("Test %d, expecting:\n%s\nGot:\n%s\n", i, tt.Raw, sraw)
+ t.Errorf("Test %d, expecting:\n%q\nGot:\n%q\n", i, tt.Raw, sraw)
continue
}
}
diff --git a/src/pkg/http/serve_test.go b/src/pkg/http/serve_test.go
index 40ad681..a6d3cab 100644
--- a/src/pkg/http/serve_test.go
+++ b/src/pkg/http/serve_test.go
@@ -144,7 +144,7 @@
type stringHandler string
func (s stringHandler) ServeHTTP(w ResponseWriter, r *Request) {
- w.SetHeader("Result", string(s))
+ w.Header().Set("Result", string(s))
}
var handlers = []struct {
@@ -216,7 +216,7 @@
mux.ServeHTTP(resp, req)
- if loc, expected := resp.Header.Get("Location"), "/foo.txt"; loc != expected {
+ if loc, expected := resp.Header().Get("Location"), "/foo.txt"; loc != expected {
t.Errorf("Expected Location header set to %q; got %q", expected, loc)
return
}
@@ -294,8 +294,8 @@
// TestIdentityResponse verifies that a handler can unset
func TestIdentityResponse(t *testing.T) {
handler := HandlerFunc(func(rw ResponseWriter, req *Request) {
- rw.SetHeader("Content-Length", "3")
- rw.SetHeader("Transfer-Encoding", req.FormValue("te"))
+ rw.Header().Set("Content-Length", "3")
+ rw.Header().Set("Transfer-Encoding", req.FormValue("te"))
switch {
case req.FormValue("overwrite") == "1":
_, err := rw.Write([]byte("foo TOO LONG"))
@@ -303,7 +303,7 @@
t.Errorf("expected ErrContentLength; got %v", err)
}
case req.FormValue("underwrite") == "1":
- rw.SetHeader("Content-Length", "500")
+ rw.Header().Set("Content-Length", "500")
rw.Write([]byte("too short"))
default:
rw.Write([]byte("foo"))
diff --git a/src/pkg/http/server.go b/src/pkg/http/server.go
index c48aa8d..8a8cdd9 100644
--- a/src/pkg/http/server.go
+++ b/src/pkg/http/server.go
@@ -54,17 +54,10 @@
// UsingTLS returns true if the client is connected using TLS
UsingTLS() bool
- // SetHeader sets a header line in the eventual response.
- // For example, SetHeader("Content-Type", "text/html; charset=utf-8")
- // will result in the header line
- //
- // Content-Type: text/html; charset=utf-8
- //
- // being sent. UTF-8 encoded HTML is the default setting for
- // Content-Type in this library, so users need not make that
- // particular call. Calls to SetHeader after WriteHeader (or Write)
- // are ignored. An empty value removes the header if previously set.
- SetHeader(string, string)
+ // Header returns the header map that will be sent by WriteHeader.
+ // Changing the header after a call to WriteHeader (or Write) has
+ // no effect.
+ Header() Header
// Write writes the data to the connection as part of an HTTP reply.
// If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK)
@@ -106,14 +99,14 @@
// A response represents the server side of an HTTP response.
type response struct {
conn *conn
- req *Request // request for this response
- chunking bool // using chunked transfer encoding for reply body
- wroteHeader bool // reply header has been written
- wroteContinue bool // 100 Continue response was written
- header map[string]string // reply header parameters
- written int64 // number of bytes written in body
- contentLength int64 // explicitly-declared Content-Length; or -1
- status int // status code passed to WriteHeader
+ req *Request // request for this response
+ chunking bool // using chunked transfer encoding for reply body
+ wroteHeader bool // reply header has been written
+ wroteContinue bool // 100 Continue response was written
+ header Header // reply header parameters
+ written int64 // number of bytes written in body
+ contentLength int64 // explicitly-declared Content-Length; or -1
+ status int // status code passed to WriteHeader
// close connection after this reply. set on request and
// updated after response from handler if there's a
@@ -174,7 +167,7 @@
w = new(response)
w.conn = c
w.req = req
- w.header = make(map[string]string)
+ w.header = make(Header)
w.contentLength = -1
// Expect 100 Continue support
@@ -185,21 +178,16 @@
return w, nil
}
-// UsingTLS implements the ResponseWriter.UsingTLS
func (w *response) UsingTLS() bool {
return w.conn.usingTLS
}
-// RemoteAddr implements the ResponseWriter.RemoteAddr method
func (w *response) RemoteAddr() string { return w.conn.remoteAddr }
-// SetHeader implements the ResponseWriter.SetHeader method
-// An empty value removes the header from the map.
-func (w *response) SetHeader(hdr, val string) {
- w.header[CanonicalHeaderKey(hdr)] = val, val != ""
+func (w *response) Header() Header {
+ return w.header
}
-// WriteHeader implements the ResponseWriter.WriteHeader method
func (w *response) WriteHeader(code int) {
if w.conn.hijacked {
log.Print("http: response.WriteHeader on hijacked connection")
@@ -214,46 +202,47 @@
if code == StatusNotModified {
// Must not have body.
for _, header := range []string{"Content-Type", "Content-Length", "Transfer-Encoding"} {
- if w.header[header] != "" {
+ if w.header.Get(header) != "" {
// TODO: return an error if WriteHeader gets a return parameter
// or set a flag on w to make future Writes() write an error page?
// for now just log and drop the header.
log.Printf("http: StatusNotModified response with header %q defined", header)
- w.header[header] = "", false
+ w.header.Del(header)
}
}
} else {
// Default output is HTML encoded in UTF-8.
- if w.header["Content-Type"] == "" {
- w.SetHeader("Content-Type", "text/html; charset=utf-8")
+ if w.header.Get("Content-Type") == "" {
+ w.header.Set("Content-Type", "text/html; charset=utf-8")
}
}
- if w.header["Date"] == "" {
- w.SetHeader("Date", time.UTC().Format(TimeFormat))
+ if w.header.Get("Date") == "" {
+ w.Header().Set("Date", time.UTC().Format(TimeFormat))
}
// Check for a explicit (and valid) Content-Length header.
var hasCL bool
var contentLength int64
- if clenStr, ok := w.header["Content-Length"]; ok {
+ if clenStr := w.header.Get("Content-Length"); clenStr != "" {
var err os.Error
contentLength, err = strconv.Atoi64(clenStr)
if err == nil {
hasCL = true
} else {
log.Printf("http: invalid Content-Length of %q sent", clenStr)
- w.SetHeader("Content-Length", "")
+ w.header.Set("Content-Length", "")
}
}
- te, hasTE := w.header["Transfer-Encoding"]
+ te := w.header.Get("Transfer-Encoding")
+ hasTE := te != ""
if hasCL && hasTE && te != "identity" {
// TODO: return an error if WriteHeader gets a return parameter
// For now just ignore the Content-Length.
log.Printf("http: WriteHeader called with both Transfer-Encoding of %q and a Content-Length of %d",
te, contentLength)
- w.SetHeader("Content-Length", "")
+ w.header.Set("Content-Length", "")
hasCL = false
}
@@ -262,7 +251,7 @@
} else if hasCL {
w.chunking = false
w.contentLength = contentLength
- w.SetHeader("Transfer-Encoding", "")
+ w.header.Del("Transfer-Encoding")
} else if w.req.ProtoAtLeast(1, 1) {
// HTTP/1.1 or greater: use chunked transfer encoding
// to avoid closing the connection at EOF.
@@ -270,20 +259,20 @@
// might have set. Deal with that as need arises once we have a valid
// use case.
w.chunking = true
- w.SetHeader("Transfer-Encoding", "chunked")
+ w.header.Set("Transfer-Encoding", "chunked")
} else {
// HTTP version < 1.1: cannot do chunked transfer
// encoding and we don't know the Content-Length so
// signal EOF by closing connection.
w.closeAfterReply = true
- w.chunking = false // redundant
- w.SetHeader("Transfer-Encoding", "") // in case already set
+ w.chunking = false // redundant
+ w.header.Del("Transfer-Encoding") // in case already set
}
if w.req.wantsHttp10KeepAlive() && (w.req.Method == "HEAD" || hasCL) {
_, connectionHeaderSet := w.header["Connection"]
if !connectionHeaderSet {
- w.SetHeader("Connection", "keep-alive")
+ w.header.Set("Connection", "keep-alive")
}
} else if !w.req.ProtoAtLeast(1, 1) {
// Client did not ask to keep connection alive.
@@ -292,7 +281,7 @@
// Cannot use Content-Length with non-identity Transfer-Encoding.
if w.chunking {
- w.SetHeader("Content-Length", "")
+ w.header.Set("Content-Length", "")
}
if !w.req.ProtoAtLeast(1, 0) {
return
@@ -307,13 +296,10 @@
text = "status code " + codestring
}
io.WriteString(w.conn.buf, proto+" "+codestring+" "+text+"\r\n")
- for k, v := range w.header {
- io.WriteString(w.conn.buf, k+": "+v+"\r\n")
- }
+ writeSortedHeader(w.conn.buf, w.header, nil)
io.WriteString(w.conn.buf, "\r\n")
}
-// Write implements the ResponseWriter.Write method
func (w *response) Write(data []byte) (n int, err os.Error) {
if w.conn.hijacked {
log.Print("http: response.Write on hijacked connection")
@@ -388,7 +374,7 @@
msg += " would ignore this error page if this text weren't here.\n"
// Is it text? ("Content-Type" is always in the map)
- baseType := strings.Split(w.header["Content-Type"], ";", 2)[0]
+ baseType := strings.Split(w.header.Get("Content-Type"), ";", 2)[0]
switch baseType {
case "text/html":
io.WriteString(w, "<!-- ")
@@ -408,8 +394,8 @@
// If this was an HTTP/1.0 request with keep-alive and we sent a Content-Length
// back, we can make this a keep-alive response ...
if w.req.wantsHttp10KeepAlive() {
- _, sentLength := w.header["Content-Length"]
- if sentLength && w.header["Connection"] == "keep-alive" {
+ sentLength := w.header.Get("Content-Length") != ""
+ if sentLength && w.header.Get("Connection") == "keep-alive" {
w.closeAfterReply = false
}
}
@@ -431,7 +417,6 @@
}
}
-// Flush implements the ResponseWriter.Flush method.
func (w *response) Flush() {
if !w.wroteHeader {
w.WriteHeader(StatusOK)
@@ -504,7 +489,7 @@
// Error replies to the request with the specified error message and HTTP code.
func Error(w ResponseWriter, error string, code int) {
- w.SetHeader("Content-Type", "text/plain; charset=utf-8")
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(code)
fmt.Fprintln(w, error)
}
@@ -557,7 +542,7 @@
}
}
- w.SetHeader("Location", url)
+ w.Header().Set("Location", url)
w.WriteHeader(code)
// RFC2616 recommends that a short note "SHOULD" be included in the
@@ -680,7 +665,7 @@
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
// Clean path to canonical form and redirect.
if p := cleanPath(r.URL.Path); p != r.URL.Path {
- w.SetHeader("Location", p)
+ w.Header().Set("Location", p)
w.WriteHeader(StatusMovedPermanently)
return
}
@@ -833,7 +818,7 @@
// )
//
// func handler(w http.ResponseWriter, req *http.Request) {
-// w.SetHeader("Content-Type", "text/plain")
+// w.Header().Set("Content-Type", "text/plain")
// w.Write([]byte("This is an example server.\n"))
// }
//
diff --git a/src/pkg/http/triv.go b/src/pkg/http/triv.go
index 52d521d..47257e3 100644
--- a/src/pkg/http/triv.go
+++ b/src/pkg/http/triv.go
@@ -56,7 +56,7 @@
var booleanflag = flag.Bool("boolean", true, "another flag for testing")
func FlagServer(w http.ResponseWriter, req *http.Request) {
- w.SetHeader("content-type", "text/plain; charset=utf-8")
+ w.Header.Set("Content-Type", "text/plain; charset=utf-8")
fmt.Fprint(w, "Flags:\n")
flag.VisitAll(func(f *flag.Flag) {
if f.Value.String() != f.DefValue {
@@ -93,7 +93,7 @@
// exec a program, redirecting output
func DateServer(rw http.ResponseWriter, req *http.Request) {
- rw.SetHeader("content-type", "text/plain; charset=utf-8")
+ rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
r, w, err := os.Pipe()
if err != nil {
fmt.Fprintf(rw, "pipe: %s\n", err)
diff --git a/src/pkg/rpc/server.go b/src/pkg/rpc/server.go
index aa51f45..90ee253 100644
--- a/src/pkg/rpc/server.go
+++ b/src/pkg/rpc/server.go
@@ -509,7 +509,7 @@
// ServeHTTP implements an http.Handler that answers RPC requests.
func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if req.Method != "CONNECT" {
- w.SetHeader("Content-Type", "text/plain; charset=utf-8")
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusMethodNotAllowed)
io.WriteString(w, "405 must CONNECT\n")
return