aboutsummaryrefslogtreecommitdiff
path: root/src/lib/http/server.go
diff options
context:
space:
mode:
authorRob Pike <r@golang.org>2009-06-09 09:53:44 -0700
committerRob Pike <r@golang.org>2009-06-09 09:53:44 -0700
commitd90e7cbac65c5792ce312ee82fbe03a5dfc98c6f (patch)
tree7032a11d0cac2ae4d3e90f7a189b575b5a50f848 /src/lib/http/server.go
parentbf5c0c957c3c3ea9add6cfd51b90c463cb4814b5 (diff)
downloadgo-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.go575
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
-}
-