From 865d5767022326e097482ec211ab24b9418afda7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20V=C3=A1zquez=20Gonz=C3=A1lez?= Date: Tue, 1 Feb 2011 13:58:59 -0500 Subject: [PATCH] http: add host patterns R=bradfitzgo, rsc CC=golang-dev https://golang.org/cl/4070043 --- src/pkg/http/serve_test.go | 65 ++++++++++++++++++++++++++++++++++++++ src/pkg/http/server.go | 50 +++++++++++++++++------------ 2 files changed, 94 insertions(+), 21 deletions(-) diff --git a/src/pkg/http/serve_test.go b/src/pkg/http/serve_test.go index 053d6dca448..7da3fc6f343 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 644724f58e6..9eb70a4c750 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,6 +597,23 @@ func cleanPath(p string) string { return np } +// 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, path) { + continue + } + if h == nil || len(k) > n { + n = len(k) + 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) { @@ -607,18 +623,10 @@ func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { w.WriteHeader(StatusMovedPermanently) return } - - // Most-specific (longest) pattern wins. - var h Handler - var n = 0 - for k, v := range mux.m { - if !pathMatch(k, r.URL.Path) { - continue - } - if h == nil || len(k) > n { - n = len(k) - h = v - } + // 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) }