Browse Source

Merge pull request #146 from aboch/jpi

Initial libnetwork rest api
Jana Radhakrishnan 10 years ago
parent
commit
a76c5e1490

+ 8 - 0
libnetwork/Godeps/Godeps.json

@@ -55,6 +55,14 @@
 			"Comment": "v2-3-g4160802",
 			"Rev": "41608027bdce7bfa8959d653a00b954591220e67"
 		},
+		{
+			"ImportPath": "github.com/gorilla/context",
+			"Rev": "215affda49addc4c8ef7e2534915df2c8c35c6cd"
+		},
+		{
+			"ImportPath": "github.com/gorilla/mux",
+			"Rev": "8096f47503459bcc74d1f4c487b7e6e42e5746b5"
+		},
 		{
 			"ImportPath": "github.com/vishvananda/netlink",
 			"Rev": "8eb64238879fed52fd51c5b30ad20b928fb4c36c"

+ 9 - 0
libnetwork/Godeps/_workspace/src/github.com/gorilla/context/.travis.yml

@@ -0,0 +1,9 @@
+language: go
+
+go:
+  - 1.0
+  - 1.1
+  - 1.2
+  - 1.3
+  - 1.4
+  - tip

+ 27 - 0
libnetwork/Godeps/_workspace/src/github.com/gorilla/context/LICENSE

@@ -0,0 +1,27 @@
+Copyright (c) 2012 Rodrigo Moraes. 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.
+	 * Neither the name of Google Inc. nor the names of its
+contributors may 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 THE COPYRIGHT
+OWNER OR CONTRIBUTORS 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.

+ 7 - 0
libnetwork/Godeps/_workspace/src/github.com/gorilla/context/README.md

@@ -0,0 +1,7 @@
+context
+=======
+[![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context)
+
+gorilla/context is a general purpose registry for global request variables.
+
+Read the full documentation here: http://www.gorillatoolkit.org/pkg/context

+ 143 - 0
libnetwork/Godeps/_workspace/src/github.com/gorilla/context/context.go

@@ -0,0 +1,143 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package context
+
+import (
+	"net/http"
+	"sync"
+	"time"
+)
+
+var (
+	mutex sync.RWMutex
+	data  = make(map[*http.Request]map[interface{}]interface{})
+	datat = make(map[*http.Request]int64)
+)
+
+// Set stores a value for a given key in a given request.
+func Set(r *http.Request, key, val interface{}) {
+	mutex.Lock()
+	if data[r] == nil {
+		data[r] = make(map[interface{}]interface{})
+		datat[r] = time.Now().Unix()
+	}
+	data[r][key] = val
+	mutex.Unlock()
+}
+
+// Get returns a value stored for a given key in a given request.
+func Get(r *http.Request, key interface{}) interface{} {
+	mutex.RLock()
+	if ctx := data[r]; ctx != nil {
+		value := ctx[key]
+		mutex.RUnlock()
+		return value
+	}
+	mutex.RUnlock()
+	return nil
+}
+
+// GetOk returns stored value and presence state like multi-value return of map access.
+func GetOk(r *http.Request, key interface{}) (interface{}, bool) {
+	mutex.RLock()
+	if _, ok := data[r]; ok {
+		value, ok := data[r][key]
+		mutex.RUnlock()
+		return value, ok
+	}
+	mutex.RUnlock()
+	return nil, false
+}
+
+// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests.
+func GetAll(r *http.Request) map[interface{}]interface{} {
+	mutex.RLock()
+	if context, ok := data[r]; ok {
+		result := make(map[interface{}]interface{}, len(context))
+		for k, v := range context {
+			result[k] = v
+		}
+		mutex.RUnlock()
+		return result
+	}
+	mutex.RUnlock()
+	return nil
+}
+
+// GetAllOk returns all stored values for the request as a map and a boolean value that indicates if
+// the request was registered.
+func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) {
+	mutex.RLock()
+	context, ok := data[r]
+	result := make(map[interface{}]interface{}, len(context))
+	for k, v := range context {
+		result[k] = v
+	}
+	mutex.RUnlock()
+	return result, ok
+}
+
+// Delete removes a value stored for a given key in a given request.
+func Delete(r *http.Request, key interface{}) {
+	mutex.Lock()
+	if data[r] != nil {
+		delete(data[r], key)
+	}
+	mutex.Unlock()
+}
+
+// Clear removes all values stored for a given request.
+//
+// This is usually called by a handler wrapper to clean up request
+// variables at the end of a request lifetime. See ClearHandler().
+func Clear(r *http.Request) {
+	mutex.Lock()
+	clear(r)
+	mutex.Unlock()
+}
+
+// clear is Clear without the lock.
+func clear(r *http.Request) {
+	delete(data, r)
+	delete(datat, r)
+}
+
+// Purge removes request data stored for longer than maxAge, in seconds.
+// It returns the amount of requests removed.
+//
+// If maxAge <= 0, all request data is removed.
+//
+// This is only used for sanity check: in case context cleaning was not
+// properly set some request data can be kept forever, consuming an increasing
+// amount of memory. In case this is detected, Purge() must be called
+// periodically until the problem is fixed.
+func Purge(maxAge int) int {
+	mutex.Lock()
+	count := 0
+	if maxAge <= 0 {
+		count = len(data)
+		data = make(map[*http.Request]map[interface{}]interface{})
+		datat = make(map[*http.Request]int64)
+	} else {
+		min := time.Now().Unix() - int64(maxAge)
+		for r := range data {
+			if datat[r] < min {
+				clear(r)
+				count++
+			}
+		}
+	}
+	mutex.Unlock()
+	return count
+}
+
+// ClearHandler wraps an http.Handler and clears request values at the end
+// of a request lifetime.
+func ClearHandler(h http.Handler) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer Clear(r)
+		h.ServeHTTP(w, r)
+	})
+}

+ 161 - 0
libnetwork/Godeps/_workspace/src/github.com/gorilla/context/context_test.go

@@ -0,0 +1,161 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package context
+
+import (
+	"net/http"
+	"testing"
+)
+
+type keyType int
+
+const (
+	key1 keyType = iota
+	key2
+)
+
+func TestContext(t *testing.T) {
+	assertEqual := func(val interface{}, exp interface{}) {
+		if val != exp {
+			t.Errorf("Expected %v, got %v.", exp, val)
+		}
+	}
+
+	r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
+	emptyR, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
+
+	// Get()
+	assertEqual(Get(r, key1), nil)
+
+	// Set()
+	Set(r, key1, "1")
+	assertEqual(Get(r, key1), "1")
+	assertEqual(len(data[r]), 1)
+
+	Set(r, key2, "2")
+	assertEqual(Get(r, key2), "2")
+	assertEqual(len(data[r]), 2)
+
+	//GetOk
+	value, ok := GetOk(r, key1)
+	assertEqual(value, "1")
+	assertEqual(ok, true)
+
+	value, ok = GetOk(r, "not exists")
+	assertEqual(value, nil)
+	assertEqual(ok, false)
+
+	Set(r, "nil value", nil)
+	value, ok = GetOk(r, "nil value")
+	assertEqual(value, nil)
+	assertEqual(ok, true)
+
+	// GetAll()
+	values := GetAll(r)
+	assertEqual(len(values), 3)
+
+	// GetAll() for empty request
+	values = GetAll(emptyR)
+	if values != nil {
+		t.Error("GetAll didn't return nil value for invalid request")
+	}
+
+	// GetAllOk()
+	values, ok = GetAllOk(r)
+	assertEqual(len(values), 3)
+	assertEqual(ok, true)
+
+	// GetAllOk() for empty request
+	values, ok = GetAllOk(emptyR)
+	assertEqual(value, nil)
+	assertEqual(ok, false)
+
+	// Delete()
+	Delete(r, key1)
+	assertEqual(Get(r, key1), nil)
+	assertEqual(len(data[r]), 2)
+
+	Delete(r, key2)
+	assertEqual(Get(r, key2), nil)
+	assertEqual(len(data[r]), 1)
+
+	// Clear()
+	Clear(r)
+	assertEqual(len(data), 0)
+}
+
+func parallelReader(r *http.Request, key string, iterations int, wait, done chan struct{}) {
+	<-wait
+	for i := 0; i < iterations; i++ {
+		Get(r, key)
+	}
+	done <- struct{}{}
+
+}
+
+func parallelWriter(r *http.Request, key, value string, iterations int, wait, done chan struct{}) {
+	<-wait
+	for i := 0; i < iterations; i++ {
+		Set(r, key, value)
+	}
+	done <- struct{}{}
+
+}
+
+func benchmarkMutex(b *testing.B, numReaders, numWriters, iterations int) {
+
+	b.StopTimer()
+	r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
+	done := make(chan struct{})
+	b.StartTimer()
+
+	for i := 0; i < b.N; i++ {
+		wait := make(chan struct{})
+
+		for i := 0; i < numReaders; i++ {
+			go parallelReader(r, "test", iterations, wait, done)
+		}
+
+		for i := 0; i < numWriters; i++ {
+			go parallelWriter(r, "test", "123", iterations, wait, done)
+		}
+
+		close(wait)
+
+		for i := 0; i < numReaders+numWriters; i++ {
+			<-done
+		}
+
+	}
+
+}
+
+func BenchmarkMutexSameReadWrite1(b *testing.B) {
+	benchmarkMutex(b, 1, 1, 32)
+}
+func BenchmarkMutexSameReadWrite2(b *testing.B) {
+	benchmarkMutex(b, 2, 2, 32)
+}
+func BenchmarkMutexSameReadWrite4(b *testing.B) {
+	benchmarkMutex(b, 4, 4, 32)
+}
+func BenchmarkMutex1(b *testing.B) {
+	benchmarkMutex(b, 2, 8, 32)
+}
+func BenchmarkMutex2(b *testing.B) {
+	benchmarkMutex(b, 16, 4, 64)
+}
+func BenchmarkMutex3(b *testing.B) {
+	benchmarkMutex(b, 1, 2, 128)
+}
+func BenchmarkMutex4(b *testing.B) {
+	benchmarkMutex(b, 128, 32, 256)
+}
+func BenchmarkMutex5(b *testing.B) {
+	benchmarkMutex(b, 1024, 2048, 64)
+}
+func BenchmarkMutex6(b *testing.B) {
+	benchmarkMutex(b, 2048, 1024, 512)
+}

+ 82 - 0
libnetwork/Godeps/_workspace/src/github.com/gorilla/context/doc.go

@@ -0,0 +1,82 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package context stores values shared during a request lifetime.
+
+For example, a router can set variables extracted from the URL and later
+application handlers can access those values, or it can be used to store
+sessions values to be saved at the end of a request. There are several
+others common uses.
+
+The idea was posted by Brad Fitzpatrick to the go-nuts mailing list:
+
+	http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53
+
+Here's the basic usage: first define the keys that you will need. The key
+type is interface{} so a key can be of any type that supports equality.
+Here we define a key using a custom int type to avoid name collisions:
+
+	package foo
+
+	import (
+		"github.com/gorilla/context"
+	)
+
+	type key int
+
+	const MyKey key = 0
+
+Then set a variable. Variables are bound to an http.Request object, so you
+need a request instance to set a value:
+
+	context.Set(r, MyKey, "bar")
+
+The application can later access the variable using the same key you provided:
+
+	func MyHandler(w http.ResponseWriter, r *http.Request) {
+		// val is "bar".
+		val := context.Get(r, foo.MyKey)
+
+		// returns ("bar", true)
+		val, ok := context.GetOk(r, foo.MyKey)
+		// ...
+	}
+
+And that's all about the basic usage. We discuss some other ideas below.
+
+Any type can be stored in the context. To enforce a given type, make the key
+private and wrap Get() and Set() to accept and return values of a specific
+type:
+
+	type key int
+
+	const mykey key = 0
+
+	// GetMyKey returns a value for this package from the request values.
+	func GetMyKey(r *http.Request) SomeType {
+		if rv := context.Get(r, mykey); rv != nil {
+			return rv.(SomeType)
+		}
+		return nil
+	}
+
+	// SetMyKey sets a value for this package in the request values.
+	func SetMyKey(r *http.Request, val SomeType) {
+		context.Set(r, mykey, val)
+	}
+
+Variables must be cleared at the end of a request, to remove all values
+that were stored. This can be done in an http.Handler, after a request was
+served. Just call Clear() passing the request:
+
+	context.Clear(r)
+
+...or use ClearHandler(), which conveniently wraps an http.Handler to clear
+variables at the end of a request lifetime.
+
+The Routers from the packages gorilla/mux and gorilla/pat call Clear()
+so if you are using either of them you don't need to clear the context manually.
+*/
+package context

+ 7 - 0
libnetwork/Godeps/_workspace/src/github.com/gorilla/mux/.travis.yml

@@ -0,0 +1,7 @@
+language: go
+
+go:
+  - 1.0
+  - 1.1
+  - 1.2
+  - tip

+ 27 - 0
libnetwork/Godeps/_workspace/src/github.com/gorilla/mux/LICENSE

@@ -0,0 +1,27 @@
+Copyright (c) 2012 Rodrigo Moraes. 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.
+	 * Neither the name of Google Inc. nor the names of its
+contributors may 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 THE COPYRIGHT
+OWNER OR CONTRIBUTORS 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.

+ 7 - 0
libnetwork/Godeps/_workspace/src/github.com/gorilla/mux/README.md

@@ -0,0 +1,7 @@
+mux
+===
+[![Build Status](https://travis-ci.org/gorilla/mux.png?branch=master)](https://travis-ci.org/gorilla/mux)
+
+gorilla/mux is a powerful URL router and dispatcher.
+
+Read the full documentation here: http://www.gorillatoolkit.org/pkg/mux

+ 21 - 0
libnetwork/Godeps/_workspace/src/github.com/gorilla/mux/bench_test.go

@@ -0,0 +1,21 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mux
+
+import (
+	"net/http"
+	"testing"
+)
+
+func BenchmarkMux(b *testing.B) {
+	router := new(Router)
+	handler := func(w http.ResponseWriter, r *http.Request) {}
+	router.HandleFunc("/v1/{v1}", handler)
+
+	request, _ := http.NewRequest("GET", "/v1/anything", nil)
+	for i := 0; i < b.N; i++ {
+		router.ServeHTTP(nil, request)
+	}
+}

+ 199 - 0
libnetwork/Godeps/_workspace/src/github.com/gorilla/mux/doc.go

@@ -0,0 +1,199 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package gorilla/mux implements a request router and dispatcher.
+
+The name mux stands for "HTTP request multiplexer". Like the standard
+http.ServeMux, mux.Router matches incoming requests against a list of
+registered routes and calls a handler for the route that matches the URL
+or other conditions. The main features are:
+
+	* Requests can be matched based on URL host, path, path prefix, schemes,
+	  header and query values, HTTP methods or using custom matchers.
+	* URL hosts and paths can have variables with an optional regular
+	  expression.
+	* Registered URLs can be built, or "reversed", which helps maintaining
+	  references to resources.
+	* Routes can be used as subrouters: nested routes are only tested if the
+	  parent route matches. This is useful to define groups of routes that
+	  share common conditions like a host, a path prefix or other repeated
+	  attributes. As a bonus, this optimizes request matching.
+	* It implements the http.Handler interface so it is compatible with the
+	  standard http.ServeMux.
+
+Let's start registering a couple of URL paths and handlers:
+
+	func main() {
+		r := mux.NewRouter()
+		r.HandleFunc("/", HomeHandler)
+		r.HandleFunc("/products", ProductsHandler)
+		r.HandleFunc("/articles", ArticlesHandler)
+		http.Handle("/", r)
+	}
+
+Here we register three routes mapping URL paths to handlers. This is
+equivalent to how http.HandleFunc() works: if an incoming request URL matches
+one of the paths, the corresponding handler is called passing
+(http.ResponseWriter, *http.Request) as parameters.
+
+Paths can have variables. They are defined using the format {name} or
+{name:pattern}. If a regular expression pattern is not defined, the matched
+variable will be anything until the next slash. For example:
+
+	r := mux.NewRouter()
+	r.HandleFunc("/products/{key}", ProductHandler)
+	r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
+	r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
+
+The names are used to create a map of route variables which can be retrieved
+calling mux.Vars():
+
+	vars := mux.Vars(request)
+	category := vars["category"]
+
+And this is all you need to know about the basic usage. More advanced options
+are explained below.
+
+Routes can also be restricted to a domain or subdomain. Just define a host
+pattern to be matched. They can also have variables:
+
+	r := mux.NewRouter()
+	// Only matches if domain is "www.domain.com".
+	r.Host("www.domain.com")
+	// Matches a dynamic subdomain.
+	r.Host("{subdomain:[a-z]+}.domain.com")
+
+There are several other matchers that can be added. To match path prefixes:
+
+	r.PathPrefix("/products/")
+
+...or HTTP methods:
+
+	r.Methods("GET", "POST")
+
+...or URL schemes:
+
+	r.Schemes("https")
+
+...or header values:
+
+	r.Headers("X-Requested-With", "XMLHttpRequest")
+
+...or query values:
+
+	r.Queries("key", "value")
+
+...or to use a custom matcher function:
+
+	r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
+		return r.ProtoMajor == 0
+    })
+
+...and finally, it is possible to combine several matchers in a single route:
+
+	r.HandleFunc("/products", ProductsHandler).
+	  Host("www.domain.com").
+	  Methods("GET").
+	  Schemes("http")
+
+Setting the same matching conditions again and again can be boring, so we have
+a way to group several routes that share the same requirements.
+We call it "subrouting".
+
+For example, let's say we have several URLs that should only match when the
+host is "www.domain.com". Create a route for that host and get a "subrouter"
+from it:
+
+	r := mux.NewRouter()
+	s := r.Host("www.domain.com").Subrouter()
+
+Then register routes in the subrouter:
+
+	s.HandleFunc("/products/", ProductsHandler)
+	s.HandleFunc("/products/{key}", ProductHandler)
+	s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
+
+The three URL paths we registered above will only be tested if the domain is
+"www.domain.com", because the subrouter is tested first. This is not
+only convenient, but also optimizes request matching. You can create
+subrouters combining any attribute matchers accepted by a route.
+
+Subrouters can be used to create domain or path "namespaces": you define
+subrouters in a central place and then parts of the app can register its
+paths relatively to a given subrouter.
+
+There's one more thing about subroutes. When a subrouter has a path prefix,
+the inner routes use it as base for their paths:
+
+	r := mux.NewRouter()
+	s := r.PathPrefix("/products").Subrouter()
+	// "/products/"
+	s.HandleFunc("/", ProductsHandler)
+	// "/products/{key}/"
+	s.HandleFunc("/{key}/", ProductHandler)
+	// "/products/{key}/details"
+	s.HandleFunc("/{key}/details", ProductDetailsHandler)
+
+Now let's see how to build registered URLs.
+
+Routes can be named. All routes that define a name can have their URLs built,
+or "reversed". We define a name calling Name() on a route. For example:
+
+	r := mux.NewRouter()
+	r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
+	  Name("article")
+
+To build a URL, get the route and call the URL() method, passing a sequence of
+key/value pairs for the route variables. For the previous route, we would do:
+
+	url, err := r.Get("article").URL("category", "technology", "id", "42")
+
+...and the result will be a url.URL with the following path:
+
+	"/articles/technology/42"
+
+This also works for host variables:
+
+	r := mux.NewRouter()
+	r.Host("{subdomain}.domain.com").
+	  Path("/articles/{category}/{id:[0-9]+}").
+	  HandlerFunc(ArticleHandler).
+	  Name("article")
+
+	// url.String() will be "http://news.domain.com/articles/technology/42"
+	url, err := r.Get("article").URL("subdomain", "news",
+									 "category", "technology",
+									 "id", "42")
+
+All variables defined in the route are required, and their values must
+conform to the corresponding patterns. These requirements guarantee that a
+generated URL will always match a registered route -- the only exception is
+for explicitly defined "build-only" routes which never match.
+
+There's also a way to build only the URL host or path for a route:
+use the methods URLHost() or URLPath() instead. For the previous route,
+we would do:
+
+	// "http://news.domain.com/"
+	host, err := r.Get("article").URLHost("subdomain", "news")
+
+	// "/articles/technology/42"
+	path, err := r.Get("article").URLPath("category", "technology", "id", "42")
+
+And if you use subrouters, host and path defined separately can be built
+as well:
+
+	r := mux.NewRouter()
+	s := r.Host("{subdomain}.domain.com").Subrouter()
+	s.Path("/articles/{category}/{id:[0-9]+}").
+	  HandlerFunc(ArticleHandler).
+	  Name("article")
+
+	// "http://news.domain.com/articles/technology/42"
+	url, err := r.Get("article").URL("subdomain", "news",
+									 "category", "technology",
+									 "id", "42")
+*/
+package mux

+ 366 - 0
libnetwork/Godeps/_workspace/src/github.com/gorilla/mux/mux.go

@@ -0,0 +1,366 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mux
+
+import (
+	"fmt"
+	"net/http"
+	"path"
+
+	"github.com/gorilla/context"
+)
+
+// NewRouter returns a new router instance.
+func NewRouter() *Router {
+	return &Router{namedRoutes: make(map[string]*Route), KeepContext: false}
+}
+
+// Router registers routes to be matched and dispatches a handler.
+//
+// It implements the http.Handler interface, so it can be registered to serve
+// requests:
+//
+//     var router = mux.NewRouter()
+//
+//     func main() {
+//         http.Handle("/", router)
+//     }
+//
+// Or, for Google App Engine, register it in a init() function:
+//
+//     func init() {
+//         http.Handle("/", router)
+//     }
+//
+// This will send all incoming requests to the router.
+type Router struct {
+	// Configurable Handler to be used when no route matches.
+	NotFoundHandler http.Handler
+	// Parent route, if this is a subrouter.
+	parent parentRoute
+	// Routes to be matched, in order.
+	routes []*Route
+	// Routes by name for URL building.
+	namedRoutes map[string]*Route
+	// See Router.StrictSlash(). This defines the flag for new routes.
+	strictSlash bool
+	// If true, do not clear the request context after handling the request
+	KeepContext bool
+}
+
+// Match matches registered routes against the request.
+func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
+	for _, route := range r.routes {
+		if route.Match(req, match) {
+			return true
+		}
+	}
+	return false
+}
+
+// ServeHTTP dispatches the handler registered in the matched route.
+//
+// When there is a match, the route variables can be retrieved calling
+// mux.Vars(request).
+func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+	// Clean path to canonical form and redirect.
+	if p := cleanPath(req.URL.Path); p != req.URL.Path {
+
+		// Added 3 lines (Philip Schlump) - It was droping the query string and #whatever from query.
+		// This matches with fix in go 1.2 r.c. 4 for same problem.  Go Issue:
+		// http://code.google.com/p/go/issues/detail?id=5252
+		url := *req.URL
+		url.Path = p
+		p = url.String()
+
+		w.Header().Set("Location", p)
+		w.WriteHeader(http.StatusMovedPermanently)
+		return
+	}
+	var match RouteMatch
+	var handler http.Handler
+	if r.Match(req, &match) {
+		handler = match.Handler
+		setVars(req, match.Vars)
+		setCurrentRoute(req, match.Route)
+	}
+	if handler == nil {
+		handler = r.NotFoundHandler
+		if handler == nil {
+			handler = http.NotFoundHandler()
+		}
+	}
+	if !r.KeepContext {
+		defer context.Clear(req)
+	}
+	handler.ServeHTTP(w, req)
+}
+
+// Get returns a route registered with the given name.
+func (r *Router) Get(name string) *Route {
+	return r.getNamedRoutes()[name]
+}
+
+// GetRoute returns a route registered with the given name. This method
+// was renamed to Get() and remains here for backwards compatibility.
+func (r *Router) GetRoute(name string) *Route {
+	return r.getNamedRoutes()[name]
+}
+
+// StrictSlash defines the trailing slash behavior for new routes. The initial
+// value is false.
+//
+// When true, if the route path is "/path/", accessing "/path" will redirect
+// to the former and vice versa. In other words, your application will always
+// see the path as specified in the route.
+//
+// When false, if the route path is "/path", accessing "/path/" will not match
+// this route and vice versa.
+//
+// Special case: when a route sets a path prefix using the PathPrefix() method,
+// strict slash is ignored for that route because the redirect behavior can't
+// be determined from a prefix alone. However, any subrouters created from that
+// route inherit the original StrictSlash setting.
+func (r *Router) StrictSlash(value bool) *Router {
+	r.strictSlash = value
+	return r
+}
+
+// ----------------------------------------------------------------------------
+// parentRoute
+// ----------------------------------------------------------------------------
+
+// getNamedRoutes returns the map where named routes are registered.
+func (r *Router) getNamedRoutes() map[string]*Route {
+	if r.namedRoutes == nil {
+		if r.parent != nil {
+			r.namedRoutes = r.parent.getNamedRoutes()
+		} else {
+			r.namedRoutes = make(map[string]*Route)
+		}
+	}
+	return r.namedRoutes
+}
+
+// getRegexpGroup returns regexp definitions from the parent route, if any.
+func (r *Router) getRegexpGroup() *routeRegexpGroup {
+	if r.parent != nil {
+		return r.parent.getRegexpGroup()
+	}
+	return nil
+}
+
+func (r *Router) buildVars(m map[string]string) map[string]string {
+	if r.parent != nil {
+		m = r.parent.buildVars(m)
+	}
+	return m
+}
+
+// ----------------------------------------------------------------------------
+// Route factories
+// ----------------------------------------------------------------------------
+
+// NewRoute registers an empty route.
+func (r *Router) NewRoute() *Route {
+	route := &Route{parent: r, strictSlash: r.strictSlash}
+	r.routes = append(r.routes, route)
+	return route
+}
+
+// Handle registers a new route with a matcher for the URL path.
+// See Route.Path() and Route.Handler().
+func (r *Router) Handle(path string, handler http.Handler) *Route {
+	return r.NewRoute().Path(path).Handler(handler)
+}
+
+// HandleFunc registers a new route with a matcher for the URL path.
+// See Route.Path() and Route.HandlerFunc().
+func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,
+	*http.Request)) *Route {
+	return r.NewRoute().Path(path).HandlerFunc(f)
+}
+
+// Headers registers a new route with a matcher for request header values.
+// See Route.Headers().
+func (r *Router) Headers(pairs ...string) *Route {
+	return r.NewRoute().Headers(pairs...)
+}
+
+// Host registers a new route with a matcher for the URL host.
+// See Route.Host().
+func (r *Router) Host(tpl string) *Route {
+	return r.NewRoute().Host(tpl)
+}
+
+// MatcherFunc registers a new route with a custom matcher function.
+// See Route.MatcherFunc().
+func (r *Router) MatcherFunc(f MatcherFunc) *Route {
+	return r.NewRoute().MatcherFunc(f)
+}
+
+// Methods registers a new route with a matcher for HTTP methods.
+// See Route.Methods().
+func (r *Router) Methods(methods ...string) *Route {
+	return r.NewRoute().Methods(methods...)
+}
+
+// Path registers a new route with a matcher for the URL path.
+// See Route.Path().
+func (r *Router) Path(tpl string) *Route {
+	return r.NewRoute().Path(tpl)
+}
+
+// PathPrefix registers a new route with a matcher for the URL path prefix.
+// See Route.PathPrefix().
+func (r *Router) PathPrefix(tpl string) *Route {
+	return r.NewRoute().PathPrefix(tpl)
+}
+
+// Queries registers a new route with a matcher for URL query values.
+// See Route.Queries().
+func (r *Router) Queries(pairs ...string) *Route {
+	return r.NewRoute().Queries(pairs...)
+}
+
+// Schemes registers a new route with a matcher for URL schemes.
+// See Route.Schemes().
+func (r *Router) Schemes(schemes ...string) *Route {
+	return r.NewRoute().Schemes(schemes...)
+}
+
+// BuildVars registers a new route with a custom function for modifying
+// route variables before building a URL.
+func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route {
+	return r.NewRoute().BuildVarsFunc(f)
+}
+
+// ----------------------------------------------------------------------------
+// Context
+// ----------------------------------------------------------------------------
+
+// RouteMatch stores information about a matched route.
+type RouteMatch struct {
+	Route   *Route
+	Handler http.Handler
+	Vars    map[string]string
+}
+
+type contextKey int
+
+const (
+	varsKey contextKey = iota
+	routeKey
+)
+
+// Vars returns the route variables for the current request, if any.
+func Vars(r *http.Request) map[string]string {
+	if rv := context.Get(r, varsKey); rv != nil {
+		return rv.(map[string]string)
+	}
+	return nil
+}
+
+// CurrentRoute returns the matched route for the current request, if any.
+func CurrentRoute(r *http.Request) *Route {
+	if rv := context.Get(r, routeKey); rv != nil {
+		return rv.(*Route)
+	}
+	return nil
+}
+
+func setVars(r *http.Request, val interface{}) {
+	context.Set(r, varsKey, val)
+}
+
+func setCurrentRoute(r *http.Request, val interface{}) {
+	context.Set(r, routeKey, val)
+}
+
+// ----------------------------------------------------------------------------
+// Helpers
+// ----------------------------------------------------------------------------
+
+// cleanPath returns the canonical path for p, eliminating . and .. elements.
+// Borrowed from the net/http package.
+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
+}
+
+// uniqueVars returns an error if two slices contain duplicated strings.
+func uniqueVars(s1, s2 []string) error {
+	for _, v1 := range s1 {
+		for _, v2 := range s2 {
+			if v1 == v2 {
+				return fmt.Errorf("mux: duplicated route variable %q", v2)
+			}
+		}
+	}
+	return nil
+}
+
+// mapFromPairs converts variadic string parameters to a string map.
+func mapFromPairs(pairs ...string) (map[string]string, error) {
+	length := len(pairs)
+	if length%2 != 0 {
+		return nil, fmt.Errorf(
+			"mux: number of parameters must be multiple of 2, got %v", pairs)
+	}
+	m := make(map[string]string, length/2)
+	for i := 0; i < length; i += 2 {
+		m[pairs[i]] = pairs[i+1]
+	}
+	return m, nil
+}
+
+// matchInArray returns true if the given string value is in the array.
+func matchInArray(arr []string, value string) bool {
+	for _, v := range arr {
+		if v == value {
+			return true
+		}
+	}
+	return false
+}
+
+// matchMap returns true if the given key/value pairs exist in a given map.
+func matchMap(toCheck map[string]string, toMatch map[string][]string,
+	canonicalKey bool) bool {
+	for k, v := range toCheck {
+		// Check if key exists.
+		if canonicalKey {
+			k = http.CanonicalHeaderKey(k)
+		}
+		if values := toMatch[k]; values == nil {
+			return false
+		} else if v != "" {
+			// If value was defined as an empty string we only check that the
+			// key exists. Otherwise we also check for equality.
+			valueExists := false
+			for _, value := range values {
+				if v == value {
+					valueExists = true
+					break
+				}
+			}
+			if !valueExists {
+				return false
+			}
+		}
+	}
+	return true
+}

+ 1003 - 0
libnetwork/Godeps/_workspace/src/github.com/gorilla/mux/mux_test.go

@@ -0,0 +1,1003 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mux
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gorilla/context"
+)
+
+type routeTest struct {
+	title          string            // title of the test
+	route          *Route            // the route being tested
+	request        *http.Request     // a request to test the route
+	vars           map[string]string // the expected vars of the match
+	host           string            // the expected host of the match
+	path           string            // the expected path of the match
+	shouldMatch    bool              // whether the request is expected to match the route at all
+	shouldRedirect bool              // whether the request should result in a redirect
+}
+
+func TestHost(t *testing.T) {
+	// newRequestHost a new request with a method, url, and host header
+	newRequestHost := func(method, url, host string) *http.Request {
+		req, err := http.NewRequest(method, url, nil)
+		if err != nil {
+			panic(err)
+		}
+		req.Host = host
+		return req
+	}
+
+	tests := []routeTest{
+		{
+			title:       "Host route match",
+			route:       new(Route).Host("aaa.bbb.ccc"),
+			request:     newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
+			vars:        map[string]string{},
+			host:        "aaa.bbb.ccc",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "Host route, wrong host in request URL",
+			route:       new(Route).Host("aaa.bbb.ccc"),
+			request:     newRequest("GET", "http://aaa.222.ccc/111/222/333"),
+			vars:        map[string]string{},
+			host:        "aaa.bbb.ccc",
+			path:        "",
+			shouldMatch: false,
+		},
+		{
+			title:       "Host route with port, match",
+			route:       new(Route).Host("aaa.bbb.ccc:1234"),
+			request:     newRequest("GET", "http://aaa.bbb.ccc:1234/111/222/333"),
+			vars:        map[string]string{},
+			host:        "aaa.bbb.ccc:1234",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "Host route with port, wrong port in request URL",
+			route:       new(Route).Host("aaa.bbb.ccc:1234"),
+			request:     newRequest("GET", "http://aaa.bbb.ccc:9999/111/222/333"),
+			vars:        map[string]string{},
+			host:        "aaa.bbb.ccc:1234",
+			path:        "",
+			shouldMatch: false,
+		},
+		{
+			title:       "Host route, match with host in request header",
+			route:       new(Route).Host("aaa.bbb.ccc"),
+			request:     newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc"),
+			vars:        map[string]string{},
+			host:        "aaa.bbb.ccc",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "Host route, wrong host in request header",
+			route:       new(Route).Host("aaa.bbb.ccc"),
+			request:     newRequestHost("GET", "/111/222/333", "aaa.222.ccc"),
+			vars:        map[string]string{},
+			host:        "aaa.bbb.ccc",
+			path:        "",
+			shouldMatch: false,
+		},
+		// BUG {new(Route).Host("aaa.bbb.ccc:1234"), newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:1234"), map[string]string{}, "aaa.bbb.ccc:1234", "", true},
+		{
+			title:       "Host route with port, wrong host in request header",
+			route:       new(Route).Host("aaa.bbb.ccc:1234"),
+			request:     newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:9999"),
+			vars:        map[string]string{},
+			host:        "aaa.bbb.ccc:1234",
+			path:        "",
+			shouldMatch: false,
+		},
+		{
+			title:       "Host route with pattern, match",
+			route:       new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"),
+			request:     newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
+			vars:        map[string]string{"v1": "bbb"},
+			host:        "aaa.bbb.ccc",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "Host route with pattern, wrong host in request URL",
+			route:       new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"),
+			request:     newRequest("GET", "http://aaa.222.ccc/111/222/333"),
+			vars:        map[string]string{"v1": "bbb"},
+			host:        "aaa.bbb.ccc",
+			path:        "",
+			shouldMatch: false,
+		},
+		{
+			title:       "Host route with multiple patterns, match",
+			route:       new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"),
+			request:     newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
+			vars:        map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"},
+			host:        "aaa.bbb.ccc",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "Host route with multiple patterns, wrong host in request URL",
+			route:       new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"),
+			request:     newRequest("GET", "http://aaa.222.ccc/111/222/333"),
+			vars:        map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"},
+			host:        "aaa.bbb.ccc",
+			path:        "",
+			shouldMatch: false,
+		},
+		{
+			title:       "Path route with single pattern with pipe, match",
+			route:       new(Route).Path("/{category:a|b/c}"),
+			request:     newRequest("GET", "http://localhost/a"),
+			vars:        map[string]string{"category": "a"},
+			host:        "",
+			path:        "/a",
+			shouldMatch: true,
+		},
+		{
+			title:       "Path route with single pattern with pipe, match",
+			route:       new(Route).Path("/{category:a|b/c}"),
+			request:     newRequest("GET", "http://localhost/b/c"),
+			vars:        map[string]string{"category": "b/c"},
+			host:        "",
+			path:        "/b/c",
+			shouldMatch: true,
+		},
+		{
+			title:       "Path route with multiple patterns with pipe, match",
+			route:       new(Route).Path("/{category:a|b/c}/{product}/{id:[0-9]+}"),
+			request:     newRequest("GET", "http://localhost/a/product_name/1"),
+			vars:        map[string]string{"category": "a", "product": "product_name", "id": "1"},
+			host:        "",
+			path:        "/a/product_name/1",
+			shouldMatch: true,
+		},
+	}
+	for _, test := range tests {
+		testRoute(t, test)
+	}
+}
+
+func TestPath(t *testing.T) {
+	tests := []routeTest{
+		{
+			title:       "Path route, match",
+			route:       new(Route).Path("/111/222/333"),
+			request:     newRequest("GET", "http://localhost/111/222/333"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "/111/222/333",
+			shouldMatch: true,
+		},
+		{
+			title:       "Path route, match with trailing slash in request and path",
+			route:       new(Route).Path("/111/"),
+			request:     newRequest("GET", "http://localhost/111/"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "/111/",
+			shouldMatch: true,
+		},
+		{
+			title:       "Path route, do not match with trailing slash in path",
+			route:       new(Route).Path("/111/"),
+			request:     newRequest("GET", "http://localhost/111"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "/111",
+			shouldMatch: false,
+		},
+		{
+			title:       "Path route, do not match with trailing slash in request",
+			route:       new(Route).Path("/111"),
+			request:     newRequest("GET", "http://localhost/111/"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "/111/",
+			shouldMatch: false,
+		},
+		{
+			title:       "Path route, wrong path in request in request URL",
+			route:       new(Route).Path("/111/222/333"),
+			request:     newRequest("GET", "http://localhost/1/2/3"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "/111/222/333",
+			shouldMatch: false,
+		},
+		{
+			title:       "Path route with pattern, match",
+			route:       new(Route).Path("/111/{v1:[0-9]{3}}/333"),
+			request:     newRequest("GET", "http://localhost/111/222/333"),
+			vars:        map[string]string{"v1": "222"},
+			host:        "",
+			path:        "/111/222/333",
+			shouldMatch: true,
+		},
+		{
+			title:       "Path route with pattern, URL in request does not match",
+			route:       new(Route).Path("/111/{v1:[0-9]{3}}/333"),
+			request:     newRequest("GET", "http://localhost/111/aaa/333"),
+			vars:        map[string]string{"v1": "222"},
+			host:        "",
+			path:        "/111/222/333",
+			shouldMatch: false,
+		},
+		{
+			title:       "Path route with multiple patterns, match",
+			route:       new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"),
+			request:     newRequest("GET", "http://localhost/111/222/333"),
+			vars:        map[string]string{"v1": "111", "v2": "222", "v3": "333"},
+			host:        "",
+			path:        "/111/222/333",
+			shouldMatch: true,
+		},
+		{
+			title:       "Path route with multiple patterns, URL in request does not match",
+			route:       new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"),
+			request:     newRequest("GET", "http://localhost/111/aaa/333"),
+			vars:        map[string]string{"v1": "111", "v2": "222", "v3": "333"},
+			host:        "",
+			path:        "/111/222/333",
+			shouldMatch: false,
+		},
+	}
+
+	for _, test := range tests {
+		testRoute(t, test)
+	}
+}
+
+func TestPathPrefix(t *testing.T) {
+	tests := []routeTest{
+		{
+			title:       "PathPrefix route, match",
+			route:       new(Route).PathPrefix("/111"),
+			request:     newRequest("GET", "http://localhost/111/222/333"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "/111",
+			shouldMatch: true,
+		},
+		{
+			title:       "PathPrefix route, match substring",
+			route:       new(Route).PathPrefix("/1"),
+			request:     newRequest("GET", "http://localhost/111/222/333"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "/1",
+			shouldMatch: true,
+		},
+		{
+			title:       "PathPrefix route, URL prefix in request does not match",
+			route:       new(Route).PathPrefix("/111"),
+			request:     newRequest("GET", "http://localhost/1/2/3"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "/111",
+			shouldMatch: false,
+		},
+		{
+			title:       "PathPrefix route with pattern, match",
+			route:       new(Route).PathPrefix("/111/{v1:[0-9]{3}}"),
+			request:     newRequest("GET", "http://localhost/111/222/333"),
+			vars:        map[string]string{"v1": "222"},
+			host:        "",
+			path:        "/111/222",
+			shouldMatch: true,
+		},
+		{
+			title:       "PathPrefix route with pattern, URL prefix in request does not match",
+			route:       new(Route).PathPrefix("/111/{v1:[0-9]{3}}"),
+			request:     newRequest("GET", "http://localhost/111/aaa/333"),
+			vars:        map[string]string{"v1": "222"},
+			host:        "",
+			path:        "/111/222",
+			shouldMatch: false,
+		},
+		{
+			title:       "PathPrefix route with multiple patterns, match",
+			route:       new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"),
+			request:     newRequest("GET", "http://localhost/111/222/333"),
+			vars:        map[string]string{"v1": "111", "v2": "222"},
+			host:        "",
+			path:        "/111/222",
+			shouldMatch: true,
+		},
+		{
+			title:       "PathPrefix route with multiple patterns, URL prefix in request does not match",
+			route:       new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"),
+			request:     newRequest("GET", "http://localhost/111/aaa/333"),
+			vars:        map[string]string{"v1": "111", "v2": "222"},
+			host:        "",
+			path:        "/111/222",
+			shouldMatch: false,
+		},
+	}
+
+	for _, test := range tests {
+		testRoute(t, test)
+	}
+}
+
+func TestHostPath(t *testing.T) {
+	tests := []routeTest{
+		{
+			title:       "Host and Path route, match",
+			route:       new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"),
+			request:     newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "Host and Path route, wrong host in request URL",
+			route:       new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"),
+			request:     newRequest("GET", "http://aaa.222.ccc/111/222/333"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: false,
+		},
+		{
+			title:       "Host and Path route with pattern, match",
+			route:       new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"),
+			request:     newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
+			vars:        map[string]string{"v1": "bbb", "v2": "222"},
+			host:        "aaa.bbb.ccc",
+			path:        "/111/222/333",
+			shouldMatch: true,
+		},
+		{
+			title:       "Host and Path route with pattern, URL in request does not match",
+			route:       new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"),
+			request:     newRequest("GET", "http://aaa.222.ccc/111/222/333"),
+			vars:        map[string]string{"v1": "bbb", "v2": "222"},
+			host:        "aaa.bbb.ccc",
+			path:        "/111/222/333",
+			shouldMatch: false,
+		},
+		{
+			title:       "Host and Path route with multiple patterns, match",
+			route:       new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"),
+			request:     newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
+			vars:        map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"},
+			host:        "aaa.bbb.ccc",
+			path:        "/111/222/333",
+			shouldMatch: true,
+		},
+		{
+			title:       "Host and Path route with multiple patterns, URL in request does not match",
+			route:       new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"),
+			request:     newRequest("GET", "http://aaa.222.ccc/111/222/333"),
+			vars:        map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"},
+			host:        "aaa.bbb.ccc",
+			path:        "/111/222/333",
+			shouldMatch: false,
+		},
+	}
+
+	for _, test := range tests {
+		testRoute(t, test)
+	}
+}
+
+func TestHeaders(t *testing.T) {
+	// newRequestHeaders creates a new request with a method, url, and headers
+	newRequestHeaders := func(method, url string, headers map[string]string) *http.Request {
+		req, err := http.NewRequest(method, url, nil)
+		if err != nil {
+			panic(err)
+		}
+		for k, v := range headers {
+			req.Header.Add(k, v)
+		}
+		return req
+	}
+
+	tests := []routeTest{
+		{
+			title:       "Headers route, match",
+			route:       new(Route).Headers("foo", "bar", "baz", "ding"),
+			request:     newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "ding"}),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "Headers route, bad header values",
+			route:       new(Route).Headers("foo", "bar", "baz", "ding"),
+			request:     newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "dong"}),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: false,
+		},
+	}
+
+	for _, test := range tests {
+		testRoute(t, test)
+	}
+
+}
+
+func TestMethods(t *testing.T) {
+	tests := []routeTest{
+		{
+			title:       "Methods route, match GET",
+			route:       new(Route).Methods("GET", "POST"),
+			request:     newRequest("GET", "http://localhost"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "Methods route, match POST",
+			route:       new(Route).Methods("GET", "POST"),
+			request:     newRequest("POST", "http://localhost"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "Methods route, bad method",
+			route:       new(Route).Methods("GET", "POST"),
+			request:     newRequest("PUT", "http://localhost"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: false,
+		},
+	}
+
+	for _, test := range tests {
+		testRoute(t, test)
+	}
+}
+
+func TestQueries(t *testing.T) {
+	tests := []routeTest{
+		{
+			title:       "Queries route, match",
+			route:       new(Route).Queries("foo", "bar", "baz", "ding"),
+			request:     newRequest("GET", "http://localhost?foo=bar&baz=ding"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "Queries route, match with a query string",
+			route:       new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"),
+			request:     newRequest("GET", "http://www.example.com/api?foo=bar&baz=ding"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "Queries route, match with a query string out of order",
+			route:       new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"),
+			request:     newRequest("GET", "http://www.example.com/api?baz=ding&foo=bar"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "Queries route, bad query",
+			route:       new(Route).Queries("foo", "bar", "baz", "ding"),
+			request:     newRequest("GET", "http://localhost?foo=bar&baz=dong"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: false,
+		},
+		{
+			title:       "Queries route with pattern, match",
+			route:       new(Route).Queries("foo", "{v1}"),
+			request:     newRequest("GET", "http://localhost?foo=bar"),
+			vars:        map[string]string{"v1": "bar"},
+			host:        "",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "Queries route with multiple patterns, match",
+			route:       new(Route).Queries("foo", "{v1}", "baz", "{v2}"),
+			request:     newRequest("GET", "http://localhost?foo=bar&baz=ding"),
+			vars:        map[string]string{"v1": "bar", "v2": "ding"},
+			host:        "",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "Queries route with regexp pattern, match",
+			route:       new(Route).Queries("foo", "{v1:[0-9]+}"),
+			request:     newRequest("GET", "http://localhost?foo=10"),
+			vars:        map[string]string{"v1": "10"},
+			host:        "",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "Queries route with regexp pattern, regexp does not match",
+			route:       new(Route).Queries("foo", "{v1:[0-9]+}"),
+			request:     newRequest("GET", "http://localhost?foo=a"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: false,
+		},
+	}
+
+	for _, test := range tests {
+		testRoute(t, test)
+	}
+}
+
+func TestSchemes(t *testing.T) {
+	tests := []routeTest{
+		// Schemes
+		{
+			title:       "Schemes route, match https",
+			route:       new(Route).Schemes("https", "ftp"),
+			request:     newRequest("GET", "https://localhost"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "Schemes route, match ftp",
+			route:       new(Route).Schemes("https", "ftp"),
+			request:     newRequest("GET", "ftp://localhost"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "Schemes route, bad scheme",
+			route:       new(Route).Schemes("https", "ftp"),
+			request:     newRequest("GET", "http://localhost"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: false,
+		},
+	}
+	for _, test := range tests {
+		testRoute(t, test)
+	}
+}
+
+func TestMatcherFunc(t *testing.T) {
+	m := func(r *http.Request, m *RouteMatch) bool {
+		if r.URL.Host == "aaa.bbb.ccc" {
+			return true
+		}
+		return false
+	}
+
+	tests := []routeTest{
+		{
+			title:       "MatchFunc route, match",
+			route:       new(Route).MatcherFunc(m),
+			request:     newRequest("GET", "http://aaa.bbb.ccc"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: true,
+		},
+		{
+			title:       "MatchFunc route, non-match",
+			route:       new(Route).MatcherFunc(m),
+			request:     newRequest("GET", "http://aaa.222.ccc"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: false,
+		},
+	}
+
+	for _, test := range tests {
+		testRoute(t, test)
+	}
+}
+
+func TestBuildVarsFunc(t *testing.T) {
+	tests := []routeTest{
+		{
+			title: "BuildVarsFunc set on route",
+			route: new(Route).Path(`/111/{v1:\d}{v2:.*}`).BuildVarsFunc(func(vars map[string]string) map[string]string {
+				vars["v1"] = "3"
+				vars["v2"] = "a"
+				return vars
+			}),
+			request:     newRequest("GET", "http://localhost/111/2"),
+			path:        "/111/3a",
+			shouldMatch: true,
+		},
+		{
+			title: "BuildVarsFunc set on route and parent route",
+			route: new(Route).PathPrefix(`/{v1:\d}`).BuildVarsFunc(func(vars map[string]string) map[string]string {
+				vars["v1"] = "2"
+				return vars
+			}).Subrouter().Path(`/{v2:\w}`).BuildVarsFunc(func(vars map[string]string) map[string]string {
+				vars["v2"] = "b"
+				return vars
+			}),
+			request:     newRequest("GET", "http://localhost/1/a"),
+			path:        "/2/b",
+			shouldMatch: true,
+		},
+	}
+
+	for _, test := range tests {
+		testRoute(t, test)
+	}
+}
+
+func TestSubRouter(t *testing.T) {
+	subrouter1 := new(Route).Host("{v1:[a-z]+}.google.com").Subrouter()
+	subrouter2 := new(Route).PathPrefix("/foo/{v1}").Subrouter()
+
+	tests := []routeTest{
+		{
+			route:       subrouter1.Path("/{v2:[a-z]+}"),
+			request:     newRequest("GET", "http://aaa.google.com/bbb"),
+			vars:        map[string]string{"v1": "aaa", "v2": "bbb"},
+			host:        "aaa.google.com",
+			path:        "/bbb",
+			shouldMatch: true,
+		},
+		{
+			route:       subrouter1.Path("/{v2:[a-z]+}"),
+			request:     newRequest("GET", "http://111.google.com/111"),
+			vars:        map[string]string{"v1": "aaa", "v2": "bbb"},
+			host:        "aaa.google.com",
+			path:        "/bbb",
+			shouldMatch: false,
+		},
+		{
+			route:       subrouter2.Path("/baz/{v2}"),
+			request:     newRequest("GET", "http://localhost/foo/bar/baz/ding"),
+			vars:        map[string]string{"v1": "bar", "v2": "ding"},
+			host:        "",
+			path:        "/foo/bar/baz/ding",
+			shouldMatch: true,
+		},
+		{
+			route:       subrouter2.Path("/baz/{v2}"),
+			request:     newRequest("GET", "http://localhost/foo/bar"),
+			vars:        map[string]string{"v1": "bar", "v2": "ding"},
+			host:        "",
+			path:        "/foo/bar/baz/ding",
+			shouldMatch: false,
+		},
+	}
+
+	for _, test := range tests {
+		testRoute(t, test)
+	}
+}
+
+func TestNamedRoutes(t *testing.T) {
+	r1 := NewRouter()
+	r1.NewRoute().Name("a")
+	r1.NewRoute().Name("b")
+	r1.NewRoute().Name("c")
+
+	r2 := r1.NewRoute().Subrouter()
+	r2.NewRoute().Name("d")
+	r2.NewRoute().Name("e")
+	r2.NewRoute().Name("f")
+
+	r3 := r2.NewRoute().Subrouter()
+	r3.NewRoute().Name("g")
+	r3.NewRoute().Name("h")
+	r3.NewRoute().Name("i")
+
+	if r1.namedRoutes == nil || len(r1.namedRoutes) != 9 {
+		t.Errorf("Expected 9 named routes, got %v", r1.namedRoutes)
+	} else if r1.Get("i") == nil {
+		t.Errorf("Subroute name not registered")
+	}
+}
+
+func TestStrictSlash(t *testing.T) {
+	r := NewRouter()
+	r.StrictSlash(true)
+
+	tests := []routeTest{
+		{
+			title:          "Redirect path without slash",
+			route:          r.NewRoute().Path("/111/"),
+			request:        newRequest("GET", "http://localhost/111"),
+			vars:           map[string]string{},
+			host:           "",
+			path:           "/111/",
+			shouldMatch:    true,
+			shouldRedirect: true,
+		},
+		{
+			title:          "Do not redirect path with slash",
+			route:          r.NewRoute().Path("/111/"),
+			request:        newRequest("GET", "http://localhost/111/"),
+			vars:           map[string]string{},
+			host:           "",
+			path:           "/111/",
+			shouldMatch:    true,
+			shouldRedirect: false,
+		},
+		{
+			title:          "Redirect path with slash",
+			route:          r.NewRoute().Path("/111"),
+			request:        newRequest("GET", "http://localhost/111/"),
+			vars:           map[string]string{},
+			host:           "",
+			path:           "/111",
+			shouldMatch:    true,
+			shouldRedirect: true,
+		},
+		{
+			title:          "Do not redirect path without slash",
+			route:          r.NewRoute().Path("/111"),
+			request:        newRequest("GET", "http://localhost/111"),
+			vars:           map[string]string{},
+			host:           "",
+			path:           "/111",
+			shouldMatch:    true,
+			shouldRedirect: false,
+		},
+		{
+			title:          "Propagate StrictSlash to subrouters",
+			route:          r.NewRoute().PathPrefix("/static/").Subrouter().Path("/images/"),
+			request:        newRequest("GET", "http://localhost/static/images"),
+			vars:           map[string]string{},
+			host:           "",
+			path:           "/static/images/",
+			shouldMatch:    true,
+			shouldRedirect: true,
+		},
+		{
+			title:          "Ignore StrictSlash for path prefix",
+			route:          r.NewRoute().PathPrefix("/static/"),
+			request:        newRequest("GET", "http://localhost/static/logo.png"),
+			vars:           map[string]string{},
+			host:           "",
+			path:           "/static/",
+			shouldMatch:    true,
+			shouldRedirect: false,
+		},
+	}
+
+	for _, test := range tests {
+		testRoute(t, test)
+	}
+}
+
+// ----------------------------------------------------------------------------
+// Helpers
+// ----------------------------------------------------------------------------
+
+func getRouteTemplate(route *Route) string {
+	host, path := "none", "none"
+	if route.regexp != nil {
+		if route.regexp.host != nil {
+			host = route.regexp.host.template
+		}
+		if route.regexp.path != nil {
+			path = route.regexp.path.template
+		}
+	}
+	return fmt.Sprintf("Host: %v, Path: %v", host, path)
+}
+
+func testRoute(t *testing.T, test routeTest) {
+	request := test.request
+	route := test.route
+	vars := test.vars
+	shouldMatch := test.shouldMatch
+	host := test.host
+	path := test.path
+	url := test.host + test.path
+	shouldRedirect := test.shouldRedirect
+
+	var match RouteMatch
+	ok := route.Match(request, &match)
+	if ok != shouldMatch {
+		msg := "Should match"
+		if !shouldMatch {
+			msg = "Should not match"
+		}
+		t.Errorf("(%v) %v:\nRoute: %#v\nRequest: %#v\nVars: %v\n", test.title, msg, route, request, vars)
+		return
+	}
+	if shouldMatch {
+		if test.vars != nil && !stringMapEqual(test.vars, match.Vars) {
+			t.Errorf("(%v) Vars not equal: expected %v, got %v", test.title, vars, match.Vars)
+			return
+		}
+		if host != "" {
+			u, _ := test.route.URLHost(mapToPairs(match.Vars)...)
+			if host != u.Host {
+				t.Errorf("(%v) URLHost not equal: expected %v, got %v -- %v", test.title, host, u.Host, getRouteTemplate(route))
+				return
+			}
+		}
+		if path != "" {
+			u, _ := route.URLPath(mapToPairs(match.Vars)...)
+			if path != u.Path {
+				t.Errorf("(%v) URLPath not equal: expected %v, got %v -- %v", test.title, path, u.Path, getRouteTemplate(route))
+				return
+			}
+		}
+		if url != "" {
+			u, _ := route.URL(mapToPairs(match.Vars)...)
+			if url != u.Host+u.Path {
+				t.Errorf("(%v) URL not equal: expected %v, got %v -- %v", test.title, url, u.Host+u.Path, getRouteTemplate(route))
+				return
+			}
+		}
+		if shouldRedirect && match.Handler == nil {
+			t.Errorf("(%v) Did not redirect", test.title)
+			return
+		}
+		if !shouldRedirect && match.Handler != nil {
+			t.Errorf("(%v) Unexpected redirect", test.title)
+			return
+		}
+	}
+}
+
+// Tests that the context is cleared or not cleared properly depending on
+// the configuration of the router
+func TestKeepContext(t *testing.T) {
+	func1 := func(w http.ResponseWriter, r *http.Request) {}
+
+	r := NewRouter()
+	r.HandleFunc("/", func1).Name("func1")
+
+	req, _ := http.NewRequest("GET", "http://localhost/", nil)
+	context.Set(req, "t", 1)
+
+	res := new(http.ResponseWriter)
+	r.ServeHTTP(*res, req)
+
+	if _, ok := context.GetOk(req, "t"); ok {
+		t.Error("Context should have been cleared at end of request")
+	}
+
+	r.KeepContext = true
+
+	req, _ = http.NewRequest("GET", "http://localhost/", nil)
+	context.Set(req, "t", 1)
+
+	r.ServeHTTP(*res, req)
+	if _, ok := context.GetOk(req, "t"); !ok {
+		t.Error("Context should NOT have been cleared at end of request")
+	}
+
+}
+
+type TestA301ResponseWriter struct {
+	hh     http.Header
+	status int
+}
+
+func (ho TestA301ResponseWriter) Header() http.Header {
+	return http.Header(ho.hh)
+}
+
+func (ho TestA301ResponseWriter) Write(b []byte) (int, error) {
+	return 0, nil
+}
+
+func (ho TestA301ResponseWriter) WriteHeader(code int) {
+	ho.status = code
+}
+
+func Test301Redirect(t *testing.T) {
+	m := make(http.Header)
+
+	func1 := func(w http.ResponseWriter, r *http.Request) {}
+	func2 := func(w http.ResponseWriter, r *http.Request) {}
+
+	r := NewRouter()
+	r.HandleFunc("/api/", func2).Name("func2")
+	r.HandleFunc("/", func1).Name("func1")
+
+	req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil)
+
+	res := TestA301ResponseWriter{
+		hh:     m,
+		status: 0,
+	}
+	r.ServeHTTP(&res, req)
+
+	if "http://localhost/api/?abc=def" != res.hh["Location"][0] {
+		t.Errorf("Should have complete URL with query string")
+	}
+}
+
+// https://plus.google.com/101022900381697718949/posts/eWy6DjFJ6uW
+func TestSubrouterHeader(t *testing.T) {
+	expected := "func1 response"
+	func1 := func(w http.ResponseWriter, r *http.Request) {
+		fmt.Fprint(w, expected)
+	}
+	func2 := func(http.ResponseWriter, *http.Request) {}
+
+	r := NewRouter()
+	s := r.Headers("SomeSpecialHeader", "").Subrouter()
+	s.HandleFunc("/", func1).Name("func1")
+	r.HandleFunc("/", func2).Name("func2")
+
+	req, _ := http.NewRequest("GET", "http://localhost/", nil)
+	req.Header.Add("SomeSpecialHeader", "foo")
+	match := new(RouteMatch)
+	matched := r.Match(req, match)
+	if !matched {
+		t.Errorf("Should match request")
+	}
+	if match.Route.GetName() != "func1" {
+		t.Errorf("Expecting func1 handler, got %s", match.Route.GetName())
+	}
+	resp := NewRecorder()
+	match.Handler.ServeHTTP(resp, req)
+	if resp.Body.String() != expected {
+		t.Errorf("Expecting %q", expected)
+	}
+}
+
+// mapToPairs converts a string map to a slice of string pairs
+func mapToPairs(m map[string]string) []string {
+	var i int
+	p := make([]string, len(m)*2)
+	for k, v := range m {
+		p[i] = k
+		p[i+1] = v
+		i += 2
+	}
+	return p
+}
+
+// stringMapEqual checks the equality of two string maps
+func stringMapEqual(m1, m2 map[string]string) bool {
+	nil1 := m1 == nil
+	nil2 := m2 == nil
+	if nil1 != nil2 || len(m1) != len(m2) {
+		return false
+	}
+	for k, v := range m1 {
+		if v != m2[k] {
+			return false
+		}
+	}
+	return true
+}
+
+// newRequest is a helper function to create a new request with a method and url
+func newRequest(method, url string) *http.Request {
+	req, err := http.NewRequest(method, url, nil)
+	if err != nil {
+		panic(err)
+	}
+	return req
+}

+ 714 - 0
libnetwork/Godeps/_workspace/src/github.com/gorilla/mux/old_test.go

@@ -0,0 +1,714 @@
+// Old tests ported to Go1. This is a mess. Want to drop it one day.
+
+// Copyright 2011 Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mux
+
+import (
+	"bytes"
+	"net/http"
+	"testing"
+)
+
+// ----------------------------------------------------------------------------
+// ResponseRecorder
+// ----------------------------------------------------------------------------
+// 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.
+
+// ResponseRecorder is an implementation of http.ResponseWriter that
+// records its mutations for later inspection in tests.
+type ResponseRecorder struct {
+	Code      int           // the HTTP response code from WriteHeader
+	HeaderMap http.Header   // the HTTP response headers
+	Body      *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
+	Flushed   bool
+}
+
+// NewRecorder returns an initialized ResponseRecorder.
+func NewRecorder() *ResponseRecorder {
+	return &ResponseRecorder{
+		HeaderMap: make(http.Header),
+		Body:      new(bytes.Buffer),
+	}
+}
+
+// DefaultRemoteAddr is the default remote address to return in RemoteAddr if
+// an explicit DefaultRemoteAddr isn't set on ResponseRecorder.
+const DefaultRemoteAddr = "1.2.3.4"
+
+// Header returns the response headers.
+func (rw *ResponseRecorder) Header() http.Header {
+	return rw.HeaderMap
+}
+
+// Write always succeeds and writes to rw.Body, if not nil.
+func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
+	if rw.Body != nil {
+		rw.Body.Write(buf)
+	}
+	if rw.Code == 0 {
+		rw.Code = http.StatusOK
+	}
+	return len(buf), nil
+}
+
+// WriteHeader sets rw.Code.
+func (rw *ResponseRecorder) WriteHeader(code int) {
+	rw.Code = code
+}
+
+// Flush sets rw.Flushed to true.
+func (rw *ResponseRecorder) Flush() {
+	rw.Flushed = true
+}
+
+// ----------------------------------------------------------------------------
+
+func TestRouteMatchers(t *testing.T) {
+	var scheme, host, path, query, method string
+	var headers map[string]string
+	var resultVars map[bool]map[string]string
+
+	router := NewRouter()
+	router.NewRoute().Host("{var1}.google.com").
+		Path("/{var2:[a-z]+}/{var3:[0-9]+}").
+		Queries("foo", "bar").
+		Methods("GET").
+		Schemes("https").
+		Headers("x-requested-with", "XMLHttpRequest")
+	router.NewRoute().Host("www.{var4}.com").
+		PathPrefix("/foo/{var5:[a-z]+}/{var6:[0-9]+}").
+		Queries("baz", "ding").
+		Methods("POST").
+		Schemes("http").
+		Headers("Content-Type", "application/json")
+
+	reset := func() {
+		// Everything match.
+		scheme = "https"
+		host = "www.google.com"
+		path = "/product/42"
+		query = "?foo=bar"
+		method = "GET"
+		headers = map[string]string{"X-Requested-With": "XMLHttpRequest"}
+		resultVars = map[bool]map[string]string{
+			true:  {"var1": "www", "var2": "product", "var3": "42"},
+			false: {},
+		}
+	}
+
+	reset2 := func() {
+		// Everything match.
+		scheme = "http"
+		host = "www.google.com"
+		path = "/foo/product/42/path/that/is/ignored"
+		query = "?baz=ding"
+		method = "POST"
+		headers = map[string]string{"Content-Type": "application/json"}
+		resultVars = map[bool]map[string]string{
+			true:  {"var4": "google", "var5": "product", "var6": "42"},
+			false: {},
+		}
+	}
+
+	match := func(shouldMatch bool) {
+		url := scheme + "://" + host + path + query
+		request, _ := http.NewRequest(method, url, nil)
+		for key, value := range headers {
+			request.Header.Add(key, value)
+		}
+
+		var routeMatch RouteMatch
+		matched := router.Match(request, &routeMatch)
+		if matched != shouldMatch {
+			// Need better messages. :)
+			if matched {
+				t.Errorf("Should match.")
+			} else {
+				t.Errorf("Should not match.")
+			}
+		}
+
+		if matched {
+			currentRoute := routeMatch.Route
+			if currentRoute == nil {
+				t.Errorf("Expected a current route.")
+			}
+			vars := routeMatch.Vars
+			expectedVars := resultVars[shouldMatch]
+			if len(vars) != len(expectedVars) {
+				t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
+			}
+			for name, value := range vars {
+				if expectedVars[name] != value {
+					t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
+				}
+			}
+		}
+	}
+
+	// 1st route --------------------------------------------------------------
+
+	// Everything match.
+	reset()
+	match(true)
+
+	// Scheme doesn't match.
+	reset()
+	scheme = "http"
+	match(false)
+
+	// Host doesn't match.
+	reset()
+	host = "www.mygoogle.com"
+	match(false)
+
+	// Path doesn't match.
+	reset()
+	path = "/product/notdigits"
+	match(false)
+
+	// Query doesn't match.
+	reset()
+	query = "?foo=baz"
+	match(false)
+
+	// Method doesn't match.
+	reset()
+	method = "POST"
+	match(false)
+
+	// Header doesn't match.
+	reset()
+	headers = map[string]string{}
+	match(false)
+
+	// Everything match, again.
+	reset()
+	match(true)
+
+	// 2nd route --------------------------------------------------------------
+
+	// Everything match.
+	reset2()
+	match(true)
+
+	// Scheme doesn't match.
+	reset2()
+	scheme = "https"
+	match(false)
+
+	// Host doesn't match.
+	reset2()
+	host = "sub.google.com"
+	match(false)
+
+	// Path doesn't match.
+	reset2()
+	path = "/bar/product/42"
+	match(false)
+
+	// Query doesn't match.
+	reset2()
+	query = "?foo=baz"
+	match(false)
+
+	// Method doesn't match.
+	reset2()
+	method = "GET"
+	match(false)
+
+	// Header doesn't match.
+	reset2()
+	headers = map[string]string{}
+	match(false)
+
+	// Everything match, again.
+	reset2()
+	match(true)
+}
+
+type headerMatcherTest struct {
+	matcher headerMatcher
+	headers map[string]string
+	result  bool
+}
+
+var headerMatcherTests = []headerMatcherTest{
+	{
+		matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
+		headers: map[string]string{"X-Requested-With": "XMLHttpRequest"},
+		result:  true,
+	},
+	{
+		matcher: headerMatcher(map[string]string{"x-requested-with": ""}),
+		headers: map[string]string{"X-Requested-With": "anything"},
+		result:  true,
+	},
+	{
+		matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
+		headers: map[string]string{},
+		result:  false,
+	},
+}
+
+type hostMatcherTest struct {
+	matcher *Route
+	url     string
+	vars    map[string]string
+	result  bool
+}
+
+var hostMatcherTests = []hostMatcherTest{
+	{
+		matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
+		url:     "http://abc.def.ghi/",
+		vars:    map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
+		result:  true,
+	},
+	{
+		matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
+		url:     "http://a.b.c/",
+		vars:    map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
+		result:  false,
+	},
+}
+
+type methodMatcherTest struct {
+	matcher methodMatcher
+	method  string
+	result  bool
+}
+
+var methodMatcherTests = []methodMatcherTest{
+	{
+		matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
+		method:  "GET",
+		result:  true,
+	},
+	{
+		matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
+		method:  "POST",
+		result:  true,
+	},
+	{
+		matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
+		method:  "PUT",
+		result:  true,
+	},
+	{
+		matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
+		method:  "DELETE",
+		result:  false,
+	},
+}
+
+type pathMatcherTest struct {
+	matcher *Route
+	url     string
+	vars    map[string]string
+	result  bool
+}
+
+var pathMatcherTests = []pathMatcherTest{
+	{
+		matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
+		url:     "http://localhost:8080/123/456/789",
+		vars:    map[string]string{"foo": "123", "bar": "456", "baz": "789"},
+		result:  true,
+	},
+	{
+		matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
+		url:     "http://localhost:8080/1/2/3",
+		vars:    map[string]string{"foo": "123", "bar": "456", "baz": "789"},
+		result:  false,
+	},
+}
+
+type schemeMatcherTest struct {
+	matcher schemeMatcher
+	url     string
+	result  bool
+}
+
+var schemeMatcherTests = []schemeMatcherTest{
+	{
+		matcher: schemeMatcher([]string{"http", "https"}),
+		url:     "http://localhost:8080/",
+		result:  true,
+	},
+	{
+		matcher: schemeMatcher([]string{"http", "https"}),
+		url:     "https://localhost:8080/",
+		result:  true,
+	},
+	{
+		matcher: schemeMatcher([]string{"https"}),
+		url:     "http://localhost:8080/",
+		result:  false,
+	},
+	{
+		matcher: schemeMatcher([]string{"http"}),
+		url:     "https://localhost:8080/",
+		result:  false,
+	},
+}
+
+type urlBuildingTest struct {
+	route *Route
+	vars  []string
+	url   string
+}
+
+var urlBuildingTests = []urlBuildingTest{
+	{
+		route: new(Route).Host("foo.domain.com"),
+		vars:  []string{},
+		url:   "http://foo.domain.com",
+	},
+	{
+		route: new(Route).Host("{subdomain}.domain.com"),
+		vars:  []string{"subdomain", "bar"},
+		url:   "http://bar.domain.com",
+	},
+	{
+		route: new(Route).Host("foo.domain.com").Path("/articles"),
+		vars:  []string{},
+		url:   "http://foo.domain.com/articles",
+	},
+	{
+		route: new(Route).Path("/articles"),
+		vars:  []string{},
+		url:   "/articles",
+	},
+	{
+		route: new(Route).Path("/articles/{category}/{id:[0-9]+}"),
+		vars:  []string{"category", "technology", "id", "42"},
+		url:   "/articles/technology/42",
+	},
+	{
+		route: new(Route).Host("{subdomain}.domain.com").Path("/articles/{category}/{id:[0-9]+}"),
+		vars:  []string{"subdomain", "foo", "category", "technology", "id", "42"},
+		url:   "http://foo.domain.com/articles/technology/42",
+	},
+}
+
+func TestHeaderMatcher(t *testing.T) {
+	for _, v := range headerMatcherTests {
+		request, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
+		for key, value := range v.headers {
+			request.Header.Add(key, value)
+		}
+		var routeMatch RouteMatch
+		result := v.matcher.Match(request, &routeMatch)
+		if result != v.result {
+			if v.result {
+				t.Errorf("%#v: should match %v.", v.matcher, request.Header)
+			} else {
+				t.Errorf("%#v: should not match %v.", v.matcher, request.Header)
+			}
+		}
+	}
+}
+
+func TestHostMatcher(t *testing.T) {
+	for _, v := range hostMatcherTests {
+		request, _ := http.NewRequest("GET", v.url, nil)
+		var routeMatch RouteMatch
+		result := v.matcher.Match(request, &routeMatch)
+		vars := routeMatch.Vars
+		if result != v.result {
+			if v.result {
+				t.Errorf("%#v: should match %v.", v.matcher, v.url)
+			} else {
+				t.Errorf("%#v: should not match %v.", v.matcher, v.url)
+			}
+		}
+		if result {
+			if len(vars) != len(v.vars) {
+				t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
+			}
+			for name, value := range vars {
+				if v.vars[name] != value {
+					t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
+				}
+			}
+		} else {
+			if len(vars) != 0 {
+				t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
+			}
+		}
+	}
+}
+
+func TestMethodMatcher(t *testing.T) {
+	for _, v := range methodMatcherTests {
+		request, _ := http.NewRequest(v.method, "http://localhost:8080/", nil)
+		var routeMatch RouteMatch
+		result := v.matcher.Match(request, &routeMatch)
+		if result != v.result {
+			if v.result {
+				t.Errorf("%#v: should match %v.", v.matcher, v.method)
+			} else {
+				t.Errorf("%#v: should not match %v.", v.matcher, v.method)
+			}
+		}
+	}
+}
+
+func TestPathMatcher(t *testing.T) {
+	for _, v := range pathMatcherTests {
+		request, _ := http.NewRequest("GET", v.url, nil)
+		var routeMatch RouteMatch
+		result := v.matcher.Match(request, &routeMatch)
+		vars := routeMatch.Vars
+		if result != v.result {
+			if v.result {
+				t.Errorf("%#v: should match %v.", v.matcher, v.url)
+			} else {
+				t.Errorf("%#v: should not match %v.", v.matcher, v.url)
+			}
+		}
+		if result {
+			if len(vars) != len(v.vars) {
+				t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
+			}
+			for name, value := range vars {
+				if v.vars[name] != value {
+					t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
+				}
+			}
+		} else {
+			if len(vars) != 0 {
+				t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
+			}
+		}
+	}
+}
+
+func TestSchemeMatcher(t *testing.T) {
+	for _, v := range schemeMatcherTests {
+		request, _ := http.NewRequest("GET", v.url, nil)
+		var routeMatch RouteMatch
+		result := v.matcher.Match(request, &routeMatch)
+		if result != v.result {
+			if v.result {
+				t.Errorf("%#v: should match %v.", v.matcher, v.url)
+			} else {
+				t.Errorf("%#v: should not match %v.", v.matcher, v.url)
+			}
+		}
+	}
+}
+
+func TestUrlBuilding(t *testing.T) {
+
+	for _, v := range urlBuildingTests {
+		u, _ := v.route.URL(v.vars...)
+		url := u.String()
+		if url != v.url {
+			t.Errorf("expected %v, got %v", v.url, url)
+			/*
+				reversePath := ""
+				reverseHost := ""
+				if v.route.pathTemplate != nil {
+						reversePath = v.route.pathTemplate.Reverse
+				}
+				if v.route.hostTemplate != nil {
+						reverseHost = v.route.hostTemplate.Reverse
+				}
+
+				t.Errorf("%#v:\nexpected: %q\ngot: %q\nreverse path: %q\nreverse host: %q", v.route, v.url, url, reversePath, reverseHost)
+			*/
+		}
+	}
+
+	ArticleHandler := func(w http.ResponseWriter, r *http.Request) {
+	}
+
+	router := NewRouter()
+	router.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).Name("article")
+
+	url, _ := router.Get("article").URL("category", "technology", "id", "42")
+	expected := "/articles/technology/42"
+	if url.String() != expected {
+		t.Errorf("Expected %v, got %v", expected, url.String())
+	}
+}
+
+func TestMatchedRouteName(t *testing.T) {
+	routeName := "stock"
+	router := NewRouter()
+	route := router.NewRoute().Path("/products/").Name(routeName)
+
+	url := "http://www.domain.com/products/"
+	request, _ := http.NewRequest("GET", url, nil)
+	var rv RouteMatch
+	ok := router.Match(request, &rv)
+
+	if !ok || rv.Route != route {
+		t.Errorf("Expected same route, got %+v.", rv.Route)
+	}
+
+	retName := rv.Route.GetName()
+	if retName != routeName {
+		t.Errorf("Expected %q, got %q.", routeName, retName)
+	}
+}
+
+func TestSubRouting(t *testing.T) {
+	// Example from docs.
+	router := NewRouter()
+	subrouter := router.NewRoute().Host("www.domain.com").Subrouter()
+	route := subrouter.NewRoute().Path("/products/").Name("products")
+
+	url := "http://www.domain.com/products/"
+	request, _ := http.NewRequest("GET", url, nil)
+	var rv RouteMatch
+	ok := router.Match(request, &rv)
+
+	if !ok || rv.Route != route {
+		t.Errorf("Expected same route, got %+v.", rv.Route)
+	}
+
+	u, _ := router.Get("products").URL()
+	builtUrl := u.String()
+	// Yay, subroute aware of the domain when building!
+	if builtUrl != url {
+		t.Errorf("Expected %q, got %q.", url, builtUrl)
+	}
+}
+
+func TestVariableNames(t *testing.T) {
+	route := new(Route).Host("{arg1}.domain.com").Path("/{arg1}/{arg2:[0-9]+}")
+	if route.err == nil {
+		t.Errorf("Expected error for duplicated variable names")
+	}
+}
+
+func TestRedirectSlash(t *testing.T) {
+	var route *Route
+	var routeMatch RouteMatch
+	r := NewRouter()
+
+	r.StrictSlash(false)
+	route = r.NewRoute()
+	if route.strictSlash != false {
+		t.Errorf("Expected false redirectSlash.")
+	}
+
+	r.StrictSlash(true)
+	route = r.NewRoute()
+	if route.strictSlash != true {
+		t.Errorf("Expected true redirectSlash.")
+	}
+
+	route = new(Route)
+	route.strictSlash = true
+	route.Path("/{arg1}/{arg2:[0-9]+}/")
+	request, _ := http.NewRequest("GET", "http://localhost/foo/123", nil)
+	routeMatch = RouteMatch{}
+	_ = route.Match(request, &routeMatch)
+	vars := routeMatch.Vars
+	if vars["arg1"] != "foo" {
+		t.Errorf("Expected foo.")
+	}
+	if vars["arg2"] != "123" {
+		t.Errorf("Expected 123.")
+	}
+	rsp := NewRecorder()
+	routeMatch.Handler.ServeHTTP(rsp, request)
+	if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123/" {
+		t.Errorf("Expected redirect header.")
+	}
+
+	route = new(Route)
+	route.strictSlash = true
+	route.Path("/{arg1}/{arg2:[0-9]+}")
+	request, _ = http.NewRequest("GET", "http://localhost/foo/123/", nil)
+	routeMatch = RouteMatch{}
+	_ = route.Match(request, &routeMatch)
+	vars = routeMatch.Vars
+	if vars["arg1"] != "foo" {
+		t.Errorf("Expected foo.")
+	}
+	if vars["arg2"] != "123" {
+		t.Errorf("Expected 123.")
+	}
+	rsp = NewRecorder()
+	routeMatch.Handler.ServeHTTP(rsp, request)
+	if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123" {
+		t.Errorf("Expected redirect header.")
+	}
+}
+
+// Test for the new regexp library, still not available in stable Go.
+func TestNewRegexp(t *testing.T) {
+	var p *routeRegexp
+	var matches []string
+
+	tests := map[string]map[string][]string{
+		"/{foo:a{2}}": {
+			"/a":    nil,
+			"/aa":   {"aa"},
+			"/aaa":  nil,
+			"/aaaa": nil,
+		},
+		"/{foo:a{2,}}": {
+			"/a":    nil,
+			"/aa":   {"aa"},
+			"/aaa":  {"aaa"},
+			"/aaaa": {"aaaa"},
+		},
+		"/{foo:a{2,3}}": {
+			"/a":    nil,
+			"/aa":   {"aa"},
+			"/aaa":  {"aaa"},
+			"/aaaa": nil,
+		},
+		"/{foo:[a-z]{3}}/{bar:[a-z]{2}}": {
+			"/a":       nil,
+			"/ab":      nil,
+			"/abc":     nil,
+			"/abcd":    nil,
+			"/abc/ab":  {"abc", "ab"},
+			"/abc/abc": nil,
+			"/abcd/ab": nil,
+		},
+		`/{foo:\w{3,}}/{bar:\d{2,}}`: {
+			"/a":        nil,
+			"/ab":       nil,
+			"/abc":      nil,
+			"/abc/1":    nil,
+			"/abc/12":   {"abc", "12"},
+			"/abcd/12":  {"abcd", "12"},
+			"/abcd/123": {"abcd", "123"},
+		},
+	}
+
+	for pattern, paths := range tests {
+		p, _ = newRouteRegexp(pattern, false, false, false, false)
+		for path, result := range paths {
+			matches = p.regexp.FindStringSubmatch(path)
+			if result == nil {
+				if matches != nil {
+					t.Errorf("%v should not match %v.", pattern, path)
+				}
+			} else {
+				if len(matches) != len(result)+1 {
+					t.Errorf("Expected %v matches, got %v.", len(result)+1, len(matches))
+				} else {
+					for k, v := range result {
+						if matches[k+1] != v {
+							t.Errorf("Expected %v, got %v.", v, matches[k+1])
+						}
+					}
+				}
+			}
+		}
+	}
+}

+ 272 - 0
libnetwork/Godeps/_workspace/src/github.com/gorilla/mux/regexp.go

@@ -0,0 +1,272 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mux
+
+import (
+	"bytes"
+	"fmt"
+	"net/http"
+	"net/url"
+	"regexp"
+	"strings"
+)
+
+// newRouteRegexp parses a route template and returns a routeRegexp,
+// used to match a host, a path or a query string.
+//
+// It will extract named variables, assemble a regexp to be matched, create
+// a "reverse" template to build URLs and compile regexps to validate variable
+// values used in URL building.
+//
+// Previously we accepted only Python-like identifiers for variable
+// names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that
+// name and pattern can't be empty, and names can't contain a colon.
+func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash bool) (*routeRegexp, error) {
+	// Check if it is well-formed.
+	idxs, errBraces := braceIndices(tpl)
+	if errBraces != nil {
+		return nil, errBraces
+	}
+	// Backup the original.
+	template := tpl
+	// Now let's parse it.
+	defaultPattern := "[^/]+"
+	if matchQuery {
+		defaultPattern = "[^?&]+"
+		matchPrefix = true
+	} else if matchHost {
+		defaultPattern = "[^.]+"
+		matchPrefix = false
+	}
+	// Only match strict slash if not matching
+	if matchPrefix || matchHost || matchQuery {
+		strictSlash = false
+	}
+	// Set a flag for strictSlash.
+	endSlash := false
+	if strictSlash && strings.HasSuffix(tpl, "/") {
+		tpl = tpl[:len(tpl)-1]
+		endSlash = true
+	}
+	varsN := make([]string, len(idxs)/2)
+	varsR := make([]*regexp.Regexp, len(idxs)/2)
+	pattern := bytes.NewBufferString("")
+	if !matchQuery {
+		pattern.WriteByte('^')
+	}
+	reverse := bytes.NewBufferString("")
+	var end int
+	var err error
+	for i := 0; i < len(idxs); i += 2 {
+		// Set all values we are interested in.
+		raw := tpl[end:idxs[i]]
+		end = idxs[i+1]
+		parts := strings.SplitN(tpl[idxs[i]+1:end-1], ":", 2)
+		name := parts[0]
+		patt := defaultPattern
+		if len(parts) == 2 {
+			patt = parts[1]
+		}
+		// Name or pattern can't be empty.
+		if name == "" || patt == "" {
+			return nil, fmt.Errorf("mux: missing name or pattern in %q",
+				tpl[idxs[i]:end])
+		}
+		// Build the regexp pattern.
+		fmt.Fprintf(pattern, "%s(%s)", regexp.QuoteMeta(raw), patt)
+		// Build the reverse template.
+		fmt.Fprintf(reverse, "%s%%s", raw)
+		// Append variable name and compiled pattern.
+		varsN[i/2] = name
+		varsR[i/2], err = regexp.Compile(fmt.Sprintf("^%s$", patt))
+		if err != nil {
+			return nil, err
+		}
+	}
+	// Add the remaining.
+	raw := tpl[end:]
+	pattern.WriteString(regexp.QuoteMeta(raw))
+	if strictSlash {
+		pattern.WriteString("[/]?")
+	}
+	if !matchPrefix {
+		pattern.WriteByte('$')
+	}
+	reverse.WriteString(raw)
+	if endSlash {
+		reverse.WriteByte('/')
+	}
+	// Compile full regexp.
+	reg, errCompile := regexp.Compile(pattern.String())
+	if errCompile != nil {
+		return nil, errCompile
+	}
+	// Done!
+	return &routeRegexp{
+		template:    template,
+		matchHost:   matchHost,
+		matchQuery:  matchQuery,
+		strictSlash: strictSlash,
+		regexp:      reg,
+		reverse:     reverse.String(),
+		varsN:       varsN,
+		varsR:       varsR,
+	}, nil
+}
+
+// routeRegexp stores a regexp to match a host or path and information to
+// collect and validate route variables.
+type routeRegexp struct {
+	// The unmodified template.
+	template string
+	// True for host match, false for path or query string match.
+	matchHost bool
+	// True for query string match, false for path and host match.
+	matchQuery bool
+	// The strictSlash value defined on the route, but disabled if PathPrefix was used.
+	strictSlash bool
+	// Expanded regexp.
+	regexp *regexp.Regexp
+	// Reverse template.
+	reverse string
+	// Variable names.
+	varsN []string
+	// Variable regexps (validators).
+	varsR []*regexp.Regexp
+}
+
+// Match matches the regexp against the URL host or path.
+func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
+	if !r.matchHost {
+		if r.matchQuery {
+			return r.regexp.MatchString(req.URL.RawQuery)
+		} else {
+			return r.regexp.MatchString(req.URL.Path)
+		}
+	}
+	return r.regexp.MatchString(getHost(req))
+}
+
+// url builds a URL part using the given values.
+func (r *routeRegexp) url(values map[string]string) (string, error) {
+	urlValues := make([]interface{}, len(r.varsN))
+	for k, v := range r.varsN {
+		value, ok := values[v]
+		if !ok {
+			return "", fmt.Errorf("mux: missing route variable %q", v)
+		}
+		urlValues[k] = value
+	}
+	rv := fmt.Sprintf(r.reverse, urlValues...)
+	if !r.regexp.MatchString(rv) {
+		// The URL is checked against the full regexp, instead of checking
+		// individual variables. This is faster but to provide a good error
+		// message, we check individual regexps if the URL doesn't match.
+		for k, v := range r.varsN {
+			if !r.varsR[k].MatchString(values[v]) {
+				return "", fmt.Errorf(
+					"mux: variable %q doesn't match, expected %q", values[v],
+					r.varsR[k].String())
+			}
+		}
+	}
+	return rv, nil
+}
+
+// braceIndices returns the first level curly brace indices from a string.
+// It returns an error in case of unbalanced braces.
+func braceIndices(s string) ([]int, error) {
+	var level, idx int
+	idxs := make([]int, 0)
+	for i := 0; i < len(s); i++ {
+		switch s[i] {
+		case '{':
+			if level++; level == 1 {
+				idx = i
+			}
+		case '}':
+			if level--; level == 0 {
+				idxs = append(idxs, idx, i+1)
+			} else if level < 0 {
+				return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
+			}
+		}
+	}
+	if level != 0 {
+		return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
+	}
+	return idxs, nil
+}
+
+// ----------------------------------------------------------------------------
+// routeRegexpGroup
+// ----------------------------------------------------------------------------
+
+// routeRegexpGroup groups the route matchers that carry variables.
+type routeRegexpGroup struct {
+	host    *routeRegexp
+	path    *routeRegexp
+	queries []*routeRegexp
+}
+
+// setMatch extracts the variables from the URL once a route matches.
+func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
+	// Store host variables.
+	if v.host != nil {
+		hostVars := v.host.regexp.FindStringSubmatch(getHost(req))
+		if hostVars != nil {
+			for k, v := range v.host.varsN {
+				m.Vars[v] = hostVars[k+1]
+			}
+		}
+	}
+	// Store path variables.
+	if v.path != nil {
+		pathVars := v.path.regexp.FindStringSubmatch(req.URL.Path)
+		if pathVars != nil {
+			for k, v := range v.path.varsN {
+				m.Vars[v] = pathVars[k+1]
+			}
+			// Check if we should redirect.
+			if v.path.strictSlash {
+				p1 := strings.HasSuffix(req.URL.Path, "/")
+				p2 := strings.HasSuffix(v.path.template, "/")
+				if p1 != p2 {
+					u, _ := url.Parse(req.URL.String())
+					if p1 {
+						u.Path = u.Path[:len(u.Path)-1]
+					} else {
+						u.Path += "/"
+					}
+					m.Handler = http.RedirectHandler(u.String(), 301)
+				}
+			}
+		}
+	}
+	// Store query string variables.
+	rawQuery := req.URL.RawQuery
+	for _, q := range v.queries {
+		queryVars := q.regexp.FindStringSubmatch(rawQuery)
+		if queryVars != nil {
+			for k, v := range q.varsN {
+				m.Vars[v] = queryVars[k+1]
+			}
+		}
+	}
+}
+
+// getHost tries its best to return the request host.
+func getHost(r *http.Request) string {
+	if r.URL.IsAbs() {
+		return r.URL.Host
+	}
+	host := r.Host
+	// Slice off any port information.
+	if i := strings.Index(host, ":"); i != -1 {
+		host = host[:i]
+	}
+	return host
+
+}

+ 571 - 0
libnetwork/Godeps/_workspace/src/github.com/gorilla/mux/route.go

@@ -0,0 +1,571 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mux
+
+import (
+	"errors"
+	"fmt"
+	"net/http"
+	"net/url"
+	"strings"
+)
+
+// Route stores information to match a request and build URLs.
+type Route struct {
+	// Parent where the route was registered (a Router).
+	parent parentRoute
+	// Request handler for the route.
+	handler http.Handler
+	// List of matchers.
+	matchers []matcher
+	// Manager for the variables from host and path.
+	regexp *routeRegexpGroup
+	// If true, when the path pattern is "/path/", accessing "/path" will
+	// redirect to the former and vice versa.
+	strictSlash bool
+	// If true, this route never matches: it is only used to build URLs.
+	buildOnly bool
+	// The name used to build URLs.
+	name string
+	// Error resulted from building a route.
+	err error
+
+	buildVarsFunc BuildVarsFunc
+}
+
+// Match matches the route against the request.
+func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
+	if r.buildOnly || r.err != nil {
+		return false
+	}
+	// Match everything.
+	for _, m := range r.matchers {
+		if matched := m.Match(req, match); !matched {
+			return false
+		}
+	}
+	// Yay, we have a match. Let's collect some info about it.
+	if match.Route == nil {
+		match.Route = r
+	}
+	if match.Handler == nil {
+		match.Handler = r.handler
+	}
+	if match.Vars == nil {
+		match.Vars = make(map[string]string)
+	}
+	// Set variables.
+	if r.regexp != nil {
+		r.regexp.setMatch(req, match, r)
+	}
+	return true
+}
+
+// ----------------------------------------------------------------------------
+// Route attributes
+// ----------------------------------------------------------------------------
+
+// GetError returns an error resulted from building the route, if any.
+func (r *Route) GetError() error {
+	return r.err
+}
+
+// BuildOnly sets the route to never match: it is only used to build URLs.
+func (r *Route) BuildOnly() *Route {
+	r.buildOnly = true
+	return r
+}
+
+// Handler --------------------------------------------------------------------
+
+// Handler sets a handler for the route.
+func (r *Route) Handler(handler http.Handler) *Route {
+	if r.err == nil {
+		r.handler = handler
+	}
+	return r
+}
+
+// HandlerFunc sets a handler function for the route.
+func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
+	return r.Handler(http.HandlerFunc(f))
+}
+
+// GetHandler returns the handler for the route, if any.
+func (r *Route) GetHandler() http.Handler {
+	return r.handler
+}
+
+// Name -----------------------------------------------------------------------
+
+// Name sets the name for the route, used to build URLs.
+// If the name was registered already it will be overwritten.
+func (r *Route) Name(name string) *Route {
+	if r.name != "" {
+		r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
+			r.name, name)
+	}
+	if r.err == nil {
+		r.name = name
+		r.getNamedRoutes()[name] = r
+	}
+	return r
+}
+
+// GetName returns the name for the route, if any.
+func (r *Route) GetName() string {
+	return r.name
+}
+
+// ----------------------------------------------------------------------------
+// Matchers
+// ----------------------------------------------------------------------------
+
+// matcher types try to match a request.
+type matcher interface {
+	Match(*http.Request, *RouteMatch) bool
+}
+
+// addMatcher adds a matcher to the route.
+func (r *Route) addMatcher(m matcher) *Route {
+	if r.err == nil {
+		r.matchers = append(r.matchers, m)
+	}
+	return r
+}
+
+// addRegexpMatcher adds a host or path matcher and builder to a route.
+func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery bool) error {
+	if r.err != nil {
+		return r.err
+	}
+	r.regexp = r.getRegexpGroup()
+	if !matchHost && !matchQuery {
+		if len(tpl) == 0 || tpl[0] != '/' {
+			return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
+		}
+		if r.regexp.path != nil {
+			tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
+		}
+	}
+	rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, matchQuery, r.strictSlash)
+	if err != nil {
+		return err
+	}
+	for _, q := range r.regexp.queries {
+		if err = uniqueVars(rr.varsN, q.varsN); err != nil {
+			return err
+		}
+	}
+	if matchHost {
+		if r.regexp.path != nil {
+			if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
+				return err
+			}
+		}
+		r.regexp.host = rr
+	} else {
+		if r.regexp.host != nil {
+			if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
+				return err
+			}
+		}
+		if matchQuery {
+			r.regexp.queries = append(r.regexp.queries, rr)
+		} else {
+			r.regexp.path = rr
+		}
+	}
+	r.addMatcher(rr)
+	return nil
+}
+
+// Headers --------------------------------------------------------------------
+
+// headerMatcher matches the request against header values.
+type headerMatcher map[string]string
+
+func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
+	return matchMap(m, r.Header, true)
+}
+
+// Headers adds a matcher for request header values.
+// It accepts a sequence of key/value pairs to be matched. For example:
+//
+//     r := mux.NewRouter()
+//     r.Headers("Content-Type", "application/json",
+//               "X-Requested-With", "XMLHttpRequest")
+//
+// The above route will only match if both request header values match.
+//
+// It the value is an empty string, it will match any value if the key is set.
+func (r *Route) Headers(pairs ...string) *Route {
+	if r.err == nil {
+		var headers map[string]string
+		headers, r.err = mapFromPairs(pairs...)
+		return r.addMatcher(headerMatcher(headers))
+	}
+	return r
+}
+
+// Host -----------------------------------------------------------------------
+
+// Host adds a matcher for the URL host.
+// It accepts a template with zero or more URL variables enclosed by {}.
+// Variables can define an optional regexp pattern to me matched:
+//
+// - {name} matches anything until the next dot.
+//
+// - {name:pattern} matches the given regexp pattern.
+//
+// For example:
+//
+//     r := mux.NewRouter()
+//     r.Host("www.domain.com")
+//     r.Host("{subdomain}.domain.com")
+//     r.Host("{subdomain:[a-z]+}.domain.com")
+//
+// Variable names must be unique in a given route. They can be retrieved
+// calling mux.Vars(request).
+func (r *Route) Host(tpl string) *Route {
+	r.err = r.addRegexpMatcher(tpl, true, false, false)
+	return r
+}
+
+// MatcherFunc ----------------------------------------------------------------
+
+// MatcherFunc is the function signature used by custom matchers.
+type MatcherFunc func(*http.Request, *RouteMatch) bool
+
+func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool {
+	return m(r, match)
+}
+
+// MatcherFunc adds a custom function to be used as request matcher.
+func (r *Route) MatcherFunc(f MatcherFunc) *Route {
+	return r.addMatcher(f)
+}
+
+// Methods --------------------------------------------------------------------
+
+// methodMatcher matches the request against HTTP methods.
+type methodMatcher []string
+
+func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
+	return matchInArray(m, r.Method)
+}
+
+// Methods adds a matcher for HTTP methods.
+// It accepts a sequence of one or more methods to be matched, e.g.:
+// "GET", "POST", "PUT".
+func (r *Route) Methods(methods ...string) *Route {
+	for k, v := range methods {
+		methods[k] = strings.ToUpper(v)
+	}
+	return r.addMatcher(methodMatcher(methods))
+}
+
+// Path -----------------------------------------------------------------------
+
+// Path adds a matcher for the URL path.
+// It accepts a template with zero or more URL variables enclosed by {}. The
+// template must start with a "/".
+// Variables can define an optional regexp pattern to me matched:
+//
+// - {name} matches anything until the next slash.
+//
+// - {name:pattern} matches the given regexp pattern.
+//
+// For example:
+//
+//     r := mux.NewRouter()
+//     r.Path("/products/").Handler(ProductsHandler)
+//     r.Path("/products/{key}").Handler(ProductsHandler)
+//     r.Path("/articles/{category}/{id:[0-9]+}").
+//       Handler(ArticleHandler)
+//
+// Variable names must be unique in a given route. They can be retrieved
+// calling mux.Vars(request).
+func (r *Route) Path(tpl string) *Route {
+	r.err = r.addRegexpMatcher(tpl, false, false, false)
+	return r
+}
+
+// PathPrefix -----------------------------------------------------------------
+
+// PathPrefix adds a matcher for the URL path prefix. This matches if the given
+// template is a prefix of the full URL path. See Route.Path() for details on
+// the tpl argument.
+//
+// Note that it does not treat slashes specially ("/foobar/" will be matched by
+// the prefix "/foo") so you may want to use a trailing slash here.
+//
+// Also note that the setting of Router.StrictSlash() has no effect on routes
+// with a PathPrefix matcher.
+func (r *Route) PathPrefix(tpl string) *Route {
+	r.err = r.addRegexpMatcher(tpl, false, true, false)
+	return r
+}
+
+// Query ----------------------------------------------------------------------
+
+// Queries adds a matcher for URL query values.
+// It accepts a sequence of key/value pairs. Values may define variables.
+// For example:
+//
+//     r := mux.NewRouter()
+//     r.Queries("foo", "bar", "id", "{id:[0-9]+}")
+//
+// The above route will only match if the URL contains the defined queries
+// values, e.g.: ?foo=bar&id=42.
+//
+// It the value is an empty string, it will match any value if the key is set.
+//
+// Variables can define an optional regexp pattern to me matched:
+//
+// - {name} matches anything until the next slash.
+//
+// - {name:pattern} matches the given regexp pattern.
+func (r *Route) Queries(pairs ...string) *Route {
+	length := len(pairs)
+	if length%2 != 0 {
+		r.err = fmt.Errorf(
+			"mux: number of parameters must be multiple of 2, got %v", pairs)
+		return nil
+	}
+	for i := 0; i < length; i += 2 {
+		if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, true, true); r.err != nil {
+			return r
+		}
+	}
+
+	return r
+}
+
+// Schemes --------------------------------------------------------------------
+
+// schemeMatcher matches the request against URL schemes.
+type schemeMatcher []string
+
+func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
+	return matchInArray(m, r.URL.Scheme)
+}
+
+// Schemes adds a matcher for URL schemes.
+// It accepts a sequence of schemes to be matched, e.g.: "http", "https".
+func (r *Route) Schemes(schemes ...string) *Route {
+	for k, v := range schemes {
+		schemes[k] = strings.ToLower(v)
+	}
+	return r.addMatcher(schemeMatcher(schemes))
+}
+
+// BuildVarsFunc --------------------------------------------------------------
+
+// BuildVarsFunc is the function signature used by custom build variable
+// functions (which can modify route variables before a route's URL is built).
+type BuildVarsFunc func(map[string]string) map[string]string
+
+// BuildVarsFunc adds a custom function to be used to modify build variables
+// before a route's URL is built.
+func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
+	r.buildVarsFunc = f
+	return r
+}
+
+// Subrouter ------------------------------------------------------------------
+
+// Subrouter creates a subrouter for the route.
+//
+// It will test the inner routes only if the parent route matched. For example:
+//
+//     r := mux.NewRouter()
+//     s := r.Host("www.domain.com").Subrouter()
+//     s.HandleFunc("/products/", ProductsHandler)
+//     s.HandleFunc("/products/{key}", ProductHandler)
+//     s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
+//
+// Here, the routes registered in the subrouter won't be tested if the host
+// doesn't match.
+func (r *Route) Subrouter() *Router {
+	router := &Router{parent: r, strictSlash: r.strictSlash}
+	r.addMatcher(router)
+	return router
+}
+
+// ----------------------------------------------------------------------------
+// URL building
+// ----------------------------------------------------------------------------
+
+// URL builds a URL for the route.
+//
+// It accepts a sequence of key/value pairs for the route variables. For
+// example, given this route:
+//
+//     r := mux.NewRouter()
+//     r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
+//       Name("article")
+//
+// ...a URL for it can be built using:
+//
+//     url, err := r.Get("article").URL("category", "technology", "id", "42")
+//
+// ...which will return an url.URL with the following path:
+//
+//     "/articles/technology/42"
+//
+// This also works for host variables:
+//
+//     r := mux.NewRouter()
+//     r.Host("{subdomain}.domain.com").
+//       HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
+//       Name("article")
+//
+//     // url.String() will be "http://news.domain.com/articles/technology/42"
+//     url, err := r.Get("article").URL("subdomain", "news",
+//                                      "category", "technology",
+//                                      "id", "42")
+//
+// All variables defined in the route are required, and their values must
+// conform to the corresponding patterns.
+func (r *Route) URL(pairs ...string) (*url.URL, error) {
+	if r.err != nil {
+		return nil, r.err
+	}
+	if r.regexp == nil {
+		return nil, errors.New("mux: route doesn't have a host or path")
+	}
+	values, err := r.prepareVars(pairs...)
+	if err != nil {
+		return nil, err
+	}
+	var scheme, host, path string
+	if r.regexp.host != nil {
+		// Set a default scheme.
+		scheme = "http"
+		if host, err = r.regexp.host.url(values); err != nil {
+			return nil, err
+		}
+	}
+	if r.regexp.path != nil {
+		if path, err = r.regexp.path.url(values); err != nil {
+			return nil, err
+		}
+	}
+	return &url.URL{
+		Scheme: scheme,
+		Host:   host,
+		Path:   path,
+	}, nil
+}
+
+// URLHost builds the host part of the URL for a route. See Route.URL().
+//
+// The route must have a host defined.
+func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
+	if r.err != nil {
+		return nil, r.err
+	}
+	if r.regexp == nil || r.regexp.host == nil {
+		return nil, errors.New("mux: route doesn't have a host")
+	}
+	values, err := r.prepareVars(pairs...)
+	if err != nil {
+		return nil, err
+	}
+	host, err := r.regexp.host.url(values)
+	if err != nil {
+		return nil, err
+	}
+	return &url.URL{
+		Scheme: "http",
+		Host:   host,
+	}, nil
+}
+
+// URLPath builds the path part of the URL for a route. See Route.URL().
+//
+// The route must have a path defined.
+func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
+	if r.err != nil {
+		return nil, r.err
+	}
+	if r.regexp == nil || r.regexp.path == nil {
+		return nil, errors.New("mux: route doesn't have a path")
+	}
+	values, err := r.prepareVars(pairs...)
+	if err != nil {
+		return nil, err
+	}
+	path, err := r.regexp.path.url(values)
+	if err != nil {
+		return nil, err
+	}
+	return &url.URL{
+		Path: path,
+	}, nil
+}
+
+// prepareVars converts the route variable pairs into a map. If the route has a
+// BuildVarsFunc, it is invoked.
+func (r *Route) prepareVars(pairs ...string) (map[string]string, error) {
+	m, err := mapFromPairs(pairs...)
+	if err != nil {
+		return nil, err
+	}
+	return r.buildVars(m), nil
+}
+
+func (r *Route) buildVars(m map[string]string) map[string]string {
+	if r.parent != nil {
+		m = r.parent.buildVars(m)
+	}
+	if r.buildVarsFunc != nil {
+		m = r.buildVarsFunc(m)
+	}
+	return m
+}
+
+// ----------------------------------------------------------------------------
+// parentRoute
+// ----------------------------------------------------------------------------
+
+// parentRoute allows routes to know about parent host and path definitions.
+type parentRoute interface {
+	getNamedRoutes() map[string]*Route
+	getRegexpGroup() *routeRegexpGroup
+	buildVars(map[string]string) map[string]string
+}
+
+// getNamedRoutes returns the map where named routes are registered.
+func (r *Route) getNamedRoutes() map[string]*Route {
+	if r.parent == nil {
+		// During tests router is not always set.
+		r.parent = NewRouter()
+	}
+	return r.parent.getNamedRoutes()
+}
+
+// getRegexpGroup returns regexp definitions from this route.
+func (r *Route) getRegexpGroup() *routeRegexpGroup {
+	if r.regexp == nil {
+		if r.parent == nil {
+			// During tests router is not always set.
+			r.parent = NewRouter()
+		}
+		regexp := r.parent.getRegexpGroup()
+		if regexp == nil {
+			r.regexp = new(routeRegexpGroup)
+		} else {
+			// Copy.
+			r.regexp = &routeRegexpGroup{
+				host:    regexp.host,
+				path:    regexp.path,
+				queries: regexp.queries,
+			}
+		}
+	}
+	return r.regexp
+}

+ 546 - 0
libnetwork/api/api.go

@@ -0,0 +1,546 @@
+package api
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+
+	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netutils"
+	"github.com/docker/libnetwork/sandbox"
+	"github.com/gorilla/mux"
+)
+
+var (
+	successResponse  = responseStatus{Status: "Success", StatusCode: http.StatusOK}
+	createdResponse  = responseStatus{Status: "Created", StatusCode: http.StatusCreated}
+	mismatchResponse = responseStatus{Status: "Body/URI parameter mismatch", StatusCode: http.StatusBadRequest}
+)
+
+const (
+	urlNwName = "name"
+	urlNwID   = "id"
+	urlEpName = "endpoint-name"
+	urlEpID   = "endpoint-id"
+	urlCnID   = "container-id"
+)
+
+// NewHTTPHandler creates and initialize the HTTP handler to serve the requests for libnetwork
+func NewHTTPHandler(c libnetwork.NetworkController) func(w http.ResponseWriter, req *http.Request) {
+	h := &httpHandler{c: c}
+	h.initRouter()
+	return h.handleRequest
+}
+
+type responseStatus struct {
+	Status     string
+	StatusCode int
+}
+
+func (r *responseStatus) isOK() bool {
+	return r.StatusCode == http.StatusOK || r.StatusCode == http.StatusCreated
+}
+
+type processor func(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus)
+
+type httpHandler struct {
+	c libnetwork.NetworkController
+	r *mux.Router
+}
+
+func (h *httpHandler) handleRequest(w http.ResponseWriter, req *http.Request) {
+	// Make sure the service is there
+	if h.c == nil {
+		http.Error(w, "NetworkController is not available", http.StatusServiceUnavailable)
+		return
+	}
+
+	// Get handler from router and execute it
+	h.r.ServeHTTP(w, req)
+}
+
+func (h *httpHandler) initRouter() {
+	m := map[string]map[string]processor{
+		"GET": {
+			"/networks":                                                                   procGetNetworks,
+			"/networks/name/{" + urlNwName + ":.*}":                                       procGetNetwork,
+			"/networks/id/{" + urlNwID + ":.*}":                                           procGetNetwork,
+			"/networks/name/{" + urlNwName + ":.*}/endpoints/":                            procGetEndpoints,
+			"/networks/id/{" + urlNwID + ":.*}/endpoints/":                                procGetEndpoints,
+			"/networks/name/{" + urlNwName + ":.*}/endpoints/name/{" + urlEpName + ":.*}": procGetEndpoint,
+			"/networks/id/{" + urlNwName + ":.*}/endpoints/name/{" + urlEpName + ":.*}":   procGetEndpoint,
+			"/networks/name/{" + urlNwName + ":.*}/endpoints/id/{" + urlEpID + ":.*}":     procGetEndpoint,
+			"/networks/id/{" + urlNwID + ":.*}/endpoints/id/{" + urlEpID + ":.*}":         procGetEndpoint,
+		},
+		"POST": {
+			"/networks/name/{" + urlNwName + ":.*}":                                                                     procCreateNetwork,
+			"/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}":                                procCreateEndpoint,
+			"/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}": procJoinEndpoint,
+		},
+		"DELETE": {
+			"/networks/name/{" + urlNwName + ":.*}":                                                                     procDeleteNetwork,
+			"/networks/id/{" + urlNwID + ":.*}":                                                                         procDeleteNetwork,
+			"/networks/name/{" + urlNwName + ":.*}/endpoints/name/{" + urlEpName + ":.*}":                               procDeleteEndpoint,
+			"/networks/name/{" + urlNwName + ":.*}/endpoints/id/{" + urlEpID + ":.*}":                                   procDeleteEndpoint,
+			"/networks/id/{" + urlNwID + ":.*}/endpoints/name/{" + urlEpName + ":.*}":                                   procDeleteEndpoint,
+			"/networks/id/{" + urlNwID + ":.*}/endpoints/id/{" + urlEpID + ":.*}":                                       procDeleteEndpoint,
+			"/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}": procLeaveEndpoint,
+			"/networks/name/{" + urlNwName + ":.*}/endpoint/id/{" + urlEpID + ":.*}/container/{" + urlCnID + ":.*}":     procLeaveEndpoint,
+			"/networks/id/{" + urlNwID + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}":     procLeaveEndpoint,
+			"/networks/id/{" + urlNwID + ":.*}/endpoint/id/{" + urlEpID + ":.*}/container/{" + urlCnID + ":.*}":         procLeaveEndpoint,
+		},
+	}
+
+	h.r = mux.NewRouter()
+	for method, routes := range m {
+		for route, fct := range routes {
+			f := makeHandler(h.c, fct)
+			h.r.Path(route).Methods(method).HandlerFunc(f)
+		}
+	}
+}
+
+func makeHandler(ctrl libnetwork.NetworkController, fct processor) http.HandlerFunc {
+	return func(w http.ResponseWriter, req *http.Request) {
+		var (
+			body []byte
+			err  error
+		)
+		if req.Body != nil {
+			body, err = ioutil.ReadAll(req.Body)
+			if err != nil {
+				http.Error(w, "Invalid body: "+err.Error(), http.StatusBadRequest)
+				return
+			}
+		}
+
+		res, rsp := fct(ctrl, mux.Vars(req), body)
+		if !rsp.isOK() {
+			http.Error(w, rsp.Status, rsp.StatusCode)
+			return
+		}
+		if res != nil {
+			writeJSON(w, rsp.StatusCode, res)
+		}
+	}
+}
+
+/***********
+ Resources
+************/
+
+// networkResource is the body of the "get network" http response message
+type networkResource struct {
+	Name      string
+	ID        string
+	Type      string
+	Endpoints []*endpointResource
+}
+
+// endpointResource is the body of the "get endpoint" http response message
+type endpointResource struct {
+	Name    string
+	ID      string
+	Network string
+	Info    sandbox.Info
+}
+
+func buildNetworkResource(nw libnetwork.Network) *networkResource {
+	r := &networkResource{}
+	if nw != nil {
+		r.Name = nw.Name()
+		r.ID = nw.ID()
+		r.Type = nw.Type()
+		epl := nw.Endpoints()
+		r.Endpoints = make([]*endpointResource, 0, len(epl))
+		for _, e := range epl {
+			epr := buildEndpointResource(e)
+			r.Endpoints = append(r.Endpoints, epr)
+		}
+	}
+	return r
+}
+
+func buildEndpointResource(ep libnetwork.Endpoint) *endpointResource {
+	r := &endpointResource{}
+	if ep != nil {
+		r.Name = ep.Name()
+		r.ID = ep.ID()
+		r.Network = ep.Network()
+
+		i := ep.SandboxInfo()
+		if i != nil {
+			r.Info = *i
+		}
+	}
+	return r
+}
+
+/***********
+ Body types
+************/
+
+// networkCreate is the expected body of the "create network" http request message
+type networkCreate struct {
+	Name        string
+	NetworkType string
+	Options     map[string]interface{}
+}
+
+// endpointCreate represents the body of the "create endpoint" http request message
+type endpointCreate struct {
+	Name         string
+	NetworkID    string
+	ExposedPorts []netutils.TransportPort
+	PortMapping  []netutils.PortBinding
+}
+
+// endpointJoin represents the expected body of the "join endpoint" or "leave endpoint" http request messages
+type endpointJoin struct {
+	ContainerID       string
+	HostName          string
+	DomainName        string
+	HostsPath         string
+	ResolvConfPath    string
+	DNS               []string
+	ExtraHosts        []endpointExtraHost
+	ParentUpdates     []endpointParentUpdate
+	UseDefaultSandbox bool
+}
+
+// EndpointExtraHost represents the extra host object
+type endpointExtraHost struct {
+	Name    string
+	Address string
+}
+
+// EndpointParentUpdate is the object carrying the information about the
+// endpoint parent that needs to be updated
+type endpointParentUpdate struct {
+	EndpointID string
+	Name       string
+	Address    string
+}
+
+func (ej *endpointJoin) parseOptions() []libnetwork.EndpointOption {
+	var setFctList []libnetwork.EndpointOption
+	if ej.HostName != "" {
+		setFctList = append(setFctList, libnetwork.JoinOptionHostname(ej.HostName))
+	}
+	if ej.DomainName != "" {
+		setFctList = append(setFctList, libnetwork.JoinOptionDomainname(ej.DomainName))
+	}
+	if ej.HostsPath != "" {
+		setFctList = append(setFctList, libnetwork.JoinOptionHostsPath(ej.HostsPath))
+	}
+	if ej.ResolvConfPath != "" {
+		setFctList = append(setFctList, libnetwork.JoinOptionResolvConfPath(ej.ResolvConfPath))
+	}
+	if ej.UseDefaultSandbox {
+		setFctList = append(setFctList, libnetwork.JoinOptionUseDefaultSandbox())
+	}
+	if ej.DNS != nil {
+		for _, d := range ej.DNS {
+			setFctList = append(setFctList, libnetwork.JoinOptionDNS(d))
+		}
+	}
+	if ej.ExtraHosts != nil {
+		for _, e := range ej.ExtraHosts {
+			setFctList = append(setFctList, libnetwork.JoinOptionExtraHost(e.Name, e.Address))
+		}
+	}
+	if ej.ParentUpdates != nil {
+		for _, p := range ej.ParentUpdates {
+			setFctList = append(setFctList, libnetwork.JoinOptionParentUpdate(p.EndpointID, p.Name, p.Address))
+		}
+	}
+	return setFctList
+}
+
+/******************
+ Process functions
+*******************/
+
+/***************************
+ NetworkController interface
+****************************/
+func procCreateNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
+	var create networkCreate
+
+	err := json.Unmarshal(body, &create)
+	if err != nil {
+		return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
+	}
+
+	name := vars[urlNwName]
+	if name != create.Name {
+		return "", &mismatchResponse
+	}
+
+	nw, err := c.NewNetwork(create.NetworkType, name, nil)
+	if err != nil {
+		return "", convertNetworkError(err)
+	}
+
+	return nw.ID(), &createdResponse
+}
+
+func procGetNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
+	t, by := detectNetworkTarget(vars)
+	nw, errRsp := findNetwork(c, t, by)
+	if !errRsp.isOK() {
+		return nil, errRsp
+	}
+	return buildNetworkResource(nw), &successResponse
+}
+
+func procGetNetworks(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
+	var list []*networkResource
+	for _, nw := range c.Networks() {
+		nwr := buildNetworkResource(nw)
+		list = append(list, nwr)
+	}
+	return list, &successResponse
+}
+
+/******************
+ Network interface
+*******************/
+func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
+	var ec endpointCreate
+
+	err := json.Unmarshal(body, &ec)
+	if err != nil {
+		return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
+	}
+
+	epn := vars[urlEpName]
+	if ec.Name != epn {
+		return "", &mismatchResponse
+	}
+
+	nwT, nwBy := detectNetworkTarget(vars)
+	n, errRsp := findNetwork(c, nwT, nwBy)
+	if !errRsp.isOK() {
+		return "", errRsp
+	}
+
+	if ec.NetworkID != n.ID() {
+		return "", &mismatchResponse
+	}
+
+	var setFctList []libnetwork.EndpointOption
+	if ec.ExposedPorts != nil {
+		setFctList = append(setFctList, libnetwork.CreateOptionExposedPorts(ec.ExposedPorts))
+	}
+	if ec.PortMapping != nil {
+		setFctList = append(setFctList, libnetwork.CreateOptionPortMapping(ec.PortMapping))
+	}
+
+	ep, err := n.CreateEndpoint(epn, setFctList...)
+	if err != nil {
+		return "", convertNetworkError(err)
+	}
+
+	return ep.ID(), &createdResponse
+}
+
+func procGetEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
+	nwT, nwBy := detectNetworkTarget(vars)
+	epT, epBy := detectEndpointTarget(vars)
+
+	ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
+	if !errRsp.isOK() {
+		return nil, errRsp
+	}
+
+	return buildEndpointResource(ep), &successResponse
+}
+
+func procGetEndpoints(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
+	target, by := detectNetworkTarget(vars)
+
+	nw, errRsp := findNetwork(c, target, by)
+	if !errRsp.isOK() {
+		return nil, errRsp
+	}
+
+	var list []*endpointResource
+	for _, ep := range nw.Endpoints() {
+		epr := buildEndpointResource(ep)
+		list = append(list, epr)
+	}
+
+	return list, &successResponse
+}
+
+func procDeleteNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
+	target, by := detectNetworkTarget(vars)
+
+	nw, errRsp := findNetwork(c, target, by)
+	if !errRsp.isOK() {
+		return nil, errRsp
+	}
+
+	err := nw.Delete()
+	if err != nil {
+		return nil, convertNetworkError(err)
+	}
+
+	return nil, &successResponse
+}
+
+/******************
+ Endpoint interface
+*******************/
+func procJoinEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
+	var ej endpointJoin
+	err := json.Unmarshal(body, &ej)
+	if err != nil {
+		return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
+	}
+
+	cid := vars[urlCnID]
+	if ej.ContainerID != cid {
+		return "", &mismatchResponse
+	}
+
+	nwT, nwBy := detectNetworkTarget(vars)
+	epT, epBy := detectEndpointTarget(vars)
+
+	ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
+	if !errRsp.isOK() {
+		return nil, errRsp
+	}
+
+	cd, err := ep.Join(ej.ContainerID, ej.parseOptions()...)
+	if err != nil {
+		return nil, convertNetworkError(err)
+	}
+	return cd, &successResponse
+}
+
+func procLeaveEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
+	nwT, nwBy := detectNetworkTarget(vars)
+	epT, epBy := detectEndpointTarget(vars)
+
+	ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
+	if !errRsp.isOK() {
+		return nil, errRsp
+	}
+
+	err := ep.Leave(vars[urlCnID], nil)
+	if err != nil {
+		return nil, convertNetworkError(err)
+	}
+
+	return nil, &successResponse
+}
+
+func procDeleteEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
+	nwT, nwBy := detectNetworkTarget(vars)
+	epT, epBy := detectEndpointTarget(vars)
+
+	ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
+	if !errRsp.isOK() {
+		return nil, errRsp
+	}
+
+	err := ep.Delete()
+	if err != nil {
+		return nil, convertNetworkError(err)
+	}
+
+	return nil, &successResponse
+}
+
+/***********
+  Utilities
+************/
+const (
+	byID = iota
+	byName
+)
+
+func detectNetworkTarget(vars map[string]string) (string, int) {
+	if target, ok := vars[urlNwName]; ok {
+		return target, byName
+	}
+	if target, ok := vars[urlNwID]; ok {
+		return target, byID
+	}
+	// vars are populated from the URL, following cannot happen
+	panic("Missing URL variable parameter for network")
+}
+
+func detectEndpointTarget(vars map[string]string) (string, int) {
+	if target, ok := vars[urlEpName]; ok {
+		return target, byName
+	}
+	if target, ok := vars[urlEpID]; ok {
+		return target, byID
+	}
+	// vars are populated from the URL, following cannot happen
+	panic("Missing URL variable parameter for endpoint")
+}
+
+func findNetwork(c libnetwork.NetworkController, s string, by int) (libnetwork.Network, *responseStatus) {
+	var (
+		nw  libnetwork.Network
+		err error
+	)
+	switch by {
+	case byID:
+		nw, err = c.NetworkByID(s)
+	case byName:
+		nw, err = c.NetworkByName(s)
+	default:
+		panic(fmt.Sprintf("unexpected selector for network search: %d", by))
+	}
+	if err != nil {
+		return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
+	}
+	if nw == nil {
+		return nil, &responseStatus{Status: "Resource not found: Network", StatusCode: http.StatusNotFound}
+	}
+	return nw, &successResponse
+}
+
+func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int) (libnetwork.Endpoint, *responseStatus) {
+	nw, errRsp := findNetwork(c, ns, nwBy)
+	if !errRsp.isOK() {
+		return nil, errRsp
+	}
+	var (
+		err error
+		ep  libnetwork.Endpoint
+	)
+	switch epBy {
+	case byID:
+		ep, err = nw.EndpointByID(es)
+	case byName:
+		ep, err = nw.EndpointByName(es)
+	default:
+		panic(fmt.Sprintf("unexpected selector for endpoint search: %d", epBy))
+	}
+	if err != nil {
+		return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
+	}
+	if ep == nil {
+		return nil, &responseStatus{Status: "Resource not found: Endpoint", StatusCode: http.StatusNotFound}
+	}
+	return ep, &successResponse
+}
+
+func convertNetworkError(err error) *responseStatus {
+	// No real libnetwork error => http error code conversion for now.
+	// Will came in later when new interface for libnetwork error is vailable
+	return &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
+}
+
+func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
+	w.Header().Set("Content-Type", "application/json")
+	w.WriteHeader(code)
+	return json.NewEncoder(w).Encode(v)
+}

+ 1306 - 0
libnetwork/api/api_test.go

@@ -0,0 +1,1306 @@
+package api
+
+import (
+	"bytes"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io"
+	"net/http"
+	"os"
+	"runtime"
+	"testing"
+
+	"github.com/docker/docker/pkg/reexec"
+	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netutils"
+	"github.com/docker/libnetwork/pkg/netlabel"
+	"github.com/docker/libnetwork/pkg/options"
+)
+
+const (
+	bridgeNetType = "bridge"
+	bridgeName    = "docker0"
+)
+
+func getEmptyGenericOption() map[string]interface{} {
+	genericOption := make(map[string]interface{})
+	genericOption[netlabel.GenericData] = options.Generic{}
+	return genericOption
+}
+
+func i2s(i interface{}) string {
+	s, ok := i.(string)
+	if !ok {
+		panic(fmt.Sprintf("Failed i2s for %v", i))
+	}
+	return s
+}
+
+func i2e(i interface{}) *endpointResource {
+	s, ok := i.(*endpointResource)
+	if !ok {
+		panic(fmt.Sprintf("Failed i2e for %v", i))
+	}
+	return s
+}
+
+func i2c(i interface{}) *libnetwork.ContainerData {
+	s, ok := i.(*libnetwork.ContainerData)
+	if !ok {
+		panic(fmt.Sprintf("Failed i2c for %v", i))
+	}
+	return s
+}
+
+func i2eL(i interface{}) []*endpointResource {
+	s, ok := i.([]*endpointResource)
+	if !ok {
+		panic(fmt.Sprintf("Failed i2eL for %v", i))
+	}
+	return s
+}
+
+func i2n(i interface{}) *networkResource {
+	s, ok := i.(*networkResource)
+	if !ok {
+		panic(fmt.Sprintf("Failed i2n for %v", i))
+	}
+	return s
+}
+
+func i2nL(i interface{}) []*networkResource {
+	s, ok := i.([]*networkResource)
+	if !ok {
+		panic(fmt.Sprintf("Failed i2nL for %v", i))
+	}
+	return s
+}
+
+func TestMain(m *testing.M) {
+	if reexec.Init() {
+		return
+	}
+	os.Exit(m.Run())
+}
+
+func TestJoinOptionParser(t *testing.T) {
+	hn := "host1"
+	dn := "docker.com"
+	hp := "/etc/hosts"
+	rc := "/etc/resolv.conf"
+	dnss := []string{"8.8.8.8", "172.28.34.5"}
+	ehs := []endpointExtraHost{endpointExtraHost{Name: "extra1", Address: "172.28.9.1"}, endpointExtraHost{Name: "extra2", Address: "172.28.9.2"}}
+	pus := []endpointParentUpdate{endpointParentUpdate{EndpointID: "abc123def456", Name: "serv1", Address: "172.28.30.123"}}
+
+	ej := endpointJoin{
+		HostName:          hn,
+		DomainName:        dn,
+		HostsPath:         hp,
+		ResolvConfPath:    rc,
+		DNS:               dnss,
+		ExtraHosts:        ehs,
+		ParentUpdates:     pus,
+		UseDefaultSandbox: true,
+	}
+
+	if len(ej.parseOptions()) != 10 {
+		t.Fatalf("Failed to generate all libnetwork.EndpointJoinOption methods libnetwork.EndpointJoinOption method")
+	}
+
+}
+
+func TestJson(t *testing.T) {
+	nc := networkCreate{Name: "mynet", NetworkType: bridgeNetType}
+	b, err := json.Marshal(nc)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var ncp networkCreate
+	err = json.Unmarshal(b, &ncp)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if nc.Name != ncp.Name || nc.NetworkType != ncp.NetworkType {
+		t.Fatalf("Incorrect networkCreate after json encoding/deconding: %v", ncp)
+	}
+
+	ec := endpointCreate{Name: "mioEp", NetworkID: "0xabcde"}
+	b, err = json.Marshal(ec)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var ecp endpointCreate
+	err = json.Unmarshal(b, &ecp)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if ec.Name != ecp.Name || ec.NetworkID != ecp.NetworkID {
+		t.Fatalf("Incorrect endpointCreate after json encoding/deconding: %v", ecp)
+	}
+
+	jl := endpointJoin{ContainerID: "abcdef456789"}
+	b, err = json.Marshal(jl)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var jld endpointJoin
+	err = json.Unmarshal(b, &jld)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if jl.ContainerID != jld.ContainerID {
+		t.Fatalf("Incorrect endpointJoin after json encoding/deconding: %v", ecp)
+	}
+}
+
+func TestCreateDeleteNetwork(t *testing.T) {
+	defer netutils.SetupTestNetNS(t)()
+
+	c, err := libnetwork.New()
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = c.ConfigureNetworkDriver(bridgeNetType, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	badBody, err := json.Marshal("bad body")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	goodVars := map[string]string{urlNwName: "myNet"}
+	_, errRsp := procCreateNetwork(c, goodVars, badBody)
+	if errRsp == &createdResponse {
+		t.Fatalf("Expected to fail but succeeded")
+	}
+
+	incompleteBody, err := json.Marshal(networkCreate{Name: "myNet"})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	_, errRsp = procCreateNetwork(c, goodVars, incompleteBody)
+	if errRsp == &createdResponse {
+		t.Fatalf("Expected to fail but succeeded")
+	}
+	if errRsp.StatusCode != http.StatusBadRequest {
+		t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp.StatusCode)
+	}
+
+	ops := make(map[string]interface{})
+	ops[netlabel.GenericData] = options.Generic{}
+	nc := networkCreate{Name: "myNet", NetworkType: bridgeNetType, Options: ops}
+	goodBody, err := json.Marshal(nc)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	badVars := map[string]string{urlNwName: ""}
+	_, errRsp = procCreateNetwork(c, badVars, goodBody)
+	if errRsp == &createdResponse {
+		t.Fatalf("Expected to fail but succeeded")
+	}
+	if errRsp.StatusCode != http.StatusBadRequest {
+		t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp.StatusCode)
+	}
+
+	badVars[urlNwName] = "badNetworkName"
+	_, errRsp = procCreateNetwork(c, badVars, goodBody)
+	if errRsp == &createdResponse {
+		t.Fatalf("Expected to fail but succeeded")
+	}
+	if errRsp.StatusCode != http.StatusBadRequest {
+		t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp.StatusCode)
+	}
+
+	_, errRsp = procCreateNetwork(c, goodVars, goodBody)
+	if errRsp != &createdResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+
+	_, errRsp = procDeleteNetwork(c, badVars, nil)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected to fail but succeeded")
+	}
+
+	badVars[urlNwName] = ""
+	_, errRsp = procDeleteNetwork(c, badVars, nil)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected to fail but succeeded")
+	}
+
+	_, errRsp = procDeleteNetwork(c, goodVars, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+}
+
+func TestGetNetworksAndEndpoints(t *testing.T) {
+	defer netutils.SetupTestNetNS(t)()
+
+	c, err := libnetwork.New()
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = c.ConfigureNetworkDriver(bridgeNetType, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	nc := networkCreate{Name: "sh", NetworkType: bridgeNetType}
+	body, err := json.Marshal(nc)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	vars := map[string]string{urlNwName: "sh"}
+	inid, errRsp := procCreateNetwork(c, vars, body)
+	if errRsp != &createdResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	nid, ok := inid.(string)
+	if !ok {
+		t.FailNow()
+	}
+
+	ec1 := endpointCreate{
+		Name:      "ep1",
+		NetworkID: string(nid),
+		ExposedPorts: []netutils.TransportPort{
+			netutils.TransportPort{Proto: netutils.TCP, Port: uint16(5000)},
+			netutils.TransportPort{Proto: netutils.UDP, Port: uint16(400)},
+			netutils.TransportPort{Proto: netutils.TCP, Port: uint16(600)},
+		},
+		PortMapping: []netutils.PortBinding{
+			netutils.PortBinding{Proto: netutils.TCP, Port: uint16(230), HostPort: uint16(23000)},
+			netutils.PortBinding{Proto: netutils.UDP, Port: uint16(200), HostPort: uint16(22000)},
+			netutils.PortBinding{Proto: netutils.TCP, Port: uint16(120), HostPort: uint16(12000)},
+		},
+	}
+	b1, err := json.Marshal(ec1)
+	if err != nil {
+		t.Fatal(err)
+	}
+	ec2 := endpointCreate{Name: "ep2", NetworkID: nid}
+	b2, err := json.Marshal(ec2)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	vars[urlEpName] = "ep1"
+	ieid1, errRsp := procCreateEndpoint(c, vars, b1)
+	if errRsp != &createdResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	eid1 := i2s(ieid1)
+	vars[urlEpName] = "ep2"
+	ieid2, errRsp := procCreateEndpoint(c, vars, b2)
+	if errRsp != &createdResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	eid2 := i2s(ieid2)
+
+	vars[urlNwName] = ""
+	vars[urlEpName] = "ep1"
+	_, errRsp = procGetEndpoint(c, vars, nil)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure but succeeded: %v", errRsp)
+	}
+	if errRsp.StatusCode != http.StatusBadRequest {
+		t.Fatalf("Expected to fail with http.StatusBadRequest, but got: %d", errRsp.StatusCode)
+	}
+
+	vars = make(map[string]string)
+	vars[urlNwName] = "sh"
+	vars[urlEpID] = ""
+	_, errRsp = procGetEndpoint(c, vars, nil)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure but succeeded: %v", errRsp)
+	}
+	if errRsp.StatusCode != http.StatusBadRequest {
+		t.Fatalf("Expected to fail with http.StatusBadRequest, but got: %d", errRsp.StatusCode)
+	}
+
+	vars = make(map[string]string)
+	vars[urlNwID] = ""
+	vars[urlEpID] = eid1
+	_, errRsp = procGetEndpoint(c, vars, nil)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure but succeeded: %v", errRsp)
+	}
+	if errRsp.StatusCode != http.StatusBadRequest {
+		t.Fatalf("Expected to fail with http.StatusBadRequest, but got: %d", errRsp.StatusCode)
+	}
+
+	// nw by name and ep by id
+	vars[urlNwName] = "sh"
+	i1, errRsp := procGetEndpoint(c, vars, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	// nw by name and ep by name
+	delete(vars, urlEpID)
+	vars[urlEpName] = "ep1"
+	i2, errRsp := procGetEndpoint(c, vars, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	// nw by id and ep by name
+	delete(vars, urlNwName)
+	vars[urlNwID] = nid
+	i3, errRsp := procGetEndpoint(c, vars, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	// nw by id and ep by id
+	delete(vars, urlEpName)
+	vars[urlEpID] = eid1
+	i4, errRsp := procGetEndpoint(c, vars, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+
+	id1 := i2e(i1).ID
+	if id1 != i2e(i2).ID || id1 != i2e(i3).ID || id1 != i2e(i4).ID {
+		t.Fatalf("Endpoints retireved via different query parameters differ: %v, %v, %v, %v", i1, i2, i3, i4)
+	}
+
+	vars[urlNwName] = ""
+	_, errRsp = procGetEndpoints(c, vars, nil)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+
+	delete(vars, urlNwName)
+	vars[urlNwID] = "fakeID"
+	_, errRsp = procGetEndpoints(c, vars, nil)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+
+	vars[urlNwID] = nid
+	_, errRsp = procGetEndpoints(c, vars, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+
+	vars[urlNwName] = "sh"
+	iepList, errRsp := procGetEndpoints(c, vars, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	epList := i2eL(iepList)
+	if len(epList) != 2 {
+		t.Fatalf("Did not return the expected number (2) of endpoint resources: %d", len(epList))
+	}
+	if "sh" != epList[0].Network || "sh" != epList[1].Network {
+		t.Fatalf("Did not find expected network name in endpoint resources")
+	}
+
+	vars = make(map[string]string)
+	vars[urlNwName] = ""
+	_, errRsp = procGetNetwork(c, vars, nil)
+	if errRsp == &successResponse {
+		t.Fatalf("Exepected failure, got: %v", errRsp)
+	}
+	vars[urlNwName] = "shhhhh"
+	_, errRsp = procGetNetwork(c, vars, nil)
+	if errRsp == &successResponse {
+		t.Fatalf("Exepected failure, got: %v", errRsp)
+	}
+	vars[urlNwName] = "sh"
+	inr1, errRsp := procGetNetwork(c, vars, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	nr1 := i2n(inr1)
+
+	delete(vars, urlNwName)
+	vars[urlNwID] = "cacca"
+	_, errRsp = procGetNetwork(c, vars, nil)
+	if errRsp == &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	vars[urlNwID] = nid
+	inr2, errRsp := procGetNetwork(c, vars, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("procgetNetworkByName() != procgetNetworkById(), %v vs %v", inr1, inr2)
+	}
+	nr2 := i2n(inr2)
+	if nr1.Name != nr2.Name || nr1.Type != nr2.Type || nr1.ID != nr2.ID || len(nr1.Endpoints) != len(nr2.Endpoints) {
+		t.Fatalf("Get by name and Get failure: %v", errRsp)
+	}
+
+	if len(nr1.Endpoints) != 2 {
+		t.Fatalf("Did not find the expected number (2) of endpoint resources in the network resource: %d", len(nr1.Endpoints))
+	}
+	for _, er := range nr1.Endpoints {
+		if er.ID != eid1 && er.ID != eid2 {
+			t.Fatalf("Did not find the expected endpoint resources in the network resource: %v", nr1.Endpoints)
+		}
+	}
+
+	iList, errRsp := procGetNetworks(c, nil, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	netList := i2nL(iList)
+	if len(netList) != 1 {
+		t.Fatalf("Did not return the expected number of network resources")
+	}
+	if nid != netList[0].ID {
+		t.Fatalf("Did not find expected network %s: %v", nid, netList)
+	}
+
+	_, errRsp = procDeleteNetwork(c, vars, nil)
+	if errRsp == &successResponse {
+		t.Fatalf("Exepected failure, got: %v", errRsp)
+	}
+
+	vars[urlEpName] = "ep1"
+	_, errRsp = procDeleteEndpoint(c, vars, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	iepList, errRsp = procGetEndpoints(c, vars, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	epList = i2eL(iepList)
+	if len(epList) != 1 {
+		t.Fatalf("Did not return the expected number (1) of endpoint resources: %d", len(epList))
+	}
+
+	vars[urlEpName] = "ep2"
+	_, errRsp = procDeleteEndpoint(c, vars, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	iepList, errRsp = procGetEndpoints(c, vars, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	epList = i2eL(iepList)
+	if len(epList) != 0 {
+		t.Fatalf("Did not return the expected number (0) of endpoint resources: %d", len(epList))
+	}
+
+	_, errRsp = procDeleteNetwork(c, vars, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+
+	iList, errRsp = procGetNetworks(c, nil, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	netList = i2nL(iList)
+	if len(netList) != 0 {
+		t.Fatalf("Did not return the expected number of network resources")
+	}
+}
+
+func TestFindNetworkUtil(t *testing.T) {
+	defer netutils.SetupTestNetNS(t)()
+
+	c, err := libnetwork.New()
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = c.ConfigureNetworkDriver(bridgeNetType, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	nw, err := c.NewNetwork(bridgeNetType, "network", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	nid := nw.ID()
+
+	defer checkPanic(t)
+	findNetwork(c, "", -1)
+
+	_, errRsp := findNetwork(c, "", byName)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected to fail but succeeded")
+	}
+	if errRsp.StatusCode != http.StatusBadRequest {
+		t.Fatalf("Expected %d, but got: %d", http.StatusBadRequest, errRsp.StatusCode)
+	}
+
+	n, errRsp := findNetwork(c, nid, byID)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexpected failure: %v", errRsp)
+	}
+	if n == nil {
+		t.Fatalf("Unexpected nil libnetwork.Network")
+	}
+	if nid != n.ID() {
+		t.Fatalf("Incorrect libnetwork.Network resource. It has different id: %v", n)
+	}
+	if "network" != n.Name() {
+		t.Fatalf("Incorrect libnetwork.Network resource. It has different name: %v", n)
+	}
+
+	n, errRsp = findNetwork(c, "network", byName)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexpected failure: %v", errRsp)
+	}
+	if n == nil {
+		t.Fatalf("Unexpected nil libnetwork.Network")
+	}
+	if nid != n.ID() {
+		t.Fatalf("Incorrect libnetwork.Network resource. It has different id: %v", n)
+	}
+	if "network" != n.Name() {
+		t.Fatalf("Incorrect libnetwork.Network resource. It has different name: %v", n)
+	}
+
+	n.Delete()
+
+	_, errRsp = findNetwork(c, nid, byID)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected to fail but succeeded")
+	}
+	if errRsp.StatusCode != http.StatusNotFound {
+		t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode)
+	}
+
+	_, errRsp = findNetwork(c, "network", byName)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected to fail but succeeded")
+	}
+	if errRsp.StatusCode != http.StatusNotFound {
+		t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode)
+	}
+}
+
+func TestCreateDeleteEndpoints(t *testing.T) {
+	defer netutils.SetupTestNetNS(t)()
+
+	c, err := libnetwork.New()
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = c.ConfigureNetworkDriver(bridgeNetType, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	nc := networkCreate{Name: "firstNet", NetworkType: bridgeNetType}
+	body, err := json.Marshal(nc)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	vars := map[string]string{urlNwName: "firstNet"}
+	i, errRsp := procCreateNetwork(c, vars, body)
+	if errRsp != &createdResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	nid := i2s(i)
+
+	vbad, err := json.Marshal("bad endppint create data")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	vars[urlEpName] = "ep1"
+	_, errRsp = procCreateEndpoint(c, vars, vbad)
+	if errRsp == &createdResponse {
+		t.Fatalf("Expected to fail but succeeded")
+	}
+
+	bad, err := json.Marshal(endpointCreate{Name: "ep1", NetworkID: "123456"})
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, errRsp = procCreateEndpoint(c, vars, bad)
+	if errRsp == &createdResponse {
+		t.Fatalf("Expected to fail but succeeded")
+	}
+
+	soso, err := json.Marshal(endpointCreate{Name: "ep11", NetworkID: nid})
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, errRsp = procCreateEndpoint(c, vars, soso)
+	if errRsp != &mismatchResponse {
+		t.Fatalf("Expected to fail with \"mismatchResponse\", but got: %v", errRsp)
+	}
+
+	bla, err := json.Marshal(endpointCreate{Name: "", NetworkID: nid})
+	if err != nil {
+		t.Fatal(err)
+	}
+	vars[urlNwName] = "firstNet"
+	vars[urlEpName] = ""
+	_, errRsp = procCreateEndpoint(c, vars, bla)
+	if errRsp == &createdResponse {
+		t.Fatalf("Expected to fail but succeeded: %v", errRsp)
+	}
+
+	b, err := json.Marshal(endpointCreate{Name: "firstEp", NetworkID: nid})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	vars[urlNwName] = "secondNet"
+	vars[urlEpName] = "firstEp"
+	_, errRsp = procCreateEndpoint(c, vars, b)
+	if errRsp == &createdResponse {
+		t.Fatalf("Expected to fail but succeeded")
+	}
+
+	vars[urlNwName] = "firstNet"
+	vars[urlEpName] = "ep1"
+	_, errRsp = procCreateEndpoint(c, vars, b)
+	if errRsp != &mismatchResponse {
+		t.Fatalf("Expected to fail with \"mismatchResponse\", but got: %v", errRsp)
+	}
+
+	vars = make(map[string]string)
+	_, errRsp = procCreateEndpoint(c, vars, b)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure but succeeded: %v", errRsp)
+	}
+
+	vars[urlNwName] = "firstNet"
+	_, errRsp = procCreateEndpoint(c, vars, b)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure but succeeded: %v", errRsp)
+	}
+
+	vars[urlEpName] = "firstEp"
+	i, errRsp = procCreateEndpoint(c, vars, b)
+	if errRsp != &createdResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	eid := i2s(i)
+
+	_, errRsp = findEndpoint(c, "myNet", "firstEp", byName, byName)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure but succeeded: %v", errRsp)
+	}
+
+	ep0, errRsp := findEndpoint(c, nid, "firstEp", byID, byName)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+
+	ep1, errRsp := findEndpoint(c, "firstNet", "firstEp", byName, byName)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+
+	ep2, errRsp := findEndpoint(c, nid, eid, byID, byID)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+
+	ep3, errRsp := findEndpoint(c, "firstNet", eid, byName, byID)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+
+	if ep0 != ep1 || ep0 != ep2 || ep0 != ep3 {
+		t.Fatalf("Diffenrent queries returned different endpoints")
+	}
+
+	vars = make(map[string]string)
+	vars[urlNwName] = ""
+	vars[urlEpName] = "ep1"
+	_, errRsp = procDeleteEndpoint(c, vars, nil)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+
+	vars[urlNwName] = "firstNet"
+	vars[urlEpName] = ""
+	_, errRsp = procDeleteEndpoint(c, vars, nil)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+
+	vars[urlEpName] = "ep2"
+	_, errRsp = procDeleteEndpoint(c, vars, nil)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+
+	vars[urlEpName] = "firstEp"
+	_, errRsp = procDeleteEndpoint(c, vars, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+
+	_, errRsp = findEndpoint(c, "firstNet", "firstEp", byName, byName)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+}
+
+func TestJoinLeave(t *testing.T) {
+	defer netutils.SetupTestNetNS(t)()
+
+	c, err := libnetwork.New()
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = c.ConfigureNetworkDriver(bridgeNetType, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	nb, err := json.Marshal(networkCreate{Name: "network", NetworkType: bridgeNetType})
+	if err != nil {
+		t.Fatal(err)
+	}
+	vars := map[string]string{urlNwName: "network"}
+	i, errRsp := procCreateNetwork(c, vars, nb)
+	if errRsp != &createdResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+	nid := i2s(i)
+
+	vars[urlEpName] = "epoint"
+	eb, err := json.Marshal(endpointCreate{Name: "epoint", NetworkID: nid})
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, errRsp = procCreateEndpoint(c, vars, eb)
+	if errRsp != &createdResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+
+	vbad, err := json.Marshal("bad data")
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, errRsp = procJoinEndpoint(c, vars, vbad)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+
+	bad, err := json.Marshal(endpointJoin{})
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, errRsp = procJoinEndpoint(c, vars, bad)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+
+	cid := "abcdefghi"
+	jl := endpointJoin{ContainerID: cid}
+	jlb, err := json.Marshal(jl)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	vars = make(map[string]string)
+	vars[urlNwName] = ""
+	vars[urlEpName] = ""
+	vars[urlCnID] = cid
+	_, errRsp = procJoinEndpoint(c, vars, jlb)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+
+	vars[urlNwName] = "network1"
+	vars[urlEpName] = ""
+	_, errRsp = procJoinEndpoint(c, vars, jlb)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+
+	vars[urlNwName] = "network"
+	vars[urlEpName] = "endpoint"
+	_, errRsp = procJoinEndpoint(c, vars, jlb)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+
+	vars[urlEpName] = "epoint"
+	delete(vars, urlCnID)
+	_, errRsp = procJoinEndpoint(c, vars, jlb)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+
+	vars[urlCnID] = "who?"
+	_, errRsp = procJoinEndpoint(c, vars, jlb)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+
+	vars[urlCnID] = cid
+	cdi, errRsp := procJoinEndpoint(c, vars, jlb)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexpected failure, got: %v", errRsp)
+	}
+	cd := i2c(cdi)
+	if cd.SandboxKey == "" {
+		t.Fatalf("Empty sandbox key")
+	}
+	_, errRsp = procDeleteEndpoint(c, vars, nil)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+
+	vars[urlNwName] = "network2"
+	_, errRsp = procLeaveEndpoint(c, vars, vbad)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+	_, errRsp = procLeaveEndpoint(c, vars, bad)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+	_, errRsp = procLeaveEndpoint(c, vars, jlb)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+	vars = make(map[string]string)
+	vars[urlNwName] = ""
+	vars[urlEpName] = ""
+	_, errRsp = procLeaveEndpoint(c, vars, jlb)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+	vars[urlNwName] = "network"
+	vars[urlEpName] = ""
+	_, errRsp = procLeaveEndpoint(c, vars, jlb)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+	vars[urlEpName] = "2epoint"
+	_, errRsp = procLeaveEndpoint(c, vars, jlb)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+	vars[urlEpName] = "epoint"
+	vars[urlCnID] = "who"
+	_, errRsp = procLeaveEndpoint(c, vars, jlb)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+
+	delete(vars, urlCnID)
+	_, errRsp = procLeaveEndpoint(c, vars, jlb)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+
+	vars[urlCnID] = cid
+	_, errRsp = procLeaveEndpoint(c, vars, jlb)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+
+	_, errRsp = procLeaveEndpoint(c, vars, jlb)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, got: %v", errRsp)
+	}
+
+	_, errRsp = procDeleteEndpoint(c, vars, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+}
+
+func TestFindEndpointUtil(t *testing.T) {
+	defer netutils.SetupTestNetNS(t)()
+
+	c, err := libnetwork.New()
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = c.ConfigureNetworkDriver(bridgeNetType, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	nw, err := c.NewNetwork(bridgeNetType, "second", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	nid := nw.ID()
+
+	ep, err := nw.CreateEndpoint("secondEp", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	eid := ep.ID()
+
+	defer checkPanic(t)
+	findEndpoint(c, nid, "", byID, -1)
+
+	_, errRsp := findEndpoint(c, nid, "", byID, byName)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, but got: %v", errRsp)
+	}
+	if errRsp.StatusCode != http.StatusBadRequest {
+		t.Fatalf("Expected %d, but got: %d", http.StatusBadRequest, errRsp.StatusCode)
+	}
+
+	ep0, errRsp := findEndpoint(c, nid, "secondEp", byID, byName)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+
+	ep1, errRsp := findEndpoint(c, "second", "secondEp", byName, byName)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+
+	ep2, errRsp := findEndpoint(c, nid, eid, byID, byID)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+
+	ep3, errRsp := findEndpoint(c, "second", eid, byName, byID)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+
+	if ep0 != ep1 || ep0 != ep2 || ep0 != ep3 {
+		t.Fatalf("Diffenrent queries returned different endpoints")
+	}
+
+	ep.Delete()
+
+	_, errRsp = findEndpoint(c, nid, "secondEp", byID, byName)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, but got: %v", errRsp)
+	}
+	if errRsp.StatusCode != http.StatusNotFound {
+		t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode)
+	}
+
+	_, errRsp = findEndpoint(c, "second", "secondEp", byName, byName)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, but got: %v", errRsp)
+	}
+	if errRsp.StatusCode != http.StatusNotFound {
+		t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode)
+	}
+
+	_, errRsp = findEndpoint(c, nid, eid, byID, byID)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, but got: %v", errRsp)
+	}
+	if errRsp.StatusCode != http.StatusNotFound {
+		t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode)
+	}
+
+	_, errRsp = findEndpoint(c, "second", eid, byName, byID)
+	if errRsp == &successResponse {
+		t.Fatalf("Expected failure, but got: %v", errRsp)
+	}
+	if errRsp.StatusCode != http.StatusNotFound {
+		t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode)
+	}
+}
+
+func checkPanic(t *testing.T) {
+	if r := recover(); r != nil {
+		if _, ok := r.(runtime.Error); ok {
+			panic(r)
+		}
+	} else {
+		t.Fatalf("Expected to panic, but suceeded")
+	}
+}
+
+func TestDetectNetworkTargetPanic(t *testing.T) {
+	defer checkPanic(t)
+	vars := make(map[string]string)
+	detectNetworkTarget(vars)
+}
+
+func TestDetectEndpointTargetPanic(t *testing.T) {
+	defer checkPanic(t)
+	vars := make(map[string]string)
+	detectEndpointTarget(vars)
+}
+
+func TestResponseStatus(t *testing.T) {
+	list := []int{
+		http.StatusBadGateway,
+		http.StatusBadRequest,
+		http.StatusConflict,
+		http.StatusContinue,
+		http.StatusExpectationFailed,
+		http.StatusForbidden,
+		http.StatusFound,
+		http.StatusGatewayTimeout,
+		http.StatusGone,
+		http.StatusHTTPVersionNotSupported,
+		http.StatusInternalServerError,
+		http.StatusLengthRequired,
+		http.StatusMethodNotAllowed,
+		http.StatusMovedPermanently,
+		http.StatusMultipleChoices,
+		http.StatusNoContent,
+		http.StatusNonAuthoritativeInfo,
+		http.StatusNotAcceptable,
+		http.StatusNotFound,
+		http.StatusNotModified,
+		http.StatusPartialContent,
+		http.StatusPaymentRequired,
+		http.StatusPreconditionFailed,
+		http.StatusProxyAuthRequired,
+		http.StatusRequestEntityTooLarge,
+		http.StatusRequestTimeout,
+		http.StatusRequestURITooLong,
+		http.StatusRequestedRangeNotSatisfiable,
+		http.StatusResetContent,
+		http.StatusServiceUnavailable,
+		http.StatusSwitchingProtocols,
+		http.StatusTemporaryRedirect,
+		http.StatusUnauthorized,
+		http.StatusUnsupportedMediaType,
+		http.StatusUseProxy,
+	}
+	for _, c := range list {
+		r := responseStatus{StatusCode: c}
+		if r.isOK() {
+			t.Fatalf("isOK() returned true for code% d", c)
+		}
+	}
+
+	r := responseStatus{StatusCode: http.StatusOK}
+	if !r.isOK() {
+		t.Fatalf("isOK() failed")
+	}
+
+	r = responseStatus{StatusCode: http.StatusCreated}
+	if !r.isOK() {
+		t.Fatalf("isOK() failed")
+	}
+}
+
+// Local structs for end to end testing of api.go
+type localReader struct {
+	data  []byte
+	beBad bool
+}
+
+func newLocalReader(data []byte) *localReader {
+	lr := &localReader{data: make([]byte, len(data))}
+	copy(lr.data, data)
+	return lr
+}
+
+func (l *localReader) Read(p []byte) (n int, err error) {
+	if l.beBad {
+		return 0, errors.New("I am a bad reader")
+	}
+	if p == nil {
+		return -1, fmt.Errorf("nil buffer passed")
+	}
+	if l.data == nil || len(l.data) == 0 {
+		return 0, io.EOF
+	}
+	copy(p[:], l.data[:])
+	return len(l.data), io.EOF
+}
+
+type localResponseWriter struct {
+	body       []byte
+	statusCode int
+}
+
+func newWriter() *localResponseWriter {
+	return &localResponseWriter{}
+}
+
+func (f *localResponseWriter) Header() http.Header {
+	return make(map[string][]string, 0)
+}
+
+func (f *localResponseWriter) Write(data []byte) (int, error) {
+	if data == nil {
+		return -1, fmt.Errorf("nil data passed")
+	}
+
+	f.body = make([]byte, len(data))
+	copy(f.body, data)
+
+	return len(f.body), nil
+}
+
+func (f *localResponseWriter) WriteHeader(c int) {
+	f.statusCode = c
+}
+
+func TestwriteJSON(t *testing.T) {
+	testCode := 55
+	testData, err := json.Marshal("test data")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	rsp := newWriter()
+	writeJSON(rsp, testCode, testData)
+	if rsp.statusCode != testCode {
+		t.Fatalf("writeJSON() failed to set the status code. Expected %d. Got %d", testCode, rsp.statusCode)
+	}
+	if !bytes.Equal(testData, rsp.body) {
+		t.Fatalf("writeJSON() failed to set the body. Expected %s. Got %s", testData, rsp.body)
+	}
+
+}
+
+func TestHttpHandlerUninit(t *testing.T) {
+	defer netutils.SetupTestNetNS(t)()
+
+	c, err := libnetwork.New()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	h := &httpHandler{c: c}
+	h.initRouter()
+	if h.r == nil {
+		t.Fatalf("initRouter() did not initialize the router")
+	}
+
+	rsp := newWriter()
+	req, err := http.NewRequest("GET", "/networks", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	handleRequest := NewHTTPHandler(nil)
+	handleRequest(rsp, req)
+	if rsp.statusCode != http.StatusServiceUnavailable {
+		t.Fatalf("Expected (%d). Got (%d): %s", http.StatusServiceUnavailable, rsp.statusCode, rsp.body)
+	}
+
+	handleRequest = NewHTTPHandler(c)
+
+	handleRequest(rsp, req)
+	if rsp.statusCode != http.StatusOK {
+		t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
+	}
+
+	n, err := c.NewNetwork(bridgeNetType, "onenet", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	nwr := buildNetworkResource(n)
+	expected, err := json.Marshal([]networkResource{*nwr})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	handleRequest(rsp, req)
+	if rsp.statusCode != http.StatusOK {
+		t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
+	}
+	if len(rsp.body) == 0 {
+		t.Fatalf("Empty list of networks")
+	}
+	if bytes.Equal(rsp.body, expected) {
+		t.Fatalf("Incorrect list of networks in response's body")
+	}
+}
+
+func TestHttpHandlerBadBody(t *testing.T) {
+	defer netutils.SetupTestNetNS(t)()
+
+	rsp := newWriter()
+
+	c, err := libnetwork.New()
+	if err != nil {
+		t.Fatal(err)
+	}
+	handleRequest := NewHTTPHandler(c)
+
+	req, err := http.NewRequest("POST", "/networks/name/zero-network", &localReader{beBad: true})
+	if err != nil {
+		t.Fatal(err)
+	}
+	handleRequest(rsp, req)
+	if rsp.statusCode != http.StatusBadRequest {
+		t.Fatalf("Unexpected status code. Expected (%d). Got (%d): %s.", http.StatusBadRequest, rsp.statusCode, string(rsp.body))
+	}
+
+	body := []byte{}
+	lr := newLocalReader(body)
+	req, err = http.NewRequest("POST", "/networks/name/zero-network", lr)
+	if err != nil {
+		t.Fatal(err)
+	}
+	handleRequest(rsp, req)
+	if rsp.statusCode != http.StatusBadRequest {
+		t.Fatalf("Unexpected status code. Expected (%d). Got (%d): %s.", http.StatusBadRequest, rsp.statusCode, string(rsp.body))
+	}
+}
+
+func TestHttpHandlerGood(t *testing.T) {
+	defer netutils.SetupTestNetNS(t)()
+
+	rsp := newWriter()
+
+	c, err := libnetwork.New()
+	if err != nil {
+		t.Fatal(err)
+	}
+	handleRequest := NewHTTPHandler(c)
+
+	nc := networkCreate{Name: "zero-network", NetworkType: bridgeNetType}
+	body, err := json.Marshal(nc)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	lr := newLocalReader(body)
+	req, err := http.NewRequest("POST", "/networks/name/zero-network", lr)
+	if err != nil {
+		t.Fatal(err)
+	}
+	handleRequest(rsp, req)
+	if rsp.statusCode != http.StatusCreated {
+		t.Fatalf("Unexpectded status code. Expected (%d). Got (%d): %s.", http.StatusCreated, rsp.statusCode, string(rsp.body))
+	}
+	if len(rsp.body) == 0 {
+		t.Fatalf("Empty response body")
+	}
+
+	var id string
+	err = json.Unmarshal(rsp.body, &id)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	req, err = http.NewRequest("GET", "/networks/id/"+id, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	handleRequest(rsp, req)
+	if rsp.statusCode != http.StatusOK {
+		t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
+	}
+
+	var nwr networkResource
+	err = json.Unmarshal(rsp.body, &nwr)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if nwr.Name != "zero-network" || id != nwr.ID {
+		t.Fatalf("Incongruent resource found")
+	}
+}