Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
go
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
go
Commits
fd9a5d22
Commit
fd9a5d22
authored
Sep 29, 2010
by
Stephen Ma
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
http: revised http Handler interface
R=rsc CC=golang-dev
https://golang.org/cl/1993043
parent
ffdb855b
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
343 additions
and
313 deletions
+343
-313
src/cmd/godoc/codewalk.go
src/cmd/godoc/codewalk.go
+21
-21
src/cmd/godoc/godoc.go
src/cmd/godoc/godoc.go
+41
-41
src/cmd/godoc/main.go
src/cmd/godoc/main.go
+12
-12
src/pkg/expvar/expvar.go
src/pkg/expvar/expvar.go
+6
-6
src/pkg/http/fs.go
src/pkg/http/fs.go
+23
-23
src/pkg/http/pprof/pprof.go
src/pkg/http/pprof/pprof.go
+13
-13
src/pkg/http/server.go
src/pkg/http/server.go
+198
-174
src/pkg/http/status.go
src/pkg/http/status.go
+6
-0
src/pkg/rpc/debug.go
src/pkg/rpc/debug.go
+3
-3
src/pkg/rpc/server.go
src/pkg/rpc/server.go
+6
-6
src/pkg/websocket/server.go
src/pkg/websocket/server.go
+14
-14
No files found.
src/cmd/godoc/codewalk.go
View file @
fd9a5d22
...
...
@@ -31,26 +31,26 @@ import (
// 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/"
)
:
]
abspath
:=
absolutePath
(
r
.
URL
.
Path
[
1
:
],
*
goroot
)
r
.
ParseForm
()
if
f
:=
r
.
FormValue
(
"fileprint"
);
f
!=
""
{
codewalkFileprint
(
c
,
r
,
f
)
codewalkFileprint
(
w
,
r
,
f
)
return
}
// If directory exists, serve list of code walks.
dir
,
err
:=
os
.
Lstat
(
abspath
)
if
err
==
nil
&&
dir
.
IsDirectory
()
{
codewalkDir
(
c
,
r
,
relpath
,
abspath
)
codewalkDir
(
w
,
r
,
relpath
,
abspath
)
return
}
// If file exists, serve using standard file server.
if
err
==
nil
{
serveFile
(
c
,
r
)
serveFile
(
w
,
r
)
return
}
...
...
@@ -59,17 +59,17 @@ func codewalk(c *http.Conn, r *http.Request) {
cw
,
err
:=
loadCodewalk
(
abspath
+
".xml"
)
if
err
!=
nil
{
log
.
Stderr
(
err
)
serveError
(
c
,
r
,
relpath
,
err
)
serveError
(
w
,
r
,
relpath
,
err
)
return
}
// Canonicalize the path and redirect if changed
if
redirect
(
c
,
r
)
{
if
redirect
(
w
,
r
)
{
return
}
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.
// It scans the directory for subdirectories or files named *.xml
// 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
{
Name
string
Title
string
...
...
@@ -187,7 +187,7 @@ func codewalkDir(c *http.Conn, r *http.Request, relpath, abspath string) {
dir
,
err
:=
ioutil
.
ReadDir
(
abspath
)
if
err
!=
nil
{
log
.
Stderr
(
err
)
serveError
(
c
,
r
,
relpath
,
err
)
serveError
(
w
,
r
,
relpath
,
err
)
return
}
var
v
vector
.
Vector
...
...
@@ -204,7 +204,7 @@ func codewalkDir(c *http.Conn, r *http.Request, relpath, abspath string) {
}
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
// of the codewalk pages. It is a separate iframe and does not get
// 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
)
data
,
err
:=
ioutil
.
ReadFile
(
abspath
)
if
err
!=
nil
{
log
.
Stderr
(
err
)
serveError
(
c
,
r
,
f
,
err
)
serveError
(
w
,
r
,
f
,
err
)
return
}
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>`
)
template
.
HTMLEscape
(
c
,
data
[
0
:
mark
])
io
.
WriteString
(
c
,
"<a name='mark'></a>"
)
template
.
HTMLEscape
(
c
,
data
[
mark
:
lo
])
io
.
WriteString
(
w
,
`<style type="text/css">@import "/doc/codewalk/codewalk.css";</style><pre>`
)
template
.
HTMLEscape
(
w
,
data
[
0
:
mark
])
io
.
WriteString
(
w
,
"<a name='mark'></a>"
)
template
.
HTMLEscape
(
w
,
data
[
mark
:
lo
])
if
lo
<
hi
{
io
.
WriteString
(
c
,
"<div class='codewalkhighlight'>"
)
template
.
HTMLEscape
(
c
,
data
[
lo
:
hi
])
io
.
WriteString
(
c
,
"</div>"
)
io
.
WriteString
(
w
,
"<div class='codewalkhighlight'>"
)
template
.
HTMLEscape
(
w
,
data
[
lo
:
hi
])
io
.
WriteString
(
w
,
"</div>"
)
}
template
.
HTMLEscape
(
c
,
data
[
hi
:
])
io
.
WriteString
(
c
,
"</pre>"
)
template
.
HTMLEscape
(
w
,
data
[
hi
:
])
io
.
WriteString
(
w
,
"</pre>"
)
}
...
...
src/cmd/godoc/godoc.go
View file @
fd9a5d22
...
...
@@ -792,7 +792,7 @@ func readTemplates() {
// ----------------------------------------------------------------------------
// 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
{
Title
string
Subtitle
string
...
...
@@ -813,15 +813,15 @@ func servePage(c *http.Conn, title, subtitle, query string, content []byte) {
Content
:
content
,
}
if
err
:=
godocHTML
.
Execute
(
&
d
,
c
);
err
!=
nil
{
if
err
:=
godocHTML
.
Execute
(
&
d
,
w
);
err
!=
nil
{
log
.
Stderrf
(
"godocHTML.Execute: %s"
,
err
)
}
}
func
serveText
(
c
*
http
.
Conn
,
text
[]
byte
)
{
c
.
SetHeader
(
"Content-Type"
,
"text/plain; charset=utf-8"
)
c
.
Write
(
text
)
func
serveText
(
w
http
.
ResponseWriter
,
text
[]
byte
)
{
w
.
SetHeader
(
"Content-Type"
,
"text/plain; charset=utf-8"
)
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
src
,
err
:=
ioutil
.
ReadFile
(
abspath
)
if
err
!=
nil
{
log
.
Stderrf
(
"ioutil.ReadFile: %s"
,
err
)
serveError
(
c
,
r
,
relpath
,
err
)
serveError
(
w
,
r
,
relpath
,
err
)
return
}
// if it begins with "<!DOCTYPE " assume it is standalone
// html that doesn't need the template wrapping.
if
bytes
.
HasPrefix
(
src
,
[]
byte
(
"<!DOCTYPE "
))
{
c
.
Write
(
src
)
w
.
Write
(
src
)
return
}
...
...
@@ -875,7 +875,7 @@ func serveHTMLDoc(c *http.Conn, r *http.Request, abspath, relpath string) {
}
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
)
if
err
!=
nil
{
log
.
Stderrf
(
"parser.ParseFile: %s"
,
err
)
serveError
(
c
,
r
,
relpath
,
err
)
serveError
(
w
,
r
,
relpath
,
err
)
return
}
...
...
@@ -911,13 +911,13 @@ func serveGoSource(c *http.Conn, r *http.Request, abspath, relpath string) {
info
:=
&
SourceInfo
{
styler
.
idList
(),
buf
.
Bytes
()}
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
{
http
.
Redirect
(
c
,
canonical
,
http
.
StatusMovedPermanently
)
http
.
Redirect
(
w
,
r
,
canonical
,
http
.
StatusMovedPermanently
)
redirected
=
true
}
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
)
if
err
!=
nil
{
log
.
Stderrf
(
"ioutil.ReadFile: %s"
,
err
)
serveError
(
c
,
r
,
relpath
,
err
)
serveError
(
w
,
r
,
relpath
,
err
)
return
}
...
...
@@ -984,19 +984,19 @@ func serveTextFile(c *http.Conn, r *http.Request, abspath, relpath string) {
template
.
HTMLEscape
(
&
buf
,
src
)
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
)
{
if
redirect
(
c
,
r
)
{
func
serveDirectory
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
abspath
,
relpath
string
)
{
if
redirect
(
w
,
r
)
{
return
}
list
,
err
:=
ioutil
.
ReadDir
(
abspath
)
if
err
!=
nil
{
log
.
Stderrf
(
"ioutil.ReadDir: %s"
,
err
)
serveError
(
c
,
r
,
relpath
,
err
)
serveError
(
w
,
r
,
relpath
,
err
)
return
}
...
...
@@ -1007,23 +1007,23 @@ func serveDirectory(c *http.Conn, r *http.Request, abspath, relpath string) {
}
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 '/'
abspath
:=
absolutePath
(
relpath
,
*
goroot
)
// pick off special cases and hand the rest to the standard file server
switch
r
.
URL
.
Path
{
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
case
"/doc/root.html"
:
// hide landing page from its real name
http
.
Redirect
(
c
,
"/"
,
http
.
StatusMovedPermanently
)
http
.
Redirect
(
w
,
r
,
"/"
,
http
.
StatusMovedPermanently
)
return
}
...
...
@@ -1032,42 +1032,42 @@ func serveFile(c *http.Conn, r *http.Request) {
if
strings
.
HasSuffix
(
abspath
,
"/index.html"
)
{
// We'll show index.html for the directory.
// 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
}
serveHTMLDoc
(
c
,
r
,
abspath
,
relpath
)
serveHTMLDoc
(
w
,
r
,
abspath
,
relpath
)
return
case
".go"
:
serveGoSource
(
c
,
r
,
abspath
,
relpath
)
serveGoSource
(
w
,
r
,
abspath
,
relpath
)
return
}
dir
,
err
:=
os
.
Lstat
(
abspath
)
if
err
!=
nil
{
log
.
Stderr
(
err
)
serveError
(
c
,
r
,
relpath
,
err
)
serveError
(
w
,
r
,
relpath
,
err
)
return
}
if
dir
!=
nil
&&
dir
.
IsDirectory
()
{
if
redirect
(
c
,
r
)
{
if
redirect
(
w
,
r
)
{
return
}
if
index
:=
abspath
+
"/index.html"
;
isTextFile
(
index
)
{
serveHTMLDoc
(
c
,
r
,
index
,
relativePath
(
index
))
serveHTMLDoc
(
w
,
r
,
index
,
relativePath
(
index
))
return
}
serveDirectory
(
c
,
r
,
abspath
,
relpath
)
serveDirectory
(
w
,
r
,
abspath
,
relpath
)
return
}
if
isTextFile
(
abspath
)
{
serveTextFile
(
c
,
r
,
abspath
,
relpath
)
serveTextFile
(
w
,
r
,
abspath
,
relpath
)
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
)
{
if
redirect
(
c
,
r
)
{
func
(
h
*
httpHandler
)
ServeHTTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
if
redirect
(
w
,
r
)
{
return
}
...
...
@@ -1257,13 +1257,13 @@ func (h *httpHandler) ServeHTTP(c *http.Conn, r *http.Request) {
info
:=
h
.
getPageInfo
(
abspath
,
relpath
,
r
.
FormValue
(
"p"
),
mode
)
if
info
.
Err
!=
nil
{
log
.
Stderr
(
info
.
Err
)
serveError
(
c
,
r
,
relpath
,
info
.
Err
)
serveError
(
w
,
r
,
relpath
,
info
.
Err
)
return
}
if
r
.
FormValue
(
"f"
)
==
"text"
{
contents
:=
applyTemplate
(
packageText
,
"packageText"
,
info
)
serveText
(
c
,
contents
)
serveText
(
w
,
contents
)
return
}
...
...
@@ -1290,7 +1290,7 @@ func (h *httpHandler) ServeHTTP(c *http.Conn, r *http.Request) {
}
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"
))
result
:=
lookup
(
query
)
if
r
.
FormValue
(
"f"
)
==
"text"
{
contents
:=
applyTemplate
(
searchText
,
"searchText"
,
result
)
serveText
(
c
,
contents
)
serveText
(
w
,
contents
)
return
}
...
...
@@ -1337,7 +1337,7 @@ func search(c *http.Conn, r *http.Request) {
}
contents
:=
applyTemplate
(
searchHTML
,
"searchHTML"
,
result
)
servePage
(
c
,
title
,
""
,
query
,
contents
)
servePage
(
w
,
title
,
""
,
query
,
contents
)
}
...
...
src/cmd/godoc/main.go
View file @
fd9a5d22
...
...
@@ -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!
c
.
WriteHeader
(
http
.
StatusNotFound
)
servePage
(
c
,
"File "
+
relpath
,
""
,
""
,
contents
)
w
.
WriteHeader
(
http
.
StatusNotFound
)
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
()
if
err
!=
nil
{
log
.
Stderrf
(
"os.Pipe(): %v
\n
"
,
err
)
...
...
@@ -109,18 +109,18 @@ func exec(c *http.Conn, args []string) (status int) {
if
*
verbose
{
os
.
Stderr
.
Write
(
buf
.
Bytes
())
}
if
c
!=
nil
{
c
.
SetHeader
(
"content-type"
,
"text/plain; charset=utf-8"
)
c
.
Write
(
buf
.
Bytes
())
if
rw
!=
nil
{
rw
.
SetHeader
(
"content-type"
,
"text/plain; charset=utf-8"
)
rw
.
Write
(
buf
.
Bytes
())
}
return
}
func
dosync
(
c
*
http
.
Conn
,
r
*
http
.
Request
)
{
func
dosync
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
args
:=
[]
string
{
"/bin/sh"
,
"-c"
,
*
syncCmd
}
switch
exec
(
c
,
args
)
{
switch
exec
(
w
,
args
)
{
case
0
:
// sync succeeded and some files have changed;
// update package tree.
...
...
@@ -150,9 +150,9 @@ func usage() {
func
loggingHandler
(
h
http
.
Handler
)
http
.
Handler
{
return
http
.
HandlerFunc
(
func
(
c
*
http
.
Conn
,
req
*
http
.
Request
)
{
log
.
Stderrf
(
"%s
\t
%s"
,
c
.
RemoteAddr
,
req
.
URL
)
h
.
ServeHTTP
(
c
,
req
)
return
http
.
HandlerFunc
(
func
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
)
{
log
.
Stderrf
(
"%s
\t
%s"
,
w
.
RemoteAddr
()
,
req
.
URL
)
h
.
ServeHTTP
(
w
,
req
)
})
}
...
...
src/pkg/expvar/expvar.go
View file @
fd9a5d22
...
...
@@ -210,18 +210,18 @@ func Iter() <-chan KeyValue {
return
c
}
func
expvarHandler
(
c
*
http
.
Conn
,
req
*
http
.
Request
)
{
c
.
SetHeader
(
"content-type"
,
"application/json; charset=utf-8"
)
fmt
.
Fprintf
(
c
,
"{
\n
"
)
func
expvarHandler
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
w
.
SetHeader
(
"content-type"
,
"application/json; charset=utf-8"
)
fmt
.
Fprintf
(
w
,
"{
\n
"
)
first
:=
true
for
name
,
value
:=
range
vars
{
if
!
first
{
fmt
.
Fprintf
(
c
,
",
\n
"
)
fmt
.
Fprintf
(
w
,
",
\n
"
)
}
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
{
...
...
src/pkg/http/fs.go
View file @
fd9a5d22
...
...
@@ -43,8 +43,8 @@ func isText(b []byte) bool {
return
true
}
func
dirList
(
c
*
Conn
,
f
*
os
.
File
)
{
fmt
.
Fprintf
(
c
,
"<pre>
\n
"
)
func
dirList
(
w
ResponseWriter
,
f
*
os
.
File
)
{
fmt
.
Fprintf
(
w
,
"<pre>
\n
"
)
for
{
dirs
,
err
:=
f
.
Readdir
(
100
)
if
err
!=
nil
||
len
(
dirs
)
==
0
{
...
...
@@ -56,25 +56,25 @@ func dirList(c *Conn, f *os.File) {
name
+=
"/"
}
// 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"
// redirect .../index.html to .../
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
}
f
,
err
:=
os
.
Open
(
name
,
os
.
O_RDONLY
,
0
)
if
err
!=
nil
{
// TODO expose actual error?
NotFound
(
c
,
r
)
NotFound
(
w
,
r
)
return
}
defer
f
.
Close
()
...
...
@@ -82,7 +82,7 @@ func serveFile(c *Conn, r *Request, name string, redirect bool) {
d
,
err1
:=
f
.
Stat
()
if
err1
!=
nil
{
// TODO expose actual error?
NotFound
(
c
,
r
)
NotFound
(
w
,
r
)
return
}
...
...
@@ -92,22 +92,22 @@ func serveFile(c *Conn, r *Request, name string, redirect bool) {
url
:=
r
.
URL
.
Path
if
d
.
IsDirectory
()
{
if
url
[
len
(
url
)
-
1
]
!=
'/'
{
Redirect
(
c
,
url
+
"/"
,
StatusMovedPermanently
)
Redirect
(
w
,
r
,
url
+
"/"
,
StatusMovedPermanently
)
return
}
}
else
{
if
url
[
len
(
url
)
-
1
]
==
'/'
{
Redirect
(
c
,
url
[
0
:
len
(
url
)
-
1
],
StatusMovedPermanently
)
Redirect
(
w
,
r
,
url
[
0
:
len
(
url
)
-
1
],
StatusMovedPermanently
)
return
}
}
}
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
}
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
if
d
.
IsDirectory
()
{
...
...
@@ -125,7 +125,7 @@ func serveFile(c *Conn, r *Request, name string, redirect bool) {
}
if
d
.
IsDirectory
()
{
dirList
(
c
,
f
)
dirList
(
w
,
f
)
return
}
...
...
@@ -133,25 +133,25 @@ func serveFile(c *Conn, r *Request, name string, redirect bool) {
// use extension to find content type.
ext
:=
path
.
Ext
(
name
)
if
ctype
:=
mime
.
TypeByExtension
(
ext
);
ctype
!=
""
{
c
.
SetHeader
(
"Content-Type"
,
ctype
)
w
.
SetHeader
(
"Content-Type"
,
ctype
)
}
else
{
// read first chunk to decide between utf-8 text and binary
var
buf
[
1024
]
byte
n
,
_
:=
io
.
ReadFull
(
f
,
buf
[
0
:
])
b
:=
buf
[
0
:
n
]
if
isText
(
b
)
{
c
.
SetHeader
(
"Content-Type"
,
"text-plain; charset=utf-8"
)
w
.
SetHeader
(
"Content-Type"
,
"text-plain; charset=utf-8"
)
}
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.
func
ServeFile
(
c
*
Conn
,
r
*
Request
,
name
string
)
{
serveFile
(
c
,
r
,
name
,
false
)
func
ServeFile
(
w
ResponseWriter
,
r
*
Request
,
name
string
)
{
serveFile
(
w
,
r
,
name
,
false
)
}
type
fileHandler
struct
{
...
...
@@ -165,12 +165,12 @@ type fileHandler struct {
// looking up the file name in the file system.
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
if
!
strings
.
HasPrefix
(
path
,
f
.
prefix
)
{
NotFound
(
c
,
r
)
NotFound
(
w
,
r
)
return
}
path
=
path
[
len
(
f
.
prefix
)
:
]
serveFile
(
c
,
r
,
f
.
root
+
"/"
+
path
,
true
)
serveFile
(
w
,
r
,
f
.
root
+
"/"
+
path
,
true
)
}
src/pkg/http/pprof/pprof.go
View file @
fd9a5d22
...
...
@@ -40,28 +40,28 @@ func init() {
// Cmdline responds with the running program's
// command line, with arguments separated by NUL bytes.
// The package initialization registers it as /debug/pprof/cmdline.
func
Cmdline
(
c
*
http
.
Conn
,
r
*
http
.
Request
)
{
c
.
SetHeader
(
"content-type"
,
"text/plain; charset=utf-8"
)
fmt
.
Fprintf
(
c
,
strings
.
Join
(
os
.
Args
,
"
\x00
"
))
func
Cmdline
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
w
.
SetHeader
(
"content-type"
,
"text/plain; charset=utf-8"
)
fmt
.
Fprintf
(
w
,
strings
.
Join
(
os
.
Args
,
"
\x00
"
))
}
// Heap responds with the pprof-formatted heap profile.
// The package initialization registers it as /debug/pprof/heap.
func
Heap
(
c
*
http
.
Conn
,
r
*
http
.
Request
)
{
c
.
SetHeader
(
"content-type"
,
"text/plain; charset=utf-8"
)
pprof
.
WriteHeapProfile
(
c
)
func
Heap
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
w
.
SetHeader
(
"content-type"
,
"text/plain; charset=utf-8"
)
pprof
.
WriteHeapProfile
(
w
)
}
// Symbol looks up the program counters listed in the request,
// responding with a table mapping program counters to function names.
// The package initialization registers it as /debug/pprof/symbol.
func
Symbol
(
c
*
http
.
Conn
,
r
*
http
.
Request
)
{
c
.
SetHeader
(
"content-type"
,
"text/plain; charset=utf-8"
)
func
Symbol
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
w
.
SetHeader
(
"content-type"
,
"text/plain; charset=utf-8"
)
// We don't know how many symbols we have, but we
// do have symbol information. Pprof only cares whether
// 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
if
r
.
Method
==
"POST"
{
...
...
@@ -71,15 +71,15 @@ func Symbol(c *http.Conn, r *http.Request) {
}
for
{
w
,
err
:=
b
.
ReadSlice
(
'+'
)
w
ord
,
err
:=
b
.
ReadSlice
(
'+'
)
if
err
==
nil
{
w
=
w
[
0
:
len
(
w
)
-
1
]
// trim +
w
ord
=
word
[
0
:
len
(
word
)
-
1
]
// trim +
}
pc
,
_
:=
strconv
.
Btoui64
(
string
(
w
),
0
)
pc
,
_
:=
strconv
.
Btoui64
(
string
(
w
ord
),
0
)
if
pc
!=
0
{
f
:=
runtime
.
FuncForPC
(
uintptr
(
pc
))
if
f
!=
nil
{
fmt
.
Fprintf
(
c
,
"%#x %s
\n
"
,
pc
,
f
.
Name
())
fmt
.
Fprintf
(
w
,
"%#x %s
\n
"
,
pc
,
f
.
Name
())
}
}
...
...
src/pkg/http/server.go
View file @
fd9a5d22
...
...
@@ -37,32 +37,78 @@ var (
// registered to serve a particular path or subtree
// 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 that the HTTP server can move on to the next request on
// the connection.
type
Handler
interface
{
ServeHTTP
(
*
Conn
,
*
Request
)
}
// A Conn represents the server side of a single active HTTP connection.
type
Conn
struct
{
RemoteAddr
string
// network address of remote side
Req
*
Request
// current HTTP request
rwc
io
.
ReadWriteCloser
// i/o connection
buf
*
bufio
.
ReadWriter
// buffered rwc
handler
Handler
// request handler
hijacked
bool
// connection has been hijacked by handler
// state for the current reply
ServeHTTP
(
ResponseWriter
,
*
Request
)
}
// A ResponseWriter interface is used by an HTTP handler to
// construct an HTTP response.
type
ResponseWriter
interface
{
// 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
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
wroteHeader
bool
// reply header has been written
wroteContinue
bool
// 100 Continue response was written
header
map
[
string
]
string
// reply header parameters
written
int64
// number of bytes written in body
status
int
// status code passed to WriteHeader
usingTLS
bool
// a flag indicating connection over TLS
// close connection after this reply. set on request and
// updated after response from handler if there's a
...
...
@@ -72,11 +118,9 @@ type Conn struct {
}
// Create new connection from rwc.
func
newConn
(
rwc
net
.
Conn
,
handler
Handler
)
(
c
*
Conn
,
err
os
.
Error
)
{
c
=
new
(
Conn
)
if
a
:=
rwc
.
RemoteAddr
();
a
!=
nil
{
c
.
RemoteAddr
=
a
.
String
()
}
func
newConn
(
rwc
net
.
Conn
,
handler
Handler
)
(
c
*
conn
,
err
os
.
Error
)
{
c
=
new
(
conn
)
c
.
remoteAddr
=
rwc
.
RemoteAddr
()
.
String
()
c
.
handler
=
handler
c
.
rwc
=
rwc
_
,
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
// HTTP/1.1 100 Continue header
type
expectContinueReader
struct
{
conn
*
Conn
resp
*
response
readCloser
io
.
ReadCloser
}
func
(
ecr
*
expectContinueReader
)
Read
(
p
[]
byte
)
(
n
int
,
err
os
.
Error
)
{
if
!
ecr
.
conn
.
wroteContinue
&&
!
ecr
.
conn
.
hijacked
{
ecr
.
conn
.
wroteContinue
=
true
if
ecr
.
conn
.
Req
.
ProtoAtLeast
(
1
,
1
)
{
io
.
WriteString
(
ecr
.
conn
.
buf
,
"HTTP/1.1 100 Continue
\r\n\r\n
"
)
ecr
.
conn
.
buf
.
Flush
()
}
if
!
ecr
.
resp
.
wroteContinue
&&
!
ecr
.
resp
.
conn
.
hijacked
{
ecr
.
resp
.
wroteContinue
=
true
io
.
WriteString
(
ecr
.
resp
.
conn
.
buf
,
"HTTP/1.1 100 Continue
\r\n\r\n
"
)
ecr
.
resp
.
conn
.
buf
.
Flush
()
}
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"
// 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
{
return
nil
,
ErrHijacked
}
var
req
*
Request
if
req
,
err
=
ReadRequest
(
c
.
buf
.
Reader
);
err
!=
nil
{
return
nil
,
err
}
// Reset per-request connection state.
c
.
header
=
make
(
map
[
string
]
string
)
c
.
wroteHeader
=
false
c
.
wroteContinue
=
false
c
.
Req
=
req
w
=
new
(
response
)
w
.
conn
=
c
w
.
req
=
req
w
.
header
=
make
(
map
[
string
]
string
)
// 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
req
.
Body
=
&
expectContinueReader
{
readCloser
:
req
.
Body
,
conn
:
c
}
req
.
Body
=
&
expectContinueReader
{
readCloser
:
req
.
Body
,
resp
:
w
}
}
// Default output is HTML encoded in UTF-8.
c
.
SetHeader
(
"Content-Type"
,
"text/html; charset=utf-8"
)
c
.
SetHeader
(
"Date"
,
time
.
UTC
()
.
Format
(
TimeFormat
))
w
.
SetHeader
(
"Content-Type"
,
"text/html; charset=utf-8"
)
w
.
SetHeader
(
"Date"
,
time
.
UTC
()
.
Format
(
TimeFormat
))
if
req
.
ProtoAtLeast
(
1
,
1
)
{
// HTTP/1.1 or greater: use chunked transfer encoding
// to avoid closing the connection at EOF.
c
.
chunking
=
true
c
.
SetHeader
(
"Transfer-Encoding"
,
"chunked"
)
w
.
chunking
=
true
w
.
SetHeader
(
"Transfer-Encoding"
,
"chunked"
)
}
else
{
// HTTP version < 1.1: cannot do chunked transfer
// encoding, so signal EOF by closing connection.
// Will be overridden if the HTTP handler ends up
// writing a Content-Length and the client requested
// "Connection: keep-alive"
c
.
closeAfterReply
=
true
c
.
chunking
=
false
w
.
closeAfterReply
=
true
}
return
req
,
nil
return
w
,
nil
}
// UsingTLS
returns true if the connection uses transport layer security (TLS).
func
(
c
*
Conn
)
UsingTLS
()
bool
{
return
c
.
usingTLS
// UsingTLS
implements the ResponseWriter.UsingTLS
func
(
w
*
response
)
UsingTLS
()
bool
{
return
w
.
conn
.
usingTLS
}
// SetHeader sets a header line in the eventual 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.
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"
)
// RemoteAddr implements the ResponseWriter.RemoteAddr method
func
(
w
*
response
)
RemoteAddr
()
string
{
return
w
.
conn
.
remoteAddr
}
// SetHeader implements the ResponseWriter.SetHeader method
func
(
w
*
response
)
SetHeader
(
hdr
,
val
string
)
{
w
.
header
[
CanonicalHeaderKey
(
hdr
)]
=
val
}
// WriteHeader implements the ResponseWriter.WriteHeader method
func
(
w
*
response
)
WriteHeader
(
code
int
)
{
if
w
.
conn
.
hijacked
{
log
.
Stderr
(
"http: response.WriteHeader on hijacked connection"
)
return
}
if
c
.
wroteHeader
{
log
.
Stderr
(
"http: multiple
Conn
.WriteHeader calls"
)
if
w
.
wroteHeader
{
log
.
Stderr
(
"http: multiple
response
.WriteHeader calls"
)
return
}
c
.
wroteHeader
=
true
c
.
status
=
code
w
.
wroteHeader
=
true
w
.
status
=
code
if
code
==
StatusNotModified
{
// Must not have body.
c
.
header
[
"Content-Type"
]
=
""
,
false
c
.
header
[
"Transfer-Encoding"
]
=
""
,
false
w
.
header
[
"Content-Type"
]
=
""
,
false
w
.
header
[
"Transfer-Encoding"
]
=
""
,
false
}
c
.
written
=
0
if
!
c
.
Req
.
ProtoAtLeast
(
1
,
0
)
{
if
!
w
.
req
.
ProtoAtLeast
(
1
,
0
)
{
return
}
proto
:=
"HTTP/1.0"
if
c
.
R
eq
.
ProtoAtLeast
(
1
,
1
)
{
if
w
.
r
eq
.
ProtoAtLeast
(
1
,
1
)
{
proto
=
"HTTP/1.1"
}
codestring
:=
strconv
.
Itoa
(
code
)
...
...
@@ -208,57 +238,55 @@ func (c *Conn) WriteHeader(code int) {
if
!
ok
{
text
=
"status code "
+
codestring
}
io
.
WriteString
(
c
.
buf
,
proto
+
" "
+
codestring
+
" "
+
text
+
"
\r\n
"
)
for
k
,
v
:=
range
c
.
header
{
io
.
WriteString
(
c
.
buf
,
k
+
": "
+
v
+
"
\r\n
"
)
io
.
WriteString
(
w
.
conn
.
buf
,
proto
+
" "
+
codestring
+
" "
+
text
+
"
\r\n
"
)
for
k
,
v
:=
range
w
.
header
{
io
.
WriteString
(
w
.
conn
.
buf
,
k
+
": "
+
v
+
"
\r\n
"
)
}
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.
// If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK)
// before writing the data.
func
(
c
*
Conn
)
Write
(
data
[]
byte
)
(
n
int
,
err
os
.
Error
)
{
if
c
.
hijacked
{
log
.
Stderr
(
"http: Conn.Write on hijacked connection"
)
// Write implements the ResponseWriter.Write method
func
(
w
*
response
)
Write
(
data
[]
byte
)
(
n
int
,
err
os
.
Error
)
{
if
w
.
conn
.
hijacked
{
log
.
Stderr
(
"http: response.Write on hijacked connection"
)
return
0
,
ErrHijacked
}
if
!
c
.
wroteHeader
{
if
c
.
R
eq
.
wantsHttp10KeepAlive
()
{
_
,
hasLength
:=
c
.
header
[
"Content-Length"
]
if
!
w
.
wroteHeader
{
if
w
.
r
eq
.
wantsHttp10KeepAlive
()
{
_
,
hasLength
:=
w
.
header
[
"Content-Length"
]
if
hasLength
{
_
,
connectionHeaderSet
:=
c
.
header
[
"Connection"
]
_
,
connectionHeaderSet
:=
w
.
header
[
"Connection"
]
if
!
connectionHeaderSet
{
c
.
header
[
"Connection"
]
=
"keep-alive"
w
.
header
[
"Connection"
]
=
"keep-alive"
}
}
}
c
.
WriteHeader
(
StatusOK
)
w
.
WriteHeader
(
StatusOK
)
}
if
len
(
data
)
==
0
{
return
0
,
nil
}
if
c
.
status
==
StatusNotModified
{
if
w
.
status
==
StatusNotModified
{
// Must not have body.
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,
// then there would be fewer chunk headers.
// On the other hand, it would make hijacking more difficult.
if
c
.
chunking
{
fmt
.
Fprintf
(
c
.
buf
,
"%x
\r\n
"
,
len
(
data
))
// TODO(rsc): use strconv not fmt
if
w
.
chunking
{
fmt
.
Fprintf
(
w
.
conn
.
buf
,
"%x
\r\n
"
,
len
(
data
))
// TODO(rsc): use strconv not fmt
}
n
,
err
=
c
.
buf
.
Write
(
data
)
if
err
==
nil
&&
c
.
chunking
{
n
,
err
=
w
.
conn
.
buf
.
Write
(
data
)
if
err
==
nil
&&
w
.
chunking
{
if
n
!=
len
(
data
)
{
err
=
io
.
ErrShortWrite
}
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
// browsers are in the 256-512 range.
// Pad to 1024 bytes.
func
errorKludge
(
c
*
Conn
,
req
*
Request
)
{
func
errorKludge
(
w
*
response
)
{
const
min
=
1024
// Is this an error?
if
kind
:=
c
.
status
/
100
;
kind
!=
4
&&
kind
!=
5
{
if
kind
:=
w
.
status
/
100
;
kind
!=
4
&&
kind
!=
5
{
return
}
// Did the handler supply any info? Enough?
if
c
.
written
==
0
||
c
.
written
>=
min
{
if
w
.
written
==
0
||
w
.
written
>=
min
{
return
}
// Is it a broken browser?
var
msg
string
switch
agent
:=
req
.
UserAgent
;
{
switch
agent
:=
w
.
req
.
UserAgent
;
{
case
strings
.
Index
(
agent
,
"MSIE"
)
>=
0
:
msg
=
"Internet Explorer"
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
"
// 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
{
case
"text/html"
:
io
.
WriteString
(
c
,
"<!-- "
)
for
c
.
written
<
min
{
io
.
WriteString
(
c
,
msg
)
io
.
WriteString
(
w
,
"<!-- "
)
for
w
.
written
<
min
{
io
.
WriteString
(
w
,
msg
)
}
io
.
WriteString
(
c
,
" -->"
)
io
.
WriteString
(
w
,
" -->"
)
case
"text/plain"
:
io
.
WriteString
(
c
,
"
\n
"
)
for
c
.
written
<
min
{
io
.
WriteString
(
c
,
msg
)
io
.
WriteString
(
w
,
"
\n
"
)
for
w
.
written
<
min
{
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
// back, we can make this a keep-alive response ...
if
c
.
R
eq
.
wantsHttp10KeepAlive
()
{
_
,
sentLength
:=
c
.
header
[
"Content-Length"
]
if
sentLength
&&
c
.
header
[
"Connection"
]
==
"keep-alive"
{
c
.
closeAfterReply
=
false
if
w
.
r
eq
.
wantsHttp10KeepAlive
()
{
_
,
sentLength
:=
w
.
header
[
"Content-Length"
]
if
sentLength
&&
w
.
header
[
"Connection"
]
==
"keep-alive"
{
w
.
closeAfterReply
=
false
}
}
if
!
c
.
wroteHeader
{
c
.
WriteHeader
(
StatusOK
)
if
!
w
.
wroteHeader
{
w
.
WriteHeader
(
StatusOK
)
}
errorKludge
(
c
,
c
.
Req
)
if
c
.
chunking
{
io
.
WriteString
(
c
.
buf
,
"0
\r\n
"
)
errorKludge
(
w
)
if
w
.
chunking
{
io
.
WriteString
(
w
.
conn
.
buf
,
"0
\r\n
"
)
// 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
.
func
(
c
*
Conn
)
Flush
()
{
if
!
c
.
wroteHeader
{
c
.
WriteHeader
(
StatusOK
)
// Flush
implements the ResponseWriter.Flush method
.
func
(
w
*
response
)
Flush
()
{
if
!
w
.
wroteHeader
{
w
.
WriteHeader
(
StatusOK
)
}
c
.
buf
.
Flush
()
w
.
conn
.
buf
.
Flush
()
}
// Close the connection.
func
(
c
*
C
onn
)
close
()
{
func
(
c
*
c
onn
)
close
()
{
if
c
.
buf
!=
nil
{
c
.
buf
.
Flush
()
c
.
buf
=
nil
...
...
@@ -356,9 +384,9 @@ func (c *Conn) close() {
}
// Serve a new connection.
func
(
c
*
C
onn
)
serve
()
{
func
(
c
*
c
onn
)
serve
()
{
for
{
req
,
err
:=
c
.
readRequest
()
w
,
err
:=
c
.
readRequest
()
if
err
!=
nil
{
break
}
...
...
@@ -367,32 +395,28 @@ func (c *Conn) serve() {
// so we might as well run the handler in this goroutine.
// [*] Not strictly true: HTTP pipelining. We could let them all process
// in parallel even if their responses need to be serialized.
c
.
handler
.
ServeHTTP
(
c
,
req
)
c
.
handler
.
ServeHTTP
(
w
,
w
.
req
)
if
c
.
hijacked
{
return
}
c
.
finishRequest
()
if
c
.
closeAfterReply
{
w
.
finishRequest
()
if
w
.
closeAfterReply
{
break
}
}
c
.
close
()
}
// Hijack lets the caller take over the connection.
// After a call to c.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.
func
(
c
*
Conn
)
Hijack
()
(
rwc
io
.
ReadWriteCloser
,
buf
*
bufio
.
ReadWriter
,
err
os
.
Error
)
{
if
c
.
hijacked
{
// Hijack impements the ResponseWriter.Hijack method.
func
(
w
*
response
)
Hijack
()
(
rwc
io
.
ReadWriteCloser
,
buf
*
bufio
.
ReadWriter
,
err
os
.
Error
)
{
if
w
.
conn
.
hijacked
{
return
nil
,
nil
,
ErrHijacked
}
c
.
hijacked
=
true
rwc
=
c
.
rwc
buf
=
c
.
buf
c
.
rwc
=
nil
c
.
buf
=
nil
w
.
conn
.
hijacked
=
true
rwc
=
w
.
conn
.
rwc
buf
=
w
.
conn
.
buf
w
.
conn
.
rwc
=
nil
w
.
conn
.
buf
=
nil
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
// with the appropriate signature, HandlerFunc(f) is a
// Handler object that calls f.
type
HandlerFunc
func
(
*
Conn
,
*
Request
)
type
HandlerFunc
func
(
ResponseWriter
,
*
Request
)
// ServeHTTP calls f(c, req).
func
(
f
HandlerFunc
)
ServeHTTP
(
c
*
Conn
,
req
*
Request
)
{
f
(
c
,
req
)
func
(
f
HandlerFunc
)
ServeHTTP
(
w
ResponseWriter
,
r
*
Request
)
{
f
(
w
,
r
)
}
// Helper handlers
// Error replies to the request with the specified error message and HTTP code.
func
Error
(
c
*
Conn
,
error
string
,
code
int
)
{
c
.
SetHeader
(
"Content-Type"
,
"text/plain; charset=utf-8"
)
c
.
WriteHeader
(
code
)
fmt
.
Fprintln
(
c
,
error
)
func
Error
(
w
ResponseWriter
,
error
string
,
code
int
)
{
w
.
SetHeader
(
"Content-Type"
,
"text/plain; charset=utf-8"
)
w
.
WriteHeader
(
code
)
fmt
.
Fprintln
(
w
,
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
// 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,
// 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
// response because older user agents may not understand 301/307.
note
:=
"<a href=
\"
%v
\"
>"
+
statusText
[
code
]
+
"</a>.
\n
"
if
c
.
Req
.
Method
==
"POST"
{
if
r
.
Method
==
"POST"
{
note
=
""
}
...
...
@@ -454,7 +478,7 @@ func Redirect(c *Conn, url string, code int) {
// Because of this problem, no one pays attention
// to the RFC; they all send back just a new path.
// So do we.
oldpath
:=
c
.
Req
.
URL
.
Path
oldpath
:=
r
.
URL
.
Path
if
oldpath
==
""
{
// should not happen, but avoid a crash if it does
oldpath
=
"/"
}
...
...
@@ -475,9 +499,9 @@ func Redirect(c *Conn, url string, code int) {
}
finish
:
c
.
SetHeader
(
"Location"
,
url
)
c
.
WriteHeader
(
code
)
fmt
.
Fprintf
(
c
,
note
,
url
)
w
.
SetHeader
(
"Location"
,
url
)
w
.
WriteHeader
(
code
)
fmt
.
Fprintf
(
w
,
note
,
url
)
}
// Redirect to a fixed URL
...
...
@@ -486,8 +510,8 @@ type redirectHandler struct {
code
int
}
func
(
rh
*
redirectHandler
)
ServeHTTP
(
c
*
Conn
,
req
*
Request
)
{
Redirect
(
c
,
rh
.
url
,
rh
.
code
)
func
(
rh
*
redirectHandler
)
ServeHTTP
(
w
ResponseWriter
,
r
*
Request
)
{
Redirect
(
w
,
r
,
rh
.
url
,
rh
.
code
)
}
// RedirectHandler returns a request handler that redirects
...
...
@@ -563,11 +587,11 @@ func cleanPath(p string) string {
// ServeHTTP dispatches the request to the handler whose
// 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.
if
p
:=
cleanPath
(
r
eq
.
URL
.
Path
);
p
!=
req
.
URL
.
Path
{
c
.
SetHeader
(
"Location"
,
p
)
c
.
WriteHeader
(
StatusMovedPermanently
)
if
p
:=
cleanPath
(
r
.
URL
.
Path
);
p
!=
r
.
URL
.
Path
{
w
.
SetHeader
(
"Location"
,
p
)
w
.
WriteHeader
(
StatusMovedPermanently
)
return
}
...
...
@@ -575,7 +599,7 @@ func (mux *ServeMux) ServeHTTP(c *Conn, req *Request) {
var
h
Handler
var
n
=
0
for
k
,
v
:=
range
mux
.
m
{
if
!
pathMatch
(
k
,
r
eq
.
URL
.
Path
)
{
if
!
pathMatch
(
k
,
r
.
URL
.
Path
)
{
continue
}
if
h
==
nil
||
len
(
k
)
>
n
{
...
...
@@ -586,7 +610,7 @@ func (mux *ServeMux) ServeHTTP(c *Conn, req *Request) {
if
h
==
nil
{
h
=
NotFoundHandler
()
}
h
.
ServeHTTP
(
c
,
req
)
h
.
ServeHTTP
(
w
,
r
)
}
// 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.
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
))
}
...
...
@@ -616,7 +640,7 @@ func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, h
// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
func
HandleFunc
(
pattern
string
,
handler
func
(
*
Conn
,
*
Request
))
{
func
HandleFunc
(
pattern
string
,
handler
func
(
ResponseWriter
,
*
Request
))
{
DefaultServeMux
.
HandleFunc
(
pattern
,
handler
)
}
...
...
@@ -658,8 +682,8 @@ func Serve(l net.Listener, handler Handler) os.Error {
// )
//
// // hello world, the web server
// func HelloServer(
c *http.Conn
, req *http.Request) {
// io.WriteString(
c
, "hello, world!\n")
// func HelloServer(
w http.ResponseWriter
, req *http.Request) {
// io.WriteString(
w
, "hello, world!\n")
// }
//
// func main() {
...
...
@@ -690,9 +714,9 @@ func ListenAndServe(addr string, handler Handler) os.Error {
// "log"
// )
//
// func handler(
conn *http.Conn
, req *http.Request) {
//
conn
.SetHeader("Content-Type", "text/plain")
//
conn
.Write([]byte("This is an example server.\n"))
// func handler(
w http.ResponseWriter
, req *http.Request) {
//
w
.SetHeader("Content-Type", "text/plain")
//
w
.Write([]byte("This is an example server.\n"))
// }
//
// func main() {
...
...
src/pkg/http/status.go
View file @
fd9a5d22
...
...
@@ -98,3 +98,9 @@ var statusText = map[int]string{
StatusGatewayTimeout
:
"Gateway Timeout"
,
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
]
}
src/pkg/rpc/debug.go
View file @
fd9a5d22
...
...
@@ -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
]
}
// 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.
var
services
=
make
(
serviceArray
,
len
(
server
.
serviceMap
))
i
:=
0
...
...
@@ -79,8 +79,8 @@ func debugHTTP(c *http.Conn, req *http.Request) {
}
server
.
Unlock
()
sort
.
Sort
(
services
)
err
:=
debug
.
Execute
(
services
,
c
)
err
:=
debug
.
Execute
(
services
,
w
)
if
err
!=
nil
{
fmt
.
Fprintln
(
c
,
"rpc: error executing template:"
,
err
.
String
())
fmt
.
Fprintln
(
w
,
"rpc: error executing template:"
,
err
.
String
())
}
}
src/pkg/rpc/server.go
View file @
fd9a5d22
...
...
@@ -445,16 +445,16 @@ var rpcPath string = "/_goRPC_"
var
debugPath
string
=
"/debug/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"
{
c
.
SetHeader
(
"Content-Type"
,
"text/plain; charset=utf-8"
)
c
.
WriteHeader
(
http
.
StatusMethodNotAllowed
)
io
.
WriteString
(
c
,
"405 must CONNECT to "
+
rpcPath
+
"
\n
"
)
w
.
SetHeader
(
"Content-Type"
,
"text/plain; charset=utf-8"
)
w
.
WriteHeader
(
http
.
StatusMethodNotAllowed
)
io
.
WriteString
(
w
,
"405 must CONNECT to "
+
rpcPath
+
"
\n
"
)
return
}
conn
,
_
,
err
:=
c
.
Hijack
()
conn
,
_
,
err
:=
w
.
Hijack
()
if
err
!=
nil
{
log
.
Stderr
(
"rpc hijacking "
,
c
.
RemoteAddr
,
": "
,
err
.
String
())
log
.
Stderr
(
"rpc hijacking "
,
w
.
RemoteAddr
()
,
": "
,
err
.
String
())
return
}
io
.
WriteString
(
conn
,
"HTTP/1.0 "
+
connected
+
"
\n\n
"
)
...
...
src/pkg/websocket/server.go
View file @
fd9a5d22
...
...
@@ -57,8 +57,8 @@ func getKeyNumber(s string) (r uint32) {
}
// ServeHTTP implements the http.Handler interface for a Web Socket
func
(
f
Handler
)
ServeHTTP
(
c
*
http
.
Conn
,
req
*
http
.
Request
)
{
rwc
,
buf
,
err
:=
c
.
Hijack
()
func
(
f
Handler
)
ServeHTTP
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
)
{
rwc
,
buf
,
err
:=
w
.
Hijack
()
if
err
!=
nil
{
panic
(
"Hijack failed: "
+
err
.
String
())
return
...
...
@@ -98,7 +98,7 @@ func (f Handler) ServeHTTP(c *http.Conn, req *http.Request) {
}
var
location
string
if
c
.
UsingTLS
()
{
if
w
.
UsingTLS
()
{
location
=
"wss://"
+
req
.
Host
+
req
.
URL
.
RawPath
}
else
{
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
)
// 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"
{
c
.
WriteHeader
(
http
.
StatusBadRequest
)
io
.
WriteString
(
c
,
"Unexpected request"
)
w
.
WriteHeader
(
http
.
StatusBadRequest
)
io
.
WriteString
(
w
,
"Unexpected request"
)
return
}
if
req
.
Header
[
"Upgrade"
]
!=
"WebSocket"
{
c
.
WriteHeader
(
http
.
StatusBadRequest
)
io
.
WriteString
(
c
,
"missing Upgrade: WebSocket header"
)
w
.
WriteHeader
(
http
.
StatusBadRequest
)
io
.
WriteString
(
w
,
"missing Upgrade: WebSocket header"
)
return
}
if
req
.
Header
[
"Connection"
]
!=
"Upgrade"
{
c
.
WriteHeader
(
http
.
StatusBadRequest
)
io
.
WriteString
(
c
,
"missing Connection: Upgrade header"
)
w
.
WriteHeader
(
http
.
StatusBadRequest
)
io
.
WriteString
(
w
,
"missing Connection: Upgrade header"
)
return
}
origin
,
found
:=
req
.
Header
[
"Origin"
]
if
!
found
{
c
.
WriteHeader
(
http
.
StatusBadRequest
)
io
.
WriteString
(
c
,
"missing Origin header"
)
w
.
WriteHeader
(
http
.
StatusBadRequest
)
io
.
WriteString
(
w
,
"missing Origin header"
)
return
}
rwc
,
buf
,
err
:=
c
.
Hijack
()
rwc
,
buf
,
err
:=
w
.
Hijack
()
if
err
!=
nil
{
panic
(
"Hijack failed: "
+
err
.
String
())
return
...
...
@@ -192,7 +192,7 @@ func (f Draft75Handler) ServeHTTP(c *http.Conn, req *http.Request) {
defer
rwc
.
Close
()
var
location
string
if
c
.
UsingTLS
()
{
if
w
.
UsingTLS
()
{
location
=
"wss://"
+
req
.
Host
+
req
.
URL
.
RawPath
}
else
{
location
=
"ws://"
+
req
.
Host
+
req
.
URL
.
RawPath
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment