mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Oauth2 consumer (#679)
* initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
This commit is contained in:
		
				
					committed by
					
						
						Kim "BKC" Carlbäcker
					
				
			
			
				
	
			
			
			
						parent
						
							fd941db246
						
					
				
				
					commit
					01d957677f
				
			
							
								
								
									
										27
									
								
								vendor/github.com/gorilla/context/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/gorilla/context/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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.
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/gorilla/context/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/gorilla/context/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
context
 | 
			
		||||
=======
 | 
			
		||||
[](https://travis-ci.org/gorilla/context)
 | 
			
		||||
 | 
			
		||||
gorilla/context is a general purpose registry for global request variables.
 | 
			
		||||
 | 
			
		||||
> Note: gorilla/context, having been born well before `context.Context` existed, does not play well
 | 
			
		||||
> with the shallow copying of the request that [`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext) (added to net/http Go 1.7 onwards) performs. You should either use *just* gorilla/context, or moving forward, the new `http.Request.Context()`.
 | 
			
		||||
 | 
			
		||||
Read the full documentation here: http://www.gorillatoolkit.org/pkg/context
 | 
			
		||||
							
								
								
									
										143
									
								
								vendor/github.com/gorilla/context/context.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								vendor/github.com/gorilla/context/context.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										88
									
								
								vendor/github.com/gorilla/context/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								vendor/github.com/gorilla/context/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
// 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.
 | 
			
		||||
 | 
			
		||||
Note: gorilla/context, having been born well before `context.Context` existed,
 | 
			
		||||
does not play well > with the shallow copying of the request that
 | 
			
		||||
[`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext)
 | 
			
		||||
(added to net/http Go 1.7 onwards) performs. You should either use *just*
 | 
			
		||||
gorilla/context, or moving forward, the new `http.Request.Context()`.
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
							
								
								
									
										27
									
								
								vendor/github.com/gorilla/mux/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/gorilla/mux/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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.
 | 
			
		||||
							
								
								
									
										299
									
								
								vendor/github.com/gorilla/mux/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										299
									
								
								vendor/github.com/gorilla/mux/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,299 @@
 | 
			
		||||
gorilla/mux
 | 
			
		||||
===
 | 
			
		||||
[](https://godoc.org/github.com/gorilla/mux)
 | 
			
		||||
[](https://travis-ci.org/gorilla/mux)
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
http://www.gorillatoolkit.org/pkg/mux
 | 
			
		||||
 | 
			
		||||
Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to
 | 
			
		||||
their respective handler.
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
* It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`.
 | 
			
		||||
* 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.
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
* [Install](#install)
 | 
			
		||||
* [Examples](#examples)
 | 
			
		||||
* [Matching Routes](#matching-routes)
 | 
			
		||||
* [Static Files](#static-files)
 | 
			
		||||
* [Registered URLs](#registered-urls)
 | 
			
		||||
* [Full Example](#full-example)
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Install
 | 
			
		||||
 | 
			
		||||
With a [correctly configured](https://golang.org/doc/install#testing) Go toolchain:
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
go get -u github.com/gorilla/mux
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Examples
 | 
			
		||||
 | 
			
		||||
Let's start registering a couple of URL paths and handlers:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
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()`:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
### Matching Routes
 | 
			
		||||
 | 
			
		||||
Routes can also be restricted to a domain or subdomain. Just define a host pattern to be matched. They can also have variables:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
r := mux.NewRouter()
 | 
			
		||||
// Only matches if domain is "www.example.com".
 | 
			
		||||
r.Host("www.example.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:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
r.PathPrefix("/products/")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
...or HTTP methods:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
r.Methods("GET", "POST")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
...or URL schemes:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
r.Schemes("https")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
...or header values:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
r.Headers("X-Requested-With", "XMLHttpRequest")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
...or query values:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
r.Queries("key", "value")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
...or to use a custom matcher function:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
r.HandleFunc("/products", ProductsHandler).
 | 
			
		||||
  Host("www.example.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.example.com`. Create a route for that host and get a "subrouter" from it:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
r := mux.NewRouter()
 | 
			
		||||
s := r.Host("www.example.com").Subrouter()
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Then register routes in the subrouter:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
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.example.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:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
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)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Static Files
 | 
			
		||||
 | 
			
		||||
Note that the path provided to `PathPrefix()` represents a "wildcard": calling
 | 
			
		||||
`PathPrefix("/static/").Handler(...)` means that the handler will be passed any
 | 
			
		||||
request that matches "/static/*". This makes it easy to serve static files with mux:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
func main() {
 | 
			
		||||
	var dir string
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
	r := mux.NewRouter()
 | 
			
		||||
 | 
			
		||||
	// This will serve files under http://localhost:8000/static/<filename>
 | 
			
		||||
	r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir))))
 | 
			
		||||
 | 
			
		||||
	srv := &http.Server{
 | 
			
		||||
		Handler:      r,
 | 
			
		||||
		Addr:         "127.0.0.1:8000",
 | 
			
		||||
		// Good practice: enforce timeouts for servers you create!
 | 
			
		||||
		WriteTimeout: 15 * time.Second,
 | 
			
		||||
		ReadTimeout:  15 * time.Second,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Fatal(srv.ListenAndServe())
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Registered URLs
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
Regex support also exists for matching Headers within a route. For example, we could do:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
r.HeadersRegexp("Content-Type", "application/(text|json)")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
...and the route will match both requests with a Content-Type of `application/json` as well as `application/text`
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
// "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:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
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")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Full Example
 | 
			
		||||
 | 
			
		||||
Here's a complete, runnable example of a small `mux` based server:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"log"
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func YourHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	w.Write([]byte("Gorilla!\n"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	r := mux.NewRouter()
 | 
			
		||||
	// Routes consist of a path and a handler function.
 | 
			
		||||
	r.HandleFunc("/", YourHandler)
 | 
			
		||||
 | 
			
		||||
	// Bind to a port and pass our router in
 | 
			
		||||
	log.Fatal(http.ListenAndServe(":8000", r))
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
 | 
			
		||||
BSD licensed. See the LICENSE file for details.
 | 
			
		||||
							
								
								
									
										26
									
								
								vendor/github.com/gorilla/mux/context_gorilla.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/gorilla/mux/context_gorilla.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
// +build !go1.7
 | 
			
		||||
 | 
			
		||||
package mux
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func contextGet(r *http.Request, key interface{}) interface{} {
 | 
			
		||||
	return context.Get(r, key)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func contextSet(r *http.Request, key, val interface{}) *http.Request {
 | 
			
		||||
	if val == nil {
 | 
			
		||||
		return r
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	context.Set(r, key, val)
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func contextClear(r *http.Request) {
 | 
			
		||||
	context.Clear(r)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								vendor/github.com/gorilla/mux/context_native.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/gorilla/mux/context_native.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
// +build go1.7
 | 
			
		||||
 | 
			
		||||
package mux
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"net/http"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func contextGet(r *http.Request, key interface{}) interface{} {
 | 
			
		||||
	return r.Context().Value(key)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func contextSet(r *http.Request, key, val interface{}) *http.Request {
 | 
			
		||||
	if val == nil {
 | 
			
		||||
		return r
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r.WithContext(context.WithValue(r.Context(), key, val))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func contextClear(r *http.Request) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										235
									
								
								vendor/github.com/gorilla/mux/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								vendor/github.com/gorilla/mux/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,235 @@
 | 
			
		||||
// 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 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)
 | 
			
		||||
 | 
			
		||||
Groups can be used inside patterns, as long as they are non-capturing (?:re). For example:
 | 
			
		||||
 | 
			
		||||
	r.HandleFunc("/articles/{category}/{sort:(?:asc|desc|new)}", ArticlesCategoryHandler)
 | 
			
		||||
 | 
			
		||||
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.example.com".
 | 
			
		||||
	r.Host("www.example.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.example.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.example.com". Create a route for that host and get a "subrouter"
 | 
			
		||||
from it:
 | 
			
		||||
 | 
			
		||||
	r := mux.NewRouter()
 | 
			
		||||
	s := r.Host("www.example.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.example.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)
 | 
			
		||||
 | 
			
		||||
Note that the path provided to PathPrefix() represents a "wildcard": calling
 | 
			
		||||
PathPrefix("/static/").Handler(...) means that the handler will be passed any
 | 
			
		||||
request that matches "/static/*". This makes it easy to serve static files with mux:
 | 
			
		||||
 | 
			
		||||
	func main() {
 | 
			
		||||
		var dir string
 | 
			
		||||
 | 
			
		||||
		flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir")
 | 
			
		||||
		flag.Parse()
 | 
			
		||||
		r := mux.NewRouter()
 | 
			
		||||
 | 
			
		||||
		// This will serve files under http://localhost:8000/static/<filename>
 | 
			
		||||
		r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir))))
 | 
			
		||||
 | 
			
		||||
		srv := &http.Server{
 | 
			
		||||
			Handler:      r,
 | 
			
		||||
			Addr:         "127.0.0.1:8000",
 | 
			
		||||
			// Good practice: enforce timeouts for servers you create!
 | 
			
		||||
			WriteTimeout: 15 * time.Second,
 | 
			
		||||
			ReadTimeout:  15 * time.Second,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		log.Fatal(srv.ListenAndServe())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
Regex support also exists for matching Headers within a route. For example, we could do:
 | 
			
		||||
 | 
			
		||||
	r.HeadersRegexp("Content-Type", "application/(text|json)")
 | 
			
		||||
 | 
			
		||||
...and the route will match both requests with a Content-Type of `application/json` as well as
 | 
			
		||||
`application/text`
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
							
								
								
									
										542
									
								
								vendor/github.com/gorilla/mux/mux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										542
									
								
								vendor/github.com/gorilla/mux/mux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,542 @@
 | 
			
		||||
// 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"
 | 
			
		||||
	"path"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
	// See Router.SkipClean(). This defines the flag for new routes.
 | 
			
		||||
	skipClean bool
 | 
			
		||||
	// If true, do not clear the request context after handling the request.
 | 
			
		||||
	// This has no effect when go1.7+ is used, since the context is stored
 | 
			
		||||
	// on the request itself.
 | 
			
		||||
	KeepContext bool
 | 
			
		||||
	// see Router.UseEncodedPath(). This defines a flag for all routes.
 | 
			
		||||
	useEncodedPath 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
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Closest match for a router (includes sub-routers)
 | 
			
		||||
	if r.NotFoundHandler != nil {
 | 
			
		||||
		match.Handler = r.NotFoundHandler
 | 
			
		||||
		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) {
 | 
			
		||||
	if !r.skipClean {
 | 
			
		||||
		path := req.URL.Path
 | 
			
		||||
		if r.useEncodedPath {
 | 
			
		||||
			path = getPath(req)
 | 
			
		||||
		}
 | 
			
		||||
		// Clean path to canonical form and redirect.
 | 
			
		||||
		if p := cleanPath(path); p != path {
 | 
			
		||||
 | 
			
		||||
			// Added 3 lines (Philip Schlump) - It was dropping 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
 | 
			
		||||
		req = setVars(req, match.Vars)
 | 
			
		||||
		req = setCurrentRoute(req, match.Route)
 | 
			
		||||
	}
 | 
			
		||||
	if handler == nil {
 | 
			
		||||
		handler = http.NotFoundHandler()
 | 
			
		||||
	}
 | 
			
		||||
	if !r.KeepContext {
 | 
			
		||||
		defer contextClear(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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SkipClean defines the path cleaning behaviour for new routes. The initial
 | 
			
		||||
// value is false. Users should be careful about which routes are not cleaned
 | 
			
		||||
//
 | 
			
		||||
// When true, if the route path is "/path//to", it will remain with the double
 | 
			
		||||
// slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/
 | 
			
		||||
//
 | 
			
		||||
// When false, the path will be cleaned, so /fetch/http://xkcd.com/534/ will
 | 
			
		||||
// become /fetch/http/xkcd.com/534
 | 
			
		||||
func (r *Router) SkipClean(value bool) *Router {
 | 
			
		||||
	r.skipClean = value
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UseEncodedPath tells the router to match the encoded original path
 | 
			
		||||
// to the routes.
 | 
			
		||||
// For eg. "/path/foo%2Fbar/to" will match the path "/path/{var}/to".
 | 
			
		||||
// This behavior has the drawback of needing to match routes against
 | 
			
		||||
// r.RequestURI instead of r.URL.Path. Any modifications (such as http.StripPrefix)
 | 
			
		||||
// to r.URL.Path will not affect routing when this flag is on and thus may
 | 
			
		||||
// induce unintended behavior.
 | 
			
		||||
//
 | 
			
		||||
// If not called, the router will match the unencoded path to the routes.
 | 
			
		||||
// For eg. "/path/foo%2Fbar/to" will match the path "/path/foo/bar/to"
 | 
			
		||||
func (r *Router) UseEncodedPath() *Router {
 | 
			
		||||
	r.useEncodedPath = true
 | 
			
		||||
	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, skipClean: r.skipClean, useEncodedPath: r.useEncodedPath}
 | 
			
		||||
	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...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BuildVarsFunc 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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Walk walks the router and all its sub-routers, calling walkFn for each route
 | 
			
		||||
// in the tree. The routes are walked in the order they were added. Sub-routers
 | 
			
		||||
// are explored depth-first.
 | 
			
		||||
func (r *Router) Walk(walkFn WalkFunc) error {
 | 
			
		||||
	return r.walk(walkFn, []*Route{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SkipRouter is used as a return value from WalkFuncs to indicate that the
 | 
			
		||||
// router that walk is about to descend down to should be skipped.
 | 
			
		||||
var SkipRouter = errors.New("skip this router")
 | 
			
		||||
 | 
			
		||||
// WalkFunc is the type of the function called for each route visited by Walk.
 | 
			
		||||
// At every invocation, it is given the current route, and the current router,
 | 
			
		||||
// and a list of ancestor routes that lead to the current route.
 | 
			
		||||
type WalkFunc func(route *Route, router *Router, ancestors []*Route) error
 | 
			
		||||
 | 
			
		||||
func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error {
 | 
			
		||||
	for _, t := range r.routes {
 | 
			
		||||
		if t.regexp == nil || t.regexp.path == nil || t.regexp.path.template == "" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		err := walkFn(t, r, ancestors)
 | 
			
		||||
		if err == SkipRouter {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		for _, sr := range t.matchers {
 | 
			
		||||
			if h, ok := sr.(*Router); ok {
 | 
			
		||||
				err := h.walk(walkFn, ancestors)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if h, ok := t.handler.(*Router); ok {
 | 
			
		||||
			ancestors = append(ancestors, t)
 | 
			
		||||
			err := h.walk(walkFn, ancestors)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			ancestors = ancestors[:len(ancestors)-1]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ----------------------------------------------------------------------------
 | 
			
		||||
// 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 := contextGet(r, varsKey); rv != nil {
 | 
			
		||||
		return rv.(map[string]string)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CurrentRoute returns the matched route for the current request, if any.
 | 
			
		||||
// This only works when called inside the handler of the matched route
 | 
			
		||||
// because the matched route is stored in the request context which is cleared
 | 
			
		||||
// after the handler returns, unless the KeepContext option is set on the
 | 
			
		||||
// Router.
 | 
			
		||||
func CurrentRoute(r *http.Request) *Route {
 | 
			
		||||
	if rv := contextGet(r, routeKey); rv != nil {
 | 
			
		||||
		return rv.(*Route)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setVars(r *http.Request, val interface{}) *http.Request {
 | 
			
		||||
	return contextSet(r, varsKey, val)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setCurrentRoute(r *http.Request, val interface{}) *http.Request {
 | 
			
		||||
	return contextSet(r, routeKey, val)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ----------------------------------------------------------------------------
 | 
			
		||||
// Helpers
 | 
			
		||||
// ----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// getPath returns the escaped path if possible; doing what URL.EscapedPath()
 | 
			
		||||
// which was added in go1.5 does
 | 
			
		||||
func getPath(req *http.Request) string {
 | 
			
		||||
	if req.RequestURI != "" {
 | 
			
		||||
		// Extract the path from RequestURI (which is escaped unlike URL.Path)
 | 
			
		||||
		// as detailed here as detailed in https://golang.org/pkg/net/url/#URL
 | 
			
		||||
		// for < 1.5 server side workaround
 | 
			
		||||
		// http://localhost/path/here?v=1 -> /path/here
 | 
			
		||||
		path := req.RequestURI
 | 
			
		||||
		path = strings.TrimPrefix(path, req.URL.Scheme+`://`)
 | 
			
		||||
		path = strings.TrimPrefix(path, req.URL.Host)
 | 
			
		||||
		if i := strings.LastIndex(path, "?"); i > -1 {
 | 
			
		||||
			path = path[:i]
 | 
			
		||||
		}
 | 
			
		||||
		if i := strings.LastIndex(path, "#"); i > -1 {
 | 
			
		||||
			path = path[:i]
 | 
			
		||||
		}
 | 
			
		||||
		return path
 | 
			
		||||
	}
 | 
			
		||||
	return req.URL.Path
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// checkPairs returns the count of strings passed in, and an error if
 | 
			
		||||
// the count is not an even number.
 | 
			
		||||
func checkPairs(pairs ...string) (int, error) {
 | 
			
		||||
	length := len(pairs)
 | 
			
		||||
	if length%2 != 0 {
 | 
			
		||||
		return length, fmt.Errorf(
 | 
			
		||||
			"mux: number of parameters must be multiple of 2, got %v", pairs)
 | 
			
		||||
	}
 | 
			
		||||
	return length, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// mapFromPairsToString converts variadic string parameters to a
 | 
			
		||||
// string to string map.
 | 
			
		||||
func mapFromPairsToString(pairs ...string) (map[string]string, error) {
 | 
			
		||||
	length, err := checkPairs(pairs...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	m := make(map[string]string, length/2)
 | 
			
		||||
	for i := 0; i < length; i += 2 {
 | 
			
		||||
		m[pairs[i]] = pairs[i+1]
 | 
			
		||||
	}
 | 
			
		||||
	return m, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// mapFromPairsToRegex converts variadic string paramers to a
 | 
			
		||||
// string to regex map.
 | 
			
		||||
func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) {
 | 
			
		||||
	length, err := checkPairs(pairs...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	m := make(map[string]*regexp.Regexp, length/2)
 | 
			
		||||
	for i := 0; i < length; i += 2 {
 | 
			
		||||
		regex, err := regexp.Compile(pairs[i+1])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		m[pairs[i]] = regex
 | 
			
		||||
	}
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// matchMapWithString returns true if the given key/value pairs exist in a given map.
 | 
			
		||||
func matchMapWithString(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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// matchMapWithRegex returns true if the given key/value pairs exist in a given map compiled against
 | 
			
		||||
// the given regex
 | 
			
		||||
func matchMapWithRegex(toCheck map[string]*regexp.Regexp, 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 != nil {
 | 
			
		||||
			// 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.MatchString(value) {
 | 
			
		||||
					valueExists = true
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if !valueExists {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										316
									
								
								vendor/github.com/gorilla/mux/regexp.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										316
									
								
								vendor/github.com/gorilla/mux/regexp.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,316 @@
 | 
			
		||||
// 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"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"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, useEncodedPath 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 = "[^?&]*"
 | 
			
		||||
	} 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("")
 | 
			
		||||
	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(?P<%s>%s)", regexp.QuoteMeta(raw), varGroupName(i/2), 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 matchQuery {
 | 
			
		||||
		// Add the default pattern if the query value is empty
 | 
			
		||||
		if queryVal := strings.SplitN(template, "=", 2)[1]; queryVal == "" {
 | 
			
		||||
			pattern.WriteString(defaultPattern)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	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,
 | 
			
		||||
		useEncodedPath: useEncodedPath,
 | 
			
		||||
		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
 | 
			
		||||
	// Determines whether to use encoded path from getPath function or unencoded
 | 
			
		||||
	// req.URL.Path for path matching
 | 
			
		||||
	useEncodedPath 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.matchQueryString(req)
 | 
			
		||||
		}
 | 
			
		||||
		path := req.URL.Path
 | 
			
		||||
		if r.useEncodedPath {
 | 
			
		||||
			path = getPath(req)
 | 
			
		||||
		}
 | 
			
		||||
		return r.regexp.MatchString(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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getURLQuery returns a single query parameter from a request URL.
 | 
			
		||||
// For a URL with foo=bar&baz=ding, we return only the relevant key
 | 
			
		||||
// value pair for the routeRegexp.
 | 
			
		||||
func (r *routeRegexp) getURLQuery(req *http.Request) string {
 | 
			
		||||
	if !r.matchQuery {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	templateKey := strings.SplitN(r.template, "=", 2)[0]
 | 
			
		||||
	for key, vals := range req.URL.Query() {
 | 
			
		||||
		if key == templateKey && len(vals) > 0 {
 | 
			
		||||
			return key + "=" + vals[0]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *routeRegexp) matchQueryString(req *http.Request) bool {
 | 
			
		||||
	return r.regexp.MatchString(r.getURLQuery(req))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
	var idxs []int
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// varGroupName builds a capturing group name for the indexed variable.
 | 
			
		||||
func varGroupName(idx int) string {
 | 
			
		||||
	return "v" + strconv.Itoa(idx)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ----------------------------------------------------------------------------
 | 
			
		||||
// 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 {
 | 
			
		||||
		host := getHost(req)
 | 
			
		||||
		matches := v.host.regexp.FindStringSubmatchIndex(host)
 | 
			
		||||
		if len(matches) > 0 {
 | 
			
		||||
			extractVars(host, matches, v.host.varsN, m.Vars)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	path := req.URL.Path
 | 
			
		||||
	if r.useEncodedPath {
 | 
			
		||||
		path = getPath(req)
 | 
			
		||||
	}
 | 
			
		||||
	// Store path variables.
 | 
			
		||||
	if v.path != nil {
 | 
			
		||||
		matches := v.path.regexp.FindStringSubmatchIndex(path)
 | 
			
		||||
		if len(matches) > 0 {
 | 
			
		||||
			extractVars(path, matches, v.path.varsN, m.Vars)
 | 
			
		||||
			// Check if we should redirect.
 | 
			
		||||
			if v.path.strictSlash {
 | 
			
		||||
				p1 := strings.HasSuffix(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.
 | 
			
		||||
	for _, q := range v.queries {
 | 
			
		||||
		queryURL := q.getURLQuery(req)
 | 
			
		||||
		matches := q.regexp.FindStringSubmatchIndex(queryURL)
 | 
			
		||||
		if len(matches) > 0 {
 | 
			
		||||
			extractVars(queryURL, matches, q.varsN, m.Vars)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func extractVars(input string, matches []int, names []string, output map[string]string) {
 | 
			
		||||
	for i, name := range names {
 | 
			
		||||
		output[name] = input[matches[2*i+2]:matches[2*i+3]]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										636
									
								
								vendor/github.com/gorilla/mux/route.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										636
									
								
								vendor/github.com/gorilla/mux/route.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,636 @@
 | 
			
		||||
// 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"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"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, when the path pattern is "/path//to", accessing "/path//to"
 | 
			
		||||
	// will not redirect
 | 
			
		||||
	skipClean bool
 | 
			
		||||
	// If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to"
 | 
			
		||||
	useEncodedPath 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Route) SkipClean() bool {
 | 
			
		||||
	return r.skipClean
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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, r.useEncodedPath)
 | 
			
		||||
	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 matchMapWithString(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.
 | 
			
		||||
// If 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 = mapFromPairsToString(pairs...)
 | 
			
		||||
		return r.addMatcher(headerMatcher(headers))
 | 
			
		||||
	}
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// headerRegexMatcher matches the request against the route given a regex for the header
 | 
			
		||||
type headerRegexMatcher map[string]*regexp.Regexp
 | 
			
		||||
 | 
			
		||||
func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool {
 | 
			
		||||
	return matchMapWithRegex(m, r.Header, true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HeadersRegexp accepts a sequence of key/value pairs, where the value has regex
 | 
			
		||||
// support. For example:
 | 
			
		||||
//
 | 
			
		||||
//     r := mux.NewRouter()
 | 
			
		||||
//     r.HeadersRegexp("Content-Type", "application/(text|json)",
 | 
			
		||||
//               "X-Requested-With", "XMLHttpRequest")
 | 
			
		||||
//
 | 
			
		||||
// The above route will only match if both the request header matches both regular expressions.
 | 
			
		||||
// It the value is an empty string, it will match any value if the key is set.
 | 
			
		||||
func (r *Route) HeadersRegexp(pairs ...string) *Route {
 | 
			
		||||
	if r.err == nil {
 | 
			
		||||
		var headers map[string]*regexp.Regexp
 | 
			
		||||
		headers, r.err = mapFromPairsToRegex(pairs...)
 | 
			
		||||
		return r.addMatcher(headerRegexMatcher(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 be matched:
 | 
			
		||||
//
 | 
			
		||||
// - {name} matches anything until the next dot.
 | 
			
		||||
//
 | 
			
		||||
// - {name:pattern} matches the given regexp pattern.
 | 
			
		||||
//
 | 
			
		||||
// For example:
 | 
			
		||||
//
 | 
			
		||||
//     r := mux.NewRouter()
 | 
			
		||||
//     r.Host("www.example.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
 | 
			
		||||
 | 
			
		||||
// Match returns the match for a given request.
 | 
			
		||||
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 be 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 be 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, false, 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.example.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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetPathTemplate returns the template used to build the
 | 
			
		||||
// route match.
 | 
			
		||||
// This is useful for building simple REST API documentation and for instrumentation
 | 
			
		||||
// against third-party services.
 | 
			
		||||
// An error will be returned if the route does not define a path.
 | 
			
		||||
func (r *Route) GetPathTemplate() (string, error) {
 | 
			
		||||
	if r.err != nil {
 | 
			
		||||
		return "", r.err
 | 
			
		||||
	}
 | 
			
		||||
	if r.regexp == nil || r.regexp.path == nil {
 | 
			
		||||
		return "", errors.New("mux: route doesn't have a path")
 | 
			
		||||
	}
 | 
			
		||||
	return r.regexp.path.template, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetHostTemplate returns the template used to build the
 | 
			
		||||
// route match.
 | 
			
		||||
// This is useful for building simple REST API documentation and for instrumentation
 | 
			
		||||
// against third-party services.
 | 
			
		||||
// An error will be returned if the route does not define a host.
 | 
			
		||||
func (r *Route) GetHostTemplate() (string, error) {
 | 
			
		||||
	if r.err != nil {
 | 
			
		||||
		return "", r.err
 | 
			
		||||
	}
 | 
			
		||||
	if r.regexp == nil || r.regexp.host == nil {
 | 
			
		||||
		return "", errors.New("mux: route doesn't have a host")
 | 
			
		||||
	}
 | 
			
		||||
	return r.regexp.host.template, 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 := mapFromPairsToString(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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								vendor/github.com/gorilla/securecookie/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/gorilla/securecookie/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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.
 | 
			
		||||
							
								
								
									
										78
									
								
								vendor/github.com/gorilla/securecookie/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								vendor/github.com/gorilla/securecookie/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
			
		||||
securecookie
 | 
			
		||||
============
 | 
			
		||||
[](https://godoc.org/github.com/gorilla/securecookie) [](https://travis-ci.org/gorilla/securecookie)
 | 
			
		||||
 | 
			
		||||
securecookie encodes and decodes authenticated and optionally encrypted 
 | 
			
		||||
cookie values.
 | 
			
		||||
 | 
			
		||||
Secure cookies can't be forged, because their values are validated using HMAC.
 | 
			
		||||
When encrypted, the content is also inaccessible to malicious eyes. It is still
 | 
			
		||||
recommended that sensitive data not be stored in cookies, and that HTTPS be used
 | 
			
		||||
to prevent cookie [replay attacks](https://en.wikipedia.org/wiki/Replay_attack).
 | 
			
		||||
 | 
			
		||||
## Examples
 | 
			
		||||
 | 
			
		||||
To use it, first create a new SecureCookie instance:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
// Hash keys should be at least 32 bytes long
 | 
			
		||||
var hashKey = []byte("very-secret")
 | 
			
		||||
// Block keys should be 16 bytes (AES-128) or 32 bytes (AES-256) long.
 | 
			
		||||
// Shorter keys may weaken the encryption used.
 | 
			
		||||
var blockKey = []byte("a-lot-secret")
 | 
			
		||||
var s = securecookie.New(hashKey, blockKey)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The hashKey is required, used to authenticate the cookie value using HMAC.
 | 
			
		||||
It is recommended to use a key with 32 or 64 bytes.
 | 
			
		||||
 | 
			
		||||
The blockKey is optional, used to encrypt the cookie value -- set it to nil
 | 
			
		||||
to not use encryption. If set, the length must correspond to the block size
 | 
			
		||||
of the encryption algorithm. For AES, used by default, valid lengths are
 | 
			
		||||
16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
 | 
			
		||||
 | 
			
		||||
Strong keys can be created using the convenience function GenerateRandomKey().
 | 
			
		||||
 | 
			
		||||
Once a SecureCookie instance is set, use it to encode a cookie value:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
func SetCookieHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	value := map[string]string{
 | 
			
		||||
		"foo": "bar",
 | 
			
		||||
	}
 | 
			
		||||
	if encoded, err := s.Encode("cookie-name", value); err == nil {
 | 
			
		||||
		cookie := &http.Cookie{
 | 
			
		||||
			Name:  "cookie-name",
 | 
			
		||||
			Value: encoded,
 | 
			
		||||
			Path:  "/",
 | 
			
		||||
			Secure: true,
 | 
			
		||||
			HttpOnly: true,
 | 
			
		||||
		}
 | 
			
		||||
		http.SetCookie(w, cookie)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Later, use the same SecureCookie instance to decode and validate a cookie
 | 
			
		||||
value:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
func ReadCookieHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	if cookie, err := r.Cookie("cookie-name"); err == nil {
 | 
			
		||||
		value := make(map[string]string)
 | 
			
		||||
		if err = s2.Decode("cookie-name", cookie.Value, &value); err == nil {
 | 
			
		||||
			fmt.Fprintf(w, "The value of foo is %q", value["foo"])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
We stored a map[string]string, but secure cookies can hold any value that
 | 
			
		||||
can be encoded using `encoding/gob`. To store custom types, they must be
 | 
			
		||||
registered first using gob.Register(). For basic types this is not needed;
 | 
			
		||||
it works out of the box. An optional JSON encoder that uses `encoding/json` is
 | 
			
		||||
available for types compatible with JSON.
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
 | 
			
		||||
BSD licensed. See the LICENSE file for details.
 | 
			
		||||
							
								
								
									
										61
									
								
								vendor/github.com/gorilla/securecookie/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								vendor/github.com/gorilla/securecookie/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
// 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 securecookie encodes and decodes authenticated and optionally
 | 
			
		||||
encrypted cookie values.
 | 
			
		||||
 | 
			
		||||
Secure cookies can't be forged, because their values are validated using HMAC.
 | 
			
		||||
When encrypted, the content is also inaccessible to malicious eyes.
 | 
			
		||||
 | 
			
		||||
To use it, first create a new SecureCookie instance:
 | 
			
		||||
 | 
			
		||||
	var hashKey = []byte("very-secret")
 | 
			
		||||
	var blockKey = []byte("a-lot-secret")
 | 
			
		||||
	var s = securecookie.New(hashKey, blockKey)
 | 
			
		||||
 | 
			
		||||
The hashKey is required, used to authenticate the cookie value using HMAC.
 | 
			
		||||
It is recommended to use a key with 32 or 64 bytes.
 | 
			
		||||
 | 
			
		||||
The blockKey is optional, used to encrypt the cookie value -- set it to nil
 | 
			
		||||
to not use encryption. If set, the length must correspond to the block size
 | 
			
		||||
of the encryption algorithm. For AES, used by default, valid lengths are
 | 
			
		||||
16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
 | 
			
		||||
 | 
			
		||||
Strong keys can be created using the convenience function GenerateRandomKey().
 | 
			
		||||
 | 
			
		||||
Once a SecureCookie instance is set, use it to encode a cookie value:
 | 
			
		||||
 | 
			
		||||
	func SetCookieHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		value := map[string]string{
 | 
			
		||||
			"foo": "bar",
 | 
			
		||||
		}
 | 
			
		||||
		if encoded, err := s.Encode("cookie-name", value); err == nil {
 | 
			
		||||
			cookie := &http.Cookie{
 | 
			
		||||
				Name:  "cookie-name",
 | 
			
		||||
				Value: encoded,
 | 
			
		||||
				Path:  "/",
 | 
			
		||||
			}
 | 
			
		||||
			http.SetCookie(w, cookie)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
Later, use the same SecureCookie instance to decode and validate a cookie
 | 
			
		||||
value:
 | 
			
		||||
 | 
			
		||||
	func ReadCookieHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		if cookie, err := r.Cookie("cookie-name"); err == nil {
 | 
			
		||||
			value := make(map[string]string)
 | 
			
		||||
			if err = s2.Decode("cookie-name", cookie.Value, &value); err == nil {
 | 
			
		||||
				fmt.Fprintf(w, "The value of foo is %q", value["foo"])
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
We stored a map[string]string, but secure cookies can hold any value that
 | 
			
		||||
can be encoded using encoding/gob. To store custom types, they must be
 | 
			
		||||
registered first using gob.Register(). For basic types this is not needed;
 | 
			
		||||
it works out of the box.
 | 
			
		||||
*/
 | 
			
		||||
package securecookie
 | 
			
		||||
							
								
								
									
										25
									
								
								vendor/github.com/gorilla/securecookie/fuzz.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/gorilla/securecookie/fuzz.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
// +build gofuzz
 | 
			
		||||
 | 
			
		||||
package securecookie
 | 
			
		||||
 | 
			
		||||
var hashKey = []byte("very-secret12345")
 | 
			
		||||
var blockKey = []byte("a-lot-secret1234")
 | 
			
		||||
var s = New(hashKey, blockKey)
 | 
			
		||||
 | 
			
		||||
type Cookie struct {
 | 
			
		||||
	B bool
 | 
			
		||||
	I int
 | 
			
		||||
	S string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Fuzz(data []byte) int {
 | 
			
		||||
	datas := string(data)
 | 
			
		||||
	var c Cookie
 | 
			
		||||
	if err := s.Decode("fuzz", datas, &c); err != nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := s.Encode("fuzz", c); err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return 1
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										646
									
								
								vendor/github.com/gorilla/securecookie/securecookie.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										646
									
								
								vendor/github.com/gorilla/securecookie/securecookie.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,646 @@
 | 
			
		||||
// 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 securecookie
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/aes"
 | 
			
		||||
	"crypto/cipher"
 | 
			
		||||
	"crypto/hmac"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
	"crypto/subtle"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"encoding/gob"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"hash"
 | 
			
		||||
	"io"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Error is the interface of all errors returned by functions in this library.
 | 
			
		||||
type Error interface {
 | 
			
		||||
	error
 | 
			
		||||
 | 
			
		||||
	// IsUsage returns true for errors indicating the client code probably
 | 
			
		||||
	// uses this library incorrectly.  For example, the client may have
 | 
			
		||||
	// failed to provide a valid hash key, or may have failed to configure
 | 
			
		||||
	// the Serializer adequately for encoding value.
 | 
			
		||||
	IsUsage() bool
 | 
			
		||||
 | 
			
		||||
	// IsDecode returns true for errors indicating that a cookie could not
 | 
			
		||||
	// be decoded and validated.  Since cookies are usually untrusted
 | 
			
		||||
	// user-provided input, errors of this type should be expected.
 | 
			
		||||
	// Usually, the proper action is simply to reject the request.
 | 
			
		||||
	IsDecode() bool
 | 
			
		||||
 | 
			
		||||
	// IsInternal returns true for unexpected errors occurring in the
 | 
			
		||||
	// securecookie implementation.
 | 
			
		||||
	IsInternal() bool
 | 
			
		||||
 | 
			
		||||
	// Cause, if it returns a non-nil value, indicates that this error was
 | 
			
		||||
	// propagated from some underlying library.  If this method returns nil,
 | 
			
		||||
	// this error was raised directly by this library.
 | 
			
		||||
	//
 | 
			
		||||
	// Cause is provided principally for debugging/logging purposes; it is
 | 
			
		||||
	// rare that application logic should perform meaningfully different
 | 
			
		||||
	// logic based on Cause.  See, for example, the caveats described on
 | 
			
		||||
	// (MultiError).Cause().
 | 
			
		||||
	Cause() error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// errorType is a bitmask giving the error type(s) of an cookieError value.
 | 
			
		||||
type errorType int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	usageError = errorType(1 << iota)
 | 
			
		||||
	decodeError
 | 
			
		||||
	internalError
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type cookieError struct {
 | 
			
		||||
	typ   errorType
 | 
			
		||||
	msg   string
 | 
			
		||||
	cause error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e cookieError) IsUsage() bool    { return (e.typ & usageError) != 0 }
 | 
			
		||||
func (e cookieError) IsDecode() bool   { return (e.typ & decodeError) != 0 }
 | 
			
		||||
func (e cookieError) IsInternal() bool { return (e.typ & internalError) != 0 }
 | 
			
		||||
 | 
			
		||||
func (e cookieError) Cause() error { return e.cause }
 | 
			
		||||
 | 
			
		||||
func (e cookieError) Error() string {
 | 
			
		||||
	parts := []string{"securecookie: "}
 | 
			
		||||
	if e.msg == "" {
 | 
			
		||||
		parts = append(parts, "error")
 | 
			
		||||
	} else {
 | 
			
		||||
		parts = append(parts, e.msg)
 | 
			
		||||
	}
 | 
			
		||||
	if c := e.Cause(); c != nil {
 | 
			
		||||
		parts = append(parts, " - caused by: ", c.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return strings.Join(parts, "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	errGeneratingIV = cookieError{typ: internalError, msg: "failed to generate random iv"}
 | 
			
		||||
 | 
			
		||||
	errNoCodecs            = cookieError{typ: usageError, msg: "no codecs provided"}
 | 
			
		||||
	errHashKeyNotSet       = cookieError{typ: usageError, msg: "hash key is not set"}
 | 
			
		||||
	errBlockKeyNotSet      = cookieError{typ: usageError, msg: "block key is not set"}
 | 
			
		||||
	errEncodedValueTooLong = cookieError{typ: usageError, msg: "the value is too long"}
 | 
			
		||||
 | 
			
		||||
	errValueToDecodeTooLong = cookieError{typ: decodeError, msg: "the value is too long"}
 | 
			
		||||
	errTimestampInvalid     = cookieError{typ: decodeError, msg: "invalid timestamp"}
 | 
			
		||||
	errTimestampTooNew      = cookieError{typ: decodeError, msg: "timestamp is too new"}
 | 
			
		||||
	errTimestampExpired     = cookieError{typ: decodeError, msg: "expired timestamp"}
 | 
			
		||||
	errDecryptionFailed     = cookieError{typ: decodeError, msg: "the value could not be decrypted"}
 | 
			
		||||
	errValueNotByte         = cookieError{typ: decodeError, msg: "value not a []byte."}
 | 
			
		||||
	errValueNotBytePtr      = cookieError{typ: decodeError, msg: "value not a pointer to []byte."}
 | 
			
		||||
 | 
			
		||||
	// ErrMacInvalid indicates that cookie decoding failed because the HMAC
 | 
			
		||||
	// could not be extracted and verified.  Direct use of this error
 | 
			
		||||
	// variable is deprecated; it is public only for legacy compatibility,
 | 
			
		||||
	// and may be privatized in the future, as it is rarely useful to
 | 
			
		||||
	// distinguish between this error and other Error implementations.
 | 
			
		||||
	ErrMacInvalid = cookieError{typ: decodeError, msg: "the value is not valid"}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Codec defines an interface to encode and decode cookie values.
 | 
			
		||||
type Codec interface {
 | 
			
		||||
	Encode(name string, value interface{}) (string, error)
 | 
			
		||||
	Decode(name, value string, dst interface{}) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New returns a new SecureCookie.
 | 
			
		||||
//
 | 
			
		||||
// hashKey is required, used to authenticate values using HMAC. Create it using
 | 
			
		||||
// GenerateRandomKey(). It is recommended to use a key with 32 or 64 bytes.
 | 
			
		||||
//
 | 
			
		||||
// blockKey is optional, used to encrypt values. Create it using
 | 
			
		||||
// GenerateRandomKey(). The key length must correspond to the block size
 | 
			
		||||
// of the encryption algorithm. For AES, used by default, valid lengths are
 | 
			
		||||
// 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
 | 
			
		||||
// The default encoder used for cookie serialization is encoding/gob.
 | 
			
		||||
//
 | 
			
		||||
// Note that keys created using GenerateRandomKey() are not automatically
 | 
			
		||||
// persisted. New keys will be created when the application is restarted, and
 | 
			
		||||
// previously issued cookies will not be able to be decoded.
 | 
			
		||||
func New(hashKey, blockKey []byte) *SecureCookie {
 | 
			
		||||
	s := &SecureCookie{
 | 
			
		||||
		hashKey:   hashKey,
 | 
			
		||||
		blockKey:  blockKey,
 | 
			
		||||
		hashFunc:  sha256.New,
 | 
			
		||||
		maxAge:    86400 * 30,
 | 
			
		||||
		maxLength: 4096,
 | 
			
		||||
		sz:        GobEncoder{},
 | 
			
		||||
	}
 | 
			
		||||
	if hashKey == nil {
 | 
			
		||||
		s.err = errHashKeyNotSet
 | 
			
		||||
	}
 | 
			
		||||
	if blockKey != nil {
 | 
			
		||||
		s.BlockFunc(aes.NewCipher)
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SecureCookie encodes and decodes authenticated and optionally encrypted
 | 
			
		||||
// cookie values.
 | 
			
		||||
type SecureCookie struct {
 | 
			
		||||
	hashKey   []byte
 | 
			
		||||
	hashFunc  func() hash.Hash
 | 
			
		||||
	blockKey  []byte
 | 
			
		||||
	block     cipher.Block
 | 
			
		||||
	maxLength int
 | 
			
		||||
	maxAge    int64
 | 
			
		||||
	minAge    int64
 | 
			
		||||
	err       error
 | 
			
		||||
	sz        Serializer
 | 
			
		||||
	// For testing purposes, the function that returns the current timestamp.
 | 
			
		||||
	// If not set, it will use time.Now().UTC().Unix().
 | 
			
		||||
	timeFunc func() int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Serializer provides an interface for providing custom serializers for cookie
 | 
			
		||||
// values.
 | 
			
		||||
type Serializer interface {
 | 
			
		||||
	Serialize(src interface{}) ([]byte, error)
 | 
			
		||||
	Deserialize(src []byte, dst interface{}) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GobEncoder encodes cookie values using encoding/gob. This is the simplest
 | 
			
		||||
// encoder and can handle complex types via gob.Register.
 | 
			
		||||
type GobEncoder struct{}
 | 
			
		||||
 | 
			
		||||
// JSONEncoder encodes cookie values using encoding/json. Users who wish to
 | 
			
		||||
// encode complex types need to satisfy the json.Marshaller and
 | 
			
		||||
// json.Unmarshaller interfaces.
 | 
			
		||||
type JSONEncoder struct{}
 | 
			
		||||
 | 
			
		||||
// NopEncoder does not encode cookie values, and instead simply accepts a []byte
 | 
			
		||||
// (as an interface{}) and returns a []byte. This is particularly useful when
 | 
			
		||||
// you encoding an object upstream and do not wish to re-encode it.
 | 
			
		||||
type NopEncoder struct{}
 | 
			
		||||
 | 
			
		||||
// MaxLength restricts the maximum length, in bytes, for the cookie value.
 | 
			
		||||
//
 | 
			
		||||
// Default is 4096, which is the maximum value accepted by Internet Explorer.
 | 
			
		||||
func (s *SecureCookie) MaxLength(value int) *SecureCookie {
 | 
			
		||||
	s.maxLength = value
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MaxAge restricts the maximum age, in seconds, for the cookie value.
 | 
			
		||||
//
 | 
			
		||||
// Default is 86400 * 30. Set it to 0 for no restriction.
 | 
			
		||||
func (s *SecureCookie) MaxAge(value int) *SecureCookie {
 | 
			
		||||
	s.maxAge = int64(value)
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MinAge restricts the minimum age, in seconds, for the cookie value.
 | 
			
		||||
//
 | 
			
		||||
// Default is 0 (no restriction).
 | 
			
		||||
func (s *SecureCookie) MinAge(value int) *SecureCookie {
 | 
			
		||||
	s.minAge = int64(value)
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HashFunc sets the hash function used to create HMAC.
 | 
			
		||||
//
 | 
			
		||||
// Default is crypto/sha256.New.
 | 
			
		||||
func (s *SecureCookie) HashFunc(f func() hash.Hash) *SecureCookie {
 | 
			
		||||
	s.hashFunc = f
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BlockFunc sets the encryption function used to create a cipher.Block.
 | 
			
		||||
//
 | 
			
		||||
// Default is crypto/aes.New.
 | 
			
		||||
func (s *SecureCookie) BlockFunc(f func([]byte) (cipher.Block, error)) *SecureCookie {
 | 
			
		||||
	if s.blockKey == nil {
 | 
			
		||||
		s.err = errBlockKeyNotSet
 | 
			
		||||
	} else if block, err := f(s.blockKey); err == nil {
 | 
			
		||||
		s.block = block
 | 
			
		||||
	} else {
 | 
			
		||||
		s.err = cookieError{cause: err, typ: usageError}
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Encoding sets the encoding/serialization method for cookies.
 | 
			
		||||
//
 | 
			
		||||
// Default is encoding/gob.  To encode special structures using encoding/gob,
 | 
			
		||||
// they must be registered first using gob.Register().
 | 
			
		||||
func (s *SecureCookie) SetSerializer(sz Serializer) *SecureCookie {
 | 
			
		||||
	s.sz = sz
 | 
			
		||||
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Encode encodes a cookie value.
 | 
			
		||||
//
 | 
			
		||||
// It serializes, optionally encrypts, signs with a message authentication code,
 | 
			
		||||
// and finally encodes the value.
 | 
			
		||||
//
 | 
			
		||||
// The name argument is the cookie name. It is stored with the encoded value.
 | 
			
		||||
// The value argument is the value to be encoded. It can be any value that can
 | 
			
		||||
// be encoded using the currently selected serializer; see SetSerializer().
 | 
			
		||||
//
 | 
			
		||||
// It is the client's responsibility to ensure that value, when encoded using
 | 
			
		||||
// the current serialization/encryption settings on s and then base64-encoded,
 | 
			
		||||
// is shorter than the maximum permissible length.
 | 
			
		||||
func (s *SecureCookie) Encode(name string, value interface{}) (string, error) {
 | 
			
		||||
	if s.err != nil {
 | 
			
		||||
		return "", s.err
 | 
			
		||||
	}
 | 
			
		||||
	if s.hashKey == nil {
 | 
			
		||||
		s.err = errHashKeyNotSet
 | 
			
		||||
		return "", s.err
 | 
			
		||||
	}
 | 
			
		||||
	var err error
 | 
			
		||||
	var b []byte
 | 
			
		||||
	// 1. Serialize.
 | 
			
		||||
	if b, err = s.sz.Serialize(value); err != nil {
 | 
			
		||||
		return "", cookieError{cause: err, typ: usageError}
 | 
			
		||||
	}
 | 
			
		||||
	// 2. Encrypt (optional).
 | 
			
		||||
	if s.block != nil {
 | 
			
		||||
		if b, err = encrypt(s.block, b); err != nil {
 | 
			
		||||
			return "", cookieError{cause: err, typ: usageError}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	b = encode(b)
 | 
			
		||||
	// 3. Create MAC for "name|date|value". Extra pipe to be used later.
 | 
			
		||||
	b = []byte(fmt.Sprintf("%s|%d|%s|", name, s.timestamp(), b))
 | 
			
		||||
	mac := createMac(hmac.New(s.hashFunc, s.hashKey), b[:len(b)-1])
 | 
			
		||||
	// Append mac, remove name.
 | 
			
		||||
	b = append(b, mac...)[len(name)+1:]
 | 
			
		||||
	// 4. Encode to base64.
 | 
			
		||||
	b = encode(b)
 | 
			
		||||
	// 5. Check length.
 | 
			
		||||
	if s.maxLength != 0 && len(b) > s.maxLength {
 | 
			
		||||
		return "", errEncodedValueTooLong
 | 
			
		||||
	}
 | 
			
		||||
	// Done.
 | 
			
		||||
	return string(b), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decode decodes a cookie value.
 | 
			
		||||
//
 | 
			
		||||
// It decodes, verifies a message authentication code, optionally decrypts and
 | 
			
		||||
// finally deserializes the value.
 | 
			
		||||
//
 | 
			
		||||
// The name argument is the cookie name. It must be the same name used when
 | 
			
		||||
// it was stored. The value argument is the encoded cookie value. The dst
 | 
			
		||||
// argument is where the cookie will be decoded. It must be a pointer.
 | 
			
		||||
func (s *SecureCookie) Decode(name, value string, dst interface{}) error {
 | 
			
		||||
	if s.err != nil {
 | 
			
		||||
		return s.err
 | 
			
		||||
	}
 | 
			
		||||
	if s.hashKey == nil {
 | 
			
		||||
		s.err = errHashKeyNotSet
 | 
			
		||||
		return s.err
 | 
			
		||||
	}
 | 
			
		||||
	// 1. Check length.
 | 
			
		||||
	if s.maxLength != 0 && len(value) > s.maxLength {
 | 
			
		||||
		return errValueToDecodeTooLong
 | 
			
		||||
	}
 | 
			
		||||
	// 2. Decode from base64.
 | 
			
		||||
	b, err := decode([]byte(value))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	// 3. Verify MAC. Value is "date|value|mac".
 | 
			
		||||
	parts := bytes.SplitN(b, []byte("|"), 3)
 | 
			
		||||
	if len(parts) != 3 {
 | 
			
		||||
		return ErrMacInvalid
 | 
			
		||||
	}
 | 
			
		||||
	h := hmac.New(s.hashFunc, s.hashKey)
 | 
			
		||||
	b = append([]byte(name+"|"), b[:len(b)-len(parts[2])-1]...)
 | 
			
		||||
	if err = verifyMac(h, b, parts[2]); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	// 4. Verify date ranges.
 | 
			
		||||
	var t1 int64
 | 
			
		||||
	if t1, err = strconv.ParseInt(string(parts[0]), 10, 64); err != nil {
 | 
			
		||||
		return errTimestampInvalid
 | 
			
		||||
	}
 | 
			
		||||
	t2 := s.timestamp()
 | 
			
		||||
	if s.minAge != 0 && t1 > t2-s.minAge {
 | 
			
		||||
		return errTimestampTooNew
 | 
			
		||||
	}
 | 
			
		||||
	if s.maxAge != 0 && t1 < t2-s.maxAge {
 | 
			
		||||
		return errTimestampExpired
 | 
			
		||||
	}
 | 
			
		||||
	// 5. Decrypt (optional).
 | 
			
		||||
	b, err = decode(parts[1])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if s.block != nil {
 | 
			
		||||
		if b, err = decrypt(s.block, b); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// 6. Deserialize.
 | 
			
		||||
	if err = s.sz.Deserialize(b, dst); err != nil {
 | 
			
		||||
		return cookieError{cause: err, typ: decodeError}
 | 
			
		||||
	}
 | 
			
		||||
	// Done.
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// timestamp returns the current timestamp, in seconds.
 | 
			
		||||
//
 | 
			
		||||
// For testing purposes, the function that generates the timestamp can be
 | 
			
		||||
// overridden. If not set, it will return time.Now().UTC().Unix().
 | 
			
		||||
func (s *SecureCookie) timestamp() int64 {
 | 
			
		||||
	if s.timeFunc == nil {
 | 
			
		||||
		return time.Now().UTC().Unix()
 | 
			
		||||
	}
 | 
			
		||||
	return s.timeFunc()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Authentication -------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// createMac creates a message authentication code (MAC).
 | 
			
		||||
func createMac(h hash.Hash, value []byte) []byte {
 | 
			
		||||
	h.Write(value)
 | 
			
		||||
	return h.Sum(nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// verifyMac verifies that a message authentication code (MAC) is valid.
 | 
			
		||||
func verifyMac(h hash.Hash, value []byte, mac []byte) error {
 | 
			
		||||
	mac2 := createMac(h, value)
 | 
			
		||||
	// Check that both MACs are of equal length, as subtle.ConstantTimeCompare
 | 
			
		||||
	// does not do this prior to Go 1.4.
 | 
			
		||||
	if len(mac) == len(mac2) && subtle.ConstantTimeCompare(mac, mac2) == 1 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return ErrMacInvalid
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Encryption -----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// encrypt encrypts a value using the given block in counter mode.
 | 
			
		||||
//
 | 
			
		||||
// A random initialization vector (http://goo.gl/zF67k) with the length of the
 | 
			
		||||
// block size is prepended to the resulting ciphertext.
 | 
			
		||||
func encrypt(block cipher.Block, value []byte) ([]byte, error) {
 | 
			
		||||
	iv := GenerateRandomKey(block.BlockSize())
 | 
			
		||||
	if iv == nil {
 | 
			
		||||
		return nil, errGeneratingIV
 | 
			
		||||
	}
 | 
			
		||||
	// Encrypt it.
 | 
			
		||||
	stream := cipher.NewCTR(block, iv)
 | 
			
		||||
	stream.XORKeyStream(value, value)
 | 
			
		||||
	// Return iv + ciphertext.
 | 
			
		||||
	return append(iv, value...), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// decrypt decrypts a value using the given block in counter mode.
 | 
			
		||||
//
 | 
			
		||||
// The value to be decrypted must be prepended by a initialization vector
 | 
			
		||||
// (http://goo.gl/zF67k) with the length of the block size.
 | 
			
		||||
func decrypt(block cipher.Block, value []byte) ([]byte, error) {
 | 
			
		||||
	size := block.BlockSize()
 | 
			
		||||
	if len(value) > size {
 | 
			
		||||
		// Extract iv.
 | 
			
		||||
		iv := value[:size]
 | 
			
		||||
		// Extract ciphertext.
 | 
			
		||||
		value = value[size:]
 | 
			
		||||
		// Decrypt it.
 | 
			
		||||
		stream := cipher.NewCTR(block, iv)
 | 
			
		||||
		stream.XORKeyStream(value, value)
 | 
			
		||||
		return value, nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil, errDecryptionFailed
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Serialization --------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// Serialize encodes a value using gob.
 | 
			
		||||
func (e GobEncoder) Serialize(src interface{}) ([]byte, error) {
 | 
			
		||||
	buf := new(bytes.Buffer)
 | 
			
		||||
	enc := gob.NewEncoder(buf)
 | 
			
		||||
	if err := enc.Encode(src); err != nil {
 | 
			
		||||
		return nil, cookieError{cause: err, typ: usageError}
 | 
			
		||||
	}
 | 
			
		||||
	return buf.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Deserialize decodes a value using gob.
 | 
			
		||||
func (e GobEncoder) Deserialize(src []byte, dst interface{}) error {
 | 
			
		||||
	dec := gob.NewDecoder(bytes.NewBuffer(src))
 | 
			
		||||
	if err := dec.Decode(dst); err != nil {
 | 
			
		||||
		return cookieError{cause: err, typ: decodeError}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Serialize encodes a value using encoding/json.
 | 
			
		||||
func (e JSONEncoder) Serialize(src interface{}) ([]byte, error) {
 | 
			
		||||
	buf := new(bytes.Buffer)
 | 
			
		||||
	enc := json.NewEncoder(buf)
 | 
			
		||||
	if err := enc.Encode(src); err != nil {
 | 
			
		||||
		return nil, cookieError{cause: err, typ: usageError}
 | 
			
		||||
	}
 | 
			
		||||
	return buf.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Deserialize decodes a value using encoding/json.
 | 
			
		||||
func (e JSONEncoder) Deserialize(src []byte, dst interface{}) error {
 | 
			
		||||
	dec := json.NewDecoder(bytes.NewReader(src))
 | 
			
		||||
	if err := dec.Decode(dst); err != nil {
 | 
			
		||||
		return cookieError{cause: err, typ: decodeError}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Serialize passes a []byte through as-is.
 | 
			
		||||
func (e NopEncoder) Serialize(src interface{}) ([]byte, error) {
 | 
			
		||||
	if b, ok := src.([]byte); ok {
 | 
			
		||||
		return b, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, errValueNotByte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Deserialize passes a []byte through as-is.
 | 
			
		||||
func (e NopEncoder) Deserialize(src []byte, dst interface{}) error {
 | 
			
		||||
	if dat, ok := dst.(*[]byte); ok {
 | 
			
		||||
		*dat = src
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return errValueNotBytePtr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Encoding -------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// encode encodes a value using base64.
 | 
			
		||||
func encode(value []byte) []byte {
 | 
			
		||||
	encoded := make([]byte, base64.URLEncoding.EncodedLen(len(value)))
 | 
			
		||||
	base64.URLEncoding.Encode(encoded, value)
 | 
			
		||||
	return encoded
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// decode decodes a cookie using base64.
 | 
			
		||||
func decode(value []byte) ([]byte, error) {
 | 
			
		||||
	decoded := make([]byte, base64.URLEncoding.DecodedLen(len(value)))
 | 
			
		||||
	b, err := base64.URLEncoding.Decode(decoded, value)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, cookieError{cause: err, typ: decodeError, msg: "base64 decode failed"}
 | 
			
		||||
	}
 | 
			
		||||
	return decoded[:b], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helpers --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// GenerateRandomKey creates a random key with the given length in bytes.
 | 
			
		||||
// On failure, returns nil.
 | 
			
		||||
//
 | 
			
		||||
// Callers should explicitly check for the possibility of a nil return, treat
 | 
			
		||||
// it as a failure of the system random number generator, and not continue.
 | 
			
		||||
func GenerateRandomKey(length int) []byte {
 | 
			
		||||
	k := make([]byte, length)
 | 
			
		||||
	if _, err := io.ReadFull(rand.Reader, k); err != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return k
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CodecsFromPairs returns a slice of SecureCookie instances.
 | 
			
		||||
//
 | 
			
		||||
// It is a convenience function to create a list of codecs for key rotation. Note
 | 
			
		||||
// that the generated Codecs will have the default options applied: callers
 | 
			
		||||
// should iterate over each Codec and type-assert the underlying *SecureCookie to
 | 
			
		||||
// change these.
 | 
			
		||||
//
 | 
			
		||||
// Example:
 | 
			
		||||
//
 | 
			
		||||
//      codecs := securecookie.CodecsFromPairs(
 | 
			
		||||
//           []byte("new-hash-key"),
 | 
			
		||||
//           []byte("new-block-key"),
 | 
			
		||||
//           []byte("old-hash-key"),
 | 
			
		||||
//           []byte("old-block-key"),
 | 
			
		||||
//       )
 | 
			
		||||
//
 | 
			
		||||
//      // Modify each instance.
 | 
			
		||||
//      for _, s := range codecs {
 | 
			
		||||
//             if cookie, ok := s.(*securecookie.SecureCookie); ok {
 | 
			
		||||
//                 cookie.MaxAge(86400 * 7)
 | 
			
		||||
//                 cookie.SetSerializer(securecookie.JSONEncoder{})
 | 
			
		||||
//                 cookie.HashFunc(sha512.New512_256)
 | 
			
		||||
//             }
 | 
			
		||||
//         }
 | 
			
		||||
//
 | 
			
		||||
func CodecsFromPairs(keyPairs ...[]byte) []Codec {
 | 
			
		||||
	codecs := make([]Codec, len(keyPairs)/2+len(keyPairs)%2)
 | 
			
		||||
	for i := 0; i < len(keyPairs); i += 2 {
 | 
			
		||||
		var blockKey []byte
 | 
			
		||||
		if i+1 < len(keyPairs) {
 | 
			
		||||
			blockKey = keyPairs[i+1]
 | 
			
		||||
		}
 | 
			
		||||
		codecs[i/2] = New(keyPairs[i], blockKey)
 | 
			
		||||
	}
 | 
			
		||||
	return codecs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EncodeMulti encodes a cookie value using a group of codecs.
 | 
			
		||||
//
 | 
			
		||||
// The codecs are tried in order. Multiple codecs are accepted to allow
 | 
			
		||||
// key rotation.
 | 
			
		||||
//
 | 
			
		||||
// On error, may return a MultiError.
 | 
			
		||||
func EncodeMulti(name string, value interface{}, codecs ...Codec) (string, error) {
 | 
			
		||||
	if len(codecs) == 0 {
 | 
			
		||||
		return "", errNoCodecs
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var errors MultiError
 | 
			
		||||
	for _, codec := range codecs {
 | 
			
		||||
		encoded, err := codec.Encode(name, value)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			return encoded, nil
 | 
			
		||||
		}
 | 
			
		||||
		errors = append(errors, err)
 | 
			
		||||
	}
 | 
			
		||||
	return "", errors
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DecodeMulti decodes a cookie value using a group of codecs.
 | 
			
		||||
//
 | 
			
		||||
// The codecs are tried in order. Multiple codecs are accepted to allow
 | 
			
		||||
// key rotation.
 | 
			
		||||
//
 | 
			
		||||
// On error, may return a MultiError.
 | 
			
		||||
func DecodeMulti(name string, value string, dst interface{}, codecs ...Codec) error {
 | 
			
		||||
	if len(codecs) == 0 {
 | 
			
		||||
		return errNoCodecs
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var errors MultiError
 | 
			
		||||
	for _, codec := range codecs {
 | 
			
		||||
		err := codec.Decode(name, value, dst)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		errors = append(errors, err)
 | 
			
		||||
	}
 | 
			
		||||
	return errors
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MultiError groups multiple errors.
 | 
			
		||||
type MultiError []error
 | 
			
		||||
 | 
			
		||||
func (m MultiError) IsUsage() bool    { return m.any(func(e Error) bool { return e.IsUsage() }) }
 | 
			
		||||
func (m MultiError) IsDecode() bool   { return m.any(func(e Error) bool { return e.IsDecode() }) }
 | 
			
		||||
func (m MultiError) IsInternal() bool { return m.any(func(e Error) bool { return e.IsInternal() }) }
 | 
			
		||||
 | 
			
		||||
// Cause returns nil for MultiError; there is no unique underlying cause in the
 | 
			
		||||
// general case.
 | 
			
		||||
//
 | 
			
		||||
// Note: we could conceivably return a non-nil Cause only when there is exactly
 | 
			
		||||
// one child error with a Cause.  However, it would be brittle for client code
 | 
			
		||||
// to rely on the arity of causes inside a MultiError, so we have opted not to
 | 
			
		||||
// provide this functionality.  Clients which really wish to access the Causes
 | 
			
		||||
// of the underlying errors are free to iterate through the errors themselves.
 | 
			
		||||
func (m MultiError) Cause() error { return nil }
 | 
			
		||||
 | 
			
		||||
func (m MultiError) Error() string {
 | 
			
		||||
	s, n := "", 0
 | 
			
		||||
	for _, e := range m {
 | 
			
		||||
		if e != nil {
 | 
			
		||||
			if n == 0 {
 | 
			
		||||
				s = e.Error()
 | 
			
		||||
			}
 | 
			
		||||
			n++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	switch n {
 | 
			
		||||
	case 0:
 | 
			
		||||
		return "(0 errors)"
 | 
			
		||||
	case 1:
 | 
			
		||||
		return s
 | 
			
		||||
	case 2:
 | 
			
		||||
		return s + " (and 1 other error)"
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s (and %d other errors)", s, n-1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// any returns true if any element of m is an Error for which pred returns true.
 | 
			
		||||
func (m MultiError) any(pred func(Error) bool) bool {
 | 
			
		||||
	for _, e := range m {
 | 
			
		||||
		if ourErr, ok := e.(Error); ok && pred(ourErr) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								vendor/github.com/gorilla/sessions/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/gorilla/sessions/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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.
 | 
			
		||||
							
								
								
									
										81
									
								
								vendor/github.com/gorilla/sessions/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								vendor/github.com/gorilla/sessions/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,81 @@
 | 
			
		||||
sessions
 | 
			
		||||
========
 | 
			
		||||
[](https://godoc.org/github.com/gorilla/sessions) [](https://travis-ci.org/gorilla/sessions)
 | 
			
		||||
 | 
			
		||||
gorilla/sessions provides cookie and filesystem sessions and infrastructure for
 | 
			
		||||
custom session backends.
 | 
			
		||||
 | 
			
		||||
The key features are:
 | 
			
		||||
 | 
			
		||||
* Simple API: use it as an easy way to set signed (and optionally
 | 
			
		||||
  encrypted) cookies.
 | 
			
		||||
* Built-in backends to store sessions in cookies or the filesystem.
 | 
			
		||||
* Flash messages: session values that last until read.
 | 
			
		||||
* Convenient way to switch session persistency (aka "remember me") and set
 | 
			
		||||
  other attributes.
 | 
			
		||||
* Mechanism to rotate authentication and encryption keys.
 | 
			
		||||
* Multiple sessions per request, even using different backends.
 | 
			
		||||
* Interfaces and infrastructure for custom session backends: sessions from
 | 
			
		||||
  different stores can be retrieved and batch-saved using a common API.
 | 
			
		||||
 | 
			
		||||
Let's start with an example that shows the sessions API in a nutshell:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
	import (
 | 
			
		||||
		"net/http"
 | 
			
		||||
		"github.com/gorilla/sessions"
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	var store = sessions.NewCookieStore([]byte("something-very-secret"))
 | 
			
		||||
 | 
			
		||||
	func MyHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		// Get a session. We're ignoring the error resulted from decoding an
 | 
			
		||||
		// existing session: Get() always returns a session, even if empty.
 | 
			
		||||
		session, _ := store.Get(r, "session-name")
 | 
			
		||||
		// Set some session values.
 | 
			
		||||
		session.Values["foo"] = "bar"
 | 
			
		||||
		session.Values[42] = 43
 | 
			
		||||
		// Save it before we write to the response/return from the handler.
 | 
			
		||||
		session.Save(r, w)
 | 
			
		||||
	}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
First we initialize a session store calling `NewCookieStore()` and passing a
 | 
			
		||||
secret key used to authenticate the session. Inside the handler, we call
 | 
			
		||||
`store.Get()` to retrieve an existing session or a new one. Then we set some
 | 
			
		||||
session values in session.Values, which is a `map[interface{}]interface{}`.
 | 
			
		||||
And finally we call `session.Save()` to save the session in the response.
 | 
			
		||||
 | 
			
		||||
Important Note: If you aren't using gorilla/mux, you need to wrap your handlers
 | 
			
		||||
with
 | 
			
		||||
[`context.ClearHandler`](http://www.gorillatoolkit.org/pkg/context#ClearHandler)
 | 
			
		||||
as or else you will leak memory! An easy way to do this is to wrap the top-level
 | 
			
		||||
mux when calling http.ListenAndServe:
 | 
			
		||||
 | 
			
		||||
More examples are available [on the Gorilla
 | 
			
		||||
website](http://www.gorillatoolkit.org/pkg/sessions).
 | 
			
		||||
 | 
			
		||||
## Store Implementations
 | 
			
		||||
 | 
			
		||||
Other implementations of the `sessions.Store` interface:
 | 
			
		||||
 | 
			
		||||
* [github.com/starJammer/gorilla-sessions-arangodb](https://github.com/starJammer/gorilla-sessions-arangodb) - ArangoDB
 | 
			
		||||
* [github.com/yosssi/boltstore](https://github.com/yosssi/boltstore) - Bolt
 | 
			
		||||
* [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase
 | 
			
		||||
* [github.com/denizeren/dynamostore](https://github.com/denizeren/dynamostore) - Dynamodb on AWS
 | 
			
		||||
* [github.com/bradleypeabody/gorilla-sessions-memcache](https://github.com/bradleypeabody/gorilla-sessions-memcache) - Memcache
 | 
			
		||||
* [github.com/dsoprea/go-appengine-sessioncascade](https://github.com/dsoprea/go-appengine-sessioncascade) - Memcache/Datastore/Context in AppEngine
 | 
			
		||||
* [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB
 | 
			
		||||
* [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL
 | 
			
		||||
* [github.com/EnumApps/clustersqlstore](https://github.com/EnumApps/clustersqlstore) - MySQL Cluster
 | 
			
		||||
* [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL
 | 
			
		||||
* [github.com/boj/redistore](https://github.com/boj/redistore) - Redis
 | 
			
		||||
* [github.com/boj/rethinkstore](https://github.com/boj/rethinkstore) - RethinkDB
 | 
			
		||||
* [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak
 | 
			
		||||
* [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite
 | 
			
		||||
* [github.com/wader/gormstore](https://github.com/wader/gormstore) - GORM (MySQL, PostgreSQL, SQLite)
 | 
			
		||||
* [github.com/gernest/qlstore](https://github.com/gernest/qlstore) - ql
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
 | 
			
		||||
BSD licensed. See the LICENSE file for details.
 | 
			
		||||
							
								
								
									
										199
									
								
								vendor/github.com/gorilla/sessions/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								vendor/github.com/gorilla/sessions/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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 sessions provides cookie and filesystem sessions and
 | 
			
		||||
infrastructure for custom session backends.
 | 
			
		||||
 | 
			
		||||
The key features are:
 | 
			
		||||
 | 
			
		||||
	* Simple API: use it as an easy way to set signed (and optionally
 | 
			
		||||
	  encrypted) cookies.
 | 
			
		||||
	* Built-in backends to store sessions in cookies or the filesystem.
 | 
			
		||||
	* Flash messages: session values that last until read.
 | 
			
		||||
	* Convenient way to switch session persistency (aka "remember me") and set
 | 
			
		||||
	  other attributes.
 | 
			
		||||
	* Mechanism to rotate authentication and encryption keys.
 | 
			
		||||
	* Multiple sessions per request, even using different backends.
 | 
			
		||||
	* Interfaces and infrastructure for custom session backends: sessions from
 | 
			
		||||
	  different stores can be retrieved and batch-saved using a common API.
 | 
			
		||||
 | 
			
		||||
Let's start with an example that shows the sessions API in a nutshell:
 | 
			
		||||
 | 
			
		||||
	import (
 | 
			
		||||
		"net/http"
 | 
			
		||||
		"github.com/gorilla/sessions"
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	var store = sessions.NewCookieStore([]byte("something-very-secret"))
 | 
			
		||||
 | 
			
		||||
	func MyHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		// Get a session. We're ignoring the error resulted from decoding an
 | 
			
		||||
		// existing session: Get() always returns a session, even if empty.
 | 
			
		||||
		session, err := store.Get(r, "session-name")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Set some session values.
 | 
			
		||||
		session.Values["foo"] = "bar"
 | 
			
		||||
		session.Values[42] = 43
 | 
			
		||||
		// Save it before we write to the response/return from the handler.
 | 
			
		||||
		session.Save(r, w)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
First we initialize a session store calling NewCookieStore() and passing a
 | 
			
		||||
secret key used to authenticate the session. Inside the handler, we call
 | 
			
		||||
store.Get() to retrieve an existing session or a new one. Then we set some
 | 
			
		||||
session values in session.Values, which is a map[interface{}]interface{}.
 | 
			
		||||
And finally we call session.Save() to save the session in the response.
 | 
			
		||||
 | 
			
		||||
Note that in production code, we should check for errors when calling
 | 
			
		||||
session.Save(r, w), and either display an error message or otherwise handle it.
 | 
			
		||||
 | 
			
		||||
Save must be called before writing to the response, otherwise the session
 | 
			
		||||
cookie will not be sent to the client.
 | 
			
		||||
 | 
			
		||||
Important Note: If you aren't using gorilla/mux, you need to wrap your handlers
 | 
			
		||||
with context.ClearHandler as or else you will leak memory! An easy way to do this
 | 
			
		||||
is to wrap the top-level mux when calling http.ListenAndServe:
 | 
			
		||||
 | 
			
		||||
    http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux))
 | 
			
		||||
 | 
			
		||||
The ClearHandler function is provided by the gorilla/context package.
 | 
			
		||||
 | 
			
		||||
That's all you need to know for the basic usage. Let's take a look at other
 | 
			
		||||
options, starting with flash messages.
 | 
			
		||||
 | 
			
		||||
Flash messages are session values that last until read. The term appeared with
 | 
			
		||||
Ruby On Rails a few years back. When we request a flash message, it is removed
 | 
			
		||||
from the session. To add a flash, call session.AddFlash(), and to get all
 | 
			
		||||
flashes, call session.Flashes(). Here is an example:
 | 
			
		||||
 | 
			
		||||
	func MyHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		// Get a session.
 | 
			
		||||
		session, err := store.Get(r, "session-name")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Get the previously flashes, if any.
 | 
			
		||||
		if flashes := session.Flashes(); len(flashes) > 0 {
 | 
			
		||||
			// Use the flash values.
 | 
			
		||||
		} else {
 | 
			
		||||
			// Set a new flash.
 | 
			
		||||
			session.AddFlash("Hello, flash messages world!")
 | 
			
		||||
		}
 | 
			
		||||
		session.Save(r, w)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
Flash messages are useful to set information to be read after a redirection,
 | 
			
		||||
like after form submissions.
 | 
			
		||||
 | 
			
		||||
There may also be cases where you want to store a complex datatype within a
 | 
			
		||||
session, such as a struct. Sessions are serialised using the encoding/gob package,
 | 
			
		||||
so it is easy to register new datatypes for storage in sessions:
 | 
			
		||||
 | 
			
		||||
	import(
 | 
			
		||||
		"encoding/gob"
 | 
			
		||||
		"github.com/gorilla/sessions"
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	type Person struct {
 | 
			
		||||
		FirstName	string
 | 
			
		||||
		LastName 	string
 | 
			
		||||
		Email		string
 | 
			
		||||
		Age			int
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type M map[string]interface{}
 | 
			
		||||
 | 
			
		||||
	func init() {
 | 
			
		||||
 | 
			
		||||
		gob.Register(&Person{})
 | 
			
		||||
		gob.Register(&M{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
As it's not possible to pass a raw type as a parameter to a function, gob.Register()
 | 
			
		||||
relies on us passing it a value of the desired type. In the example above we've passed
 | 
			
		||||
it a pointer to a struct and a pointer to a custom type representing a
 | 
			
		||||
map[string]interface. (We could have passed non-pointer values if we wished.) This will
 | 
			
		||||
then allow us to serialise/deserialise values of those types to and from our sessions.
 | 
			
		||||
 | 
			
		||||
Note that because session values are stored in a map[string]interface{}, there's
 | 
			
		||||
a need to type-assert data when retrieving it. We'll use the Person struct we registered above:
 | 
			
		||||
 | 
			
		||||
	func MyHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		session, err := store.Get(r, "session-name")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Retrieve our struct and type-assert it
 | 
			
		||||
		val := session.Values["person"]
 | 
			
		||||
		var person = &Person{}
 | 
			
		||||
		if person, ok := val.(*Person); !ok {
 | 
			
		||||
			// Handle the case that it's not an expected type
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Now we can use our person object
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
By default, session cookies last for a month. This is probably too long for
 | 
			
		||||
some cases, but it is easy to change this and other attributes during
 | 
			
		||||
runtime. Sessions can be configured individually or the store can be
 | 
			
		||||
configured and then all sessions saved using it will use that configuration.
 | 
			
		||||
We access session.Options or store.Options to set a new configuration. The
 | 
			
		||||
fields are basically a subset of http.Cookie fields. Let's change the
 | 
			
		||||
maximum age of a session to one week:
 | 
			
		||||
 | 
			
		||||
	session.Options = &sessions.Options{
 | 
			
		||||
		Path:     "/",
 | 
			
		||||
		MaxAge:   86400 * 7,
 | 
			
		||||
		HttpOnly: true,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
Sometimes we may want to change authentication and/or encryption keys without
 | 
			
		||||
breaking existing sessions. The CookieStore supports key rotation, and to use
 | 
			
		||||
it you just need to set multiple authentication and encryption keys, in pairs,
 | 
			
		||||
to be tested in order:
 | 
			
		||||
 | 
			
		||||
	var store = sessions.NewCookieStore(
 | 
			
		||||
		[]byte("new-authentication-key"),
 | 
			
		||||
		[]byte("new-encryption-key"),
 | 
			
		||||
		[]byte("old-authentication-key"),
 | 
			
		||||
		[]byte("old-encryption-key"),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
New sessions will be saved using the first pair. Old sessions can still be
 | 
			
		||||
read because the first pair will fail, and the second will be tested. This
 | 
			
		||||
makes it easy to "rotate" secret keys and still be able to validate existing
 | 
			
		||||
sessions. Note: for all pairs the encryption key is optional; set it to nil
 | 
			
		||||
or omit it and and encryption won't be used.
 | 
			
		||||
 | 
			
		||||
Multiple sessions can be used in the same request, even with different
 | 
			
		||||
session backends. When this happens, calling Save() on each session
 | 
			
		||||
individually would be cumbersome, so we have a way to save all sessions
 | 
			
		||||
at once: it's sessions.Save(). Here's an example:
 | 
			
		||||
 | 
			
		||||
	var store = sessions.NewCookieStore([]byte("something-very-secret"))
 | 
			
		||||
 | 
			
		||||
	func MyHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		// Get a session and set a value.
 | 
			
		||||
		session1, _ := store.Get(r, "session-one")
 | 
			
		||||
		session1.Values["foo"] = "bar"
 | 
			
		||||
		// Get another session and set another value.
 | 
			
		||||
		session2, _ := store.Get(r, "session-two")
 | 
			
		||||
		session2.Values[42] = 43
 | 
			
		||||
		// Save all sessions.
 | 
			
		||||
		sessions.Save(r, w)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
This is possible because when we call Get() from a session store, it adds the
 | 
			
		||||
session to a common registry. Save() uses it to save all registered sessions.
 | 
			
		||||
*/
 | 
			
		||||
package sessions
 | 
			
		||||
							
								
								
									
										102
									
								
								vendor/github.com/gorilla/sessions/lex.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								vendor/github.com/gorilla/sessions/lex.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
			
		||||
// This file contains code adapted from the Go standard library
 | 
			
		||||
// https://github.com/golang/go/blob/39ad0fd0789872f9469167be7fe9578625ff246e/src/net/http/lex.go
 | 
			
		||||
 | 
			
		||||
package sessions
 | 
			
		||||
 | 
			
		||||
import "strings"
 | 
			
		||||
 | 
			
		||||
var isTokenTable = [127]bool{
 | 
			
		||||
	'!':  true,
 | 
			
		||||
	'#':  true,
 | 
			
		||||
	'$':  true,
 | 
			
		||||
	'%':  true,
 | 
			
		||||
	'&':  true,
 | 
			
		||||
	'\'': true,
 | 
			
		||||
	'*':  true,
 | 
			
		||||
	'+':  true,
 | 
			
		||||
	'-':  true,
 | 
			
		||||
	'.':  true,
 | 
			
		||||
	'0':  true,
 | 
			
		||||
	'1':  true,
 | 
			
		||||
	'2':  true,
 | 
			
		||||
	'3':  true,
 | 
			
		||||
	'4':  true,
 | 
			
		||||
	'5':  true,
 | 
			
		||||
	'6':  true,
 | 
			
		||||
	'7':  true,
 | 
			
		||||
	'8':  true,
 | 
			
		||||
	'9':  true,
 | 
			
		||||
	'A':  true,
 | 
			
		||||
	'B':  true,
 | 
			
		||||
	'C':  true,
 | 
			
		||||
	'D':  true,
 | 
			
		||||
	'E':  true,
 | 
			
		||||
	'F':  true,
 | 
			
		||||
	'G':  true,
 | 
			
		||||
	'H':  true,
 | 
			
		||||
	'I':  true,
 | 
			
		||||
	'J':  true,
 | 
			
		||||
	'K':  true,
 | 
			
		||||
	'L':  true,
 | 
			
		||||
	'M':  true,
 | 
			
		||||
	'N':  true,
 | 
			
		||||
	'O':  true,
 | 
			
		||||
	'P':  true,
 | 
			
		||||
	'Q':  true,
 | 
			
		||||
	'R':  true,
 | 
			
		||||
	'S':  true,
 | 
			
		||||
	'T':  true,
 | 
			
		||||
	'U':  true,
 | 
			
		||||
	'W':  true,
 | 
			
		||||
	'V':  true,
 | 
			
		||||
	'X':  true,
 | 
			
		||||
	'Y':  true,
 | 
			
		||||
	'Z':  true,
 | 
			
		||||
	'^':  true,
 | 
			
		||||
	'_':  true,
 | 
			
		||||
	'`':  true,
 | 
			
		||||
	'a':  true,
 | 
			
		||||
	'b':  true,
 | 
			
		||||
	'c':  true,
 | 
			
		||||
	'd':  true,
 | 
			
		||||
	'e':  true,
 | 
			
		||||
	'f':  true,
 | 
			
		||||
	'g':  true,
 | 
			
		||||
	'h':  true,
 | 
			
		||||
	'i':  true,
 | 
			
		||||
	'j':  true,
 | 
			
		||||
	'k':  true,
 | 
			
		||||
	'l':  true,
 | 
			
		||||
	'm':  true,
 | 
			
		||||
	'n':  true,
 | 
			
		||||
	'o':  true,
 | 
			
		||||
	'p':  true,
 | 
			
		||||
	'q':  true,
 | 
			
		||||
	'r':  true,
 | 
			
		||||
	's':  true,
 | 
			
		||||
	't':  true,
 | 
			
		||||
	'u':  true,
 | 
			
		||||
	'v':  true,
 | 
			
		||||
	'w':  true,
 | 
			
		||||
	'x':  true,
 | 
			
		||||
	'y':  true,
 | 
			
		||||
	'z':  true,
 | 
			
		||||
	'|':  true,
 | 
			
		||||
	'~':  true,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isToken(r rune) bool {
 | 
			
		||||
	i := int(r)
 | 
			
		||||
	return i < len(isTokenTable) && isTokenTable[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isNotToken(r rune) bool {
 | 
			
		||||
	return !isToken(r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isCookieNameValid(raw string) bool {
 | 
			
		||||
	if raw == "" {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return strings.IndexFunc(raw, isNotToken) < 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										241
									
								
								vendor/github.com/gorilla/sessions/sessions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								vendor/github.com/gorilla/sessions/sessions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,241 @@
 | 
			
		||||
// 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 sessions
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/gob"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Default flashes key.
 | 
			
		||||
const flashesKey = "_flash"
 | 
			
		||||
 | 
			
		||||
// Options --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// Options stores configuration for a session or session store.
 | 
			
		||||
//
 | 
			
		||||
// Fields are a subset of http.Cookie fields.
 | 
			
		||||
type Options struct {
 | 
			
		||||
	Path   string
 | 
			
		||||
	Domain string
 | 
			
		||||
	// MaxAge=0 means no 'Max-Age' attribute specified.
 | 
			
		||||
	// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'.
 | 
			
		||||
	// MaxAge>0 means Max-Age attribute present and given in seconds.
 | 
			
		||||
	MaxAge   int
 | 
			
		||||
	Secure   bool
 | 
			
		||||
	HttpOnly bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Session --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// NewSession is called by session stores to create a new session instance.
 | 
			
		||||
func NewSession(store Store, name string) *Session {
 | 
			
		||||
	return &Session{
 | 
			
		||||
		Values: make(map[interface{}]interface{}),
 | 
			
		||||
		store:  store,
 | 
			
		||||
		name:   name,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Session stores the values and optional configuration for a session.
 | 
			
		||||
type Session struct {
 | 
			
		||||
	// The ID of the session, generated by stores. It should not be used for
 | 
			
		||||
	// user data.
 | 
			
		||||
	ID string
 | 
			
		||||
	// Values contains the user-data for the session.
 | 
			
		||||
	Values  map[interface{}]interface{}
 | 
			
		||||
	Options *Options
 | 
			
		||||
	IsNew   bool
 | 
			
		||||
	store   Store
 | 
			
		||||
	name    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Flashes returns a slice of flash messages from the session.
 | 
			
		||||
//
 | 
			
		||||
// A single variadic argument is accepted, and it is optional: it defines
 | 
			
		||||
// the flash key. If not defined "_flash" is used by default.
 | 
			
		||||
func (s *Session) Flashes(vars ...string) []interface{} {
 | 
			
		||||
	var flashes []interface{}
 | 
			
		||||
	key := flashesKey
 | 
			
		||||
	if len(vars) > 0 {
 | 
			
		||||
		key = vars[0]
 | 
			
		||||
	}
 | 
			
		||||
	if v, ok := s.Values[key]; ok {
 | 
			
		||||
		// Drop the flashes and return it.
 | 
			
		||||
		delete(s.Values, key)
 | 
			
		||||
		flashes = v.([]interface{})
 | 
			
		||||
	}
 | 
			
		||||
	return flashes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddFlash adds a flash message to the session.
 | 
			
		||||
//
 | 
			
		||||
// A single variadic argument is accepted, and it is optional: it defines
 | 
			
		||||
// the flash key. If not defined "_flash" is used by default.
 | 
			
		||||
func (s *Session) AddFlash(value interface{}, vars ...string) {
 | 
			
		||||
	key := flashesKey
 | 
			
		||||
	if len(vars) > 0 {
 | 
			
		||||
		key = vars[0]
 | 
			
		||||
	}
 | 
			
		||||
	var flashes []interface{}
 | 
			
		||||
	if v, ok := s.Values[key]; ok {
 | 
			
		||||
		flashes = v.([]interface{})
 | 
			
		||||
	}
 | 
			
		||||
	s.Values[key] = append(flashes, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Save is a convenience method to save this session. It is the same as calling
 | 
			
		||||
// store.Save(request, response, session). You should call Save before writing to
 | 
			
		||||
// the response or returning from the handler.
 | 
			
		||||
func (s *Session) Save(r *http.Request, w http.ResponseWriter) error {
 | 
			
		||||
	return s.store.Save(r, w, s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name returns the name used to register the session.
 | 
			
		||||
func (s *Session) Name() string {
 | 
			
		||||
	return s.name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Store returns the session store used to register the session.
 | 
			
		||||
func (s *Session) Store() Store {
 | 
			
		||||
	return s.store
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Registry -------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// sessionInfo stores a session tracked by the registry.
 | 
			
		||||
type sessionInfo struct {
 | 
			
		||||
	s *Session
 | 
			
		||||
	e error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// contextKey is the type used to store the registry in the context.
 | 
			
		||||
type contextKey int
 | 
			
		||||
 | 
			
		||||
// registryKey is the key used to store the registry in the context.
 | 
			
		||||
const registryKey contextKey = 0
 | 
			
		||||
 | 
			
		||||
// GetRegistry returns a registry instance for the current request.
 | 
			
		||||
func GetRegistry(r *http.Request) *Registry {
 | 
			
		||||
	registry := context.Get(r, registryKey)
 | 
			
		||||
	if registry != nil {
 | 
			
		||||
		return registry.(*Registry)
 | 
			
		||||
	}
 | 
			
		||||
	newRegistry := &Registry{
 | 
			
		||||
		request:  r,
 | 
			
		||||
		sessions: make(map[string]sessionInfo),
 | 
			
		||||
	}
 | 
			
		||||
	context.Set(r, registryKey, newRegistry)
 | 
			
		||||
	return newRegistry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Registry stores sessions used during a request.
 | 
			
		||||
type Registry struct {
 | 
			
		||||
	request  *http.Request
 | 
			
		||||
	sessions map[string]sessionInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get registers and returns a session for the given name and session store.
 | 
			
		||||
//
 | 
			
		||||
// It returns a new session if there are no sessions registered for the name.
 | 
			
		||||
func (s *Registry) Get(store Store, name string) (session *Session, err error) {
 | 
			
		||||
	if !isCookieNameValid(name) {
 | 
			
		||||
		return nil, fmt.Errorf("sessions: invalid character in cookie name: %s", name)
 | 
			
		||||
	}
 | 
			
		||||
	if info, ok := s.sessions[name]; ok {
 | 
			
		||||
		session, err = info.s, info.e
 | 
			
		||||
	} else {
 | 
			
		||||
		session, err = store.New(s.request, name)
 | 
			
		||||
		session.name = name
 | 
			
		||||
		s.sessions[name] = sessionInfo{s: session, e: err}
 | 
			
		||||
	}
 | 
			
		||||
	session.store = store
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Save saves all sessions registered for the current request.
 | 
			
		||||
func (s *Registry) Save(w http.ResponseWriter) error {
 | 
			
		||||
	var errMulti MultiError
 | 
			
		||||
	for name, info := range s.sessions {
 | 
			
		||||
		session := info.s
 | 
			
		||||
		if session.store == nil {
 | 
			
		||||
			errMulti = append(errMulti, fmt.Errorf(
 | 
			
		||||
				"sessions: missing store for session %q", name))
 | 
			
		||||
		} else if err := session.store.Save(s.request, w, session); err != nil {
 | 
			
		||||
			errMulti = append(errMulti, fmt.Errorf(
 | 
			
		||||
				"sessions: error saving session %q -- %v", name, err))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if errMulti != nil {
 | 
			
		||||
		return errMulti
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helpers --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	gob.Register([]interface{}{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Save saves all sessions used during the current request.
 | 
			
		||||
func Save(r *http.Request, w http.ResponseWriter) error {
 | 
			
		||||
	return GetRegistry(r).Save(w)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewCookie returns an http.Cookie with the options set. It also sets
 | 
			
		||||
// the Expires field calculated based on the MaxAge value, for Internet
 | 
			
		||||
// Explorer compatibility.
 | 
			
		||||
func NewCookie(name, value string, options *Options) *http.Cookie {
 | 
			
		||||
	cookie := &http.Cookie{
 | 
			
		||||
		Name:     name,
 | 
			
		||||
		Value:    value,
 | 
			
		||||
		Path:     options.Path,
 | 
			
		||||
		Domain:   options.Domain,
 | 
			
		||||
		MaxAge:   options.MaxAge,
 | 
			
		||||
		Secure:   options.Secure,
 | 
			
		||||
		HttpOnly: options.HttpOnly,
 | 
			
		||||
	}
 | 
			
		||||
	if options.MaxAge > 0 {
 | 
			
		||||
		d := time.Duration(options.MaxAge) * time.Second
 | 
			
		||||
		cookie.Expires = time.Now().Add(d)
 | 
			
		||||
	} else if options.MaxAge < 0 {
 | 
			
		||||
		// Set it to the past to expire now.
 | 
			
		||||
		cookie.Expires = time.Unix(1, 0)
 | 
			
		||||
	}
 | 
			
		||||
	return cookie
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error ----------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// MultiError stores multiple errors.
 | 
			
		||||
//
 | 
			
		||||
// Borrowed from the App Engine SDK.
 | 
			
		||||
type MultiError []error
 | 
			
		||||
 | 
			
		||||
func (m MultiError) Error() string {
 | 
			
		||||
	s, n := "", 0
 | 
			
		||||
	for _, e := range m {
 | 
			
		||||
		if e != nil {
 | 
			
		||||
			if n == 0 {
 | 
			
		||||
				s = e.Error()
 | 
			
		||||
			}
 | 
			
		||||
			n++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	switch n {
 | 
			
		||||
	case 0:
 | 
			
		||||
		return "(0 errors)"
 | 
			
		||||
	case 1:
 | 
			
		||||
		return s
 | 
			
		||||
	case 2:
 | 
			
		||||
		return s + " (and 1 other error)"
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s (and %d other errors)", s, n-1)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										295
									
								
								vendor/github.com/gorilla/sessions/store.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										295
									
								
								vendor/github.com/gorilla/sessions/store.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,295 @@
 | 
			
		||||
// 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 sessions
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/base32"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/securecookie"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Store is an interface for custom session stores.
 | 
			
		||||
//
 | 
			
		||||
// See CookieStore and FilesystemStore for examples.
 | 
			
		||||
type Store interface {
 | 
			
		||||
	// Get should return a cached session.
 | 
			
		||||
	Get(r *http.Request, name string) (*Session, error)
 | 
			
		||||
 | 
			
		||||
	// New should create and return a new session.
 | 
			
		||||
	//
 | 
			
		||||
	// Note that New should never return a nil session, even in the case of
 | 
			
		||||
	// an error if using the Registry infrastructure to cache the session.
 | 
			
		||||
	New(r *http.Request, name string) (*Session, error)
 | 
			
		||||
 | 
			
		||||
	// Save should persist session to the underlying store implementation.
 | 
			
		||||
	Save(r *http.Request, w http.ResponseWriter, s *Session) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CookieStore ----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// NewCookieStore returns a new CookieStore.
 | 
			
		||||
//
 | 
			
		||||
// Keys are defined in pairs to allow key rotation, but the common case is
 | 
			
		||||
// to set a single authentication key and optionally an encryption key.
 | 
			
		||||
//
 | 
			
		||||
// The first key in a pair is used for authentication and the second for
 | 
			
		||||
// encryption. The encryption key can be set to nil or omitted in the last
 | 
			
		||||
// pair, but the authentication key is required in all pairs.
 | 
			
		||||
//
 | 
			
		||||
// It is recommended to use an authentication key with 32 or 64 bytes.
 | 
			
		||||
// The encryption key, if set, must be either 16, 24, or 32 bytes to select
 | 
			
		||||
// AES-128, AES-192, or AES-256 modes.
 | 
			
		||||
//
 | 
			
		||||
// Use the convenience function securecookie.GenerateRandomKey() to create
 | 
			
		||||
// strong keys.
 | 
			
		||||
func NewCookieStore(keyPairs ...[]byte) *CookieStore {
 | 
			
		||||
	cs := &CookieStore{
 | 
			
		||||
		Codecs: securecookie.CodecsFromPairs(keyPairs...),
 | 
			
		||||
		Options: &Options{
 | 
			
		||||
			Path:   "/",
 | 
			
		||||
			MaxAge: 86400 * 30,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cs.MaxAge(cs.Options.MaxAge)
 | 
			
		||||
	return cs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CookieStore stores sessions using secure cookies.
 | 
			
		||||
type CookieStore struct {
 | 
			
		||||
	Codecs  []securecookie.Codec
 | 
			
		||||
	Options *Options // default configuration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get returns a session for the given name after adding it to the registry.
 | 
			
		||||
//
 | 
			
		||||
// It returns a new session if the sessions doesn't exist. Access IsNew on
 | 
			
		||||
// the session to check if it is an existing session or a new one.
 | 
			
		||||
//
 | 
			
		||||
// It returns a new session and an error if the session exists but could
 | 
			
		||||
// not be decoded.
 | 
			
		||||
func (s *CookieStore) Get(r *http.Request, name string) (*Session, error) {
 | 
			
		||||
	return GetRegistry(r).Get(s, name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New returns a session for the given name without adding it to the registry.
 | 
			
		||||
//
 | 
			
		||||
// The difference between New() and Get() is that calling New() twice will
 | 
			
		||||
// decode the session data twice, while Get() registers and reuses the same
 | 
			
		||||
// decoded session after the first call.
 | 
			
		||||
func (s *CookieStore) New(r *http.Request, name string) (*Session, error) {
 | 
			
		||||
	session := NewSession(s, name)
 | 
			
		||||
	opts := *s.Options
 | 
			
		||||
	session.Options = &opts
 | 
			
		||||
	session.IsNew = true
 | 
			
		||||
	var err error
 | 
			
		||||
	if c, errCookie := r.Cookie(name); errCookie == nil {
 | 
			
		||||
		err = securecookie.DecodeMulti(name, c.Value, &session.Values,
 | 
			
		||||
			s.Codecs...)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			session.IsNew = false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return session, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Save adds a single session to the response.
 | 
			
		||||
func (s *CookieStore) Save(r *http.Request, w http.ResponseWriter,
 | 
			
		||||
	session *Session) error {
 | 
			
		||||
	encoded, err := securecookie.EncodeMulti(session.Name(), session.Values,
 | 
			
		||||
		s.Codecs...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	http.SetCookie(w, NewCookie(session.Name(), encoded, session.Options))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MaxAge sets the maximum age for the store and the underlying cookie
 | 
			
		||||
// implementation. Individual sessions can be deleted by setting Options.MaxAge
 | 
			
		||||
// = -1 for that session.
 | 
			
		||||
func (s *CookieStore) MaxAge(age int) {
 | 
			
		||||
	s.Options.MaxAge = age
 | 
			
		||||
 | 
			
		||||
	// Set the maxAge for each securecookie instance.
 | 
			
		||||
	for _, codec := range s.Codecs {
 | 
			
		||||
		if sc, ok := codec.(*securecookie.SecureCookie); ok {
 | 
			
		||||
			sc.MaxAge(age)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilesystemStore ------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
var fileMutex sync.RWMutex
 | 
			
		||||
 | 
			
		||||
// NewFilesystemStore returns a new FilesystemStore.
 | 
			
		||||
//
 | 
			
		||||
// The path argument is the directory where sessions will be saved. If empty
 | 
			
		||||
// it will use os.TempDir().
 | 
			
		||||
//
 | 
			
		||||
// See NewCookieStore() for a description of the other parameters.
 | 
			
		||||
func NewFilesystemStore(path string, keyPairs ...[]byte) *FilesystemStore {
 | 
			
		||||
	if path == "" {
 | 
			
		||||
		path = os.TempDir()
 | 
			
		||||
	}
 | 
			
		||||
	fs := &FilesystemStore{
 | 
			
		||||
		Codecs: securecookie.CodecsFromPairs(keyPairs...),
 | 
			
		||||
		Options: &Options{
 | 
			
		||||
			Path:   "/",
 | 
			
		||||
			MaxAge: 86400 * 30,
 | 
			
		||||
		},
 | 
			
		||||
		path: path,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fs.MaxAge(fs.Options.MaxAge)
 | 
			
		||||
	return fs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilesystemStore stores sessions in the filesystem.
 | 
			
		||||
//
 | 
			
		||||
// It also serves as a reference for custom stores.
 | 
			
		||||
//
 | 
			
		||||
// This store is still experimental and not well tested. Feedback is welcome.
 | 
			
		||||
type FilesystemStore struct {
 | 
			
		||||
	Codecs  []securecookie.Codec
 | 
			
		||||
	Options *Options // default configuration
 | 
			
		||||
	path    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MaxLength restricts the maximum length of new sessions to l.
 | 
			
		||||
// If l is 0 there is no limit to the size of a session, use with caution.
 | 
			
		||||
// The default for a new FilesystemStore is 4096.
 | 
			
		||||
func (s *FilesystemStore) MaxLength(l int) {
 | 
			
		||||
	for _, c := range s.Codecs {
 | 
			
		||||
		if codec, ok := c.(*securecookie.SecureCookie); ok {
 | 
			
		||||
			codec.MaxLength(l)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get returns a session for the given name after adding it to the registry.
 | 
			
		||||
//
 | 
			
		||||
// See CookieStore.Get().
 | 
			
		||||
func (s *FilesystemStore) Get(r *http.Request, name string) (*Session, error) {
 | 
			
		||||
	return GetRegistry(r).Get(s, name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New returns a session for the given name without adding it to the registry.
 | 
			
		||||
//
 | 
			
		||||
// See CookieStore.New().
 | 
			
		||||
func (s *FilesystemStore) New(r *http.Request, name string) (*Session, error) {
 | 
			
		||||
	session := NewSession(s, name)
 | 
			
		||||
	opts := *s.Options
 | 
			
		||||
	session.Options = &opts
 | 
			
		||||
	session.IsNew = true
 | 
			
		||||
	var err error
 | 
			
		||||
	if c, errCookie := r.Cookie(name); errCookie == nil {
 | 
			
		||||
		err = securecookie.DecodeMulti(name, c.Value, &session.ID, s.Codecs...)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			err = s.load(session)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				session.IsNew = false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return session, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Save adds a single session to the response.
 | 
			
		||||
//
 | 
			
		||||
// If the Options.MaxAge of the session is <= 0 then the session file will be
 | 
			
		||||
// deleted from the store path. With this process it enforces the properly
 | 
			
		||||
// session cookie handling so no need to trust in the cookie management in the
 | 
			
		||||
// web browser.
 | 
			
		||||
func (s *FilesystemStore) Save(r *http.Request, w http.ResponseWriter,
 | 
			
		||||
	session *Session) error {
 | 
			
		||||
	// Delete if max-age is <= 0
 | 
			
		||||
	if session.Options.MaxAge <= 0 {
 | 
			
		||||
		if err := s.erase(session); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		http.SetCookie(w, NewCookie(session.Name(), "", session.Options))
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if session.ID == "" {
 | 
			
		||||
		// Because the ID is used in the filename, encode it to
 | 
			
		||||
		// use alphanumeric characters only.
 | 
			
		||||
		session.ID = strings.TrimRight(
 | 
			
		||||
			base32.StdEncoding.EncodeToString(
 | 
			
		||||
				securecookie.GenerateRandomKey(32)), "=")
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.save(session); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	encoded, err := securecookie.EncodeMulti(session.Name(), session.ID,
 | 
			
		||||
		s.Codecs...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	http.SetCookie(w, NewCookie(session.Name(), encoded, session.Options))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MaxAge sets the maximum age for the store and the underlying cookie
 | 
			
		||||
// implementation. Individual sessions can be deleted by setting Options.MaxAge
 | 
			
		||||
// = -1 for that session.
 | 
			
		||||
func (s *FilesystemStore) MaxAge(age int) {
 | 
			
		||||
	s.Options.MaxAge = age
 | 
			
		||||
 | 
			
		||||
	// Set the maxAge for each securecookie instance.
 | 
			
		||||
	for _, codec := range s.Codecs {
 | 
			
		||||
		if sc, ok := codec.(*securecookie.SecureCookie); ok {
 | 
			
		||||
			sc.MaxAge(age)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// save writes encoded session.Values to a file.
 | 
			
		||||
func (s *FilesystemStore) save(session *Session) error {
 | 
			
		||||
	encoded, err := securecookie.EncodeMulti(session.Name(), session.Values,
 | 
			
		||||
		s.Codecs...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	filename := filepath.Join(s.path, "session_"+session.ID)
 | 
			
		||||
	fileMutex.Lock()
 | 
			
		||||
	defer fileMutex.Unlock()
 | 
			
		||||
	return ioutil.WriteFile(filename, []byte(encoded), 0600)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// load reads a file and decodes its content into session.Values.
 | 
			
		||||
func (s *FilesystemStore) load(session *Session) error {
 | 
			
		||||
	filename := filepath.Join(s.path, "session_"+session.ID)
 | 
			
		||||
	fileMutex.RLock()
 | 
			
		||||
	defer fileMutex.RUnlock()
 | 
			
		||||
	fdata, err := ioutil.ReadFile(filename)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err = securecookie.DecodeMulti(session.Name(), string(fdata),
 | 
			
		||||
		&session.Values, s.Codecs...); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// delete session file
 | 
			
		||||
func (s *FilesystemStore) erase(session *Session) error {
 | 
			
		||||
	filename := filepath.Join(s.path, "session_"+session.ID)
 | 
			
		||||
 | 
			
		||||
	fileMutex.RLock()
 | 
			
		||||
	defer fileMutex.RUnlock()
 | 
			
		||||
 | 
			
		||||
	err := os.Remove(filename)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/github.com/markbates/goth/LICENSE.txt
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/markbates/goth/LICENSE.txt
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
Copyright (c) 2014 Mark Bates
 | 
			
		||||
 | 
			
		||||
MIT License
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining
 | 
			
		||||
a copy of this software and associated documentation files (the
 | 
			
		||||
"Software"), to deal in the Software without restriction, including
 | 
			
		||||
without limitation the rights to use, copy, modify, merge, publish,
 | 
			
		||||
distribute, sublicense, and/or sell copies of the Software, and to
 | 
			
		||||
permit persons to whom the Software is furnished to do so, subject to
 | 
			
		||||
the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be
 | 
			
		||||
included in all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | 
			
		||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | 
			
		||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | 
			
		||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 | 
			
		||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 | 
			
		||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 | 
			
		||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
			
		||||
							
								
								
									
										143
									
								
								vendor/github.com/markbates/goth/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								vendor/github.com/markbates/goth/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,143 @@
 | 
			
		||||
# Goth: Multi-Provider Authentication for Go [](https://godoc.org/github.com/markbates/goth) [](https://travis-ci.org/markbates/goth)
 | 
			
		||||
 | 
			
		||||
Package goth provides a simple, clean, and idiomatic way to write authentication
 | 
			
		||||
packages for Go web applications.
 | 
			
		||||
 | 
			
		||||
Unlike other similar packages, Goth, lets you write OAuth, OAuth2, or any other
 | 
			
		||||
protocol providers, as long as they implement the `Provider` and `Session` interfaces.
 | 
			
		||||
 | 
			
		||||
This package was inspired by [https://github.com/intridea/omniauth](https://github.com/intridea/omniauth).
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
```text
 | 
			
		||||
$ go get github.com/markbates/goth
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Supported Providers
 | 
			
		||||
 | 
			
		||||
* Amazon
 | 
			
		||||
* Auth0
 | 
			
		||||
* Bitbucket
 | 
			
		||||
* Box
 | 
			
		||||
* Cloud Foundry
 | 
			
		||||
* Dailymotion
 | 
			
		||||
* Deezer
 | 
			
		||||
* Digital Ocean
 | 
			
		||||
* Discord
 | 
			
		||||
* Dropbox
 | 
			
		||||
* Facebook
 | 
			
		||||
* Fitbit
 | 
			
		||||
* GitHub
 | 
			
		||||
* Gitlab
 | 
			
		||||
* Google+
 | 
			
		||||
* Heroku
 | 
			
		||||
* InfluxCloud
 | 
			
		||||
* Instagram
 | 
			
		||||
* Intercom
 | 
			
		||||
* Lastfm
 | 
			
		||||
* Linkedin
 | 
			
		||||
* Meetup
 | 
			
		||||
* OneDrive
 | 
			
		||||
* OpenID Connect (auto discovery)
 | 
			
		||||
* Paypal
 | 
			
		||||
* SalesForce
 | 
			
		||||
* Slack
 | 
			
		||||
* Soundcloud
 | 
			
		||||
* Spotify
 | 
			
		||||
* Steam
 | 
			
		||||
* Stripe
 | 
			
		||||
* Twitch
 | 
			
		||||
* Twitter
 | 
			
		||||
* Uber
 | 
			
		||||
* Wepay
 | 
			
		||||
* Yahoo
 | 
			
		||||
* Yammer
 | 
			
		||||
 | 
			
		||||
## Examples
 | 
			
		||||
 | 
			
		||||
See the [examples](examples) folder for a working application that lets users authenticate
 | 
			
		||||
through Twitter, Facebook, Google Plus etc.
 | 
			
		||||
 | 
			
		||||
To run the example either clone the source from GitHub
 | 
			
		||||
 | 
			
		||||
```text
 | 
			
		||||
$ git clone git@github.com:markbates/goth.git
 | 
			
		||||
```
 | 
			
		||||
or use
 | 
			
		||||
```text
 | 
			
		||||
$ go get github.com/markbates/goth
 | 
			
		||||
```
 | 
			
		||||
```text
 | 
			
		||||
$ cd goth/examples
 | 
			
		||||
$ go get -v
 | 
			
		||||
$ go build 
 | 
			
		||||
$ ./examples
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Now open up your browser and go to [http://localhost:3000](http://localhost:3000) to see the example.
 | 
			
		||||
 | 
			
		||||
To actually use the different providers, please make sure you configure them given the system environments as defined in the examples/main.go file
 | 
			
		||||
 | 
			
		||||
## Issues
 | 
			
		||||
 | 
			
		||||
Issues always stand a significantly better chance of getting fixed if the are accompanied by a
 | 
			
		||||
pull request.
 | 
			
		||||
 | 
			
		||||
## Contributing
 | 
			
		||||
 | 
			
		||||
Would I love to see more providers? Certainly! Would you love to contribute one? Hopefully, yes!
 | 
			
		||||
 | 
			
		||||
1. Fork it
 | 
			
		||||
2. Create your feature branch (git checkout -b my-new-feature)
 | 
			
		||||
3. Write Tests!
 | 
			
		||||
4. Commit your changes (git commit -am 'Add some feature')
 | 
			
		||||
5. Push to the branch (git push origin my-new-feature)
 | 
			
		||||
6. Create new Pull Request
 | 
			
		||||
 | 
			
		||||
## Contributors
 | 
			
		||||
 | 
			
		||||
* Mark Bates
 | 
			
		||||
* Tyler Bunnell
 | 
			
		||||
* Corey McGrillis
 | 
			
		||||
* willemvd
 | 
			
		||||
* Rakesh Goyal
 | 
			
		||||
* Andy Grunwald
 | 
			
		||||
* Glenn Walker
 | 
			
		||||
* Kevin Fitzpatrick
 | 
			
		||||
* Ben Tranter
 | 
			
		||||
* Sharad Ganapathy
 | 
			
		||||
* Andrew Chilton
 | 
			
		||||
* sharadgana
 | 
			
		||||
* Aurorae
 | 
			
		||||
* Craig P Jolicoeur
 | 
			
		||||
* Zac Bergquist
 | 
			
		||||
* Geoff Franks
 | 
			
		||||
* Raphael Geronimi
 | 
			
		||||
* Noah Shibley
 | 
			
		||||
* lumost
 | 
			
		||||
* oov
 | 
			
		||||
* Felix Lamouroux
 | 
			
		||||
* Rafael Quintela
 | 
			
		||||
* Tyler
 | 
			
		||||
* DenSm
 | 
			
		||||
* Samy KACIMI
 | 
			
		||||
* dante gray
 | 
			
		||||
* Noah
 | 
			
		||||
* Jacob Walker
 | 
			
		||||
* Marin Martinic
 | 
			
		||||
* Roy
 | 
			
		||||
* Omni Adams
 | 
			
		||||
* Sasa Brankovic
 | 
			
		||||
* dkhamsing
 | 
			
		||||
* Dante Swift
 | 
			
		||||
* Attila Domokos
 | 
			
		||||
* Albin Gilles
 | 
			
		||||
* Syed Zubairuddin
 | 
			
		||||
* Johnny Boursiquot
 | 
			
		||||
* Jerome Touffe-Blin
 | 
			
		||||
* bryanl
 | 
			
		||||
* Masanobu YOSHIOKA
 | 
			
		||||
* Jonathan Hall
 | 
			
		||||
* HaiMing.Yin
 | 
			
		||||
* Sairam Kunala
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/markbates/goth/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/markbates/goth/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
/*
 | 
			
		||||
Package goth provides a simple, clean, and idiomatic way to write authentication
 | 
			
		||||
packages for Go web applications.
 | 
			
		||||
 | 
			
		||||
This package was inspired by https://github.com/intridea/omniauth.
 | 
			
		||||
 | 
			
		||||
See the examples folder for a working application that lets users authenticate
 | 
			
		||||
through Twitter or Facebook.
 | 
			
		||||
*/
 | 
			
		||||
package goth
 | 
			
		||||
							
								
								
									
										219
									
								
								vendor/github.com/markbates/goth/gothic/gothic.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								vendor/github.com/markbates/goth/gothic/gothic.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,219 @@
 | 
			
		||||
/*
 | 
			
		||||
Package gothic wraps common behaviour when using Goth. This makes it quick, and easy, to get up
 | 
			
		||||
and running with Goth. Of course, if you want complete control over how things flow, in regards
 | 
			
		||||
to the authentication process, feel free and use Goth directly.
 | 
			
		||||
 | 
			
		||||
See https://github.com/markbates/goth/examples/main.go to see this in action.
 | 
			
		||||
*/
 | 
			
		||||
package gothic
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
	"github.com/gorilla/sessions"
 | 
			
		||||
	"github.com/markbates/goth"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SessionName is the key used to access the session store.
 | 
			
		||||
const SessionName = "_gothic_session"
 | 
			
		||||
 | 
			
		||||
// Store can/should be set by applications using gothic. The default is a cookie store.
 | 
			
		||||
var Store sessions.Store
 | 
			
		||||
var defaultStore sessions.Store
 | 
			
		||||
 | 
			
		||||
var keySet = false
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	key := []byte(os.Getenv("SESSION_SECRET"))
 | 
			
		||||
	keySet = len(key) != 0
 | 
			
		||||
	Store = sessions.NewCookieStore([]byte(key))
 | 
			
		||||
	defaultStore = Store
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
BeginAuthHandler is a convienence handler for starting the authentication process.
 | 
			
		||||
It expects to be able to get the name of the provider from the query parameters
 | 
			
		||||
as either "provider" or ":provider".
 | 
			
		||||
 | 
			
		||||
BeginAuthHandler will redirect the user to the appropriate authentication end-point
 | 
			
		||||
for the requested provider.
 | 
			
		||||
 | 
			
		||||
See https://github.com/markbates/goth/examples/main.go to see this in action.
 | 
			
		||||
*/
 | 
			
		||||
func BeginAuthHandler(res http.ResponseWriter, req *http.Request) {
 | 
			
		||||
	url, err := GetAuthURL(res, req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		res.WriteHeader(http.StatusBadRequest)
 | 
			
		||||
		fmt.Fprintln(res, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	http.Redirect(res, req, url, http.StatusTemporaryRedirect)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetState sets the state string associated with the given request.
 | 
			
		||||
// If no state string is associated with the request, one will be generated.
 | 
			
		||||
// This state is sent to the provider and can be retrieved during the
 | 
			
		||||
// callback.
 | 
			
		||||
var SetState = func(req *http.Request) string {
 | 
			
		||||
	state := req.URL.Query().Get("state")
 | 
			
		||||
	if len(state) > 0 {
 | 
			
		||||
		return state
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "state"
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetState gets the state returned by the provider during the callback.
 | 
			
		||||
// This is used to prevent CSRF attacks, see
 | 
			
		||||
// http://tools.ietf.org/html/rfc6749#section-10.12
 | 
			
		||||
var GetState = func(req *http.Request) string {
 | 
			
		||||
	return req.URL.Query().Get("state")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
GetAuthURL starts the authentication process with the requested provided.
 | 
			
		||||
It will return a URL that should be used to send users to.
 | 
			
		||||
 | 
			
		||||
It expects to be able to get the name of the provider from the query parameters
 | 
			
		||||
as either "provider" or ":provider".
 | 
			
		||||
 | 
			
		||||
I would recommend using the BeginAuthHandler instead of doing all of these steps
 | 
			
		||||
yourself, but that's entirely up to you.
 | 
			
		||||
*/
 | 
			
		||||
func GetAuthURL(res http.ResponseWriter, req *http.Request) (string, error) {
 | 
			
		||||
 | 
			
		||||
	if !keySet && defaultStore == Store {
 | 
			
		||||
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	providerName, err := GetProviderName(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	provider, err := goth.GetProvider(providerName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	sess, err := provider.BeginAuth(SetState(req))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	url, err := sess.GetAuthURL()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = storeInSession(providerName, sess.Marshal(), req, res)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return url, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
CompleteUserAuth does what it says on the tin. It completes the authentication
 | 
			
		||||
process and fetches all of the basic information about the user from the provider.
 | 
			
		||||
 | 
			
		||||
It expects to be able to get the name of the provider from the query parameters
 | 
			
		||||
as either "provider" or ":provider".
 | 
			
		||||
 | 
			
		||||
See https://github.com/markbates/goth/examples/main.go to see this in action.
 | 
			
		||||
*/
 | 
			
		||||
var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
 | 
			
		||||
 | 
			
		||||
	if !keySet && defaultStore == Store {
 | 
			
		||||
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	providerName, err := GetProviderName(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return goth.User{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	provider, err := goth.GetProvider(providerName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return goth.User{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	value, err := getFromSession(providerName, req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return goth.User{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sess, err := provider.UnmarshalSession(value)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return goth.User{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	user, err := provider.FetchUser(sess)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		// user can be found with existing session data
 | 
			
		||||
		return user, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// get new token and retry fetch
 | 
			
		||||
	_, err = sess.Authorize(provider, req.URL.Query())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return goth.User{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = storeInSession(providerName, sess.Marshal(), req, res)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return goth.User{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return provider.FetchUser(sess)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetProviderName is a function used to get the name of a provider
 | 
			
		||||
// for a given request. By default, this provider is fetched from
 | 
			
		||||
// the URL query string. If you provide it in a different way,
 | 
			
		||||
// assign your own function to this variable that returns the provider
 | 
			
		||||
// name for your request.
 | 
			
		||||
var GetProviderName = getProviderName
 | 
			
		||||
 | 
			
		||||
func getProviderName(req *http.Request) (string, error) {
 | 
			
		||||
	provider := req.URL.Query().Get("provider")
 | 
			
		||||
	if provider == "" {
 | 
			
		||||
		if p, ok := mux.Vars(req)["provider"]; ok {
 | 
			
		||||
			return p, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if provider == "" {
 | 
			
		||||
		provider = req.URL.Query().Get(":provider")
 | 
			
		||||
	}
 | 
			
		||||
	if provider == "" {
 | 
			
		||||
		return provider, errors.New("you must select a provider")
 | 
			
		||||
	}
 | 
			
		||||
	return provider, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func storeInSession(key string, value string, req *http.Request, res http.ResponseWriter) error {
 | 
			
		||||
	session, _ := Store.Get(req, key + SessionName)
 | 
			
		||||
 | 
			
		||||
	session.Values[key] = value
 | 
			
		||||
 | 
			
		||||
	return session.Save(req, res)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getFromSession(key string, req *http.Request) (string, error) {
 | 
			
		||||
	session, _ := Store.Get(req, key + SessionName)
 | 
			
		||||
 | 
			
		||||
	value := session.Values[key]
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		return "", errors.New("could not find a matching session for this request")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return value.(string), nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										75
									
								
								vendor/github.com/markbates/goth/provider.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								vendor/github.com/markbates/goth/provider.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
package goth
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
	"golang.org/x/oauth2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Provider needs to be implemented for each 3rd party authentication provider
 | 
			
		||||
// e.g. Facebook, Twitter, etc...
 | 
			
		||||
type Provider interface {
 | 
			
		||||
	Name() string
 | 
			
		||||
	SetName(name string)
 | 
			
		||||
	BeginAuth(state string) (Session, error)
 | 
			
		||||
	UnmarshalSession(string) (Session, error)
 | 
			
		||||
	FetchUser(Session) (User, error)
 | 
			
		||||
	Debug(bool)
 | 
			
		||||
	RefreshToken(refreshToken string) (*oauth2.Token, error) //Get new access token based on the refresh token
 | 
			
		||||
	RefreshTokenAvailable() bool                             //Refresh token is provided by auth provider or not
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const NoAuthUrlErrorMessage = "an AuthURL has not been set"
 | 
			
		||||
 | 
			
		||||
// Providers is list of known/available providers.
 | 
			
		||||
type Providers map[string]Provider
 | 
			
		||||
 | 
			
		||||
var providers = Providers{}
 | 
			
		||||
 | 
			
		||||
// UseProviders adds a list of available providers for use with Goth.
 | 
			
		||||
// Can be called multiple times. If you pass the same provider more
 | 
			
		||||
// than once, the last will be used.
 | 
			
		||||
func UseProviders(viders ...Provider) {
 | 
			
		||||
	for _, provider := range viders {
 | 
			
		||||
		providers[provider.Name()] = provider
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetProviders returns a list of all the providers currently in use.
 | 
			
		||||
func GetProviders() Providers {
 | 
			
		||||
	return providers
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetProvider returns a previously created provider. If Goth has not
 | 
			
		||||
// been told to use the named provider it will return an error.
 | 
			
		||||
func GetProvider(name string) (Provider, error) {
 | 
			
		||||
	provider := providers[name]
 | 
			
		||||
	if provider == nil {
 | 
			
		||||
		return nil, fmt.Errorf("no provider for %s exists", name)
 | 
			
		||||
	}
 | 
			
		||||
	return provider, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClearProviders will remove all providers currently in use.
 | 
			
		||||
// This is useful, mostly, for testing purposes.
 | 
			
		||||
func ClearProviders() {
 | 
			
		||||
	providers = Providers{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ContextForClient provides a context for use with oauth2.
 | 
			
		||||
func ContextForClient(h *http.Client) context.Context {
 | 
			
		||||
	if h == nil {
 | 
			
		||||
		return oauth2.NoContext
 | 
			
		||||
	}
 | 
			
		||||
	return context.WithValue(oauth2.NoContext, oauth2.HTTPClient, h)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HTTPClientWithFallBack to be used in all fetch operations.
 | 
			
		||||
func HTTPClientWithFallBack(h *http.Client) *http.Client {
 | 
			
		||||
	if h != nil {
 | 
			
		||||
		return h
 | 
			
		||||
	}
 | 
			
		||||
	return http.DefaultClient
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										224
									
								
								vendor/github.com/markbates/goth/providers/github/github.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								vendor/github.com/markbates/goth/providers/github/github.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,224 @@
 | 
			
		||||
// Package github implements the OAuth2 protocol for authenticating users through Github.
 | 
			
		||||
// This package can be used as a reference implementation of an OAuth2 provider for Goth.
 | 
			
		||||
package github
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/markbates/goth"
 | 
			
		||||
	"golang.org/x/oauth2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// These vars define the Authentication, Token, and API URLS for GitHub. If
 | 
			
		||||
// using GitHub enterprise you should change these values before calling New.
 | 
			
		||||
//
 | 
			
		||||
// Examples:
 | 
			
		||||
//	github.AuthURL = "https://github.acme.com/login/oauth/authorize
 | 
			
		||||
//	github.TokenURL = "https://github.acme.com/login/oauth/access_token
 | 
			
		||||
//	github.ProfileURL = "https://github.acme.com/api/v3/user
 | 
			
		||||
//	github.EmailURL = "https://github.acme.com/api/v3/user/emails
 | 
			
		||||
var (
 | 
			
		||||
	AuthURL    = "https://github.com/login/oauth/authorize"
 | 
			
		||||
	TokenURL   = "https://github.com/login/oauth/access_token"
 | 
			
		||||
	ProfileURL = "https://api.github.com/user"
 | 
			
		||||
	EmailURL   = "https://api.github.com/user/emails"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// New creates a new Github provider, and sets up important connection details.
 | 
			
		||||
// You should always call `github.New` to get a new Provider. Never try to create
 | 
			
		||||
// one manually.
 | 
			
		||||
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
 | 
			
		||||
	p := &Provider{
 | 
			
		||||
		ClientKey:           clientKey,
 | 
			
		||||
		Secret:              secret,
 | 
			
		||||
		CallbackURL:         callbackURL,
 | 
			
		||||
		providerName:        "github",
 | 
			
		||||
	}
 | 
			
		||||
	p.config = newConfig(p, scopes)
 | 
			
		||||
	return p
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Provider is the implementation of `goth.Provider` for accessing Github.
 | 
			
		||||
type Provider struct {
 | 
			
		||||
	ClientKey    string
 | 
			
		||||
	Secret       string
 | 
			
		||||
	CallbackURL  string
 | 
			
		||||
	HTTPClient   *http.Client
 | 
			
		||||
	config       *oauth2.Config
 | 
			
		||||
	providerName string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name is the name used to retrieve this provider later.
 | 
			
		||||
func (p *Provider) Name() string {
 | 
			
		||||
	return p.providerName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetName is to update the name of the provider (needed in case of multiple providers of 1 type)
 | 
			
		||||
func (p *Provider) SetName(name string) {
 | 
			
		||||
	p.providerName = name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *Provider) Client() *http.Client {
 | 
			
		||||
	return goth.HTTPClientWithFallBack(p.HTTPClient)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Debug is a no-op for the github package.
 | 
			
		||||
func (p *Provider) Debug(debug bool) {}
 | 
			
		||||
 | 
			
		||||
// BeginAuth asks Github for an authentication end-point.
 | 
			
		||||
func (p *Provider) BeginAuth(state string) (goth.Session, error) {
 | 
			
		||||
	url := p.config.AuthCodeURL(state)
 | 
			
		||||
	session := &Session{
 | 
			
		||||
		AuthURL: url,
 | 
			
		||||
	}
 | 
			
		||||
	return session, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FetchUser will go to Github and access basic information about the user.
 | 
			
		||||
func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
 | 
			
		||||
	sess := session.(*Session)
 | 
			
		||||
	user := goth.User{
 | 
			
		||||
		AccessToken: sess.AccessToken,
 | 
			
		||||
		Provider:    p.Name(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if user.AccessToken == "" {
 | 
			
		||||
		// data is not yet retrieved since accessToken is still empty
 | 
			
		||||
		return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	response, err := p.Client().Get(ProfileURL + "?access_token=" + url.QueryEscape(sess.AccessToken))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return user, err
 | 
			
		||||
	}
 | 
			
		||||
	defer response.Body.Close()
 | 
			
		||||
 | 
			
		||||
	if response.StatusCode != http.StatusOK {
 | 
			
		||||
		return user, fmt.Errorf("GitHub API responded with a %d trying to fetch user information", response.StatusCode)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bits, err := ioutil.ReadAll(response.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return user, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return user, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = userFromReader(bytes.NewReader(bits), &user)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return user, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if user.Email == "" {
 | 
			
		||||
		for _, scope := range p.config.Scopes {
 | 
			
		||||
			if strings.TrimSpace(scope) == "user" || strings.TrimSpace(scope) == "user:email" {
 | 
			
		||||
				user.Email, err = getPrivateMail(p, sess)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return user, err
 | 
			
		||||
				}
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return user, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func userFromReader(reader io.Reader, user *goth.User) error {
 | 
			
		||||
	u := struct {
 | 
			
		||||
		ID       int    `json:"id"`
 | 
			
		||||
		Email    string `json:"email"`
 | 
			
		||||
		Bio      string `json:"bio"`
 | 
			
		||||
		Name     string `json:"name"`
 | 
			
		||||
		Login    string `json:"login"`
 | 
			
		||||
		Picture  string `json:"avatar_url"`
 | 
			
		||||
		Location string `json:"location"`
 | 
			
		||||
	}{}
 | 
			
		||||
 | 
			
		||||
	err := json.NewDecoder(reader).Decode(&u)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	user.Name = u.Name
 | 
			
		||||
	user.NickName = u.Login
 | 
			
		||||
	user.Email = u.Email
 | 
			
		||||
	user.Description = u.Bio
 | 
			
		||||
	user.AvatarURL = u.Picture
 | 
			
		||||
	user.UserID = strconv.Itoa(u.ID)
 | 
			
		||||
	user.Location = u.Location
 | 
			
		||||
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getPrivateMail(p *Provider, sess *Session) (email string, err error) {
 | 
			
		||||
	response, err := p.Client().Get(EmailURL + "?access_token=" + url.QueryEscape(sess.AccessToken))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if response != nil {
 | 
			
		||||
			response.Body.Close()
 | 
			
		||||
		}
 | 
			
		||||
		return email, err
 | 
			
		||||
	}
 | 
			
		||||
	defer response.Body.Close()
 | 
			
		||||
 | 
			
		||||
	if response.StatusCode != http.StatusOK {
 | 
			
		||||
		return email, fmt.Errorf("GitHub API responded with a %d trying to fetch user email", response.StatusCode)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var mailList = []struct {
 | 
			
		||||
		Email    string `json:"email"`
 | 
			
		||||
		Primary  bool   `json:"primary"`
 | 
			
		||||
		Verified bool   `json:"verified"`
 | 
			
		||||
	}{}
 | 
			
		||||
	err = json.NewDecoder(response.Body).Decode(&mailList)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return email, err
 | 
			
		||||
	}
 | 
			
		||||
	for _, v := range mailList {
 | 
			
		||||
		if v.Primary && v.Verified {
 | 
			
		||||
			return v.Email, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// can't get primary email - shouldn't be possible
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newConfig(provider *Provider, scopes []string) *oauth2.Config {
 | 
			
		||||
	c := &oauth2.Config{
 | 
			
		||||
		ClientID:     provider.ClientKey,
 | 
			
		||||
		ClientSecret: provider.Secret,
 | 
			
		||||
		RedirectURL:  provider.CallbackURL,
 | 
			
		||||
		Endpoint: oauth2.Endpoint{
 | 
			
		||||
			AuthURL:  AuthURL,
 | 
			
		||||
			TokenURL: TokenURL,
 | 
			
		||||
		},
 | 
			
		||||
		Scopes: []string{},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, scope := range scopes {
 | 
			
		||||
		c.Scopes = append(c.Scopes, scope)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//RefreshToken refresh token is not provided by github
 | 
			
		||||
func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
 | 
			
		||||
	return nil, errors.New("Refresh token is not provided by github")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//RefreshTokenAvailable refresh token is not provided by github
 | 
			
		||||
func (p *Provider) RefreshTokenAvailable() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										56
									
								
								vendor/github.com/markbates/goth/providers/github/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								vendor/github.com/markbates/goth/providers/github/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
package github
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/markbates/goth"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Session stores data during the auth process with Github.
 | 
			
		||||
type Session struct {
 | 
			
		||||
	AuthURL     string
 | 
			
		||||
	AccessToken string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAuthURL will return the URL set by calling the `BeginAuth` function on the Github provider.
 | 
			
		||||
func (s Session) GetAuthURL() (string, error) {
 | 
			
		||||
	if s.AuthURL == "" {
 | 
			
		||||
		return "", errors.New(goth.NoAuthUrlErrorMessage)
 | 
			
		||||
	}
 | 
			
		||||
	return s.AuthURL, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Authorize the session with Github and return the access token to be stored for future use.
 | 
			
		||||
func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string, error) {
 | 
			
		||||
	p := provider.(*Provider)
 | 
			
		||||
	token, err := p.config.Exchange(goth.ContextForClient(p.Client()), params.Get("code"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !token.Valid() {
 | 
			
		||||
		return "", errors.New("Invalid token received from provider")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.AccessToken = token.AccessToken
 | 
			
		||||
	return token.AccessToken, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Marshal the session into a string
 | 
			
		||||
func (s Session) Marshal() string {
 | 
			
		||||
	b, _ := json.Marshal(s)
 | 
			
		||||
	return string(b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s Session) String() string {
 | 
			
		||||
	return s.Marshal()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalSession will unmarshal a JSON string into a session.
 | 
			
		||||
func (p *Provider) UnmarshalSession(data string) (goth.Session, error) {
 | 
			
		||||
	sess := &Session{}
 | 
			
		||||
	err := json.NewDecoder(strings.NewReader(data)).Decode(sess)
 | 
			
		||||
	return sess, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								vendor/github.com/markbates/goth/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/markbates/goth/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
package goth
 | 
			
		||||
 | 
			
		||||
// Params is used to pass data to sessions for authorization. An existing
 | 
			
		||||
// implementation, and the one most likely to be used, is `url.Values`.
 | 
			
		||||
type Params interface {
 | 
			
		||||
	Get(string) string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Session needs to be implemented as part of the provider package.
 | 
			
		||||
// It will be marshaled and persisted between requests to "tie"
 | 
			
		||||
// the start and the end of the authorization process with a
 | 
			
		||||
// 3rd party provider.
 | 
			
		||||
type Session interface {
 | 
			
		||||
	// GetAuthURL returns the URL for the authentication end-point for the provider.
 | 
			
		||||
	GetAuthURL() (string, error)
 | 
			
		||||
	// Marshal generates a string representation of the Session for storing between requests.
 | 
			
		||||
	Marshal() string
 | 
			
		||||
	// Authorize should validate the data from the provider and return an access token
 | 
			
		||||
	// that can be stored for later access to the provider.
 | 
			
		||||
	Authorize(Provider, Params) (string, error)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								vendor/github.com/markbates/goth/user.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/markbates/goth/user.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
package goth
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/gob"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	gob.Register(User{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// User contains the information common amongst most OAuth and OAuth2 providers.
 | 
			
		||||
// All of the "raw" datafrom the provider can be found in the `RawData` field.
 | 
			
		||||
type User struct {
 | 
			
		||||
	RawData           map[string]interface{}
 | 
			
		||||
	Provider          string
 | 
			
		||||
	Email             string
 | 
			
		||||
	Name              string
 | 
			
		||||
	FirstName         string
 | 
			
		||||
	LastName          string
 | 
			
		||||
	NickName          string
 | 
			
		||||
	Description       string
 | 
			
		||||
	UserID            string
 | 
			
		||||
	AvatarURL         string
 | 
			
		||||
	Location          string
 | 
			
		||||
	AccessToken       string
 | 
			
		||||
	AccessTokenSecret string
 | 
			
		||||
	RefreshToken      string
 | 
			
		||||
	ExpiresAt         time.Time
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user