aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJose Luis Vázquez González <josvazg@gmail.com>2011-02-01 13:58:59 -0500
committerRuss Cox <rsc@golang.org>2011-02-01 13:58:59 -0500
commit865d5767022326e097482ec211ab24b9418afda7 (patch)
tree59b6f48ae38f66a8d2204ba9c9a3a2daab42890b
parent9557103ea6fc41d5dae125a917295288f98f257b (diff)
downloadgo-865d5767022326e097482ec211ab24b9418afda7.tar.gz
go-865d5767022326e097482ec211ab24b9418afda7.zip
http: add host patterns
R=bradfitzgo, rsc CC=golang-dev https://golang.org/cl/4070043
-rw-r--r--src/pkg/http/serve_test.go65
-rw-r--r--src/pkg/http/server.go50
2 files changed, 94 insertions, 21 deletions
diff --git a/src/pkg/http/serve_test.go b/src/pkg/http/serve_test.go
index 053d6dca44..7da3fc6f34 100644
--- a/src/pkg/http/serve_test.go
+++ b/src/pkg/http/serve_test.go
@@ -136,6 +136,71 @@ func TestConsumingBodyOnNextConn(t *testing.T) {
}
}
+type stringHandler string
+
+func (s stringHandler) ServeHTTP(w ResponseWriter, r *Request) {
+ w.SetHeader("Result", string(s))
+}
+
+var handlers = []struct {
+ pattern string
+ msg string
+}{
+ {"/", "Default"},
+ {"/someDir/", "someDir"},
+ {"someHost.com/someDir/", "someHost.com/someDir"},
+}
+
+var vtests = []struct {
+ url string
+ expected string
+}{
+ {"http://localhost/someDir/apage", "someDir"},
+ {"http://localhost/otherDir/apage", "Default"},
+ {"http://someHost.com/someDir/apage", "someHost.com/someDir"},
+ {"http://otherHost.com/someDir/apage", "someDir"},
+ {"http://otherHost.com/aDir/apage", "Default"},
+}
+
+func TestHostHandlers(t *testing.T) {
+ for _, h := range handlers {
+ Handle(h.pattern, stringHandler(h.msg))
+ }
+ l, err := net.Listen("tcp", "127.0.0.1:0") // any port
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer l.Close()
+ go Serve(l, nil)
+ conn, err := net.Dial("tcp", "", l.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer conn.Close()
+ cc := NewClientConn(conn, nil)
+ for _, vt := range vtests {
+ var r *Response
+ var req Request
+ if req.URL, err = ParseURL(vt.url); err != nil {
+ t.Errorf("cannot parse url: %v", err)
+ continue
+ }
+ if err := cc.Write(&req); err != nil {
+ t.Errorf("writing request: %v", err)
+ continue
+ }
+ r, err := cc.Read()
+ if err != nil {
+ t.Errorf("reading response: %v", err)
+ continue
+ }
+ s := r.Header["Result"]
+ if s != vt.expected {
+ t.Errorf("Get(%q) = %q, want %q", vt.url, s, vt.expected)
+ }
+ }
+}
+
type responseWriterMethodCall struct {
method string
headerKey, headerValue string // if method == "SetHeader"
diff --git a/src/pkg/http/server.go b/src/pkg/http/server.go
index 644724f58e..9eb70a4c75 100644
--- a/src/pkg/http/server.go
+++ b/src/pkg/http/server.go
@@ -539,9 +539,8 @@ func RedirectHandler(url string, code int) Handler {
// 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 /.
+// Patterns named fixed, rooted paths, like "/favicon.ico",
+// or rooted subtrees, like "/images/" (note the trailing slash).
// 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
@@ -549,11 +548,11 @@ func RedirectHandler(url string, code int) Handler {
// 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/.
+// Patterns may optionally begin with a host name, restricting matches to
+// URLs on that host only. Host-specific patterns take precedence over
+// general patterns, so that a handler might register for the two patterns
+// "/codesearch" and "codesearch.google.com/" without also 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
@@ -598,21 +597,13 @@ func cleanPath(p string) string {
return np
}
-// ServeHTTP dispatches the request to the handler whose
-// pattern most closely matches the request URL.
-func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
- // Clean path to canonical form and redirect.
- if p := cleanPath(r.URL.Path); p != r.URL.Path {
- w.SetHeader("Location", p)
- w.WriteHeader(StatusMovedPermanently)
- return
- }
-
- // Most-specific (longest) pattern wins.
+// Find a handler on a handler map given a path string
+// Most-specific (longest) pattern wins
+func (mux *ServeMux) match(path string) Handler {
var h Handler
var n = 0
for k, v := range mux.m {
- if !pathMatch(k, r.URL.Path) {
+ if !pathMatch(k, path) {
continue
}
if h == nil || len(k) > n {
@@ -620,6 +611,23 @@ func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
h = v
}
}
+ return h
+}
+
+// ServeHTTP dispatches the request to the handler whose
+// pattern most closely matches the request URL.
+func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
+ // Clean path to canonical form and redirect.
+ if p := cleanPath(r.URL.Path); p != r.URL.Path {
+ w.SetHeader("Location", p)
+ w.WriteHeader(StatusMovedPermanently)
+ return
+ }
+ // Host-specific pattern takes precedence over generic ones
+ h := mux.match(r.Host + r.URL.Path)
+ if h == nil {
+ h = mux.match(r.URL.Path)
+ }
if h == nil {
h = NotFoundHandler()
}
@@ -628,7 +636,7 @@ func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
// Handle registers the handler for the given pattern.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
- if pattern == "" || pattern[0] != '/' {
+ if pattern == "" {
panic("http: invalid pattern " + pattern)
}