summaryrefslogtreecommitdiff
path: root/src/pkg/http/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/http/server.go')
-rw-r--r--src/pkg/http/server.go254
1 files changed, 127 insertions, 127 deletions
diff --git a/src/pkg/http/server.go b/src/pkg/http/server.go
index 9178d5438..f9771f9b4 100644
--- a/src/pkg/http/server.go
+++ b/src/pkg/http/server.go
@@ -12,61 +12,61 @@
package http
import (
- "bufio";
- "fmt";
- "io";
- "log";
- "net";
- "os";
- "path";
- "strconv";
- "strings";
+ "bufio"
+ "fmt"
+ "io"
+ "log"
+ "net"
+ "os"
+ "path"
+ "strconv"
+ "strings"
)
// Errors introduced by the HTTP server.
var (
- ErrWriteAfterFlush = os.NewError("Conn.Write called after Flush");
- ErrHijacked = os.NewError("Conn has been hijacked");
+ ErrWriteAfterFlush = os.NewError("Conn.Write called after Flush")
+ ErrHijacked = os.NewError("Conn has been hijacked")
)
// Objects implementing the Handler interface can be
// registered to serve a particular path or subtree
// in the HTTP server.
type Handler interface {
- ServeHTTP(*Conn, *Request);
+ 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
+ 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
+ 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
- closeAfterReply bool; // close connection after this reply
- chunking bool; // using chunked transfer encoding for reply body
- wroteHeader bool; // reply header has been written
- header map[string]string; // reply header parameters
- written int64; // number of bytes written in body
- status int; // status code passed to WriteHeader
+ closeAfterReply bool // close connection after this reply
+ chunking bool // using chunked transfer encoding for reply body
+ wroteHeader bool // reply header has been written
+ header map[string]string // reply header parameters
+ written int64 // number of bytes written in body
+ status int // status code passed to WriteHeader
}
// Create new connection from rwc.
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 = a.String()
}
- c.handler = handler;
- c.rwc = rwc;
- br := bufio.NewReader(rwc);
- bw := bufio.NewWriter(rwc);
- c.buf = bufio.NewReadWriter(br, bw);
- return c, nil;
+ c.handler = handler
+ c.rwc = rwc
+ br := bufio.NewReader(rwc)
+ bw := bufio.NewWriter(rwc)
+ c.buf = bufio.NewReadWriter(br, bw)
+ return c, nil
}
// Read next request from connection.
@@ -79,18 +79,18 @@ func (c *Conn) readRequest() (req *Request, err os.Error) {
}
// Reset per-request connection state.
- c.header = make(map[string]string);
- c.wroteHeader = false;
- c.Req = req;
+ c.header = make(map[string]string)
+ c.wroteHeader = false
+ c.Req = req
// Default output is HTML encoded in UTF-8.
- c.SetHeader("Content-Type", "text/html; charset=utf-8");
+ c.SetHeader("Content-Type", "text/html; charset=utf-8")
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");
+ c.chunking = true
+ c.SetHeader("Transfer-Encoding", "chunked")
} else {
// HTTP version < 1.1: cannot do chunked transfer
// encoding, so signal EOF by closing connection.
@@ -98,11 +98,11 @@ func (c *Conn) readRequest() (req *Request, err os.Error) {
// a Content-Length: header in the response,
// but everyone who expects persistent connections
// does HTTP/1.1 now.
- c.closeAfterReply = true;
- c.chunking = false;
+ c.closeAfterReply = true
+ c.chunking = false
}
- return req, nil;
+ return req, nil
}
// SetHeader sets a header line in the eventual reply.
@@ -115,7 +115,7 @@ func (c *Conn) readRequest() (req *Request, err os.Error) {
// 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 }
+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
@@ -124,33 +124,33 @@ func (c *Conn) SetHeader(hdr, val string) { c.header[CanonicalHeaderKey(hdr)] =
// send error codes.
func (c *Conn) WriteHeader(code int) {
if c.hijacked {
- log.Stderr("http: Conn.WriteHeader on hijacked connection");
- return;
+ log.Stderr("http: Conn.WriteHeader on hijacked connection")
+ return
}
if c.wroteHeader {
- log.Stderr("http: multiple Conn.WriteHeader calls");
- return;
+ log.Stderr("http: multiple Conn.WriteHeader calls")
+ return
}
- c.wroteHeader = true;
- c.status = code;
- c.written = 0;
+ c.wroteHeader = true
+ c.status = code
+ c.written = 0
if !c.Req.ProtoAtLeast(1, 0) {
return
}
- proto := "HTTP/1.0";
+ proto := "HTTP/1.0"
if c.Req.ProtoAtLeast(1, 1) {
proto = "HTTP/1.1"
}
- codestring := strconv.Itoa(code);
- text, ok := statusText[code];
+ codestring := strconv.Itoa(code)
+ text, ok := statusText[code]
if !ok {
text = "status code " + codestring
}
- io.WriteString(c.buf, proto+" "+codestring+" "+text+"\r\n");
+ 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(c.buf, "\r\n");
+ io.WriteString(c.buf, "\r\n")
}
// Write writes the data to the connection as part of an HTTP reply.
@@ -158,8 +158,8 @@ func (c *Conn) WriteHeader(code int) {
// 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");
- return 0, ErrHijacked;
+ log.Stderr("http: Conn.Write on hijacked connection")
+ return 0, ErrHijacked
}
if !c.wroteHeader {
c.WriteHeader(StatusOK)
@@ -168,15 +168,15 @@ func (c *Conn) Write(data []byte) (n int, err os.Error) {
return 0, nil
}
- c.written += int64(len(data)); // ignoring errors, for errorKludge
+ c.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
+ fmt.Fprintf(c.buf, "%x\r\n", len(data)) // TODO(rsc): use strconv not fmt
}
- n, err = c.buf.Write(data);
+ n, err = c.buf.Write(data)
if err == nil && c.chunking {
if n != len(data) {
err = io.ErrShortWrite
@@ -186,7 +186,7 @@ func (c *Conn) Write(data []byte) (n int, err os.Error) {
}
}
- return n, err;
+ return n, err
}
// If this is an error reply (4xx or 5xx)
@@ -197,7 +197,7 @@ func (c *Conn) Write(data []byte) (n int, err os.Error) {
// browsers are in the 256-512 range.
// Pad to 1024 bytes.
func errorKludge(c *Conn, req *Request) {
- const min = 1024;
+ const min = 1024
// Is this an error?
if kind := c.status / 100; kind != 4 && kind != 5 {
@@ -210,7 +210,7 @@ func errorKludge(c *Conn, req *Request) {
}
// Is it a broken browser?
- var msg string;
+ var msg string
switch agent := req.UserAgent; {
case strings.Index(agent, "MSIE") >= 0:
msg = "Internet Explorer"
@@ -219,19 +219,19 @@ func errorKludge(c *Conn, req *Request) {
default:
return
}
- 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)
- baseType := strings.Split(c.header["Content-Type"], ";", 2)[0];
+ baseType := strings.Split(c.header["Content-Type"], ";", 2)[0]
switch baseType {
case "text/html":
- io.WriteString(c, "<!-- ");
+ io.WriteString(c, "<!-- ")
for c.written < min {
io.WriteString(c, msg)
}
- io.WriteString(c, " -->");
+ io.WriteString(c, " -->")
case "text/plain":
- io.WriteString(c, "\n");
+ io.WriteString(c, "\n")
for c.written < min {
io.WriteString(c, msg)
}
@@ -242,13 +242,13 @@ func (c *Conn) finishRequest() {
if !c.wroteHeader {
c.WriteHeader(StatusOK)
}
- errorKludge(c, c.Req);
+ errorKludge(c, c.Req)
if c.chunking {
- io.WriteString(c.buf, "0\r\n");
+ io.WriteString(c.buf, "0\r\n")
// trailer key/value pairs, followed by blank line
- io.WriteString(c.buf, "\r\n");
+ io.WriteString(c.buf, "\r\n")
}
- c.buf.Flush();
+ c.buf.Flush()
}
// Flush sends any buffered data to the client.
@@ -256,41 +256,41 @@ func (c *Conn) Flush() {
if !c.wroteHeader {
c.WriteHeader(StatusOK)
}
- c.buf.Flush();
+ c.buf.Flush()
}
// Close the connection.
func (c *Conn) close() {
if c.buf != nil {
- c.buf.Flush();
- c.buf = nil;
+ c.buf.Flush()
+ c.buf = nil
}
if c.rwc != nil {
- c.rwc.Close();
- c.rwc = nil;
+ c.rwc.Close()
+ c.rwc = nil
}
}
// Serve a new connection.
func (c *Conn) serve() {
for {
- req, err := c.readRequest();
+ req, err := c.readRequest()
if err != nil {
break
}
// HTTP cannot have multiple simultaneous active requests.
// Until the server replies to this request, it can't read another,
// so we might as well run the handler in this goroutine.
- c.handler.ServeHTTP(c, req);
+ c.handler.ServeHTTP(c, req)
if c.hijacked {
return
}
- c.finishRequest();
+ c.finishRequest()
if c.closeAfterReply {
break
}
}
- c.close();
+ c.close()
}
// Hijack lets the caller take over the connection.
@@ -302,12 +302,12 @@ func (c *Conn) Hijack() (rwc io.ReadWriteCloser, buf *bufio.ReadWriter, err os.E
if c.hijacked {
return nil, nil, ErrHijacked
}
- c.hijacked = true;
- rwc = c.rwc;
- buf = c.buf;
- c.rwc = nil;
- c.buf = nil;
- return;
+ c.hijacked = true
+ rwc = c.rwc
+ buf = c.buf
+ c.rwc = nil
+ c.buf = nil
+ return
}
// The HandlerFunc type is an adapter to allow the use of
@@ -325,26 +325,26 @@ func (f HandlerFunc) ServeHTTP(c *Conn, req *Request) {
// NotFound replies to the request with an HTTP 404 not found error.
func NotFound(c *Conn, req *Request) {
- c.SetHeader("Content-Type", "text/plain; charset=utf-8");
- c.WriteHeader(StatusNotFound);
- io.WriteString(c, "404 page not found\n");
+ c.SetHeader("Content-Type", "text/plain; charset=utf-8")
+ c.WriteHeader(StatusNotFound)
+ io.WriteString(c, "404 page not found\n")
}
// NotFoundHandler returns a simple request handler
// that replies to each request with a ``404 page not found'' reply.
-func NotFoundHandler() Handler { return HandlerFunc(NotFound) }
+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) {
// 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";
+ note := "<a href=\"%v\">" + statusText[code] + "</a>.\n"
if c.Req.Method == "POST" {
note = ""
}
- u, err := ParseURL(url);
+ u, err := ParseURL(url)
if err != nil {
goto finish
}
@@ -365,36 +365,36 @@ 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;
- if oldpath == "" { // should not happen, but avoid a crash if it does
+ oldpath := c.Req.URL.Path
+ if oldpath == "" { // should not happen, but avoid a crash if it does
oldpath = "/"
}
if u.Scheme == "" {
// no leading http://server
if url == "" || url[0] != '/' {
// make relative path absolute
- olddir, _ := path.Split(oldpath);
- url = olddir + url;
+ olddir, _ := path.Split(oldpath)
+ url = olddir + url
}
// clean up but preserve trailing slash
- trailing := url[len(url)-1] == '/';
- url = path.Clean(url);
+ trailing := url[len(url)-1] == '/'
+ url = path.Clean(url)
if trailing && url[len(url)-1] != '/' {
url += "/"
}
}
finish:
- c.SetHeader("Location", url);
- c.WriteHeader(code);
- fmt.Fprintf(c, note, url);
+ c.SetHeader("Location", url)
+ c.WriteHeader(code)
+ fmt.Fprintf(c, note, url)
}
// Redirect to a fixed URL
type redirectHandler struct {
- url string;
- code int;
+ url string
+ code int
}
func (rh *redirectHandler) ServeHTTP(c *Conn, req *Request) {
@@ -433,11 +433,11 @@ func RedirectHandler(url string, code int) Handler {
// redirecting any request containing . or .. elements to an
// equivalent .- and ..-free URL.
type ServeMux struct {
- m map[string]Handler;
+ m map[string]Handler
}
// NewServeMux allocates and returns a new ServeMux.
-func NewServeMux() *ServeMux { return &ServeMux{make(map[string]Handler)} }
+func NewServeMux() *ServeMux { return &ServeMux{make(map[string]Handler)} }
// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = NewServeMux()
@@ -448,11 +448,11 @@ func pathMatch(pattern, path string) bool {
// should not happen
return false
}
- n := len(pattern);
+ n := len(pattern)
if pattern[n-1] != '/' {
return pattern == path
}
- return len(path) >= n && path[0:n] == pattern;
+ return len(path) >= n && path[0:n] == pattern
}
// Return the canonical path for p, eliminating . and .. elements.
@@ -463,13 +463,13 @@ func cleanPath(p string) string {
if p[0] != '/' {
p = "/" + p
}
- np := path.Clean(p);
+ np := path.Clean(p)
// path.Clean removes trailing slash except for root;
// put the trailing slash back if necessary.
if p[len(p)-1] == '/' && np != "/" {
np += "/"
}
- return np;
+ return np
}
// ServeHTTP dispatches the request to the handler whose
@@ -477,27 +477,27 @@ func cleanPath(p string) string {
func (mux *ServeMux) ServeHTTP(c *Conn, req *Request) {
// Clean path to canonical form and redirect.
if p := cleanPath(req.URL.Path); p != req.URL.Path {
- c.SetHeader("Location", p);
- c.WriteHeader(StatusMovedPermanently);
- return;
+ c.SetHeader("Location", p)
+ c.WriteHeader(StatusMovedPermanently)
+ return
}
// Most-specific (longest) pattern wins.
- var h Handler;
- var n = 0;
+ var h Handler
+ var n = 0
for k, v := range mux.m {
if !pathMatch(k, req.URL.Path) {
continue
}
if h == nil || len(k) > n {
- n = len(k);
- h = v;
+ n = len(k)
+ h = v
}
}
if h == nil {
h = NotFoundHandler()
}
- h.ServeHTTP(c, req);
+ h.ServeHTTP(c, req)
}
// Handle registers the handler for the given pattern.
@@ -506,11 +506,11 @@ func (mux *ServeMux) Handle(pattern string, handler Handler) {
panicln("http: invalid pattern", pattern)
}
- mux.m[pattern] = handler;
+ mux.m[pattern] = handler
// Helpful behavior:
// If pattern is /tree/, insert permanent redirect for /tree.
- n := len(pattern);
+ n := len(pattern)
if n > 0 && pattern[n-1] == '/' {
mux.m[pattern[0:n-1]] = RedirectHandler(pattern, StatusMovedPermanently)
}
@@ -518,7 +518,7 @@ func (mux *ServeMux) Handle(pattern string, handler Handler) {
// Handle registers the handler for the given pattern
// in the DefaultServeMux.
-func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
+func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
// Serve accepts incoming HTTP connections on the listener l,
// creating a new service thread for each. The service threads
@@ -529,17 +529,17 @@ func Serve(l net.Listener, handler Handler) os.Error {
handler = DefaultServeMux
}
for {
- rw, e := l.Accept();
+ rw, e := l.Accept()
if e != nil {
return e
}
- c, err := newConn(rw, handler);
+ c, err := newConn(rw, handler)
if err != nil {
continue
}
- go c.serve();
+ go c.serve()
}
- panic("not reached");
+ panic("not reached")
}
// ListenAndServe listens on the TCP network address addr
@@ -569,11 +569,11 @@ func Serve(l net.Listener, handler Handler) os.Error {
// }
// }
func ListenAndServe(addr string, handler Handler) os.Error {
- l, e := net.Listen("tcp", addr);
+ l, e := net.Listen("tcp", addr)
if e != nil {
return e
}
- e = Serve(l, handler);
- l.Close();
- return e;
+ e = Serve(l, handler)
+ l.Close()
+ return e
}