Commit fd9a5d22 authored by Stephen Ma's avatar Stephen Ma

http: revised http Handler interface

R=rsc
CC=golang-dev
https://golang.org/cl/1993043
parent ffdb855b
...@@ -31,26 +31,26 @@ import ( ...@@ -31,26 +31,26 @@ import (
// Handler for /doc/codewalk/ and below. // Handler for /doc/codewalk/ and below.
func codewalk(c *http.Conn, r *http.Request) { func codewalk(w http.ResponseWriter, r *http.Request) {
relpath := r.URL.Path[len("/doc/codewalk/"):] relpath := r.URL.Path[len("/doc/codewalk/"):]
abspath := absolutePath(r.URL.Path[1:], *goroot) abspath := absolutePath(r.URL.Path[1:], *goroot)
r.ParseForm() r.ParseForm()
if f := r.FormValue("fileprint"); f != "" { if f := r.FormValue("fileprint"); f != "" {
codewalkFileprint(c, r, f) codewalkFileprint(w, r, f)
return return
} }
// If directory exists, serve list of code walks. // If directory exists, serve list of code walks.
dir, err := os.Lstat(abspath) dir, err := os.Lstat(abspath)
if err == nil && dir.IsDirectory() { if err == nil && dir.IsDirectory() {
codewalkDir(c, r, relpath, abspath) codewalkDir(w, r, relpath, abspath)
return return
} }
// If file exists, serve using standard file server. // If file exists, serve using standard file server.
if err == nil { if err == nil {
serveFile(c, r) serveFile(w, r)
return return
} }
...@@ -59,17 +59,17 @@ func codewalk(c *http.Conn, r *http.Request) { ...@@ -59,17 +59,17 @@ func codewalk(c *http.Conn, r *http.Request) {
cw, err := loadCodewalk(abspath + ".xml") cw, err := loadCodewalk(abspath + ".xml")
if err != nil { if err != nil {
log.Stderr(err) log.Stderr(err)
serveError(c, r, relpath, err) serveError(w, r, relpath, err)
return return
} }
// Canonicalize the path and redirect if changed // Canonicalize the path and redirect if changed
if redirect(c, r) { if redirect(w, r) {
return return
} }
b := applyTemplate(codewalkHTML, "codewalk", cw) b := applyTemplate(codewalkHTML, "codewalk", cw)
servePage(c, "Codewalk: "+cw.Title, "", "", b) servePage(w, "Codewalk: "+cw.Title, "", "", b)
} }
...@@ -178,7 +178,7 @@ func loadCodewalk(file string) (*Codewalk, os.Error) { ...@@ -178,7 +178,7 @@ func loadCodewalk(file string) (*Codewalk, os.Error) {
// codewalkDir serves the codewalk directory listing. // codewalkDir serves the codewalk directory listing.
// It scans the directory for subdirectories or files named *.xml // It scans the directory for subdirectories or files named *.xml
// and prepares a table. // and prepares a table.
func codewalkDir(c *http.Conn, r *http.Request, relpath, abspath string) { func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string) {
type elem struct { type elem struct {
Name string Name string
Title string Title string
...@@ -187,7 +187,7 @@ func codewalkDir(c *http.Conn, r *http.Request, relpath, abspath string) { ...@@ -187,7 +187,7 @@ func codewalkDir(c *http.Conn, r *http.Request, relpath, abspath string) {
dir, err := ioutil.ReadDir(abspath) dir, err := ioutil.ReadDir(abspath)
if err != nil { if err != nil {
log.Stderr(err) log.Stderr(err)
serveError(c, r, relpath, err) serveError(w, r, relpath, err)
return return
} }
var v vector.Vector var v vector.Vector
...@@ -204,7 +204,7 @@ func codewalkDir(c *http.Conn, r *http.Request, relpath, abspath string) { ...@@ -204,7 +204,7 @@ func codewalkDir(c *http.Conn, r *http.Request, relpath, abspath string) {
} }
b := applyTemplate(codewalkdirHTML, "codewalkdir", v) b := applyTemplate(codewalkdirHTML, "codewalkdir", v)
servePage(c, "Codewalks", "", "", b) servePage(w, "Codewalks", "", "", b)
} }
...@@ -214,12 +214,12 @@ func codewalkDir(c *http.Conn, r *http.Request, relpath, abspath string) { ...@@ -214,12 +214,12 @@ func codewalkDir(c *http.Conn, r *http.Request, relpath, abspath string) {
// in the response. This format is used for the middle window pane // in the response. This format is used for the middle window pane
// of the codewalk pages. It is a separate iframe and does not get // of the codewalk pages. It is a separate iframe and does not get
// the usual godoc HTML wrapper. // the usual godoc HTML wrapper.
func codewalkFileprint(c *http.Conn, r *http.Request, f string) { func codewalkFileprint(w http.ResponseWriter, r *http.Request, f string) {
abspath := absolutePath(f, *goroot) abspath := absolutePath(f, *goroot)
data, err := ioutil.ReadFile(abspath) data, err := ioutil.ReadFile(abspath)
if err != nil { if err != nil {
log.Stderr(err) log.Stderr(err)
serveError(c, r, f, err) serveError(w, r, f, err)
return return
} }
lo, _ := strconv.Atoi(r.FormValue("lo")) lo, _ := strconv.Atoi(r.FormValue("lo"))
...@@ -243,17 +243,17 @@ func codewalkFileprint(c *http.Conn, r *http.Request, f string) { ...@@ -243,17 +243,17 @@ func codewalkFileprint(c *http.Conn, r *http.Request, f string) {
} }
} }
io.WriteString(c, `<style type="text/css">@import "/doc/codewalk/codewalk.css";</style><pre>`) io.WriteString(w, `<style type="text/css">@import "/doc/codewalk/codewalk.css";</style><pre>`)
template.HTMLEscape(c, data[0:mark]) template.HTMLEscape(w, data[0:mark])
io.WriteString(c, "<a name='mark'></a>") io.WriteString(w, "<a name='mark'></a>")
template.HTMLEscape(c, data[mark:lo]) template.HTMLEscape(w, data[mark:lo])
if lo < hi { if lo < hi {
io.WriteString(c, "<div class='codewalkhighlight'>") io.WriteString(w, "<div class='codewalkhighlight'>")
template.HTMLEscape(c, data[lo:hi]) template.HTMLEscape(w, data[lo:hi])
io.WriteString(c, "</div>") io.WriteString(w, "</div>")
} }
template.HTMLEscape(c, data[hi:]) template.HTMLEscape(w, data[hi:])
io.WriteString(c, "</pre>") io.WriteString(w, "</pre>")
} }
......
...@@ -792,7 +792,7 @@ func readTemplates() { ...@@ -792,7 +792,7 @@ func readTemplates() {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Generic HTML wrapper // Generic HTML wrapper
func servePage(c *http.Conn, title, subtitle, query string, content []byte) { func servePage(w http.ResponseWriter, title, subtitle, query string, content []byte) {
type Data struct { type Data struct {
Title string Title string
Subtitle string Subtitle string
...@@ -813,15 +813,15 @@ func servePage(c *http.Conn, title, subtitle, query string, content []byte) { ...@@ -813,15 +813,15 @@ func servePage(c *http.Conn, title, subtitle, query string, content []byte) {
Content: content, Content: content,
} }
if err := godocHTML.Execute(&d, c); err != nil { if err := godocHTML.Execute(&d, w); err != nil {
log.Stderrf("godocHTML.Execute: %s", err) log.Stderrf("godocHTML.Execute: %s", err)
} }
} }
func serveText(c *http.Conn, text []byte) { func serveText(w http.ResponseWriter, text []byte) {
c.SetHeader("Content-Type", "text/plain; charset=utf-8") w.SetHeader("Content-Type", "text/plain; charset=utf-8")
c.Write(text) w.Write(text)
} }
...@@ -844,19 +844,19 @@ func extractString(src []byte, rx *regexp.Regexp) (s string) { ...@@ -844,19 +844,19 @@ func extractString(src []byte, rx *regexp.Regexp) (s string) {
} }
func serveHTMLDoc(c *http.Conn, r *http.Request, abspath, relpath string) { func serveHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
// get HTML body contents // get HTML body contents
src, err := ioutil.ReadFile(abspath) src, err := ioutil.ReadFile(abspath)
if err != nil { if err != nil {
log.Stderrf("ioutil.ReadFile: %s", err) log.Stderrf("ioutil.ReadFile: %s", err)
serveError(c, r, relpath, err) serveError(w, r, relpath, err)
return return
} }
// if it begins with "<!DOCTYPE " assume it is standalone // if it begins with "<!DOCTYPE " assume it is standalone
// html that doesn't need the template wrapping. // html that doesn't need the template wrapping.
if bytes.HasPrefix(src, []byte("<!DOCTYPE ")) { if bytes.HasPrefix(src, []byte("<!DOCTYPE ")) {
c.Write(src) w.Write(src)
return return
} }
...@@ -875,7 +875,7 @@ func serveHTMLDoc(c *http.Conn, r *http.Request, abspath, relpath string) { ...@@ -875,7 +875,7 @@ func serveHTMLDoc(c *http.Conn, r *http.Request, abspath, relpath string) {
} }
subtitle := extractString(src, subtitleRx) subtitle := extractString(src, subtitleRx)
servePage(c, title, subtitle, "", src) servePage(w, title, subtitle, "", src)
} }
...@@ -888,11 +888,11 @@ func applyTemplate(t *template.Template, name string, data interface{}) []byte { ...@@ -888,11 +888,11 @@ func applyTemplate(t *template.Template, name string, data interface{}) []byte {
} }
func serveGoSource(c *http.Conn, r *http.Request, abspath, relpath string) { func serveGoSource(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
file, err := parser.ParseFile(abspath, nil, parser.ParseComments) file, err := parser.ParseFile(abspath, nil, parser.ParseComments)
if err != nil { if err != nil {
log.Stderrf("parser.ParseFile: %s", err) log.Stderrf("parser.ParseFile: %s", err)
serveError(c, r, relpath, err) serveError(w, r, relpath, err)
return return
} }
...@@ -911,13 +911,13 @@ func serveGoSource(c *http.Conn, r *http.Request, abspath, relpath string) { ...@@ -911,13 +911,13 @@ func serveGoSource(c *http.Conn, r *http.Request, abspath, relpath string) {
info := &SourceInfo{styler.idList(), buf.Bytes()} info := &SourceInfo{styler.idList(), buf.Bytes()}
contents := applyTemplate(sourceHTML, "sourceHTML", info) contents := applyTemplate(sourceHTML, "sourceHTML", info)
servePage(c, "Source file "+relpath, "", "", contents) servePage(w, "Source file "+relpath, "", "", contents)
} }
func redirect(c *http.Conn, r *http.Request) (redirected bool) { func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) {
if canonical := pathutil.Clean(r.URL.Path) + "/"; r.URL.Path != canonical { if canonical := pathutil.Clean(r.URL.Path) + "/"; r.URL.Path != canonical {
http.Redirect(c, canonical, http.StatusMovedPermanently) http.Redirect(w, r, canonical, http.StatusMovedPermanently)
redirected = true redirected = true
} }
return return
...@@ -971,11 +971,11 @@ func isTextFile(path string) bool { ...@@ -971,11 +971,11 @@ func isTextFile(path string) bool {
} }
func serveTextFile(c *http.Conn, r *http.Request, abspath, relpath string) { func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
src, err := ioutil.ReadFile(abspath) src, err := ioutil.ReadFile(abspath)
if err != nil { if err != nil {
log.Stderrf("ioutil.ReadFile: %s", err) log.Stderrf("ioutil.ReadFile: %s", err)
serveError(c, r, relpath, err) serveError(w, r, relpath, err)
return return
} }
...@@ -984,19 +984,19 @@ func serveTextFile(c *http.Conn, r *http.Request, abspath, relpath string) { ...@@ -984,19 +984,19 @@ func serveTextFile(c *http.Conn, r *http.Request, abspath, relpath string) {
template.HTMLEscape(&buf, src) template.HTMLEscape(&buf, src)
fmt.Fprintln(&buf, "</pre>") fmt.Fprintln(&buf, "</pre>")
servePage(c, "Text file "+relpath, "", "", buf.Bytes()) servePage(w, "Text file "+relpath, "", "", buf.Bytes())
} }
func serveDirectory(c *http.Conn, r *http.Request, abspath, relpath string) { func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath string) {
if redirect(c, r) { if redirect(w, r) {
return return
} }
list, err := ioutil.ReadDir(abspath) list, err := ioutil.ReadDir(abspath)
if err != nil { if err != nil {
log.Stderrf("ioutil.ReadDir: %s", err) log.Stderrf("ioutil.ReadDir: %s", err)
serveError(c, r, relpath, err) serveError(w, r, relpath, err)
return return
} }
...@@ -1007,23 +1007,23 @@ func serveDirectory(c *http.Conn, r *http.Request, abspath, relpath string) { ...@@ -1007,23 +1007,23 @@ func serveDirectory(c *http.Conn, r *http.Request, abspath, relpath string) {
} }
contents := applyTemplate(dirlistHTML, "dirlistHTML", list) contents := applyTemplate(dirlistHTML, "dirlistHTML", list)
servePage(c, "Directory "+relpath, "", "", contents) servePage(w, "Directory "+relpath, "", "", contents)
} }
func serveFile(c *http.Conn, r *http.Request) { func serveFile(w http.ResponseWriter, r *http.Request) {
relpath := r.URL.Path[1:] // serveFile URL paths start with '/' relpath := r.URL.Path[1:] // serveFile URL paths start with '/'
abspath := absolutePath(relpath, *goroot) abspath := absolutePath(relpath, *goroot)
// pick off special cases and hand the rest to the standard file server // pick off special cases and hand the rest to the standard file server
switch r.URL.Path { switch r.URL.Path {
case "/": case "/":
serveHTMLDoc(c, r, pathutil.Join(*goroot, "doc/root.html"), "doc/root.html") serveHTMLDoc(w, r, pathutil.Join(*goroot, "doc/root.html"), "doc/root.html")
return return
case "/doc/root.html": case "/doc/root.html":
// hide landing page from its real name // hide landing page from its real name
http.Redirect(c, "/", http.StatusMovedPermanently) http.Redirect(w, r, "/", http.StatusMovedPermanently)
return return
} }
...@@ -1032,42 +1032,42 @@ func serveFile(c *http.Conn, r *http.Request) { ...@@ -1032,42 +1032,42 @@ func serveFile(c *http.Conn, r *http.Request) {
if strings.HasSuffix(abspath, "/index.html") { if strings.HasSuffix(abspath, "/index.html") {
// We'll show index.html for the directory. // We'll show index.html for the directory.
// Use the dir/ version as canonical instead of dir/index.html. // Use the dir/ version as canonical instead of dir/index.html.
http.Redirect(c, r.URL.Path[0:len(r.URL.Path)-len("index.html")], http.StatusMovedPermanently) http.Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len("index.html")], http.StatusMovedPermanently)
return return
} }
serveHTMLDoc(c, r, abspath, relpath) serveHTMLDoc(w, r, abspath, relpath)
return return
case ".go": case ".go":
serveGoSource(c, r, abspath, relpath) serveGoSource(w, r, abspath, relpath)
return return
} }
dir, err := os.Lstat(abspath) dir, err := os.Lstat(abspath)
if err != nil { if err != nil {
log.Stderr(err) log.Stderr(err)
serveError(c, r, relpath, err) serveError(w, r, relpath, err)
return return
} }
if dir != nil && dir.IsDirectory() { if dir != nil && dir.IsDirectory() {
if redirect(c, r) { if redirect(w, r) {
return return
} }
if index := abspath + "/index.html"; isTextFile(index) { if index := abspath + "/index.html"; isTextFile(index) {
serveHTMLDoc(c, r, index, relativePath(index)) serveHTMLDoc(w, r, index, relativePath(index))
return return
} }
serveDirectory(c, r, abspath, relpath) serveDirectory(w, r, abspath, relpath)
return return
} }
if isTextFile(abspath) { if isTextFile(abspath) {
serveTextFile(c, r, abspath, relpath) serveTextFile(w, r, abspath, relpath)
return return
} }
fileServer.ServeHTTP(c, r) fileServer.ServeHTTP(w, r)
} }
...@@ -1243,8 +1243,8 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf ...@@ -1243,8 +1243,8 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf
} }
func (h *httpHandler) ServeHTTP(c *http.Conn, r *http.Request) { func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if redirect(c, r) { if redirect(w, r) {
return return
} }
...@@ -1257,13 +1257,13 @@ func (h *httpHandler) ServeHTTP(c *http.Conn, r *http.Request) { ...@@ -1257,13 +1257,13 @@ func (h *httpHandler) ServeHTTP(c *http.Conn, r *http.Request) {
info := h.getPageInfo(abspath, relpath, r.FormValue("p"), mode) info := h.getPageInfo(abspath, relpath, r.FormValue("p"), mode)
if info.Err != nil { if info.Err != nil {
log.Stderr(info.Err) log.Stderr(info.Err)
serveError(c, r, relpath, info.Err) serveError(w, r, relpath, info.Err)
return return
} }
if r.FormValue("f") == "text" { if r.FormValue("f") == "text" {
contents := applyTemplate(packageText, "packageText", info) contents := applyTemplate(packageText, "packageText", info)
serveText(c, contents) serveText(w, contents)
return return
} }
...@@ -1290,7 +1290,7 @@ func (h *httpHandler) ServeHTTP(c *http.Conn, r *http.Request) { ...@@ -1290,7 +1290,7 @@ func (h *httpHandler) ServeHTTP(c *http.Conn, r *http.Request) {
} }
contents := applyTemplate(packageHTML, "packageHTML", info) contents := applyTemplate(packageHTML, "packageHTML", info)
servePage(c, title, subtitle, "", contents) servePage(w, title, subtitle, "", contents)
} }
...@@ -1319,13 +1319,13 @@ func lookup(query string) (result SearchResult) { ...@@ -1319,13 +1319,13 @@ func lookup(query string) (result SearchResult) {
} }
func search(c *http.Conn, r *http.Request) { func search(w http.ResponseWriter, r *http.Request) {
query := strings.TrimSpace(r.FormValue("q")) query := strings.TrimSpace(r.FormValue("q"))
result := lookup(query) result := lookup(query)
if r.FormValue("f") == "text" { if r.FormValue("f") == "text" {
contents := applyTemplate(searchText, "searchText", result) contents := applyTemplate(searchText, "searchText", result)
serveText(c, contents) serveText(w, contents)
return return
} }
...@@ -1337,7 +1337,7 @@ func search(c *http.Conn, r *http.Request) { ...@@ -1337,7 +1337,7 @@ func search(c *http.Conn, r *http.Request) {
} }
contents := applyTemplate(searchHTML, "searchHTML", result) contents := applyTemplate(searchHTML, "searchHTML", result)
servePage(c, title, "", query, contents) servePage(w, title, "", query, contents)
} }
......
...@@ -64,14 +64,14 @@ var ( ...@@ -64,14 +64,14 @@ var (
) )
func serveError(c *http.Conn, r *http.Request, relpath string, err os.Error) { func serveError(w http.ResponseWriter, r *http.Request, relpath string, err os.Error) {
contents := applyTemplate(errorHTML, "errorHTML", err) // err may contain an absolute path! contents := applyTemplate(errorHTML, "errorHTML", err) // err may contain an absolute path!
c.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
servePage(c, "File "+relpath, "", "", contents) servePage(w, "File "+relpath, "", "", contents)
} }
func exec(c *http.Conn, args []string) (status int) { func exec(rw http.ResponseWriter, args []string) (status int) {
r, w, err := os.Pipe() r, w, err := os.Pipe()
if err != nil { if err != nil {
log.Stderrf("os.Pipe(): %v\n", err) log.Stderrf("os.Pipe(): %v\n", err)
...@@ -109,18 +109,18 @@ func exec(c *http.Conn, args []string) (status int) { ...@@ -109,18 +109,18 @@ func exec(c *http.Conn, args []string) (status int) {
if *verbose { if *verbose {
os.Stderr.Write(buf.Bytes()) os.Stderr.Write(buf.Bytes())
} }
if c != nil { if rw != nil {
c.SetHeader("content-type", "text/plain; charset=utf-8") rw.SetHeader("content-type", "text/plain; charset=utf-8")
c.Write(buf.Bytes()) rw.Write(buf.Bytes())
} }
return return
} }
func dosync(c *http.Conn, r *http.Request) { func dosync(w http.ResponseWriter, r *http.Request) {
args := []string{"/bin/sh", "-c", *syncCmd} args := []string{"/bin/sh", "-c", *syncCmd}
switch exec(c, args) { switch exec(w, args) {
case 0: case 0:
// sync succeeded and some files have changed; // sync succeeded and some files have changed;
// update package tree. // update package tree.
...@@ -150,9 +150,9 @@ func usage() { ...@@ -150,9 +150,9 @@ func usage() {
func loggingHandler(h http.Handler) http.Handler { func loggingHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(c *http.Conn, req *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
log.Stderrf("%s\t%s", c.RemoteAddr, req.URL) log.Stderrf("%s\t%s", w.RemoteAddr(), req.URL)
h.ServeHTTP(c, req) h.ServeHTTP(w, req)
}) })
} }
......
...@@ -210,18 +210,18 @@ func Iter() <-chan KeyValue { ...@@ -210,18 +210,18 @@ func Iter() <-chan KeyValue {
return c return c
} }
func expvarHandler(c *http.Conn, req *http.Request) { func expvarHandler(w http.ResponseWriter, r *http.Request) {
c.SetHeader("content-type", "application/json; charset=utf-8") w.SetHeader("content-type", "application/json; charset=utf-8")
fmt.Fprintf(c, "{\n") fmt.Fprintf(w, "{\n")
first := true first := true
for name, value := range vars { for name, value := range vars {
if !first { if !first {
fmt.Fprintf(c, ",\n") fmt.Fprintf(w, ",\n")
} }
first = false first = false
fmt.Fprintf(c, "%q: %s", name, value) fmt.Fprintf(w, "%q: %s", name, value)
} }
fmt.Fprintf(c, "\n}\n") fmt.Fprintf(w, "\n}\n")
} }
func memstats() string { func memstats() string {
......
...@@ -43,8 +43,8 @@ func isText(b []byte) bool { ...@@ -43,8 +43,8 @@ func isText(b []byte) bool {
return true return true
} }
func dirList(c *Conn, f *os.File) { func dirList(w ResponseWriter, f *os.File) {
fmt.Fprintf(c, "<pre>\n") fmt.Fprintf(w, "<pre>\n")
for { for {
dirs, err := f.Readdir(100) dirs, err := f.Readdir(100)
if err != nil || len(dirs) == 0 { if err != nil || len(dirs) == 0 {
...@@ -56,25 +56,25 @@ func dirList(c *Conn, f *os.File) { ...@@ -56,25 +56,25 @@ func dirList(c *Conn, f *os.File) {
name += "/" name += "/"
} }
// TODO htmlescape // TODO htmlescape
fmt.Fprintf(c, "<a href=\"%s\">%s</a>\n", name, name) fmt.Fprintf(w, "<a href=\"%s\">%s</a>\n", name, name)
} }
} }
fmt.Fprintf(c, "</pre>\n") fmt.Fprintf(w, "</pre>\n")
} }
func serveFile(c *Conn, r *Request, name string, redirect bool) { func serveFile(w ResponseWriter, r *Request, name string, redirect bool) {
const indexPage = "/index.html" const indexPage = "/index.html"
// redirect .../index.html to .../ // redirect .../index.html to .../
if strings.HasSuffix(r.URL.Path, indexPage) { if strings.HasSuffix(r.URL.Path, indexPage) {
Redirect(c, r.URL.Path[0:len(r.URL.Path)-len(indexPage)+1], StatusMovedPermanently) Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len(indexPage)+1], StatusMovedPermanently)
return return
} }
f, err := os.Open(name, os.O_RDONLY, 0) f, err := os.Open(name, os.O_RDONLY, 0)
if err != nil { if err != nil {
// TODO expose actual error? // TODO expose actual error?
NotFound(c, r) NotFound(w, r)
return return
} }
defer f.Close() defer f.Close()
...@@ -82,7 +82,7 @@ func serveFile(c *Conn, r *Request, name string, redirect bool) { ...@@ -82,7 +82,7 @@ func serveFile(c *Conn, r *Request, name string, redirect bool) {
d, err1 := f.Stat() d, err1 := f.Stat()
if err1 != nil { if err1 != nil {
// TODO expose actual error? // TODO expose actual error?
NotFound(c, r) NotFound(w, r)
return return
} }
...@@ -92,22 +92,22 @@ func serveFile(c *Conn, r *Request, name string, redirect bool) { ...@@ -92,22 +92,22 @@ func serveFile(c *Conn, r *Request, name string, redirect bool) {
url := r.URL.Path url := r.URL.Path
if d.IsDirectory() { if d.IsDirectory() {
if url[len(url)-1] != '/' { if url[len(url)-1] != '/' {
Redirect(c, url+"/", StatusMovedPermanently) Redirect(w, r, url+"/", StatusMovedPermanently)
return return
} }
} else { } else {
if url[len(url)-1] == '/' { if url[len(url)-1] == '/' {
Redirect(c, url[0:len(url)-1], StatusMovedPermanently) Redirect(w, r, url[0:len(url)-1], StatusMovedPermanently)
return return
} }
} }
} }
if t, _ := time.Parse(TimeFormat, r.Header["If-Modified-Since"]); t != nil && d.Mtime_ns/1e9 <= t.Seconds() { if t, _ := time.Parse(TimeFormat, r.Header["If-Modified-Since"]); t != nil && d.Mtime_ns/1e9 <= t.Seconds() {
c.WriteHeader(StatusNotModified) w.WriteHeader(StatusNotModified)
return return
} }
c.SetHeader("Last-Modified", time.SecondsToUTC(d.Mtime_ns/1e9).Format(TimeFormat)) w.SetHeader("Last-Modified", time.SecondsToUTC(d.Mtime_ns/1e9).Format(TimeFormat))
// use contents of index.html for directory, if present // use contents of index.html for directory, if present
if d.IsDirectory() { if d.IsDirectory() {
...@@ -125,7 +125,7 @@ func serveFile(c *Conn, r *Request, name string, redirect bool) { ...@@ -125,7 +125,7 @@ func serveFile(c *Conn, r *Request, name string, redirect bool) {
} }
if d.IsDirectory() { if d.IsDirectory() {
dirList(c, f) dirList(w, f)
return return
} }
...@@ -133,25 +133,25 @@ func serveFile(c *Conn, r *Request, name string, redirect bool) { ...@@ -133,25 +133,25 @@ func serveFile(c *Conn, r *Request, name string, redirect bool) {
// use extension to find content type. // use extension to find content type.
ext := path.Ext(name) ext := path.Ext(name)
if ctype := mime.TypeByExtension(ext); ctype != "" { if ctype := mime.TypeByExtension(ext); ctype != "" {
c.SetHeader("Content-Type", ctype) w.SetHeader("Content-Type", ctype)
} else { } else {
// read first chunk to decide between utf-8 text and binary // read first chunk to decide between utf-8 text and binary
var buf [1024]byte var buf [1024]byte
n, _ := io.ReadFull(f, buf[0:]) n, _ := io.ReadFull(f, buf[0:])
b := buf[0:n] b := buf[0:n]
if isText(b) { if isText(b) {
c.SetHeader("Content-Type", "text-plain; charset=utf-8") w.SetHeader("Content-Type", "text-plain; charset=utf-8")
} else { } else {
c.SetHeader("Content-Type", "application/octet-stream") // generic binary w.SetHeader("Content-Type", "application/octet-stream") // generic binary
} }
c.Write(b) w.Write(b)
} }
io.Copy(c, f) io.Copy(w, f)
} }
// ServeFile replies to the request with the contents of the named file or directory. // ServeFile replies to the request with the contents of the named file or directory.
func ServeFile(c *Conn, r *Request, name string) { func ServeFile(w ResponseWriter, r *Request, name string) {
serveFile(c, r, name, false) serveFile(w, r, name, false)
} }
type fileHandler struct { type fileHandler struct {
...@@ -165,12 +165,12 @@ type fileHandler struct { ...@@ -165,12 +165,12 @@ type fileHandler struct {
// looking up the file name in the file system. // looking up the file name in the file system.
func FileServer(root, prefix string) Handler { return &fileHandler{root, prefix} } func FileServer(root, prefix string) Handler { return &fileHandler{root, prefix} }
func (f *fileHandler) ServeHTTP(c *Conn, r *Request) { func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) {
path := r.URL.Path path := r.URL.Path
if !strings.HasPrefix(path, f.prefix) { if !strings.HasPrefix(path, f.prefix) {
NotFound(c, r) NotFound(w, r)
return return
} }
path = path[len(f.prefix):] path = path[len(f.prefix):]
serveFile(c, r, f.root+"/"+path, true) serveFile(w, r, f.root+"/"+path, true)
} }
...@@ -40,28 +40,28 @@ func init() { ...@@ -40,28 +40,28 @@ func init() {
// Cmdline responds with the running program's // Cmdline responds with the running program's
// command line, with arguments separated by NUL bytes. // command line, with arguments separated by NUL bytes.
// The package initialization registers it as /debug/pprof/cmdline. // The package initialization registers it as /debug/pprof/cmdline.
func Cmdline(c *http.Conn, r *http.Request) { func Cmdline(w http.ResponseWriter, r *http.Request) {
c.SetHeader("content-type", "text/plain; charset=utf-8") w.SetHeader("content-type", "text/plain; charset=utf-8")
fmt.Fprintf(c, strings.Join(os.Args, "\x00")) fmt.Fprintf(w, strings.Join(os.Args, "\x00"))
} }
// Heap responds with the pprof-formatted heap profile. // Heap responds with the pprof-formatted heap profile.
// The package initialization registers it as /debug/pprof/heap. // The package initialization registers it as /debug/pprof/heap.
func Heap(c *http.Conn, r *http.Request) { func Heap(w http.ResponseWriter, r *http.Request) {
c.SetHeader("content-type", "text/plain; charset=utf-8") w.SetHeader("content-type", "text/plain; charset=utf-8")
pprof.WriteHeapProfile(c) pprof.WriteHeapProfile(w)
} }
// Symbol looks up the program counters listed in the request, // Symbol looks up the program counters listed in the request,
// responding with a table mapping program counters to function names. // responding with a table mapping program counters to function names.
// The package initialization registers it as /debug/pprof/symbol. // The package initialization registers it as /debug/pprof/symbol.
func Symbol(c *http.Conn, r *http.Request) { func Symbol(w http.ResponseWriter, r *http.Request) {
c.SetHeader("content-type", "text/plain; charset=utf-8") w.SetHeader("content-type", "text/plain; charset=utf-8")
// We don't know how many symbols we have, but we // We don't know how many symbols we have, but we
// do have symbol information. Pprof only cares whether // do have symbol information. Pprof only cares whether
// this number is 0 (no symbols available) or > 0. // this number is 0 (no symbols available) or > 0.
fmt.Fprintf(c, "num_symbols: 1\n") fmt.Fprintf(w, "num_symbols: 1\n")
var b *bufio.Reader var b *bufio.Reader
if r.Method == "POST" { if r.Method == "POST" {
...@@ -71,15 +71,15 @@ func Symbol(c *http.Conn, r *http.Request) { ...@@ -71,15 +71,15 @@ func Symbol(c *http.Conn, r *http.Request) {
} }
for { for {
w, err := b.ReadSlice('+') word, err := b.ReadSlice('+')
if err == nil { if err == nil {
w = w[0 : len(w)-1] // trim + word = word[0 : len(word)-1] // trim +
} }
pc, _ := strconv.Btoui64(string(w), 0) pc, _ := strconv.Btoui64(string(word), 0)
if pc != 0 { if pc != 0 {
f := runtime.FuncForPC(uintptr(pc)) f := runtime.FuncForPC(uintptr(pc))
if f != nil { if f != nil {
fmt.Fprintf(c, "%#x %s\n", pc, f.Name()) fmt.Fprintf(w, "%#x %s\n", pc, f.Name())
} }
} }
......
...@@ -37,32 +37,78 @@ var ( ...@@ -37,32 +37,78 @@ var (
// registered to serve a particular path or subtree // registered to serve a particular path or subtree
// in the HTTP server. // in the HTTP server.
// //
// ServeHTTP should write reply headers and data to the Conn // ServeHTTP should write reply headers and data to the ResponseWriter
// and then return. Returning signals that the request is finished // and then return. Returning signals that the request is finished
// and that the HTTP server can move on to the next request on // and that the HTTP server can move on to the next request on
// the connection. // the connection.
type Handler interface { type Handler interface {
ServeHTTP(*Conn, *Request) ServeHTTP(ResponseWriter, *Request)
} }
// A Conn represents the server side of a single active HTTP connection. // A ResponseWriter interface is used by an HTTP handler to
type Conn struct { // construct an HTTP response.
RemoteAddr string // network address of remote side type ResponseWriter interface {
Req *Request // current HTTP request // RemoteAddr returns the address of the client that sent the current request
RemoteAddr() string
rwc io.ReadWriteCloser // i/o connection
buf *bufio.ReadWriter // buffered rwc // UsingTLS returns true if the client is connected using TLS
handler Handler // request handler UsingTLS() bool
hijacked bool // connection has been hijacked by handler
// SetHeader sets a header line in the eventual response.
// state for the current reply // 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.
SetHeader(string, string)
// 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)
// before writing the data.
Write([]byte) (int, os.Error)
// WriteHeader sends an HTTP response header with status code.
// If WriteHeader is not called explicitly, the first call to Write
// will trigger an implicit WriteHeader(http.StatusOK).
// Thus explicit calls to WriteHeader are mainly used to
// send error codes.
WriteHeader(int)
// Flush sends any buffered data to the client.
Flush()
// Hijack lets the caller take over the connection.
// After a call to Hijack(), the HTTP server library
// will not do anything else with the connection.
// It becomes the caller's responsibility to manage
// and close the connection.
Hijack() (io.ReadWriteCloser, *bufio.ReadWriter, os.Error)
}
// A conn represents the server side of an HTTP connection.
type conn struct {
remoteAddr string // network address of remote side
handler Handler // request handler
rwc io.ReadWriteCloser // i/o connection
buf *bufio.ReadWriter // buffered rwc
hijacked bool // connection has been hijacked by handler
usingTLS bool // a flag indicating connection over TLS
}
// 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 chunking bool // using chunked transfer encoding for reply body
wroteHeader bool // reply header has been written wroteHeader bool // reply header has been written
wroteContinue bool // 100 Continue response was written wroteContinue bool // 100 Continue response was written
header map[string]string // reply header parameters header map[string]string // reply header parameters
written int64 // number of bytes written in body written int64 // number of bytes written in body
status int // status code passed to WriteHeader status int // status code passed to WriteHeader
usingTLS bool // a flag indicating connection over TLS
// close connection after this reply. set on request and // close connection after this reply. set on request and
// updated after response from handler if there's a // updated after response from handler if there's a
...@@ -72,11 +118,9 @@ type Conn struct { ...@@ -72,11 +118,9 @@ type Conn struct {
} }
// Create new connection from rwc. // Create new connection from rwc.
func newConn(rwc net.Conn, handler Handler) (c *Conn, err os.Error) { func newConn(rwc net.Conn, handler Handler) (c *conn, err os.Error) {
c = new(Conn) c = new(conn)
if a := rwc.RemoteAddr(); a != nil { c.remoteAddr = rwc.RemoteAddr().String()
c.RemoteAddr = a.String()
}
c.handler = handler c.handler = handler
c.rwc = rwc c.rwc = rwc
_, c.usingTLS = rwc.(*tls.Conn) _, c.usingTLS = rwc.(*tls.Conn)
...@@ -89,17 +133,15 @@ func newConn(rwc net.Conn, handler Handler) (c *Conn, err os.Error) { ...@@ -89,17 +133,15 @@ func newConn(rwc net.Conn, handler Handler) (c *Conn, err os.Error) {
// wrapper around io.ReaderCloser which on first read, sends an // wrapper around io.ReaderCloser which on first read, sends an
// HTTP/1.1 100 Continue header // HTTP/1.1 100 Continue header
type expectContinueReader struct { type expectContinueReader struct {
conn *Conn resp *response
readCloser io.ReadCloser readCloser io.ReadCloser
} }
func (ecr *expectContinueReader) Read(p []byte) (n int, err os.Error) { func (ecr *expectContinueReader) Read(p []byte) (n int, err os.Error) {
if !ecr.conn.wroteContinue && !ecr.conn.hijacked { if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked {
ecr.conn.wroteContinue = true ecr.resp.wroteContinue = true
if ecr.conn.Req.ProtoAtLeast(1, 1) { io.WriteString(ecr.resp.conn.buf, "HTTP/1.1 100 Continue\r\n\r\n")
io.WriteString(ecr.conn.buf, "HTTP/1.1 100 Continue\r\n\r\n") ecr.resp.conn.buf.Flush()
ecr.conn.buf.Flush()
}
} }
return ecr.readCloser.Read(p) return ecr.readCloser.Read(p)
} }
...@@ -115,92 +157,80 @@ func (ecr *expectContinueReader) Close() os.Error { ...@@ -115,92 +157,80 @@ func (ecr *expectContinueReader) Close() os.Error {
const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT" const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
// Read next request from connection. // Read next request from connection.
func (c *Conn) readRequest() (req *Request, err os.Error) { func (c *conn) readRequest() (w *response, err os.Error) {
if c.hijacked { if c.hijacked {
return nil, ErrHijacked return nil, ErrHijacked
} }
var req *Request
if req, err = ReadRequest(c.buf.Reader); err != nil { if req, err = ReadRequest(c.buf.Reader); err != nil {
return nil, err return nil, err
} }
// Reset per-request connection state. w = new(response)
c.header = make(map[string]string) w.conn = c
c.wroteHeader = false w.req = req
c.wroteContinue = false w.header = make(map[string]string)
c.Req = req
// Expect 100 Continue support // Expect 100 Continue support
if req.expectsContinue() { if req.expectsContinue() && req.ProtoAtLeast(1, 1) {
// Wrap the Body reader with one that replies on the connection // Wrap the Body reader with one that replies on the connection
req.Body = &expectContinueReader{readCloser: req.Body, conn: c} req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
} }
// Default output is HTML encoded in UTF-8. // Default output is HTML encoded in UTF-8.
c.SetHeader("Content-Type", "text/html; charset=utf-8") w.SetHeader("Content-Type", "text/html; charset=utf-8")
c.SetHeader("Date", time.UTC().Format(TimeFormat)) w.SetHeader("Date", time.UTC().Format(TimeFormat))
if req.ProtoAtLeast(1, 1) { if req.ProtoAtLeast(1, 1) {
// HTTP/1.1 or greater: use chunked transfer encoding // HTTP/1.1 or greater: use chunked transfer encoding
// to avoid closing the connection at EOF. // to avoid closing the connection at EOF.
c.chunking = true w.chunking = true
c.SetHeader("Transfer-Encoding", "chunked") w.SetHeader("Transfer-Encoding", "chunked")
} else { } else {
// HTTP version < 1.1: cannot do chunked transfer // HTTP version < 1.1: cannot do chunked transfer
// encoding, so signal EOF by closing connection. // encoding, so signal EOF by closing connection.
// Will be overridden if the HTTP handler ends up // Will be overridden if the HTTP handler ends up
// writing a Content-Length and the client requested // writing a Content-Length and the client requested
// "Connection: keep-alive" // "Connection: keep-alive"
c.closeAfterReply = true w.closeAfterReply = true
c.chunking = false
} }
return req, nil return w, nil
} }
// UsingTLS returns true if the connection uses transport layer security (TLS). // UsingTLS implements the ResponseWriter.UsingTLS
func (c *Conn) UsingTLS() bool { func (w *response) UsingTLS() bool {
return c.usingTLS return w.conn.usingTLS
} }
// SetHeader sets a header line in the eventual reply. // RemoteAddr implements the ResponseWriter.RemoteAddr method
// For example, SetHeader("Content-Type", "text/html; charset=utf-8") func (w *response) RemoteAddr() string { return w.conn.remoteAddr }
// will result in the header line
// // SetHeader implements the ResponseWriter.SetHeader method
// Content-Type: text/html; charset=utf-8 func (w *response) SetHeader(hdr, val string) { w.header[CanonicalHeaderKey(hdr)] = val }
//
// being sent. UTF-8 encoded HTML is the default setting for // WriteHeader implements the ResponseWriter.WriteHeader method
// Content-Type in this library, so users need not make that func (w *response) WriteHeader(code int) {
// particular call. Calls to SetHeader after WriteHeader (or Write) if w.conn.hijacked {
// are ignored. log.Stderr("http: response.WriteHeader on hijacked connection")
func (c *Conn) SetHeader(hdr, val string) { c.header[CanonicalHeaderKey(hdr)] = val }
// WriteHeader sends an HTTP response header with status code.
// If WriteHeader is not called explicitly, the first call to Write
// will trigger an implicit WriteHeader(http.StatusOK).
// Thus explicit calls to WriteHeader are mainly used to
// send error codes.
func (c *Conn) WriteHeader(code int) {
if c.hijacked {
log.Stderr("http: Conn.WriteHeader on hijacked connection")
return return
} }
if c.wroteHeader { if w.wroteHeader {
log.Stderr("http: multiple Conn.WriteHeader calls") log.Stderr("http: multiple response.WriteHeader calls")
return return
} }
c.wroteHeader = true w.wroteHeader = true
c.status = code w.status = code
if code == StatusNotModified { if code == StatusNotModified {
// Must not have body. // Must not have body.
c.header["Content-Type"] = "", false w.header["Content-Type"] = "", false
c.header["Transfer-Encoding"] = "", false w.header["Transfer-Encoding"] = "", false
} }
c.written = 0 if !w.req.ProtoAtLeast(1, 0) {
if !c.Req.ProtoAtLeast(1, 0) {
return return
} }
proto := "HTTP/1.0" proto := "HTTP/1.0"
if c.Req.ProtoAtLeast(1, 1) { if w.req.ProtoAtLeast(1, 1) {
proto = "HTTP/1.1" proto = "HTTP/1.1"
} }
codestring := strconv.Itoa(code) codestring := strconv.Itoa(code)
...@@ -208,57 +238,55 @@ func (c *Conn) WriteHeader(code int) { ...@@ -208,57 +238,55 @@ func (c *Conn) WriteHeader(code int) {
if !ok { if !ok {
text = "status code " + codestring text = "status code " + codestring
} }
io.WriteString(c.buf, proto+" "+codestring+" "+text+"\r\n") io.WriteString(w.conn.buf, proto+" "+codestring+" "+text+"\r\n")
for k, v := range c.header { for k, v := range w.header {
io.WriteString(c.buf, k+": "+v+"\r\n") io.WriteString(w.conn.buf, k+": "+v+"\r\n")
} }
io.WriteString(c.buf, "\r\n") io.WriteString(w.conn.buf, "\r\n")
} }
// Write writes the data to the connection as part of an HTTP reply. // Write implements the ResponseWriter.Write method
// If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK) func (w *response) Write(data []byte) (n int, err os.Error) {
// before writing the data. if w.conn.hijacked {
func (c *Conn) Write(data []byte) (n int, err os.Error) { log.Stderr("http: response.Write on hijacked connection")
if c.hijacked {
log.Stderr("http: Conn.Write on hijacked connection")
return 0, ErrHijacked return 0, ErrHijacked
} }
if !c.wroteHeader { if !w.wroteHeader {
if c.Req.wantsHttp10KeepAlive() { if w.req.wantsHttp10KeepAlive() {
_, hasLength := c.header["Content-Length"] _, hasLength := w.header["Content-Length"]
if hasLength { if hasLength {
_, connectionHeaderSet := c.header["Connection"] _, connectionHeaderSet := w.header["Connection"]
if !connectionHeaderSet { if !connectionHeaderSet {
c.header["Connection"] = "keep-alive" w.header["Connection"] = "keep-alive"
} }
} }
} }
c.WriteHeader(StatusOK) w.WriteHeader(StatusOK)
} }
if len(data) == 0 { if len(data) == 0 {
return 0, nil return 0, nil
} }
if c.status == StatusNotModified { if w.status == StatusNotModified {
// Must not have body. // Must not have body.
return 0, ErrBodyNotAllowed return 0, ErrBodyNotAllowed
} }
c.written += int64(len(data)) // ignoring errors, for errorKludge w.written += int64(len(data)) // ignoring errors, for errorKludge
// TODO(rsc): if chunking happened after the buffering, // TODO(rsc): if chunking happened after the buffering,
// then there would be fewer chunk headers. // then there would be fewer chunk headers.
// On the other hand, it would make hijacking more difficult. // On the other hand, it would make hijacking more difficult.
if c.chunking { if w.chunking {
fmt.Fprintf(c.buf, "%x\r\n", len(data)) // TODO(rsc): use strconv not fmt fmt.Fprintf(w.conn.buf, "%x\r\n", len(data)) // TODO(rsc): use strconv not fmt
} }
n, err = c.buf.Write(data) n, err = w.conn.buf.Write(data)
if err == nil && c.chunking { if err == nil && w.chunking {
if n != len(data) { if n != len(data) {
err = io.ErrShortWrite err = io.ErrShortWrite
} }
if err == nil { if err == nil {
io.WriteString(c.buf, "\r\n") io.WriteString(w.conn.buf, "\r\n")
} }
} }
...@@ -272,22 +300,22 @@ func (c *Conn) Write(data []byte) (n int, err os.Error) { ...@@ -272,22 +300,22 @@ func (c *Conn) Write(data []byte) (n int, err os.Error) {
// long enough. The minimum lengths used in those // long enough. The minimum lengths used in those
// browsers are in the 256-512 range. // browsers are in the 256-512 range.
// Pad to 1024 bytes. // Pad to 1024 bytes.
func errorKludge(c *Conn, req *Request) { func errorKludge(w *response) {
const min = 1024 const min = 1024
// Is this an error? // Is this an error?
if kind := c.status / 100; kind != 4 && kind != 5 { if kind := w.status / 100; kind != 4 && kind != 5 {
return return
} }
// Did the handler supply any info? Enough? // Did the handler supply any info? Enough?
if c.written == 0 || c.written >= min { if w.written == 0 || w.written >= min {
return return
} }
// Is it a broken browser? // Is it a broken browser?
var msg string var msg string
switch agent := req.UserAgent; { switch agent := w.req.UserAgent; {
case strings.Index(agent, "MSIE") >= 0: case strings.Index(agent, "MSIE") >= 0:
msg = "Internet Explorer" msg = "Internet Explorer"
case strings.Index(agent, "Chrome/") >= 0: case strings.Index(agent, "Chrome/") >= 0:
...@@ -298,53 +326,53 @@ func errorKludge(c *Conn, req *Request) { ...@@ -298,53 +326,53 @@ func errorKludge(c *Conn, req *Request) {
msg += " would ignore this error page if this text weren't here.\n" msg += " would ignore this error page if this text weren't here.\n"
// Is it text? ("Content-Type" is always in the map) // Is it text? ("Content-Type" is always in the map)
baseType := strings.Split(c.header["Content-Type"], ";", 2)[0] baseType := strings.Split(w.header["Content-Type"], ";", 2)[0]
switch baseType { switch baseType {
case "text/html": case "text/html":
io.WriteString(c, "<!-- ") io.WriteString(w, "<!-- ")
for c.written < min { for w.written < min {
io.WriteString(c, msg) io.WriteString(w, msg)
} }
io.WriteString(c, " -->") io.WriteString(w, " -->")
case "text/plain": case "text/plain":
io.WriteString(c, "\n") io.WriteString(w, "\n")
for c.written < min { for w.written < min {
io.WriteString(c, msg) io.WriteString(w, msg)
} }
} }
} }
func (c *Conn) finishRequest() { func (w *response) finishRequest() {
// If this was an HTTP/1.0 request with keep-alive and we sent a Content-Length // 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 ... // back, we can make this a keep-alive response ...
if c.Req.wantsHttp10KeepAlive() { if w.req.wantsHttp10KeepAlive() {
_, sentLength := c.header["Content-Length"] _, sentLength := w.header["Content-Length"]
if sentLength && c.header["Connection"] == "keep-alive" { if sentLength && w.header["Connection"] == "keep-alive" {
c.closeAfterReply = false w.closeAfterReply = false
} }
} }
if !c.wroteHeader { if !w.wroteHeader {
c.WriteHeader(StatusOK) w.WriteHeader(StatusOK)
} }
errorKludge(c, c.Req) errorKludge(w)
if c.chunking { if w.chunking {
io.WriteString(c.buf, "0\r\n") io.WriteString(w.conn.buf, "0\r\n")
// trailer key/value pairs, followed by blank line // trailer key/value pairs, followed by blank line
io.WriteString(c.buf, "\r\n") io.WriteString(w.conn.buf, "\r\n")
} }
c.buf.Flush() w.conn.buf.Flush()
} }
// Flush sends any buffered data to the client. // Flush implements the ResponseWriter.Flush method.
func (c *Conn) Flush() { func (w *response) Flush() {
if !c.wroteHeader { if !w.wroteHeader {
c.WriteHeader(StatusOK) w.WriteHeader(StatusOK)
} }
c.buf.Flush() w.conn.buf.Flush()
} }
// Close the connection. // Close the connection.
func (c *Conn) close() { func (c *conn) close() {
if c.buf != nil { if c.buf != nil {
c.buf.Flush() c.buf.Flush()
c.buf = nil c.buf = nil
...@@ -356,9 +384,9 @@ func (c *Conn) close() { ...@@ -356,9 +384,9 @@ func (c *Conn) close() {
} }
// Serve a new connection. // Serve a new connection.
func (c *Conn) serve() { func (c *conn) serve() {
for { for {
req, err := c.readRequest() w, err := c.readRequest()
if err != nil { if err != nil {
break break
} }
...@@ -367,32 +395,28 @@ func (c *Conn) serve() { ...@@ -367,32 +395,28 @@ func (c *Conn) serve() {
// so we might as well run the handler in this goroutine. // so we might as well run the handler in this goroutine.
// [*] Not strictly true: HTTP pipelining. We could let them all process // [*] Not strictly true: HTTP pipelining. We could let them all process
// in parallel even if their responses need to be serialized. // in parallel even if their responses need to be serialized.
c.handler.ServeHTTP(c, req) c.handler.ServeHTTP(w, w.req)
if c.hijacked { if c.hijacked {
return return
} }
c.finishRequest() w.finishRequest()
if c.closeAfterReply { if w.closeAfterReply {
break break
} }
} }
c.close() c.close()
} }
// Hijack lets the caller take over the connection. // Hijack impements the ResponseWriter.Hijack method.
// After a call to c.Hijack(), the HTTP server library func (w *response) Hijack() (rwc io.ReadWriteCloser, buf *bufio.ReadWriter, err os.Error) {
// will not do anything else with the connection. if w.conn.hijacked {
// It becomes the caller's responsibility to manage
// and close the connection.
func (c *Conn) Hijack() (rwc io.ReadWriteCloser, buf *bufio.ReadWriter, err os.Error) {
if c.hijacked {
return nil, nil, ErrHijacked return nil, nil, ErrHijacked
} }
c.hijacked = true w.conn.hijacked = true
rwc = c.rwc rwc = w.conn.rwc
buf = c.buf buf = w.conn.buf
c.rwc = nil w.conn.rwc = nil
c.buf = nil w.conn.buf = nil
return return
} }
...@@ -400,24 +424,24 @@ func (c *Conn) Hijack() (rwc io.ReadWriteCloser, buf *bufio.ReadWriter, err os.E ...@@ -400,24 +424,24 @@ func (c *Conn) Hijack() (rwc io.ReadWriteCloser, buf *bufio.ReadWriter, err os.E
// ordinary functions as HTTP handlers. If f is a function // ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a // with the appropriate signature, HandlerFunc(f) is a
// Handler object that calls f. // Handler object that calls f.
type HandlerFunc func(*Conn, *Request) type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(c, req). // ServeHTTP calls f(c, req).
func (f HandlerFunc) ServeHTTP(c *Conn, req *Request) { func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(c, req) f(w, r)
} }
// Helper handlers // Helper handlers
// Error replies to the request with the specified error message and HTTP code. // Error replies to the request with the specified error message and HTTP code.
func Error(c *Conn, error string, code int) { func Error(w ResponseWriter, error string, code int) {
c.SetHeader("Content-Type", "text/plain; charset=utf-8") w.SetHeader("Content-Type", "text/plain; charset=utf-8")
c.WriteHeader(code) w.WriteHeader(code)
fmt.Fprintln(c, error) fmt.Fprintln(w, error)
} }
// NotFound replies to the request with an HTTP 404 not found error. // NotFound replies to the request with an HTTP 404 not found error.
func NotFound(c *Conn, req *Request) { Error(c, "404 page not found", StatusNotFound) } func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", StatusNotFound) }
// NotFoundHandler returns a simple request handler // NotFoundHandler returns a simple request handler
// that replies to each request with a ``404 page not found'' reply. // that replies to each request with a ``404 page not found'' reply.
...@@ -425,11 +449,11 @@ func NotFoundHandler() Handler { return HandlerFunc(NotFound) } ...@@ -425,11 +449,11 @@ func NotFoundHandler() Handler { return HandlerFunc(NotFound) }
// Redirect replies to the request with a redirect to url, // Redirect replies to the request with a redirect to url,
// which may be a path relative to the request path. // which may be a path relative to the request path.
func Redirect(c *Conn, url string, code int) { func Redirect(w ResponseWriter, r *Request, url string, code int) {
// RFC2616 recommends that a short note "SHOULD" be included in the // RFC2616 recommends that a short note "SHOULD" be included in the
// response because older user agents may not understand 301/307. // response because older user agents may not understand 301/307.
note := "<a href=\"%v\">" + statusText[code] + "</a>.\n" note := "<a href=\"%v\">" + statusText[code] + "</a>.\n"
if c.Req.Method == "POST" { if r.Method == "POST" {
note = "" note = ""
} }
...@@ -454,7 +478,7 @@ func Redirect(c *Conn, url string, code int) { ...@@ -454,7 +478,7 @@ func Redirect(c *Conn, url string, code int) {
// Because of this problem, no one pays attention // Because of this problem, no one pays attention
// to the RFC; they all send back just a new path. // to the RFC; they all send back just a new path.
// So do we. // So do we.
oldpath := c.Req.URL.Path oldpath := r.URL.Path
if oldpath == "" { // should not happen, but avoid a crash if it does if oldpath == "" { // should not happen, but avoid a crash if it does
oldpath = "/" oldpath = "/"
} }
...@@ -475,9 +499,9 @@ func Redirect(c *Conn, url string, code int) { ...@@ -475,9 +499,9 @@ func Redirect(c *Conn, url string, code int) {
} }
finish: finish:
c.SetHeader("Location", url) w.SetHeader("Location", url)
c.WriteHeader(code) w.WriteHeader(code)
fmt.Fprintf(c, note, url) fmt.Fprintf(w, note, url)
} }
// Redirect to a fixed URL // Redirect to a fixed URL
...@@ -486,8 +510,8 @@ type redirectHandler struct { ...@@ -486,8 +510,8 @@ type redirectHandler struct {
code int code int
} }
func (rh *redirectHandler) ServeHTTP(c *Conn, req *Request) { func (rh *redirectHandler) ServeHTTP(w ResponseWriter, r *Request) {
Redirect(c, rh.url, rh.code) Redirect(w, r, rh.url, rh.code)
} }
// RedirectHandler returns a request handler that redirects // RedirectHandler returns a request handler that redirects
...@@ -563,11 +587,11 @@ func cleanPath(p string) string { ...@@ -563,11 +587,11 @@ func cleanPath(p string) string {
// ServeHTTP dispatches the request to the handler whose // ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL. // pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(c *Conn, req *Request) { func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
// Clean path to canonical form and redirect. // Clean path to canonical form and redirect.
if p := cleanPath(req.URL.Path); p != req.URL.Path { if p := cleanPath(r.URL.Path); p != r.URL.Path {
c.SetHeader("Location", p) w.SetHeader("Location", p)
c.WriteHeader(StatusMovedPermanently) w.WriteHeader(StatusMovedPermanently)
return return
} }
...@@ -575,7 +599,7 @@ func (mux *ServeMux) ServeHTTP(c *Conn, req *Request) { ...@@ -575,7 +599,7 @@ func (mux *ServeMux) ServeHTTP(c *Conn, req *Request) {
var h Handler var h Handler
var n = 0 var n = 0
for k, v := range mux.m { for k, v := range mux.m {
if !pathMatch(k, req.URL.Path) { if !pathMatch(k, r.URL.Path) {
continue continue
} }
if h == nil || len(k) > n { if h == nil || len(k) > n {
...@@ -586,7 +610,7 @@ func (mux *ServeMux) ServeHTTP(c *Conn, req *Request) { ...@@ -586,7 +610,7 @@ func (mux *ServeMux) ServeHTTP(c *Conn, req *Request) {
if h == nil { if h == nil {
h = NotFoundHandler() h = NotFoundHandler()
} }
h.ServeHTTP(c, req) h.ServeHTTP(w, r)
} }
// Handle registers the handler for the given pattern. // Handle registers the handler for the given pattern.
...@@ -606,7 +630,7 @@ func (mux *ServeMux) Handle(pattern string, handler Handler) { ...@@ -606,7 +630,7 @@ func (mux *ServeMux) Handle(pattern string, handler Handler) {
} }
// HandleFunc registers the handler function for the given pattern. // HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(*Conn, *Request)) { func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler)) mux.Handle(pattern, HandlerFunc(handler))
} }
...@@ -616,7 +640,7 @@ func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, h ...@@ -616,7 +640,7 @@ func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, h
// HandleFunc registers the handler function for the given pattern // HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux. // in the DefaultServeMux.
func HandleFunc(pattern string, handler func(*Conn, *Request)) { func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler) DefaultServeMux.HandleFunc(pattern, handler)
} }
...@@ -658,8 +682,8 @@ func Serve(l net.Listener, handler Handler) os.Error { ...@@ -658,8 +682,8 @@ func Serve(l net.Listener, handler Handler) os.Error {
// ) // )
// //
// // hello world, the web server // // hello world, the web server
// func HelloServer(c *http.Conn, req *http.Request) { // func HelloServer(w http.ResponseWriter, req *http.Request) {
// io.WriteString(c, "hello, world!\n") // io.WriteString(w, "hello, world!\n")
// } // }
// //
// func main() { // func main() {
...@@ -690,9 +714,9 @@ func ListenAndServe(addr string, handler Handler) os.Error { ...@@ -690,9 +714,9 @@ func ListenAndServe(addr string, handler Handler) os.Error {
// "log" // "log"
// ) // )
// //
// func handler(conn *http.Conn, req *http.Request) { // func handler(w http.ResponseWriter, req *http.Request) {
// conn.SetHeader("Content-Type", "text/plain") // w.SetHeader("Content-Type", "text/plain")
// conn.Write([]byte("This is an example server.\n")) // w.Write([]byte("This is an example server.\n"))
// } // }
// //
// func main() { // func main() {
......
...@@ -98,3 +98,9 @@ var statusText = map[int]string{ ...@@ -98,3 +98,9 @@ var statusText = map[int]string{
StatusGatewayTimeout: "Gateway Timeout", StatusGatewayTimeout: "Gateway Timeout",
StatusHTTPVersionNotSupported: "HTTP Version Not Supported", StatusHTTPVersionNotSupported: "HTTP Version Not Supported",
} }
// StatusText returns a text for the HTTP status code. It returns the empty
// string if the code is unknown.
func StatusText(code int) string {
return statusText[code]
}
...@@ -62,7 +62,7 @@ func (m methodArray) Less(i, j int) bool { return m[i].name < m[j].name } ...@@ -62,7 +62,7 @@ func (m methodArray) Less(i, j int) bool { return m[i].name < m[j].name }
func (m methodArray) Swap(i, j int) { m[i], m[j] = m[j], m[i] } func (m methodArray) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
// Runs at /debug/rpc // Runs at /debug/rpc
func debugHTTP(c *http.Conn, req *http.Request) { func debugHTTP(w http.ResponseWriter, req *http.Request) {
// Build a sorted version of the data. // Build a sorted version of the data.
var services = make(serviceArray, len(server.serviceMap)) var services = make(serviceArray, len(server.serviceMap))
i := 0 i := 0
...@@ -79,8 +79,8 @@ func debugHTTP(c *http.Conn, req *http.Request) { ...@@ -79,8 +79,8 @@ func debugHTTP(c *http.Conn, req *http.Request) {
} }
server.Unlock() server.Unlock()
sort.Sort(services) sort.Sort(services)
err := debug.Execute(services, c) err := debug.Execute(services, w)
if err != nil { if err != nil {
fmt.Fprintln(c, "rpc: error executing template:", err.String()) fmt.Fprintln(w, "rpc: error executing template:", err.String())
} }
} }
...@@ -445,16 +445,16 @@ var rpcPath string = "/_goRPC_" ...@@ -445,16 +445,16 @@ var rpcPath string = "/_goRPC_"
var debugPath string = "/debug/rpc" var debugPath string = "/debug/rpc"
var connected = "200 Connected to Go RPC" var connected = "200 Connected to Go RPC"
func serveHTTP(c *http.Conn, req *http.Request) { func serveHTTP(w http.ResponseWriter, req *http.Request) {
if req.Method != "CONNECT" { if req.Method != "CONNECT" {
c.SetHeader("Content-Type", "text/plain; charset=utf-8") w.SetHeader("Content-Type", "text/plain; charset=utf-8")
c.WriteHeader(http.StatusMethodNotAllowed) w.WriteHeader(http.StatusMethodNotAllowed)
io.WriteString(c, "405 must CONNECT to "+rpcPath+"\n") io.WriteString(w, "405 must CONNECT to "+rpcPath+"\n")
return return
} }
conn, _, err := c.Hijack() conn, _, err := w.Hijack()
if err != nil { if err != nil {
log.Stderr("rpc hijacking ", c.RemoteAddr, ": ", err.String()) log.Stderr("rpc hijacking ", w.RemoteAddr(), ": ", err.String())
return return
} }
io.WriteString(conn, "HTTP/1.0 "+connected+"\n\n") io.WriteString(conn, "HTTP/1.0 "+connected+"\n\n")
......
...@@ -57,8 +57,8 @@ func getKeyNumber(s string) (r uint32) { ...@@ -57,8 +57,8 @@ func getKeyNumber(s string) (r uint32) {
} }
// ServeHTTP implements the http.Handler interface for a Web Socket // ServeHTTP implements the http.Handler interface for a Web Socket
func (f Handler) ServeHTTP(c *http.Conn, req *http.Request) { func (f Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
rwc, buf, err := c.Hijack() rwc, buf, err := w.Hijack()
if err != nil { if err != nil {
panic("Hijack failed: " + err.String()) panic("Hijack failed: " + err.String())
return return
...@@ -98,7 +98,7 @@ func (f Handler) ServeHTTP(c *http.Conn, req *http.Request) { ...@@ -98,7 +98,7 @@ func (f Handler) ServeHTTP(c *http.Conn, req *http.Request) {
} }
var location string var location string
if c.UsingTLS() { if w.UsingTLS() {
location = "wss://" + req.Host + req.URL.RawPath location = "wss://" + req.Host + req.URL.RawPath
} else { } else {
location = "ws://" + req.Host + req.URL.RawPath location = "ws://" + req.Host + req.URL.RawPath
...@@ -161,30 +161,30 @@ Draft75Handler is an interface to a WebSocket based on the ...@@ -161,30 +161,30 @@ Draft75Handler is an interface to a WebSocket based on the
type Draft75Handler func(*Conn) type Draft75Handler func(*Conn)
// ServeHTTP implements the http.Handler interface for a Web Socket. // ServeHTTP implements the http.Handler interface for a Web Socket.
func (f Draft75Handler) ServeHTTP(c *http.Conn, req *http.Request) { func (f Draft75Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if req.Method != "GET" || req.Proto != "HTTP/1.1" { if req.Method != "GET" || req.Proto != "HTTP/1.1" {
c.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
io.WriteString(c, "Unexpected request") io.WriteString(w, "Unexpected request")
return return
} }
if req.Header["Upgrade"] != "WebSocket" { if req.Header["Upgrade"] != "WebSocket" {
c.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
io.WriteString(c, "missing Upgrade: WebSocket header") io.WriteString(w, "missing Upgrade: WebSocket header")
return return
} }
if req.Header["Connection"] != "Upgrade" { if req.Header["Connection"] != "Upgrade" {
c.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
io.WriteString(c, "missing Connection: Upgrade header") io.WriteString(w, "missing Connection: Upgrade header")
return return
} }
origin, found := req.Header["Origin"] origin, found := req.Header["Origin"]
if !found { if !found {
c.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
io.WriteString(c, "missing Origin header") io.WriteString(w, "missing Origin header")
return return
} }
rwc, buf, err := c.Hijack() rwc, buf, err := w.Hijack()
if err != nil { if err != nil {
panic("Hijack failed: " + err.String()) panic("Hijack failed: " + err.String())
return return
...@@ -192,7 +192,7 @@ func (f Draft75Handler) ServeHTTP(c *http.Conn, req *http.Request) { ...@@ -192,7 +192,7 @@ func (f Draft75Handler) ServeHTTP(c *http.Conn, req *http.Request) {
defer rwc.Close() defer rwc.Close()
var location string var location string
if c.UsingTLS() { if w.UsingTLS() {
location = "wss://" + req.Host + req.URL.RawPath location = "wss://" + req.Host + req.URL.RawPath
} else { } else {
location = "ws://" + req.Host + req.URL.RawPath location = "ws://" + req.Host + req.URL.RawPath
......
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