diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 319e87d1..1d01fe02 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -53,10 +53,6 @@ "ImportPath": "github.com/inconshreveable/mousetrap", "Rev": "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" }, - { - "ImportPath": "github.com/julienschmidt/httprouter", - "Rev": "b428fda53bb0a764fea9c76c9413512eda291dec" - }, { "ImportPath": "github.com/kr/pretty", "Comment": "go.weekly.2011-12-22-27-ge6ac2fc", diff --git a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/.travis.yml b/Godeps/_workspace/src/github.com/julienschmidt/httprouter/.travis.yml deleted file mode 100644 index a4d6cc53..00000000 --- a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -sudo: false -language: go -go: - - 1.1 - - 1.2 - - 1.3 - - 1.4 - - tip diff --git a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/LICENSE b/Godeps/_workspace/src/github.com/julienschmidt/httprouter/LICENSE deleted file mode 100644 index b829abc8..00000000 --- a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2013 Julien Schmidt. All rights reserved. - - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The names of the contributors may not be used to endorse or promote - products derived from this software without specific prior written - permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL JULIEN SCHMIDT BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/README.md b/Godeps/_workspace/src/github.com/julienschmidt/httprouter/README.md deleted file mode 100644 index 6d74842f..00000000 --- a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/README.md +++ /dev/null @@ -1,319 +0,0 @@ -# HttpRouter [![Build Status](https://travis-ci.org/julienschmidt/httprouter.png?branch=master)](https://travis-ci.org/julienschmidt/httprouter) [![Coverage](http://gocover.io/_badge/github.com/julienschmidt/httprouter?0)](http://gocover.io/github.com/julienschmidt/httprouter) [![GoDoc](http://godoc.org/github.com/julienschmidt/httprouter?status.png)](http://godoc.org/github.com/julienschmidt/httprouter) - -HttpRouter is a lightweight high performance HTTP request router -(also called *multiplexer* or just *mux* for short) for [Go](http://golang.org/). - -In contrast to the [default mux](http://golang.org/pkg/net/http/#ServeMux) of Go's net/http package, this router supports -variables in the routing pattern and matches against the request method. -It also scales better. - -The router is optimized for high performance and a small memory footprint. -It scales well even with very long paths and a large number of routes. -A compressing dynamic trie (radix tree) structure is used for efficient matching. - -## Features -**Only explicit matches:** With other routers, like [http.ServeMux](http://golang.org/pkg/net/http/#ServeMux), -a requested URL path could match multiple patterns. Therefore they have some -awkward pattern priority rules, like *longest match* or *first registered, -first matched*. By design of this router, a request can only match exactly one -or no route. As a result, there are also no unintended matches, which makes it -great for SEO and improves the user experience. - -**Stop caring about trailing slashes:** Choose the URL style you like, the -router automatically redirects the client if a trailing slash is missing or if -there is one extra. Of course it only does so, if the new path has a handler. -If you don't like it, you can [turn off this behavior](http://godoc.org/github.com/julienschmidt/httprouter#Router.RedirectTrailingSlash). - -**Path auto-correction:** Besides detecting the missing or additional trailing -slash at no extra cost, the router can also fix wrong cases and remove -superfluous path elements (like `../` or `//`). -Is [CAPTAIN CAPS LOCK](http://www.urbandictionary.com/define.php?term=Captain+Caps+Lock) one of your users? -HttpRouter can help him by making a case-insensitive look-up and redirecting him -to the correct URL. - -**Parameters in your routing pattern:** Stop parsing the requested URL path, -just give the path segment a name and the router delivers the dynamic value to -you. Because of the design of the router, path parameters are very cheap. - -**Zero Garbage:** The matching and dispatching process generates zero bytes of -garbage. In fact, the only heap allocations that are made, is by building the -slice of the key-value pairs for path parameters. If the request path contains -no parameters, not a single heap allocation is necessary. - -**Best Performance:** [Benchmarks speak for themselves](https://github.com/julienschmidt/go-http-routing-benchmark). -See below for technical details of the implementation. - -**No more server crashes:** You can set a [Panic handler](http://godoc.org/github.com/julienschmidt/httprouter#Router.PanicHandler) to deal with panics -occurring during handling a HTTP request. The router then recovers and lets the -PanicHandler log what happened and deliver a nice error page. - -Of course you can also set **custom [NotFound](http://godoc.org/github.com/julienschmidt/httprouter#Router.NotFound) and [MethodNotAllowed](http://godoc.org/github.com/julienschmidt/httprouter#Router.MethodNotAllowed) handlers** and [**serve static files**](http://godoc.org/github.com/julienschmidt/httprouter#Router.ServeFiles). - -## Usage -This is just a quick introduction, view the [GoDoc](http://godoc.org/github.com/julienschmidt/httprouter) for details. - -Let's start with a trivial example: -```go -package main - -import ( - "fmt" - "github.com/julienschmidt/httprouter" - "net/http" - "log" -) - -func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - fmt.Fprint(w, "Welcome!\n") -} - -func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name")) -} - -func main() { - router := httprouter.New() - router.GET("/", Index) - router.GET("/hello/:name", Hello) - - log.Fatal(http.ListenAndServe(":8080", router)) -} -``` - -### Named parameters -As you can see, `:name` is a *named parameter*. -The values are accessible via `httprouter.Params`, which is just a slice of `httprouter.Param`s. -You can get the value of a parameter either by its index in the slice, or by using the `ByName(name)` method: -`:name` can be retrived by `ByName("name")`. - -Named parameters only match a single path segment: -``` -Pattern: /user/:user - - /user/gordon match - /user/you match - /user/gordon/profile no match - /user/ no match -``` - -**Note:** Since this router has only explicit matches, you can not register static routes and parameters for the same path segment. For example you can not register the patterns `/user/new` and `/user/:user` for the same request method at the same time. The routing of different request methods is independent from each other. - -### Catch-All parameters -The second type are *catch-all* parameters and have the form `*name`. -Like the name suggests, they match everything. -Therefore they must always be at the **end** of the pattern: -``` -Pattern: /src/*filepath - - /src/ match - /src/somefile.go match - /src/subdir/somefile.go match -``` - -## How does it work? -The router relies on a tree structure which makes heavy use of *common prefixes*, -it is basically a *compact* [*prefix tree*](http://en.wikipedia.org/wiki/Trie) -(or just [*Radix tree*](http://en.wikipedia.org/wiki/Radix_tree)). -Nodes with a common prefix also share a common parent. Here is a short example -what the routing tree for the `GET` request method could look like: - -``` -Priority Path Handle -9 \ *<1> -3 ├s nil -2 |├earch\ *<2> -1 |└upport\ *<3> -2 ├blog\ *<4> -1 | └:post nil -1 | └\ *<5> -2 ├about-us\ *<6> -1 | └team\ *<7> -1 └contact\ *<8> -``` -Every `*` represents the memory address of a handler function (a pointer). -If you follow a path trough the tree from the root to the leaf, you get the -complete route path, e.g `\blog\:post\`, where `:post` is just a placeholder -([*parameter*](#named-parameters)) for an actual post name. Unlike hash-maps, a -tree structure also allows us to use dynamic parts like the `:post` parameter, -since we actually match against the routing patterns instead of just comparing -hashes. [As benchmarks show](https://github.com/julienschmidt/go-http-routing-benchmark), -this works very well and efficient. - -Since URL paths have a hierarchical structure and make use only of a limited set -of characters (byte values), it is very likely that there are a lot of common -prefixes. This allows us to easily reduce the routing into ever smaller problems. -Moreover the router manages a separate tree for every request method. -For one thing it is more space efficient than holding a method->handle map in -every single node, for another thing is also allows us to greatly reduce the -routing problem before even starting the look-up in the prefix-tree. - -For even better scalability, the child nodes on each tree level are ordered by -priority, where the priority is just the number of handles registered in sub -nodes (children, grandchildren, and so on..). -This helps in two ways: - -1. Nodes which are part of the most routing paths are evaluated first. This -helps to make as much routes as possible to be reachable as fast as possible. -2. It is some sort of cost compensation. The longest reachable path (highest -cost) can always be evaluated first. The following scheme visualizes the tree -structure. Nodes are evaluated from top to bottom and from left to right. - -``` -├------------ -├--------- -├----- -├---- -├-- -├-- -└- -``` - - -## Why doesn't this work with http.Handler? -**It does!** The router itself implements the http.Handler interface. -Moreover the router provides convenient [adapters for http.Handler](http://godoc.org/github.com/julienschmidt/httprouter#Router.Handler)s and [http.HandlerFunc](http://godoc.org/github.com/julienschmidt/httprouter#Router.HandlerFunc)s -which allows them to be used as a [httprouter.Handle](http://godoc.org/github.com/julienschmidt/httprouter#Router.Handle) when registering a route. -The only disadvantage is, that no parameter values can be retrieved when a -http.Handler or http.HandlerFunc is used, since there is no efficient way to -pass the values with the existing function parameters. -Therefore [httprouter.Handle](http://godoc.org/github.com/julienschmidt/httprouter#Router.Handle) has a third function parameter. - -Just try it out for yourself, the usage of HttpRouter is very straightforward. The package is compact and minimalistic, but also probably one of the easiest routers to set up. - - -## Where can I find Middleware *X*? -This package just provides a very efficient request router with a few extra -features. The router is just a [http.Handler](http://golang.org/pkg/net/http/#Handler), -you can chain any http.Handler compatible middleware before the router, -for example the [Gorilla handlers](http://www.gorillatoolkit.org/pkg/handlers). -Or you could [just write your own](http://justinas.org/writing-http-middleware-in-go/), -it's very easy! - -Alternatively, you could try [a web framework based on HttpRouter](#web-frameworks-based-on-httprouter). - -### Multi-domain / Sub-domains -Here is a quick example: Does your server serve multiple domains / hosts? -You want to use sub-domains? -Define a router per host! -```go -// We need an object that implements the http.Handler interface. -// Therefore we need a type for which we implement the ServeHTTP method. -// We just use a map here, in which we map host names (with port) to http.Handlers -type HostSwitch map[string]http.Handler - -// Implement the ServerHTTP method on our new type -func (hs HostSwitch) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // Check if a http.Handler is registered for the given host. - // If yes, use it to handle the request. - if handler := hs[r.Host]; handler != nil { - handler.ServeHTTP(w, r) - } else { - // Handle host names for wich no handler is registered - http.Error(w, "Forbidden", 403) // Or Redirect? - } -} - -func main() { - // Initialize a router as usual - router := httprouter.New() - router.GET("/", Index) - router.GET("/hello/:name", Hello) - - // Make a new HostSwitch and insert the router (our http handler) - // for example.com and port 12345 - hs := make(HostSwitch) - hs["example.com:12345"] = router - - // Use the HostSwitch to listen and serve on port 12345 - log.Fatal(http.ListenAndServe(":12345", hs)) -} -``` - -### Basic Authentication -Another quick example: Basic Authentification (RFC 2617) for handles: - -```go -package main - -import ( - "bytes" - "encoding/base64" - "fmt" - "github.com/julienschmidt/httprouter" - "net/http" - "log" - "strings" -) - -func BasicAuth(h httprouter.Handle, user, pass []byte) httprouter.Handle { - return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - const basicAuthPrefix string = "Basic " - - // Get the Basic Authentication credentials - auth := r.Header.Get("Authorization") - if strings.HasPrefix(auth, basicAuthPrefix) { - // Check credentials - payload, err := base64.StdEncoding.DecodeString(auth[len(basicAuthPrefix):]) - if err == nil { - pair := bytes.SplitN(payload, []byte(":"), 2) - if len(pair) == 2 && bytes.Equal(pair[0], user) && bytes.Equal(pair[1], pass) { - // Delegate request to the given handle - h(w, r, ps) - return - } - } - } - - // Request Basic Authentication otherwise - w.Header().Set("WWW-Authenticate", "Basic realm=Restricted") - http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) - } -} - -func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - fmt.Fprint(w, "Not protected!\n") -} - -func Protected(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - fmt.Fprint(w, "Protected!\n") -} - -func main() { - user := []byte("gordon") - pass := []byte("secret!") - - router := httprouter.New() - router.GET("/", Index) - router.GET("/protected/", BasicAuth(Protected, user, pass)) - - log.Fatal(http.ListenAndServe(":8080", router)) -} -``` - -## Chaining with the NotFound handler - -**NOTE: It might be required to set [Router.HandleMethodNotAllowed](http://godoc.org/github.com/julienschmidt/httprouter#Router.HandleMethodNotAllowed) to `false` to avoid problems.** - -You can use another [http.HandlerFunc](http://golang.org/pkg/net/http/#HandlerFunc), for example another router, to handle requests which could not be matched by this router by using the [Router.NotFound](http://godoc.org/github.com/julienschmidt/httprouter#Router.NotFound) handler. This allows chaining. - -### Static files -The `NotFound` handler can for example be used to serve static files from the root path `/` (like an index.html file along with other assets): -```go -// Serve static files from the ./public directory -router.NotFound = http.FileServer(http.Dir("public")).ServeHTTP -``` - -But this approach sidesteps the strict core rules of this router to avoid routing problems. A cleaner approach is to use a distinct sub-path for serving files, like `/static/*filepath` or `/files/*filepath`. - -## Web Frameworks based on HttpRouter -If the HttpRouter is a bit too minimalistic for you, you might try one of the following more high-level 3rd-party web frameworks building upon the HttpRouter package: -* [Ace](https://github.com/plimble/ace): Blazing fast Go Web Framework -* [api2go](https://github.com/univedo/api2go): A JSON API Implementation for Go -* [Gin](https://github.com/gin-gonic/gin): Features a martini-like API with much better performance -* [Goat](https://github.com/bahlo/goat): A minimalistic REST API server in Go -* [Hikaru](https://github.com/najeira/hikaru): Supports standalone and Google AppEngine -* [Hitch](https://github.com/nbio/hitch): Hitch ties httprouter, [httpcontext](https://github.com/nbio/httpcontext), and middleware up in a bow -* [kami](https://github.com/guregu/kami): A tiny web framework using x/net/context -* [Medeina](https://github.com/imdario/medeina): Inspired by Ruby's Roda and Cuba -* [Neko](https://github.com/rocwong/neko): A lightweight web application framework for Golang -* [Roxanna](https://github.com/iamthemuffinman/Roxanna): An amalgamation of httprouter, better logging, and hot reload diff --git a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/path.go b/Godeps/_workspace/src/github.com/julienschmidt/httprouter/path.go deleted file mode 100644 index 486134db..00000000 --- a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/path.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2013 Julien Schmidt. All rights reserved. -// Based on the path package, Copyright 2009 The Go Authors. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -package httprouter - -// CleanPath is the URL version of path.Clean, it returns a canonical URL path -// for p, eliminating . and .. elements. -// -// The following rules are applied iteratively until no further processing can -// be done: -// 1. Replace multiple slashes with a single slash. -// 2. Eliminate each . path name element (the current directory). -// 3. Eliminate each inner .. path name element (the parent directory) -// along with the non-.. element that precedes it. -// 4. Eliminate .. elements that begin a rooted path: -// that is, replace "/.." by "/" at the beginning of a path. -// -// If the result of this process is an empty string, "/" is returned -func CleanPath(p string) string { - // Turn empty string into "/" - if p == "" { - return "/" - } - - n := len(p) - var buf []byte - - // Invariants: - // reading from path; r is index of next byte to process. - // writing to buf; w is index of next byte to write. - - // path must start with '/' - r := 1 - w := 1 - - if p[0] != '/' { - r = 0 - buf = make([]byte, n+1) - buf[0] = '/' - } - - trailing := n > 2 && p[n-1] == '/' - - // A bit more clunky without a 'lazybuf' like the path package, but the loop - // gets completely inlined (bufApp). So in contrast to the path package this - // loop has no expensive function calls (except 1x make) - - for r < n { - switch { - case p[r] == '/': - // empty path element, trailing slash is added after the end - r++ - - case p[r] == '.' && r+1 == n: - trailing = true - r++ - - case p[r] == '.' && p[r+1] == '/': - // . element - r++ - - case p[r] == '.' && p[r+1] == '.' && (r+2 == n || p[r+2] == '/'): - // .. element: remove to last / - r += 2 - - if w > 1 { - // can backtrack - w-- - - if buf == nil { - for w > 1 && p[w] != '/' { - w-- - } - } else { - for w > 1 && buf[w] != '/' { - w-- - } - } - } - - default: - // real path element. - // add slash if needed - if w > 1 { - bufApp(&buf, p, w, '/') - w++ - } - - // copy element - for r < n && p[r] != '/' { - bufApp(&buf, p, w, p[r]) - w++ - r++ - } - } - } - - // re-append trailing slash - if trailing && w > 1 { - bufApp(&buf, p, w, '/') - w++ - } - - if buf == nil { - return p[:w] - } - return string(buf[:w]) -} - -// internal helper to lazily create a buffer if necessary -func bufApp(buf *[]byte, s string, w int, c byte) { - if *buf == nil { - if s[w] == c { - return - } - - *buf = make([]byte, len(s)) - copy(*buf, s[:w]) - } - (*buf)[w] = c -} diff --git a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/path_test.go b/Godeps/_workspace/src/github.com/julienschmidt/httprouter/path_test.go deleted file mode 100644 index c4ceda5d..00000000 --- a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/path_test.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2013 Julien Schmidt. All rights reserved. -// Based on the path package, Copyright 2009 The Go Authors. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -package httprouter - -import ( - "runtime" - "testing" -) - -var cleanTests = []struct { - path, result string -}{ - // Already clean - {"/", "/"}, - {"/abc", "/abc"}, - {"/a/b/c", "/a/b/c"}, - {"/abc/", "/abc/"}, - {"/a/b/c/", "/a/b/c/"}, - - // missing root - {"", "/"}, - {"abc", "/abc"}, - {"abc/def", "/abc/def"}, - {"a/b/c", "/a/b/c"}, - - // Remove doubled slash - {"//", "/"}, - {"/abc//", "/abc/"}, - {"/abc/def//", "/abc/def/"}, - {"/a/b/c//", "/a/b/c/"}, - {"/abc//def//ghi", "/abc/def/ghi"}, - {"//abc", "/abc"}, - {"///abc", "/abc"}, - {"//abc//", "/abc/"}, - - // Remove . elements - {".", "/"}, - {"./", "/"}, - {"/abc/./def", "/abc/def"}, - {"/./abc/def", "/abc/def"}, - {"/abc/.", "/abc/"}, - - // Remove .. elements - {"..", "/"}, - {"../", "/"}, - {"../../", "/"}, - {"../..", "/"}, - {"../../abc", "/abc"}, - {"/abc/def/ghi/../jkl", "/abc/def/jkl"}, - {"/abc/def/../ghi/../jkl", "/abc/jkl"}, - {"/abc/def/..", "/abc"}, - {"/abc/def/../..", "/"}, - {"/abc/def/../../..", "/"}, - {"/abc/def/../../..", "/"}, - {"/abc/def/../../../ghi/jkl/../../../mno", "/mno"}, - - // Combinations - {"abc/./../def", "/def"}, - {"abc//./../def", "/def"}, - {"abc/../../././../def", "/def"}, -} - -func TestPathClean(t *testing.T) { - for _, test := range cleanTests { - if s := CleanPath(test.path); s != test.result { - t.Errorf("CleanPath(%q) = %q, want %q", test.path, s, test.result) - } - if s := CleanPath(test.result); s != test.result { - t.Errorf("CleanPath(%q) = %q, want %q", test.result, s, test.result) - } - } -} - -func TestPathCleanMallocs(t *testing.T) { - if testing.Short() { - t.Skip("skipping malloc count in short mode") - } - if runtime.GOMAXPROCS(0) > 1 { - t.Log("skipping AllocsPerRun checks; GOMAXPROCS>1") - return - } - - for _, test := range cleanTests { - allocs := testing.AllocsPerRun(100, func() { CleanPath(test.result) }) - if allocs > 0 { - t.Errorf("CleanPath(%q): %v allocs, want zero", test.result, allocs) - } - } -} diff --git a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/router.go b/Godeps/_workspace/src/github.com/julienschmidt/httprouter/router.go deleted file mode 100644 index 8c9550fa..00000000 --- a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/router.go +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright 2013 Julien Schmidt. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -// Package httprouter is a trie based high performance HTTP request router. -// -// A trivial example is: -// -// package main -// -// import ( -// "fmt" -// "github.com/julienschmidt/httprouter" -// "net/http" -// "log" -// ) -// -// func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { -// fmt.Fprint(w, "Welcome!\n") -// } -// -// func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { -// fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name")) -// } -// -// func main() { -// router := httprouter.New() -// router.GET("/", Index) -// router.GET("/hello/:name", Hello) -// -// log.Fatal(http.ListenAndServe(":8080", router)) -// } -// -// The router matches incoming requests by the request method and the path. -// If a handle is registered for this path and method, the router delegates the -// request to that function. -// For the methods GET, POST, PUT, PATCH and DELETE shortcut functions exist to -// register handles, for all other methods router.Handle can be used. -// -// The registered path, against which the router matches incoming requests, can -// contain two types of parameters: -// Syntax Type -// :name named parameter -// *name catch-all parameter -// -// Named parameters are dynamic path segments. They match anything until the -// next '/' or the path end: -// Path: /blog/:category/:post -// -// Requests: -// /blog/go/request-routers match: category="go", post="request-routers" -// /blog/go/request-routers/ no match, but the router would redirect -// /blog/go/ no match -// /blog/go/request-routers/comments no match -// -// Catch-all parameters match anything until the path end, including the -// directory index (the '/' before the catch-all). Since they match anything -// until the end, catch-all paramerters must always be the final path element. -// Path: /files/*filepath -// -// Requests: -// /files/ match: filepath="/" -// /files/LICENSE match: filepath="/LICENSE" -// /files/templates/article.html match: filepath="/templates/article.html" -// /files no match, but the router would redirect -// -// The value of parameters is saved as a slice of the Param struct, consisting -// each of a key and a value. The slice is passed to the Handle func as a third -// parameter. -// There are two ways to retrieve the value of a parameter: -// // by the name of the parameter -// user := ps.ByName("user") // defined by :user or *user -// -// // by the index of the parameter. This way you can also get the name (key) -// thirdKey := ps[2].Key // the name of the 3rd parameter -// thirdValue := ps[2].Value // the value of the 3rd parameter -package httprouter - -import ( - "net/http" -) - -// Handle is a function that can be registered to a route to handle HTTP -// requests. Like http.HandlerFunc, but has a third parameter for the values of -// wildcards (variables). -type Handle func(http.ResponseWriter, *http.Request, Params) - -// Param is a single URL parameter, consisting of a key and a value. -type Param struct { - Key string - Value string -} - -// Params is a Param-slice, as returned by the router. -// The slice is ordered, the first URL parameter is also the first slice value. -// It is therefore safe to read values by the index. -type Params []Param - -// ByName returns the value of the first Param which key matches the given name. -// If no matching Param is found, an empty string is returned. -func (ps Params) ByName(name string) string { - for i := range ps { - if ps[i].Key == name { - return ps[i].Value - } - } - return "" -} - -// Router is a http.Handler which can be used to dispatch requests to different -// handler functions via configurable routes -type Router struct { - trees map[string]*node - - // Enables automatic redirection if the current route can't be matched but a - // handler for the path with (without) the trailing slash exists. - // For example if /foo/ is requested but a route only exists for /foo, the - // client is redirected to /foo with http status code 301 for GET requests - // and 307 for all other request methods. - RedirectTrailingSlash bool - - // If enabled, the router tries to fix the current request path, if no - // handle is registered for it. - // First superfluous path elements like ../ or // are removed. - // Afterwards the router does a case-insensitive lookup of the cleaned path. - // If a handle can be found for this route, the router makes a redirection - // to the corrected path with status code 301 for GET requests and 307 for - // all other request methods. - // For example /FOO and /..//Foo could be redirected to /foo. - // RedirectTrailingSlash is independent of this option. - RedirectFixedPath bool - - // If enabled, the router checks if another method is allowed for the - // current route, if the current request can not be routed. - // If this is the case, the request is answered with 'Method Not Allowed' - // and HTTP status code 405. - // If no other Method is allowed, the request is delegated to the NotFound - // handler. - HandleMethodNotAllowed bool - - // Configurable http.HandlerFunc which is called when no matching route is - // found. If it is not set, http.NotFound is used. - NotFound http.HandlerFunc - - // Configurable http.HandlerFunc which is called when a request - // cannot be routed and HandleMethodNotAllowed is true. - // If it is not set, http.Error with http.StatusMethodNotAllowed is used. - MethodNotAllowed http.HandlerFunc - - // Function to handle panics recovered from http handlers. - // It should be used to generate a error page and return the http error code - // 500 (Internal Server Error). - // The handler can be used to keep your server from crashing because of - // unrecovered panics. - PanicHandler func(http.ResponseWriter, *http.Request, interface{}) -} - -// Make sure the Router conforms with the http.Handler interface -var _ http.Handler = New() - -// New returns a new initialized Router. -// Path auto-correction, including trailing slashes, is enabled by default. -func New() *Router { - return &Router{ - RedirectTrailingSlash: true, - RedirectFixedPath: true, - HandleMethodNotAllowed: true, - } -} - -// GET is a shortcut for router.Handle("GET", path, handle) -func (r *Router) GET(path string, handle Handle) { - r.Handle("GET", path, handle) -} - -// HEAD is a shortcut for router.Handle("HEAD", path, handle) -func (r *Router) HEAD(path string, handle Handle) { - r.Handle("HEAD", path, handle) -} - -// POST is a shortcut for router.Handle("POST", path, handle) -func (r *Router) POST(path string, handle Handle) { - r.Handle("POST", path, handle) -} - -// PUT is a shortcut for router.Handle("PUT", path, handle) -func (r *Router) PUT(path string, handle Handle) { - r.Handle("PUT", path, handle) -} - -// PATCH is a shortcut for router.Handle("PATCH", path, handle) -func (r *Router) PATCH(path string, handle Handle) { - r.Handle("PATCH", path, handle) -} - -// DELETE is a shortcut for router.Handle("DELETE", path, handle) -func (r *Router) DELETE(path string, handle Handle) { - r.Handle("DELETE", path, handle) -} - -// Handle registers a new request handle with the given path and method. -// -// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut -// functions can be used. -// -// This function is intended for bulk loading and to allow the usage of less -// frequently used, non-standardized or custom methods (e.g. for internal -// communication with a proxy). -func (r *Router) Handle(method, path string, handle Handle) { - if path[0] != '/' { - panic("path must begin with '/'") - } - - if r.trees == nil { - r.trees = make(map[string]*node) - } - - root := r.trees[method] - if root == nil { - root = new(node) - r.trees[method] = root - } - - root.addRoute(path, handle) -} - -// Handler is an adapter which allows the usage of an http.Handler as a -// request handle. -func (r *Router) Handler(method, path string, handler http.Handler) { - r.Handle(method, path, - func(w http.ResponseWriter, req *http.Request, _ Params) { - handler.ServeHTTP(w, req) - }, - ) -} - -// HandlerFunc is an adapter which allows the usage of an http.HandlerFunc as a -// request handle. -func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) { - r.Handle(method, path, - func(w http.ResponseWriter, req *http.Request, _ Params) { - handler(w, req) - }, - ) -} - -// ServeFiles serves files from the given file system root. -// The path must end with "/*filepath", files are then served from the local -// path /defined/root/dir/*filepath. -// For example if root is "/etc" and *filepath is "passwd", the local file -// "/etc/passwd" would be served. -// Internally a http.FileServer is used, therefore http.NotFound is used instead -// of the Router's NotFound handler. -// To use the operating system's file system implementation, -// use http.Dir: -// router.ServeFiles("/src/*filepath", http.Dir("/var/www")) -func (r *Router) ServeFiles(path string, root http.FileSystem) { - if len(path) < 10 || path[len(path)-10:] != "/*filepath" { - panic("path must end with /*filepath") - } - - fileServer := http.FileServer(root) - - r.GET(path, func(w http.ResponseWriter, req *http.Request, ps Params) { - req.URL.Path = ps.ByName("filepath") - fileServer.ServeHTTP(w, req) - }) -} - -func (r *Router) recv(w http.ResponseWriter, req *http.Request) { - if rcv := recover(); rcv != nil { - r.PanicHandler(w, req, rcv) - } -} - -// Lookup allows the manual lookup of a method + path combo. -// This is e.g. useful to build a framework around this router. -// If the path was found, it returns the handle function and the path parameter -// values. Otherwise the third return value indicates whether a redirection to -// the same path with an extra / without the trailing slash should be performed. -func (r *Router) Lookup(method, path string) (Handle, Params, bool) { - if root := r.trees[method]; root != nil { - return root.getValue(path) - } - return nil, nil, false -} - -// ServeHTTP makes the router implement the http.Handler interface. -func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { - if r.PanicHandler != nil { - defer r.recv(w, req) - } - - if root := r.trees[req.Method]; root != nil { - path := req.URL.Path - - if handle, ps, tsr := root.getValue(path); handle != nil { - handle(w, req, ps) - return - } else if req.Method != "CONNECT" && path != "/" { - code := 301 // Permanent redirect, request with GET method - if req.Method != "GET" { - // Temporary redirect, request with same method - // As of Go 1.3, Go does not support status code 308. - code = 307 - } - - if tsr && r.RedirectTrailingSlash { - if len(path) > 1 && path[len(path)-1] == '/' { - req.URL.Path = path[:len(path)-1] - } else { - req.URL.Path = path + "/" - } - http.Redirect(w, req, req.URL.String(), code) - return - } - - // Try to fix the request path - if r.RedirectFixedPath { - fixedPath, found := root.findCaseInsensitivePath( - CleanPath(path), - r.RedirectTrailingSlash, - ) - if found { - req.URL.Path = string(fixedPath) - http.Redirect(w, req, req.URL.String(), code) - return - } - } - } - } - - // Handle 405 - if r.HandleMethodNotAllowed { - for method := range r.trees { - // Skip the requested method - we already tried this one - if method == req.Method { - continue - } - - handle, _, _ := r.trees[method].getValue(req.URL.Path) - if handle != nil { - if r.MethodNotAllowed != nil { - r.MethodNotAllowed(w, req) - } else { - http.Error(w, - http.StatusText(http.StatusMethodNotAllowed), - http.StatusMethodNotAllowed, - ) - } - return - } - } - } - - // Handle 404 - if r.NotFound != nil { - r.NotFound(w, req) - } else { - http.NotFound(w, req) - } -} diff --git a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/router_test.go b/Godeps/_workspace/src/github.com/julienschmidt/httprouter/router_test.go deleted file mode 100644 index 75d2dfcc..00000000 --- a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/router_test.go +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright 2013 Julien Schmidt. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -package httprouter - -import ( - "errors" - "fmt" - "net/http" - "net/http/httptest" - "reflect" - "testing" -) - -type mockResponseWriter struct{} - -func (m *mockResponseWriter) Header() (h http.Header) { - return http.Header{} -} - -func (m *mockResponseWriter) Write(p []byte) (n int, err error) { - return len(p), nil -} - -func (m *mockResponseWriter) WriteString(s string) (n int, err error) { - return len(s), nil -} - -func (m *mockResponseWriter) WriteHeader(int) {} - -func TestParams(t *testing.T) { - ps := Params{ - Param{"param1", "value1"}, - Param{"param2", "value2"}, - Param{"param3", "value3"}, - } - for i := range ps { - if val := ps.ByName(ps[i].Key); val != ps[i].Value { - t.Errorf("Wrong value for %s: Got %s; Want %s", ps[i].Key, val, ps[i].Value) - } - } - if val := ps.ByName("noKey"); val != "" { - t.Errorf("Expected empty string for not found key; got: %s", val) - } -} - -func TestRouter(t *testing.T) { - router := New() - - routed := false - router.Handle("GET", "/user/:name", func(w http.ResponseWriter, r *http.Request, ps Params) { - routed = true - want := Params{Param{"name", "gopher"}} - if !reflect.DeepEqual(ps, want) { - t.Fatalf("wrong wildcard values: want %v, got %v", want, ps) - } - }) - - w := new(mockResponseWriter) - - req, _ := http.NewRequest("GET", "/user/gopher", nil) - router.ServeHTTP(w, req) - - if !routed { - t.Fatal("routing failed") - } -} - -type handlerStruct struct { - handeled *bool -} - -func (h handlerStruct) ServeHTTP(w http.ResponseWriter, r *http.Request) { - *h.handeled = true -} - -func TestRouterAPI(t *testing.T) { - var get, head, post, put, patch, delete, handler, handlerFunc bool - - httpHandler := handlerStruct{&handler} - - router := New() - router.GET("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) { - get = true - }) - router.HEAD("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) { - head = true - }) - router.POST("/POST", func(w http.ResponseWriter, r *http.Request, _ Params) { - post = true - }) - router.PUT("/PUT", func(w http.ResponseWriter, r *http.Request, _ Params) { - put = true - }) - router.PATCH("/PATCH", func(w http.ResponseWriter, r *http.Request, _ Params) { - patch = true - }) - router.DELETE("/DELETE", func(w http.ResponseWriter, r *http.Request, _ Params) { - delete = true - }) - router.Handler("GET", "/Handler", httpHandler) - router.HandlerFunc("GET", "/HandlerFunc", func(w http.ResponseWriter, r *http.Request) { - handlerFunc = true - }) - - w := new(mockResponseWriter) - - r, _ := http.NewRequest("GET", "/GET", nil) - router.ServeHTTP(w, r) - if !get { - t.Error("routing GET failed") - } - - r, _ = http.NewRequest("HEAD", "/GET", nil) - router.ServeHTTP(w, r) - if !head { - t.Error("routing HEAD failed") - } - - r, _ = http.NewRequest("POST", "/POST", nil) - router.ServeHTTP(w, r) - if !post { - t.Error("routing POST failed") - } - - r, _ = http.NewRequest("PUT", "/PUT", nil) - router.ServeHTTP(w, r) - if !put { - t.Error("routing PUT failed") - } - - r, _ = http.NewRequest("PATCH", "/PATCH", nil) - router.ServeHTTP(w, r) - if !patch { - t.Error("routing PATCH failed") - } - - r, _ = http.NewRequest("DELETE", "/DELETE", nil) - router.ServeHTTP(w, r) - if !delete { - t.Error("routing DELETE failed") - } - - r, _ = http.NewRequest("GET", "/Handler", nil) - router.ServeHTTP(w, r) - if !handler { - t.Error("routing Handler failed") - } - - r, _ = http.NewRequest("GET", "/HandlerFunc", nil) - router.ServeHTTP(w, r) - if !handlerFunc { - t.Error("routing HandlerFunc failed") - } -} - -func TestRouterRoot(t *testing.T) { - router := New() - recv := catchPanic(func() { - router.GET("noSlashRoot", nil) - }) - if recv == nil { - t.Fatal("registering path not beginning with '/' did not panic") - } -} - -func TestRouterNotAllowed(t *testing.T) { - handlerFunc := func(_ http.ResponseWriter, _ *http.Request, _ Params) {} - - router := New() - router.POST("/path", handlerFunc) - - // Test not allowed - r, _ := http.NewRequest("GET", "/path", nil) - w := httptest.NewRecorder() - router.ServeHTTP(w, r) - if !(w.Code == http.StatusMethodNotAllowed) { - t.Errorf("NotAllowed handling failed: Code=%d, Header=%v", w.Code, w.Header()) - } - - w = httptest.NewRecorder() - responseText := "custom method" - router.MethodNotAllowed = func(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(http.StatusTeapot) - w.Write([]byte(responseText)) - } - router.ServeHTTP(w, r) - if got := w.Body.String(); !(got == responseText) { - t.Errorf("unexpected response got %q want %q", got, responseText) - } - if w.Code != http.StatusTeapot { - t.Errorf("unexpected response code %d want %d", w.Code, http.StatusTeapot) - } -} - -func TestRouterNotFound(t *testing.T) { - handlerFunc := func(_ http.ResponseWriter, _ *http.Request, _ Params) {} - - router := New() - router.GET("/path", handlerFunc) - router.GET("/dir/", handlerFunc) - router.GET("/", handlerFunc) - - testRoutes := []struct { - route string - code int - header string - }{ - {"/path/", 301, "map[Location:[/path]]"}, // TSR -/ - {"/dir", 301, "map[Location:[/dir/]]"}, // TSR +/ - {"", 301, "map[Location:[/]]"}, // TSR +/ - {"/PATH", 301, "map[Location:[/path]]"}, // Fixed Case - {"/DIR/", 301, "map[Location:[/dir/]]"}, // Fixed Case - {"/PATH/", 301, "map[Location:[/path]]"}, // Fixed Case -/ - {"/DIR", 301, "map[Location:[/dir/]]"}, // Fixed Case +/ - {"/../path", 301, "map[Location:[/path]]"}, // CleanPath - {"/nope", 404, ""}, // NotFound - } - for _, tr := range testRoutes { - r, _ := http.NewRequest("GET", tr.route, nil) - w := httptest.NewRecorder() - router.ServeHTTP(w, r) - if !(w.Code == tr.code && (w.Code == 404 || fmt.Sprint(w.Header()) == tr.header)) { - t.Errorf("NotFound handling route %s failed: Code=%d, Header=%v", tr.route, w.Code, w.Header()) - } - } - - // Test custom not found handler - var notFound bool - router.NotFound = func(rw http.ResponseWriter, r *http.Request) { - rw.WriteHeader(404) - notFound = true - } - r, _ := http.NewRequest("GET", "/nope", nil) - w := httptest.NewRecorder() - router.ServeHTTP(w, r) - if !(w.Code == 404 && notFound == true) { - t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", w.Code, w.Header()) - } - - // Test other method than GET (want 307 instead of 301) - router.PATCH("/path", handlerFunc) - r, _ = http.NewRequest("PATCH", "/path/", nil) - w = httptest.NewRecorder() - router.ServeHTTP(w, r) - if !(w.Code == 307 && fmt.Sprint(w.Header()) == "map[Location:[/path]]") { - t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", w.Code, w.Header()) - } - - // Test special case where no node for the prefix "/" exists - router = New() - router.GET("/a", handlerFunc) - r, _ = http.NewRequest("GET", "/", nil) - w = httptest.NewRecorder() - router.ServeHTTP(w, r) - if !(w.Code == 404) { - t.Errorf("NotFound handling route / failed: Code=%d", w.Code) - } -} - -func TestRouterPanicHandler(t *testing.T) { - router := New() - panicHandled := false - - router.PanicHandler = func(rw http.ResponseWriter, r *http.Request, p interface{}) { - panicHandled = true - } - - router.Handle("PUT", "/user/:name", func(_ http.ResponseWriter, _ *http.Request, _ Params) { - panic("oops!") - }) - - w := new(mockResponseWriter) - req, _ := http.NewRequest("PUT", "/user/gopher", nil) - - defer func() { - if rcv := recover(); rcv != nil { - t.Fatal("handling panic failed") - } - }() - - router.ServeHTTP(w, req) - - if !panicHandled { - t.Fatal("simulating failed") - } -} - -func TestRouterLookup(t *testing.T) { - routed := false - wantHandle := func(_ http.ResponseWriter, _ *http.Request, _ Params) { - routed = true - } - wantParams := Params{Param{"name", "gopher"}} - - router := New() - - // try empty router first - handle, _, tsr := router.Lookup("GET", "/nope") - if handle != nil { - t.Fatalf("Got handle for unregistered pattern: %v", handle) - } - if tsr { - t.Error("Got wrong TSR recommendation!") - } - - // insert route and try again - router.GET("/user/:name", wantHandle) - - handle, params, tsr := router.Lookup("GET", "/user/gopher") - if handle == nil { - t.Fatal("Got no handle!") - } else { - handle(nil, nil, nil) - if !routed { - t.Fatal("Routing failed!") - } - } - - if !reflect.DeepEqual(params, wantParams) { - t.Fatalf("Wrong parameter values: want %v, got %v", wantParams, params) - } - - handle, _, tsr = router.Lookup("GET", "/user/gopher/") - if handle != nil { - t.Fatalf("Got handle for unregistered pattern: %v", handle) - } - if !tsr { - t.Error("Got no TSR recommendation!") - } - - handle, _, tsr = router.Lookup("GET", "/nope") - if handle != nil { - t.Fatalf("Got handle for unregistered pattern: %v", handle) - } - if tsr { - t.Error("Got wrong TSR recommendation!") - } -} - -type mockFileSystem struct { - opened bool -} - -func (mfs *mockFileSystem) Open(name string) (http.File, error) { - mfs.opened = true - return nil, errors.New("this is just a mock") -} - -func TestRouterServeFiles(t *testing.T) { - router := New() - mfs := &mockFileSystem{} - - recv := catchPanic(func() { - router.ServeFiles("/noFilepath", mfs) - }) - if recv == nil { - t.Fatal("registering path not ending with '*filepath' did not panic") - } - - router.ServeFiles("/*filepath", mfs) - w := new(mockResponseWriter) - r, _ := http.NewRequest("GET", "/favicon.ico", nil) - router.ServeHTTP(w, r) - if !mfs.opened { - t.Error("serving file failed") - } -} diff --git a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/tree.go b/Godeps/_workspace/src/github.com/julienschmidt/httprouter/tree.go deleted file mode 100644 index af91966e..00000000 --- a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/tree.go +++ /dev/null @@ -1,548 +0,0 @@ -// Copyright 2013 Julien Schmidt. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -package httprouter - -import ( - "strings" - "unicode" -) - -func min(a, b int) int { - if a <= b { - return a - } - return b -} - -func countParams(path string) uint8 { - var n uint - for i := 0; i < len(path); i++ { - if path[i] != ':' && path[i] != '*' { - continue - } - n++ - } - if n >= 255 { - return 255 - } - return uint8(n) -} - -type nodeType uint8 - -const ( - static nodeType = 0 - param nodeType = 1 - catchAll nodeType = 2 -) - -type node struct { - path string - wildChild bool - nType nodeType - maxParams uint8 - indices string - children []*node - handle Handle - priority uint32 -} - -// increments priority of the given child and reorders if necessary -func (n *node) incrementChildPrio(pos int) int { - n.children[pos].priority++ - prio := n.children[pos].priority - - // adjust position (move to front) - newPos := pos - for newPos > 0 && n.children[newPos-1].priority < prio { - // swap node positions - tmpN := n.children[newPos-1] - n.children[newPos-1] = n.children[newPos] - n.children[newPos] = tmpN - - newPos-- - } - - // build new index char string - if newPos != pos { - n.indices = n.indices[:newPos] + // unchanged prefix, might be empty - n.indices[pos:pos+1] + // the index char we move - n.indices[newPos:pos] + n.indices[pos+1:] // rest without char at 'pos' - } - - return newPos -} - -// addRoute adds a node with the given handle to the path. -// Not concurrency-safe! -func (n *node) addRoute(path string, handle Handle) { - n.priority++ - numParams := countParams(path) - - // non-empty tree - if len(n.path) > 0 || len(n.children) > 0 { - walk: - for { - // Update maxParams of the current node - if numParams > n.maxParams { - n.maxParams = numParams - } - - // Find the longest common prefix. - // This also implies that the common prefix contains no ':' or '*' - // since the existing key can't contain those chars. - i := 0 - max := min(len(path), len(n.path)) - for i < max && path[i] == n.path[i] { - i++ - } - - // Split edge - if i < len(n.path) { - child := node{ - path: n.path[i:], - wildChild: n.wildChild, - indices: n.indices, - children: n.children, - handle: n.handle, - priority: n.priority - 1, - } - - // Update maxParams (max of all children) - for i := range child.children { - if child.children[i].maxParams > child.maxParams { - child.maxParams = child.children[i].maxParams - } - } - - n.children = []*node{&child} - n.indices = string(n.path[i]) - n.path = path[:i] - n.handle = nil - n.wildChild = false - } - - // Make new node a child of this node - if i < len(path) { - path = path[i:] - - if n.wildChild { - n = n.children[0] - n.priority++ - - // Update maxParams of the child node - if numParams > n.maxParams { - n.maxParams = numParams - } - numParams-- - - // Check if the wildcard matches - if len(path) >= len(n.path) && n.path == path[:len(n.path)] { - // check for longer wildcard, e.g. :name and :names - if len(n.path) >= len(path) || path[len(n.path)] == '/' { - continue walk - } - } - - panic("conflict with wildcard route") - } - - c := path[0] - - // slash after param - if n.nType == param && c == '/' && len(n.children) == 1 { - n = n.children[0] - n.priority++ - continue walk - } - - // Check if a child with the next path byte exists - for i := 0; i < len(n.indices); i++ { - if c == n.indices[i] { - i = n.incrementChildPrio(i) - n = n.children[i] - continue walk - } - } - - // Otherwise insert it - if c != ':' && c != '*' { - n.indices += string(c) - child := &node{ - maxParams: numParams, - } - n.children = append(n.children, child) - n.incrementChildPrio(len(n.indices) - 1) - n = child - } - n.insertChild(numParams, path, handle) - return - - } else if i == len(path) { // Make node a (in-path) leaf - if n.handle != nil { - panic("a Handle is already registered for this path") - } - n.handle = handle - } - return - } - } else { // Empty tree - n.insertChild(numParams, path, handle) - } -} - -func (n *node) insertChild(numParams uint8, path string, handle Handle) { - var offset int // already handled bytes of the path - - // find prefix until first wildcard (beginning with ':'' or '*'') - for i, max := 0, len(path); numParams > 0; i++ { - c := path[i] - if c != ':' && c != '*' { - continue - } - - // check if this Node existing children which would be - // unreachable if we insert the wildcard here - if len(n.children) > 0 { - panic("wildcard route conflicts with existing children") - } - - // find wildcard end (either '/' or path end) - end := i + 1 - for end < max && path[end] != '/' { - switch path[end] { - // the wildcard name must not contain ':' and '*' - case ':', '*': - panic("only one wildcard per path segment is allowed") - default: - end++ - } - } - - // check if the wildcard has a name - if end-i < 2 { - panic("wildcards must be named with a non-empty name") - } - - if c == ':' { // param - // split path at the beginning of the wildcard - if i > 0 { - n.path = path[offset:i] - offset = i - } - - child := &node{ - nType: param, - maxParams: numParams, - } - n.children = []*node{child} - n.wildChild = true - n = child - n.priority++ - numParams-- - - // if the path doesn't end with the wildcard, then there - // will be another non-wildcard subpath starting with '/' - if end < max { - n.path = path[offset:end] - offset = end - - child := &node{ - maxParams: numParams, - priority: 1, - } - n.children = []*node{child} - n = child - } - - } else { // catchAll - if end != max || numParams > 1 { - panic("catch-all routes are only allowed at the end of the path") - } - - if len(n.path) > 0 && n.path[len(n.path)-1] == '/' { - panic("catch-all conflicts with existing handle for the path segment root") - } - - // currently fixed width 1 for '/' - i-- - if path[i] != '/' { - panic("no / before catch-all") - } - - n.path = path[offset:i] - - // first node: catchAll node with empty path - child := &node{ - wildChild: true, - nType: catchAll, - maxParams: 1, - } - n.children = []*node{child} - n.indices = string(path[i]) - n = child - n.priority++ - - // second node: node holding the variable - child = &node{ - path: path[i:], - nType: catchAll, - maxParams: 1, - handle: handle, - priority: 1, - } - n.children = []*node{child} - - return - } - } - - // insert remaining path part and handle to the leaf - n.path = path[offset:] - n.handle = handle -} - -// Returns the handle registered with the given path (key). The values of -// wildcards are saved to a map. -// If no handle can be found, a TSR (trailing slash redirect) recommendation is -// made if a handle exists with an extra (without the) trailing slash for the -// given path. -func (n *node) getValue(path string) (handle Handle, p Params, tsr bool) { -walk: // Outer loop for walking the tree - for { - if len(path) > len(n.path) { - if path[:len(n.path)] == n.path { - path = path[len(n.path):] - // If this node does not have a wildcard (param or catchAll) - // child, we can just look up the next child node and continue - // to walk down the tree - if !n.wildChild { - c := path[0] - for i := 0; i < len(n.indices); i++ { - if c == n.indices[i] { - n = n.children[i] - continue walk - } - } - - // Nothing found. - // We can recommend to redirect to the same URL without a - // trailing slash if a leaf exists for that path. - tsr = (path == "/" && n.handle != nil) - return - - } - - // handle wildcard child - n = n.children[0] - switch n.nType { - case param: - // find param end (either '/' or path end) - end := 0 - for end < len(path) && path[end] != '/' { - end++ - } - - // save param value - if p == nil { - // lazy allocation - p = make(Params, 0, n.maxParams) - } - i := len(p) - p = p[:i+1] // expand slice within preallocated capacity - p[i].Key = n.path[1:] - p[i].Value = path[:end] - - // we need to go deeper! - if end < len(path) { - if len(n.children) > 0 { - path = path[end:] - n = n.children[0] - continue walk - } - - // ... but we can't - tsr = (len(path) == end+1) - return - } - - if handle = n.handle; handle != nil { - return - } else if len(n.children) == 1 { - // No handle found. Check if a handle for this path + a - // trailing slash exists for TSR recommendation - n = n.children[0] - tsr = (n.path == "/" && n.handle != nil) - } - - return - - case catchAll: - // save param value - if p == nil { - // lazy allocation - p = make(Params, 0, n.maxParams) - } - i := len(p) - p = p[:i+1] // expand slice within preallocated capacity - p[i].Key = n.path[2:] - p[i].Value = path - - handle = n.handle - return - - default: - panic("Invalid node type") - } - } - } else if path == n.path { - // We should have reached the node containing the handle. - // Check if this node has a handle registered. - if handle = n.handle; handle != nil { - return - } - - // No handle found. Check if a handle for this path + a - // trailing slash exists for trailing slash recommendation - for i := 0; i < len(n.indices); i++ { - if n.indices[i] == '/' { - n = n.children[i] - tsr = (len(n.path) == 1 && n.handle != nil) || - (n.nType == catchAll && n.children[0].handle != nil) - return - } - } - - return - } - - // Nothing found. We can recommend to redirect to the same URL with an - // extra trailing slash if a leaf exists for that path - tsr = (path == "/") || - (len(n.path) == len(path)+1 && n.path[len(path)] == '/' && - path == n.path[:len(n.path)-1] && n.handle != nil) - return - } -} - -// Makes a case-insensitive lookup of the given path and tries to find a handler. -// It can optionally also fix trailing slashes. -// It returns the case-corrected path and a bool indicating whether the lookup -// was successful. -func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPath []byte, found bool) { - ciPath = make([]byte, 0, len(path)+1) // preallocate enough memory - - // Outer loop for walking the tree - for len(path) >= len(n.path) && strings.ToLower(path[:len(n.path)]) == strings.ToLower(n.path) { - path = path[len(n.path):] - ciPath = append(ciPath, n.path...) - - if len(path) > 0 { - // If this node does not have a wildcard (param or catchAll) child, - // we can just look up the next child node and continue to walk down - // the tree - if !n.wildChild { - r := unicode.ToLower(rune(path[0])) - for i, index := range n.indices { - // must use recursive approach since both index and - // ToLower(index) could exist. We must check both. - if r == unicode.ToLower(index) { - out, found := n.children[i].findCaseInsensitivePath(path, fixTrailingSlash) - if found { - return append(ciPath, out...), true - } - } - } - - // Nothing found. We can recommend to redirect to the same URL - // without a trailing slash if a leaf exists for that path - found = (fixTrailingSlash && path == "/" && n.handle != nil) - return - } - - n = n.children[0] - switch n.nType { - case param: - // find param end (either '/' or path end) - k := 0 - for k < len(path) && path[k] != '/' { - k++ - } - - // add param value to case insensitive path - ciPath = append(ciPath, path[:k]...) - - // we need to go deeper! - if k < len(path) { - if len(n.children) > 0 { - path = path[k:] - n = n.children[0] - continue - } - - // ... but we can't - if fixTrailingSlash && len(path) == k+1 { - return ciPath, true - } - return - } - - if n.handle != nil { - return ciPath, true - } else if fixTrailingSlash && len(n.children) == 1 { - // No handle found. Check if a handle for this path + a - // trailing slash exists - n = n.children[0] - if n.path == "/" && n.handle != nil { - return append(ciPath, '/'), true - } - } - return - - case catchAll: - return append(ciPath, path...), true - - default: - panic("Invalid node type") - } - } else { - // We should have reached the node containing the handle. - // Check if this node has a handle registered. - if n.handle != nil { - return ciPath, true - } - - // No handle found. - // Try to fix the path by adding a trailing slash - if fixTrailingSlash { - for i := 0; i < len(n.indices); i++ { - if n.indices[i] == '/' { - n = n.children[i] - if (len(n.path) == 1 && n.handle != nil) || - (n.nType == catchAll && n.children[0].handle != nil) { - return append(ciPath, '/'), true - } - return - } - } - } - return - } - } - - // Nothing found. - // Try to fix the path by adding / removing a trailing slash - if fixTrailingSlash { - if path == "/" { - return ciPath, true - } - if len(path)+1 == len(n.path) && n.path[len(path)] == '/' && - strings.ToLower(path) == strings.ToLower(n.path[:len(path)]) && - n.handle != nil { - return append(ciPath, n.path...), true - } - } - return -} diff --git a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/tree_test.go b/Godeps/_workspace/src/github.com/julienschmidt/httprouter/tree_test.go deleted file mode 100644 index 484621e2..00000000 --- a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/tree_test.go +++ /dev/null @@ -1,605 +0,0 @@ -// Copyright 2013 Julien Schmidt. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -package httprouter - -import ( - "fmt" - "net/http" - "reflect" - "strings" - "testing" -) - -func printChildren(n *node, prefix string) { - fmt.Printf(" %02d:%02d %s%s[%d] %v %t %d \r\n", n.priority, n.maxParams, prefix, n.path, len(n.children), n.handle, n.wildChild, n.nType) - for l := len(n.path); l > 0; l-- { - prefix += " " - } - for _, child := range n.children { - printChildren(child, prefix) - } -} - -// Used as a workaround since we can't compare functions or their adresses -var fakeHandlerValue string - -func fakeHandler(val string) Handle { - return func(http.ResponseWriter, *http.Request, Params) { - fakeHandlerValue = val - } -} - -type testRequests []struct { - path string - nilHandler bool - route string - ps Params -} - -func checkRequests(t *testing.T, tree *node, requests testRequests) { - for _, request := range requests { - handler, ps, _ := tree.getValue(request.path) - - if handler == nil { - if !request.nilHandler { - t.Errorf("handle mismatch for route '%s': Expected non-nil handle", request.path) - } - } else if request.nilHandler { - t.Errorf("handle mismatch for route '%s': Expected nil handle", request.path) - } else { - handler(nil, nil, nil) - if fakeHandlerValue != request.route { - t.Errorf("handle mismatch for route '%s': Wrong handle (%s != %s)", request.path, fakeHandlerValue, request.route) - } - } - - if !reflect.DeepEqual(ps, request.ps) { - t.Errorf("Params mismatch for route '%s'", request.path) - } - } -} - -func checkPriorities(t *testing.T, n *node) uint32 { - var prio uint32 - for i := range n.children { - prio += checkPriorities(t, n.children[i]) - } - - if n.handle != nil { - prio++ - } - - if n.priority != prio { - t.Errorf( - "priority mismatch for node '%s': is %d, should be %d", - n.path, n.priority, prio, - ) - } - - return prio -} - -func checkMaxParams(t *testing.T, n *node) uint8 { - var maxParams uint8 - for i := range n.children { - params := checkMaxParams(t, n.children[i]) - if params > maxParams { - maxParams = params - } - } - if n.nType != static && !n.wildChild { - maxParams++ - } - - if n.maxParams != maxParams { - t.Errorf( - "maxParams mismatch for node '%s': is %d, should be %d", - n.path, n.maxParams, maxParams, - ) - } - - return maxParams -} - -func TestCountParams(t *testing.T) { - if countParams("/path/:param1/static/*catch-all") != 2 { - t.Fail() - } - if countParams(strings.Repeat("/:param", 256)) != 255 { - t.Fail() - } -} - -func TestTreeAddAndGet(t *testing.T) { - tree := &node{} - - routes := [...]string{ - "/hi", - "/contact", - "/co", - "/c", - "/a", - "/ab", - "/doc/", - "/doc/go_faq.html", - "/doc/go1.html", - } - for _, route := range routes { - tree.addRoute(route, fakeHandler(route)) - } - - //printChildren(tree, "") - - checkRequests(t, tree, testRequests{ - {"/a", false, "/a", nil}, - {"/", true, "", nil}, - {"/hi", false, "/hi", nil}, - {"/contact", false, "/contact", nil}, - {"/co", false, "/co", nil}, - {"/con", true, "", nil}, // key mismatch - {"/cona", true, "", nil}, // key mismatch - {"/no", true, "", nil}, // no matching child - {"/ab", false, "/ab", nil}, - }) - - checkPriorities(t, tree) - checkMaxParams(t, tree) -} - -func TestTreeWildcard(t *testing.T) { - tree := &node{} - - routes := [...]string{ - "/", - "/cmd/:tool/:sub", - "/cmd/:tool/", - "/src/*filepath", - "/search/", - "/search/:query", - "/user_:name", - "/user_:name/about", - "/files/:dir/*filepath", - "/doc/", - "/doc/go_faq.html", - "/doc/go1.html", - "/info/:user/public", - "/info/:user/project/:project", - } - for _, route := range routes { - tree.addRoute(route, fakeHandler(route)) - } - - //printChildren(tree, "") - - checkRequests(t, tree, testRequests{ - {"/", false, "/", nil}, - {"/cmd/test/", false, "/cmd/:tool/", Params{Param{"tool", "test"}}}, - {"/cmd/test", true, "", Params{Param{"tool", "test"}}}, - {"/cmd/test/3", false, "/cmd/:tool/:sub", Params{Param{"tool", "test"}, Param{"sub", "3"}}}, - {"/src/", false, "/src/*filepath", Params{Param{"filepath", "/"}}}, - {"/src/some/file.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file.png"}}}, - {"/search/", false, "/search/", nil}, - {"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{"query", "someth!ng+in+ünìcodé"}}}, - {"/search/someth!ng+in+ünìcodé/", true, "", Params{Param{"query", "someth!ng+in+ünìcodé"}}}, - {"/user_gopher", false, "/user_:name", Params{Param{"name", "gopher"}}}, - {"/user_gopher/about", false, "/user_:name/about", Params{Param{"name", "gopher"}}}, - {"/files/js/inc/framework.js", false, "/files/:dir/*filepath", Params{Param{"dir", "js"}, Param{"filepath", "/inc/framework.js"}}}, - {"/info/gordon/public", false, "/info/:user/public", Params{Param{"user", "gordon"}}}, - {"/info/gordon/project/go", false, "/info/:user/project/:project", Params{Param{"user", "gordon"}, Param{"project", "go"}}}, - }) - - checkPriorities(t, tree) - checkMaxParams(t, tree) -} - -func catchPanic(testFunc func()) (recv interface{}) { - defer func() { - recv = recover() - }() - - testFunc() - return -} - -type testRoute struct { - path string - conflict bool -} - -func testRoutes(t *testing.T, routes []testRoute) { - tree := &node{} - - for _, route := range routes { - recv := catchPanic(func() { - tree.addRoute(route.path, nil) - }) - - if route.conflict { - if recv == nil { - t.Errorf("no panic for conflicting route '%s'", route.path) - } - } else if recv != nil { - t.Errorf("unexpected panic for route '%s': %v", route.path, recv) - } - } - - //printChildren(tree, "") -} - -func TestTreeWildcardConflict(t *testing.T) { - routes := []testRoute{ - {"/cmd/:tool/:sub", false}, - {"/cmd/vet", true}, - {"/src/*filepath", false}, - {"/src/*filepathx", true}, - {"/src/", true}, - {"/src1/", false}, - {"/src1/*filepath", true}, - {"/src2*filepath", true}, - {"/search/:query", false}, - {"/search/invalid", true}, - {"/user_:name", false}, - {"/user_x", true}, - {"/user_:name", false}, - {"/id:id", false}, - {"/id/:id", true}, - } - testRoutes(t, routes) -} - -func TestTreeChildConflict(t *testing.T) { - routes := []testRoute{ - {"/cmd/vet", false}, - {"/cmd/:tool/:sub", true}, - {"/src/AUTHORS", false}, - {"/src/*filepath", true}, - {"/user_x", false}, - {"/user_:name", true}, - {"/id/:id", false}, - {"/id:id", true}, - {"/:id", true}, - {"/*filepath", true}, - } - testRoutes(t, routes) -} - -func TestTreeDupliatePath(t *testing.T) { - tree := &node{} - - routes := [...]string{ - "/", - "/doc/", - "/src/*filepath", - "/search/:query", - "/user_:name", - } - for _, route := range routes { - recv := catchPanic(func() { - tree.addRoute(route, fakeHandler(route)) - }) - if recv != nil { - t.Fatalf("panic inserting route '%s': %v", route, recv) - } - - // Add again - recv = catchPanic(func() { - tree.addRoute(route, nil) - }) - if recv == nil { - t.Fatalf("no panic while inserting duplicate route '%s", route) - } - } - - //printChildren(tree, "") - - checkRequests(t, tree, testRequests{ - {"/", false, "/", nil}, - {"/doc/", false, "/doc/", nil}, - {"/src/some/file.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file.png"}}}, - {"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{"query", "someth!ng+in+ünìcodé"}}}, - {"/user_gopher", false, "/user_:name", Params{Param{"name", "gopher"}}}, - }) -} - -func TestEmptyWildcardName(t *testing.T) { - tree := &node{} - - routes := [...]string{ - "/user:", - "/user:/", - "/cmd/:/", - "/src/*", - } - for _, route := range routes { - recv := catchPanic(func() { - tree.addRoute(route, nil) - }) - if recv == nil { - t.Fatalf("no panic while inserting route with empty wildcard name '%s", route) - } - } -} - -func TestTreeCatchAllConflict(t *testing.T) { - routes := []testRoute{ - {"/src/*filepath/x", true}, - {"/src2/", false}, - {"/src2/*filepath/x", true}, - } - testRoutes(t, routes) -} - -func TestTreeCatchAllConflictRoot(t *testing.T) { - routes := []testRoute{ - {"/", false}, - {"/*filepath", true}, - } - testRoutes(t, routes) -} - -func TestTreeDoubleWildcard(t *testing.T) { - const panicMsg = "only one wildcard per path segment is allowed" - - routes := [...]string{ - "/:foo:bar", - "/:foo:bar/", - "/:foo*bar", - } - - for _, route := range routes { - tree := &node{} - recv := catchPanic(func() { - tree.addRoute(route, nil) - }) - - if rs, ok := recv.(string); !ok || rs != panicMsg { - t.Fatalf(`"Expected panic "%s" for route '%s', got "%v"`, panicMsg, route, recv) - } - } -} - -/*func TestTreeDuplicateWildcard(t *testing.T) { - tree := &node{} - - routes := [...]string{ - "/:id/:name/:id", - } - for _, route := range routes { - ... - } -}*/ - -func TestTreeTrailingSlashRedirect(t *testing.T) { - tree := &node{} - - routes := [...]string{ - "/hi", - "/b/", - "/search/:query", - "/cmd/:tool/", - "/src/*filepath", - "/x", - "/x/y", - "/y/", - "/y/z", - "/0/:id", - "/0/:id/1", - "/1/:id/", - "/1/:id/2", - "/aa", - "/a/", - "/doc", - "/doc/go_faq.html", - "/doc/go1.html", - "/no/a", - "/no/b", - "/api/hello/:name", - } - for _, route := range routes { - recv := catchPanic(func() { - tree.addRoute(route, fakeHandler(route)) - }) - if recv != nil { - t.Fatalf("panic inserting route '%s': %v", route, recv) - } - } - - //printChildren(tree, "") - - tsrRoutes := [...]string{ - "/hi/", - "/b", - "/search/gopher/", - "/cmd/vet", - "/src", - "/x/", - "/y", - "/0/go/", - "/1/go", - "/a", - "/doc/", - } - for _, route := range tsrRoutes { - handler, _, tsr := tree.getValue(route) - if handler != nil { - t.Fatalf("non-nil handler for TSR route '%s", route) - } else if !tsr { - t.Errorf("expected TSR recommendation for route '%s'", route) - } - } - - noTsrRoutes := [...]string{ - "/", - "/no", - "/no/", - "/_", - "/_/", - "/api/world/abc", - } - for _, route := range noTsrRoutes { - handler, _, tsr := tree.getValue(route) - if handler != nil { - t.Fatalf("non-nil handler for No-TSR route '%s", route) - } else if tsr { - t.Errorf("expected no TSR recommendation for route '%s'", route) - } - } -} - -func TestTreeFindCaseInsensitivePath(t *testing.T) { - tree := &node{} - - routes := [...]string{ - "/hi", - "/b/", - "/ABC/", - "/search/:query", - "/cmd/:tool/", - "/src/*filepath", - "/x", - "/x/y", - "/y/", - "/y/z", - "/0/:id", - "/0/:id/1", - "/1/:id/", - "/1/:id/2", - "/aa", - "/a/", - "/doc", - "/doc/go_faq.html", - "/doc/go1.html", - "/doc/go/away", - "/no/a", - "/no/b", - } - - for _, route := range routes { - recv := catchPanic(func() { - tree.addRoute(route, fakeHandler(route)) - }) - if recv != nil { - t.Fatalf("panic inserting route '%s': %v", route, recv) - } - } - - // Check out == in for all registered routes - // With fixTrailingSlash = true - for _, route := range routes { - out, found := tree.findCaseInsensitivePath(route, true) - if !found { - t.Errorf("Route '%s' not found!", route) - } else if string(out) != route { - t.Errorf("Wrong result for route '%s': %s", route, string(out)) - } - } - // With fixTrailingSlash = false - for _, route := range routes { - out, found := tree.findCaseInsensitivePath(route, false) - if !found { - t.Errorf("Route '%s' not found!", route) - } else if string(out) != route { - t.Errorf("Wrong result for route '%s': %s", route, string(out)) - } - } - - tests := []struct { - in string - out string - found bool - slash bool - }{ - {"/HI", "/hi", true, false}, - {"/HI/", "/hi", true, true}, - {"/B", "/b/", true, true}, - {"/B/", "/b/", true, false}, - {"/abc", "/ABC/", true, true}, - {"/abc/", "/ABC/", true, false}, - {"/aBc", "/ABC/", true, true}, - {"/aBc/", "/ABC/", true, false}, - {"/abC", "/ABC/", true, true}, - {"/abC/", "/ABC/", true, false}, - {"/SEARCH/QUERY", "/search/QUERY", true, false}, - {"/SEARCH/QUERY/", "/search/QUERY", true, true}, - {"/CMD/TOOL/", "/cmd/TOOL/", true, false}, - {"/CMD/TOOL", "/cmd/TOOL/", true, true}, - {"/SRC/FILE/PATH", "/src/FILE/PATH", true, false}, - {"/x/Y", "/x/y", true, false}, - {"/x/Y/", "/x/y", true, true}, - {"/X/y", "/x/y", true, false}, - {"/X/y/", "/x/y", true, true}, - {"/X/Y", "/x/y", true, false}, - {"/X/Y/", "/x/y", true, true}, - {"/Y/", "/y/", true, false}, - {"/Y", "/y/", true, true}, - {"/Y/z", "/y/z", true, false}, - {"/Y/z/", "/y/z", true, true}, - {"/Y/Z", "/y/z", true, false}, - {"/Y/Z/", "/y/z", true, true}, - {"/y/Z", "/y/z", true, false}, - {"/y/Z/", "/y/z", true, true}, - {"/Aa", "/aa", true, false}, - {"/Aa/", "/aa", true, true}, - {"/AA", "/aa", true, false}, - {"/AA/", "/aa", true, true}, - {"/aA", "/aa", true, false}, - {"/aA/", "/aa", true, true}, - {"/A/", "/a/", true, false}, - {"/A", "/a/", true, true}, - {"/DOC", "/doc", true, false}, - {"/DOC/", "/doc", true, true}, - {"/NO", "", false, true}, - {"/DOC/GO", "", false, true}, - } - // With fixTrailingSlash = true - for _, test := range tests { - out, found := tree.findCaseInsensitivePath(test.in, true) - if found != test.found || (found && (string(out) != test.out)) { - t.Errorf("Wrong result for '%s': got %s, %t; want %s, %t", - test.in, string(out), found, test.out, test.found) - return - } - } - // With fixTrailingSlash = false - for _, test := range tests { - out, found := tree.findCaseInsensitivePath(test.in, false) - if test.slash { - if found { // test needs a trailingSlash fix. It must not be found! - t.Errorf("Found without fixTrailingSlash: %s; got %s", test.in, string(out)) - } - } else { - if found != test.found || (found && (string(out) != test.out)) { - t.Errorf("Wrong result for '%s': got %s, %t; want %s, %t", - test.in, string(out), found, test.out, test.found) - return - } - } - } -} - -func TestTreeInvalidNodeType(t *testing.T) { - tree := &node{} - tree.addRoute("/", fakeHandler("/")) - tree.addRoute("/:page", fakeHandler("/:page")) - - // set invalid node type - tree.children[0].nType = 42 - - // normal lookup - recv := catchPanic(func() { - tree.getValue("/test") - }) - if rs, ok := recv.(string); !ok || rs != "Invalid node type" { - t.Fatalf(`Expected panic "Invalid node type", got "%v"`, recv) - } - - // case-insensitive lookup - recv = catchPanic(func() { - tree.findCaseInsensitivePath("/test", true) - }) - if rs, ok := recv.(string); !ok || rs != "Invalid node type" { - t.Fatalf(`Expected panic "Invalid node type", got "%v"`, recv) - } -} diff --git a/main.go b/main.go index 6f85b06a..76726f52 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,12 @@ package main import ( + "runtime" + "github.com/khlieng/name_pending/commands" ) func main() { + runtime.GOMAXPROCS(runtime.NumCPU()) commands.Execute() } diff --git a/server/server.go b/server/server.go index b79aea95..e69033b6 100644 --- a/server/server.go +++ b/server/server.go @@ -7,7 +7,6 @@ import ( "sync" "github.com/khlieng/name_pending/Godeps/_workspace/src/github.com/gorilla/websocket" - "github.com/khlieng/name_pending/Godeps/_workspace/src/github.com/julienschmidt/httprouter" "github.com/khlieng/name_pending/irc" "github.com/khlieng/name_pending/storage" @@ -35,13 +34,22 @@ func Run(port int) { reconnect() - router := httprouter.New() - - router.HandlerFunc("GET", "/ws", upgradeWS) - router.NotFound = serveFiles - log.Println("Listening on port", port) - log.Fatal(http.ListenAndServe(":"+strconv.Itoa(port), router)) + log.Fatal(http.ListenAndServe(":"+strconv.Itoa(port), handler{})) +} + +type handler struct{} + +func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.Method != "GET" { + return + } + + if r.URL.Path == "/ws" { + upgradeWS(w, r) + } else { + serveFiles(w, r) + } } func upgradeWS(w http.ResponseWriter, r *http.Request) {