mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
http: revised http Handler interface
R=rsc CC=golang-dev https://golang.org/cl/1993043
This commit is contained in:
parent
ffdb855be1
commit
fd9a5d22c6
11 changed files with 338 additions and 308 deletions
|
|
@ -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) {
|
||||||
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) {
|
||||||
// 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) {
|
||||||
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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
// 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) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// 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) {
|
||||||
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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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) {
|
||||||
}
|
}
|
||||||
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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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) {
|
||||||
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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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) {
|
||||||
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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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) {
|
||||||
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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
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) {
|
||||||
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() {
|
||||||
|
|
||||||
|
|
||||||
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 {
|
||||||
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 {
|
||||||
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) {
|
||||||
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) {
|
||||||
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) {
|
||||||
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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
// 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 {
|
||||||
// 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() {
|
||||||
// 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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (
|
||||||
// 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
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
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
|
rwc io.ReadWriteCloser // i/o connection
|
||||||
buf *bufio.ReadWriter // buffered rwc
|
buf *bufio.ReadWriter // buffered rwc
|
||||||
handler Handler // request handler
|
|
||||||
hijacked bool // connection has been hijacked by handler
|
hijacked bool // connection has been hijacked by handler
|
||||||
|
usingTLS bool // a flag indicating connection over TLS
|
||||||
|
}
|
||||||
|
|
||||||
// state for the current reply
|
// 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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
// 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 {
|
||||||
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
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
func (c *Conn) SetHeader(hdr, val string) { c.header[CanonicalHeaderKey(hdr)] = val }
|
|
||||||
|
|
||||||
// WriteHeader sends an HTTP response header with status code.
|
// SetHeader implements the ResponseWriter.SetHeader method
|
||||||
// If WriteHeader is not called explicitly, the first call to Write
|
func (w *response) SetHeader(hdr, val string) { w.header[CanonicalHeaderKey(hdr)] = val }
|
||||||
// will trigger an implicit WriteHeader(http.StatusOK).
|
|
||||||
// Thus explicit calls to WriteHeader are mainly used to
|
// WriteHeader implements the ResponseWriter.WriteHeader method
|
||||||
// send error codes.
|
func (w *response) WriteHeader(code int) {
|
||||||
func (c *Conn) WriteHeader(code int) {
|
if w.conn.hijacked {
|
||||||
if c.hijacked {
|
log.Stderr("http: response.WriteHeader on hijacked connection")
|
||||||
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) {
|
||||||
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) {
|
||||||
// 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) {
|
||||||
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() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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() {
|
||||||
// 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
|
||||||
// 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) }
|
||||||
|
|
||||||
// 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) {
|
||||||
// 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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
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 {
|
||||||
|
|
||||||
// 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) {
|
||||||
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) {
|
||||||
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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|
||||||
// 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 {
|
||||||
// )
|
// )
|
||||||
//
|
//
|
||||||
// // 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 {
|
||||||
// "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{
|
||||||
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 }
|
||||||
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) {
|
||||||
}
|
}
|
||||||
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_"
|
||||||
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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
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) {
|
||||||
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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue