diff options
| author | Rob Pike <r@golang.org> | 2009-06-09 09:53:44 -0700 |
|---|---|---|
| committer | Rob Pike <r@golang.org> | 2009-06-09 09:53:44 -0700 |
| commit | d90e7cbac65c5792ce312ee82fbe03a5dfc98c6f (patch) | |
| tree | 7032a11d0cac2ae4d3e90f7a189b575b5a50f848 /src/lib/http/server.go | |
| parent | bf5c0c957c3c3ea9add6cfd51b90c463cb4814b5 (diff) | |
| download | go-d90e7cbac65c5792ce312ee82fbe03a5dfc98c6f.tar.xz | |
mv src/lib to src/pkg
tests: all.bash passes, gobuild still works, godoc still works.
R=rsc
OCL=30096
CL=30102
Diffstat (limited to 'src/lib/http/server.go')
| -rw-r--r-- | src/lib/http/server.go | 575 |
1 files changed, 0 insertions, 575 deletions
diff --git a/src/lib/http/server.go b/src/lib/http/server.go deleted file mode 100644 index c1de5de789..0000000000 --- a/src/lib/http/server.go +++ /dev/null @@ -1,575 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// HTTP server. See RFC 2616. - -// TODO(rsc): -// logging -// cgi support -// post support - -package http - -import ( - "bufio"; - "fmt"; - "http"; - "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"); -) - -type Conn struct - -// Objects implemeting the Handler interface can be -// registered to serve a particular path or subtree -// in the HTTP server. -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 - 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 io.ReadWriteCloser, raddr string, handler Handler) (c *Conn, err os.Error) { - c = new(Conn); - c.RemoteAddr = raddr; - c.handler = handler; - c.rwc = rwc; - br := bufio.NewReader(rwc); - bw := bufio.NewWriter(rwc); - c.buf = bufio.NewReadWriter(br, bw); - return c, nil -} - -func (c *Conn) SetHeader(hdr, val string) - -// Read next request from connection. -func (c *Conn) readRequest() (req *Request, err os.Error) { - if c.hijacked { - return nil, ErrHijacked - } - 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.Req = req; - - // Default output is HTML encoded in 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"); - } else { - // HTTP version < 1.1: cannot do chunked transfer - // encoding, so signal EOF by closing connection. - // Could avoid closing the connection if there is - // a Content-Length: header in the response, - // but everyone who expects persistent connections - // does HTTP/1.1 now. - c.closeAfterReply = true; - c.chunking = false; - } - - return req, nil -} - -// 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"); - return - } - if c.wroteHeader { - log.Stderr("http: multiple Conn.WriteHeader calls"); - return - } - c.wroteHeader = true; - c.status = code; - c.written = 0; - if !c.Req.ProtoAtLeast(1, 0) { - return - } - proto := "HTTP/1.0"; - if c.Req.ProtoAtLeast(1, 1) { - proto = "HTTP/1.1"; - } - codestring := strconv.Itoa(code); - text, ok := statusText[code]; - 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(c.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"); - return 0, ErrHijacked - } - if !c.wroteHeader { - c.WriteHeader(StatusOK); - } - if len(data) == 0 { - return 0, nil - } - - 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 - } - n, err = c.buf.Write(data); - if err == nil && c.chunking { - if n != len(data) { - err = io.ErrShortWrite; - } - if err == nil { - io.WriteString(c.buf, "\r\n"); - } - } - - return n, err; -} - -// If this is an error reply (4xx or 5xx) -// and the handler wrote some data explaining the error, -// some browsers (i.e., Chrome, Internet Explorer) -// will show their own error instead unless the error is -// 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) { - const min = 1024; - - // Is this an error? - if kind := c.status/100; kind != 4 && kind != 5 { - return; - } - - // Did the handler supply any info? Enough? - if c.written == 0 || c.written >= min { - return; - } - - // Is it text? ("Content-Type" is always in the map) - if s := c.header["Content-Type"]; len(s) < 5 || s[0:5] != "text/" { - return; - } - - // Is it a broken browser? - var msg string; - switch agent := req.UserAgent; { - case strings.Index(agent, "MSIE") >= 0: - msg = "Internet Explorer"; - case strings.Index(agent, "Chrome/") >= 0: - msg = "Chrome"; - default: - return; - } - msg += " would ignore this error page if this text weren't here.\n"; - io.WriteString(c, "\n"); - for c.written < min { - io.WriteString(c, msg); - } -} - -func (c *Conn) flush() { - if !c.wroteHeader { - c.WriteHeader(StatusOK); - } - errorKludge(c, c.Req); - if c.chunking { - io.WriteString(c.buf, "0\r\n"); - // trailer key/value pairs, followed by blank line - io.WriteString(c.buf, "\r\n"); - } - c.buf.Flush(); -} - -// Close the connection. -func (c *Conn) close() { - if c.buf != nil { - c.buf.Flush(); - c.buf = nil; - } - if c.rwc != nil { - c.rwc.Close(); - c.rwc = nil; - } -} - -// Serve a new connection. -func (c *Conn) serve() { - for { - 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); - if c.hijacked { - return; - } - c.flush(); - if c.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 { - return nil, nil, ErrHijacked; - } - 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 -// 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) - -// ServeHTTP calls f(c, req). -func (f HandlerFunc) ServeHTTP(c *Conn, req *Request) { - f(c, req); -} - -// Helper handlers - -// 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"); -} - -// NotFoundHandler returns a simple request handler -// that replies to each request with a ``404 page not found'' reply. -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"; - if c.Req.Method == "POST" { - note = ""; - } - - u, err := ParseURL(url); - if err != nil { - goto finish - } - - // If url was relative, make absolute by - // combining with request path. - // The browser would probably do this for us, - // but doing it ourselves is more reliable. - - // NOTE(rsc): RFC 2616 says that the Location - // line must be an absolute URI, like - // "http://www.google.com/redirect/", - // not a path like "/redirect/". - // Unfortunately, we don't know what to - // put in the host name section to get the - // client to connect to us again, so we can't - // know the right absolute URI to send back. - // 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 = "/" - } - if u.Scheme == "" { - // no leading http://server - if url == "" || url[0] != '/' { - // make relative path absolute - olddir, oldfile := path.Split(oldpath); - url = olddir + url; - } - - // clean up but preserve trailing slash - 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); -} - -// Redirect to a fixed URL -type redirectHandler struct { - url string; - code int; -} -func (rh *redirectHandler) ServeHTTP(c *Conn, req *Request) { - Redirect(c, rh.url, rh.code); -} - -// RedirectHandler returns a request handler that redirects -// each request it receives to the given url using the given -// status code. -func RedirectHandler(url string, code int) Handler { - return &redirectHandler{ url, code } -} - -// ServeMux is an HTTP request multiplexer. -// It matches the URL of each incoming request against a list of registered -// patterns and calls the handler for the pattern that -// most closely matches the URL. -// -// Patterns named fixed paths, like "/favicon.ico", -// or subtrees, like "/images/" (note the trailing slash). -// Patterns must begin with /. -// Longer patterns take precedence over shorter ones, so that -// if there are handlers registered for both "/images/" -// and "/images/thumbnails/", the latter handler will be -// called for paths beginning "/images/thumbnails/" and the -// former will receiver requests for any other paths in the -// "/images/" subtree. -// -// In the future, the pattern syntax may be relaxed to allow -// an optional host-name at the beginning of the pattern, -// so that a handler might register for the two patterns -// "/codesearch" and "codesearch.google.com/" -// without taking over requests for http://www.google.com/. -// -// ServeMux also takes care of sanitizing the URL request path, -// redirecting any request containing . or .. elements to an -// equivalent .- and ..-free URL. -type ServeMux struct { - m map[string] Handler -} - -// NewServeMux allocates and returns a new ServeMux. -func NewServeMux() *ServeMux { - return &ServeMux{make(map[string] Handler)}; -} - -// DefaultServeMux is the default ServeMux used by Serve. -var DefaultServeMux = NewServeMux(); - -// Does path match pattern? -func pathMatch(pattern, path string) bool { - if len(pattern) == 0 { - // should not happen - return false - } - n := len(pattern); - if pattern[n-1] != '/' { - return pattern == path - } - return len(path) >= n && path[0:n] == pattern; -} - -// Return the canonical path for p, eliminating . and .. elements. -func cleanPath(p string) string { - if p == "" { - return "/"; - } - if p[0] != '/' { - p = "/" + 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; -} - -// ServeHTTP dispatches the request to the handler whose -// pattern most closely matches the request URL. -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; - } - - // Most-specific (longest) pattern wins. - 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; - } - } - if h == nil { - h = NotFoundHandler(); - } - h.ServeHTTP(c, req); -} - -// Handle registers the handler for the given pattern. -func (mux *ServeMux) Handle(pattern string, handler Handler) { - if pattern == "" || pattern[0] != '/' { - panicln("http: invalid pattern", pattern); - } - - mux.m[pattern] = handler; - - // Helpful behavior: - // If pattern is /tree/, insert permanent redirect for /tree. - n := len(pattern); - if n > 0 && pattern[n-1] == '/' { - mux.m[pattern[0:n-1]] = RedirectHandler(pattern, StatusMovedPermanently); - } -} - -// Handle registers the handler for the given pattern -// in the DefaultServeMux. -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 -// read requests and then call handler to reply to them. -// Handler is typically nil, in which case the DefaultServeMux is used. -func Serve(l net.Listener, handler Handler) os.Error { - if handler == nil { - handler = DefaultServeMux; - } - for { - rw, raddr, e := l.Accept(); - if e != nil { - return e - } - c, err := newConn(rw, raddr, handler); - if err != nil { - continue; - } - go c.serve(); - } - panic("not reached") -} - -// ListenAndServe listens on the TCP network address addr -// and then calls Serve with handler to handle requests -// on incoming connections. Handler is typically nil, -// in which case the DefaultServeMux is used. -// -// A trivial example server is: -// -// package main -// -// import ( -// "http"; -// "io"; -// ) -// -// // hello world, the web server -// func HelloServer(c *http.Conn, req *http.Request) { -// io.WriteString(c, "hello, world!\n"); -// } -// -// func main() { -// http.Handle("/hello", http.HandlerFunc(HelloServer)); -// err := http.ListenAndServe(":12345", nil); -// if err != nil { -// panic("ListenAndServe: ", err.String()) -// } -// } -func ListenAndServe(addr string, handler Handler) os.Error { - l, e := net.Listen("tcp", addr); - if e != nil { - return e - } - e = Serve(l, handler); - l.Close(); - return e -} - |
