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)tokarchuk/v1.17
							parent
							
								
									fd941db246
								
							
						
					
					
						commit
						01d957677f
					
				@ -0,0 +1,74 @@ | 
				
			||||
// Copyright 2017 The Gitea Authors. All rights reserved.
 | 
				
			||||
// Use of this source code is governed by a MIT-style
 | 
				
			||||
// license that can be found in the LICENSE file.
 | 
				
			||||
 | 
				
			||||
package models | 
				
			||||
 | 
				
			||||
import "github.com/markbates/goth" | 
				
			||||
 | 
				
			||||
// ExternalLoginUser makes the connecting between some existing user and additional external login sources
 | 
				
			||||
type ExternalLoginUser struct { | 
				
			||||
	ExternalID    string `xorm:"NOT NULL"` | 
				
			||||
	UserID        int64 `xorm:"NOT NULL"` | 
				
			||||
	LoginSourceID int64 `xorm:"NOT NULL"` | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// GetExternalLogin checks if a externalID in loginSourceID scope already exists
 | 
				
			||||
func GetExternalLogin(externalLoginUser *ExternalLoginUser) (bool, error) { | 
				
			||||
	return x.Get(externalLoginUser) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// ListAccountLinks returns a map with the ExternalLoginUser and its LoginSource
 | 
				
			||||
func ListAccountLinks(user *User) ([]*ExternalLoginUser, error) { | 
				
			||||
	externalAccounts := make([]*ExternalLoginUser, 0, 5) | 
				
			||||
	err := x.Where("user_id=?", user.ID). | 
				
			||||
		Desc("login_source_id"). | 
				
			||||
		Find(&externalAccounts) | 
				
			||||
 | 
				
			||||
	if err != nil { | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return externalAccounts, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// LinkAccountToUser link the gothUser to the user
 | 
				
			||||
func LinkAccountToUser(user *User, gothUser goth.User) error { | 
				
			||||
	loginSource, err := GetActiveOAuth2LoginSourceByName(gothUser.Provider) | 
				
			||||
	if err != nil { | 
				
			||||
		return err | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	externalLoginUser := &ExternalLoginUser{ | 
				
			||||
		ExternalID:    gothUser.UserID, | 
				
			||||
		UserID:        user.ID, | 
				
			||||
		LoginSourceID: loginSource.ID, | 
				
			||||
	} | 
				
			||||
	has, err := x.Get(externalLoginUser) | 
				
			||||
	if err != nil { | 
				
			||||
		return err | 
				
			||||
	} else if has { | 
				
			||||
		return ErrExternalLoginUserAlreadyExist{gothUser.UserID, user.ID, loginSource.ID} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	_, err = x.Insert(externalLoginUser) | 
				
			||||
	return err | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// RemoveAccountLink will remove all external login sources for the given user
 | 
				
			||||
func RemoveAccountLink(user *User, loginSourceID int64) (int64, error) { | 
				
			||||
	deleted, err := x.Delete(&ExternalLoginUser{UserID: user.ID, LoginSourceID: loginSourceID}) | 
				
			||||
	if err != nil { | 
				
			||||
		return deleted, err | 
				
			||||
	} | 
				
			||||
	if deleted < 1 { | 
				
			||||
		return deleted, ErrExternalLoginUserNotExist{user.ID, loginSourceID} | 
				
			||||
	} | 
				
			||||
	return deleted, err | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// RemoveAllAccountLinks will remove all external login sources for the given user
 | 
				
			||||
func RemoveAllAccountLinks(user *User) error { | 
				
			||||
	_, err := x.Delete(&ExternalLoginUser{UserID: user.ID}) | 
				
			||||
	return err | 
				
			||||
} | 
				
			||||
@ -0,0 +1,25 @@ | 
				
			||||
// Copyright 2016 Gitea. All rights reserved.
 | 
				
			||||
// Use of this source code is governed by a MIT-style
 | 
				
			||||
// license that can be found in the LICENSE file.
 | 
				
			||||
 | 
				
			||||
package migrations | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"fmt" | 
				
			||||
 | 
				
			||||
	"github.com/go-xorm/xorm" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// ExternalLoginUser makes the connecting between some existing user and additional external login sources
 | 
				
			||||
type ExternalLoginUser struct { | 
				
			||||
	ExternalID    string `xorm:"NOT NULL"` | 
				
			||||
	UserID        int64 `xorm:"NOT NULL"` | 
				
			||||
	LoginSourceID int64 `xorm:"NOT NULL"` | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func addExternalLoginUser(x *xorm.Engine) error { | 
				
			||||
	if err := x.Sync2(new(ExternalLoginUser)); err != nil { | 
				
			||||
		return fmt.Errorf("Sync2: %v", err) | 
				
			||||
	} | 
				
			||||
	return nil | 
				
			||||
} | 
				
			||||
@ -0,0 +1,105 @@ | 
				
			||||
// Copyright 2017 The Gitea Authors. All rights reserved.
 | 
				
			||||
// Use of this source code is governed by a MIT-style
 | 
				
			||||
// license that can be found in the LICENSE file.
 | 
				
			||||
 | 
				
			||||
package oauth2 | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"code.gitea.io/gitea/modules/setting" | 
				
			||||
	"code.gitea.io/gitea/modules/log" | 
				
			||||
	"github.com/gorilla/sessions" | 
				
			||||
	"github.com/markbates/goth" | 
				
			||||
	"github.com/markbates/goth/gothic" | 
				
			||||
	"net/http" | 
				
			||||
	"os" | 
				
			||||
	"github.com/satori/go.uuid" | 
				
			||||
	"path/filepath" | 
				
			||||
	"github.com/markbates/goth/providers/github" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
var ( | 
				
			||||
	sessionUsersStoreKey = "gitea-oauth2-sessions" | 
				
			||||
	providerHeaderKey    = "gitea-oauth2-provider" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// Init initialize the setup of the OAuth2 library
 | 
				
			||||
func Init() { | 
				
			||||
	sessionDir := filepath.Join(setting.AppDataPath, "sessions", "oauth2") | 
				
			||||
	if err := os.MkdirAll(sessionDir, 0700); err != nil { | 
				
			||||
		log.Fatal(4, "Fail to create dir %s: %v", sessionDir, err) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	gothic.Store = sessions.NewFilesystemStore(sessionDir, []byte(sessionUsersStoreKey)) | 
				
			||||
 | 
				
			||||
	gothic.SetState = func(req *http.Request) string { | 
				
			||||
		return uuid.NewV4().String() | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	gothic.GetProviderName = func(req *http.Request) (string, error) { | 
				
			||||
		return req.Header.Get(providerHeaderKey), nil | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Auth OAuth2 auth service
 | 
				
			||||
func Auth(provider string, request *http.Request, response http.ResponseWriter) error { | 
				
			||||
	// not sure if goth is thread safe (?) when using multiple providers
 | 
				
			||||
	request.Header.Set(providerHeaderKey, provider) | 
				
			||||
 | 
				
			||||
	// don't use the default gothic begin handler to prevent issues when some error occurs
 | 
				
			||||
	// normally the gothic library will write some custom stuff to the response instead of our own nice error page
 | 
				
			||||
	//gothic.BeginAuthHandler(response, request)
 | 
				
			||||
 | 
				
			||||
	url, err := gothic.GetAuthURL(response, request) | 
				
			||||
	if err == nil { | 
				
			||||
		http.Redirect(response, request, url, http.StatusTemporaryRedirect) | 
				
			||||
	} | 
				
			||||
	return err | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// ProviderCallback handles OAuth callback, resolve to a goth user and send back to original url
 | 
				
			||||
// this will trigger a new authentication request, but because we save it in the session we can use that
 | 
				
			||||
func ProviderCallback(provider string, request *http.Request, response http.ResponseWriter) (goth.User, error) { | 
				
			||||
	// not sure if goth is thread safe (?) when using multiple providers
 | 
				
			||||
	request.Header.Set(providerHeaderKey, provider) | 
				
			||||
 | 
				
			||||
	user, err := gothic.CompleteUserAuth(response, request) | 
				
			||||
	if err != nil { | 
				
			||||
		return user, err | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return user, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// RegisterProvider register a OAuth2 provider in goth lib
 | 
				
			||||
func RegisterProvider(providerName, providerType, clientID, clientSecret string) { | 
				
			||||
	provider := createProvider(providerName, providerType, clientID, clientSecret) | 
				
			||||
 | 
				
			||||
	if provider != nil { | 
				
			||||
		goth.UseProviders(provider) | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// RemoveProvider removes the given OAuth2 provider from the goth lib
 | 
				
			||||
func RemoveProvider(providerName string) { | 
				
			||||
	delete(goth.GetProviders(), providerName) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// used to create different types of goth providers
 | 
				
			||||
func createProvider(providerName, providerType, clientID, clientSecret string) goth.Provider { | 
				
			||||
	callbackURL := setting.AppURL + "user/oauth2/" + providerName + "/callback" | 
				
			||||
 | 
				
			||||
	var provider goth.Provider | 
				
			||||
 | 
				
			||||
	switch providerType { | 
				
			||||
	case "github": | 
				
			||||
		provider = github.New(clientID, clientSecret, callbackURL, "user:email") | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	// always set the name if provider is created so we can support multiple setups of 1 provider
 | 
				
			||||
	if provider != nil { | 
				
			||||
		provider.SetName(providerName) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return provider | 
				
			||||
} | 
				
			||||
| 
		 After Width: | Height: | Size: 1.1 KiB  | 
@ -0,0 +1,13 @@ | 
				
			||||
{{template "base/head" .}} | 
				
			||||
<div class="user link-account"> | 
				
			||||
	<div class="ui middle very relaxed page grid"> | 
				
			||||
		<div class="column"> | 
				
			||||
			<p class="large center"> | 
				
			||||
				{{.i18n.Tr "link_account_signin_or_signup"}} | 
				
			||||
			</p> | 
				
			||||
		</div> | 
				
			||||
	</div> | 
				
			||||
</div> | 
				
			||||
{{template "user/auth/signin_inner" .}} | 
				
			||||
{{template "user/auth/signup_inner" .}} | 
				
			||||
{{template "base/footer" .}} | 
				
			||||
@ -1,44 +1,3 @@ | 
				
			||||
{{template "base/head" .}} | 
				
			||||
<div class="user signin"> | 
				
			||||
	<div class="ui middle very relaxed page grid"> | 
				
			||||
		<div class="column"> | 
				
			||||
			<form class="ui form" action="{{.Link}}" method="post"> | 
				
			||||
				{{.CsrfTokenHtml}} | 
				
			||||
				<h3 class="ui top attached header"> | 
				
			||||
					{{.i18n.Tr "sign_in"}} | 
				
			||||
				</h3> | 
				
			||||
				<div class="ui attached segment"> | 
				
			||||
					{{template "base/alert" .}} | 
				
			||||
					<div class="required inline field {{if .Err_UserName}}error{{end}}"> | 
				
			||||
						<label for="user_name">{{.i18n.Tr "home.uname_holder"}}</label> | 
				
			||||
						<input id="user_name" name="user_name" value="{{.user_name}}" autofocus required> | 
				
			||||
					</div> | 
				
			||||
					<div class="required inline field {{if .Err_Password}}error{{end}}"> | 
				
			||||
						<label for="password">{{.i18n.Tr "password"}}</label> | 
				
			||||
						<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required> | 
				
			||||
					</div> | 
				
			||||
					<div class="inline field"> | 
				
			||||
						<label></label> | 
				
			||||
						<div class="ui checkbox"> | 
				
			||||
							<label>{{.i18n.Tr "auth.remember_me"}}</label> | 
				
			||||
							<input name="remember" type="checkbox"> | 
				
			||||
						</div> | 
				
			||||
					</div> | 
				
			||||
 | 
				
			||||
					<div class="inline field"> | 
				
			||||
						<label></label> | 
				
			||||
						<button class="ui green button">{{.i18n.Tr "sign_in"}}</button> | 
				
			||||
						<a href="{{AppSubUrl}}/user/forget_password">{{.i18n.Tr "auth.forget_password"}}</a> | 
				
			||||
					</div> | 
				
			||||
					{{if .ShowRegistrationButton}} | 
				
			||||
						<div class="inline field"> | 
				
			||||
							<label></label> | 
				
			||||
							<a href="{{AppSubUrl}}/user/sign_up">{{.i18n.Tr "auth.sign_up_now" | Str2html}}</a> | 
				
			||||
						</div> | 
				
			||||
					{{end}} | 
				
			||||
				</div> | 
				
			||||
			</form> | 
				
			||||
		</div> | 
				
			||||
	</div> | 
				
			||||
</div> | 
				
			||||
{{template "user/auth/signin_inner" .}} | 
				
			||||
{{template "base/footer" .}} | 
				
			||||
 | 
				
			||||
@ -0,0 +1,57 @@ | 
				
			||||
<div class="user signin{{if .LinkAccountMode}} icon{{end}}"> | 
				
			||||
	<div class="ui middle very relaxed page grid"> | 
				
			||||
		<div class="column"> | 
				
			||||
			<form class="ui form" action="{{if not .LinkAccountMode}}{{.Link}}{{else}}{{.SignInLink}}{{end}}" method="post"> | 
				
			||||
				{{.CsrfTokenHtml}} | 
				
			||||
				<h3 class="ui top attached header"> | 
				
			||||
					{{.i18n.Tr "sign_in"}} | 
				
			||||
				</h3> | 
				
			||||
				<div class="ui attached segment"> | 
				
			||||
					{{if or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn)}} | 
				
			||||
					{{template "base/alert" .}} | 
				
			||||
					{{end}} | 
				
			||||
					<div class="required inline field {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}"> | 
				
			||||
						<label for="user_name">{{.i18n.Tr "home.uname_holder"}}</label> | 
				
			||||
						<input id="user_name" name="user_name" value="{{.user_name}}" autofocus required> | 
				
			||||
					</div> | 
				
			||||
					<div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}"> | 
				
			||||
						<label for="password">{{.i18n.Tr "password"}}</label> | 
				
			||||
						<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required> | 
				
			||||
					</div> | 
				
			||||
					{{if not .LinkAccountMode}} | 
				
			||||
					<div class="inline field"> | 
				
			||||
						<label></label> | 
				
			||||
						<div class="ui checkbox"> | 
				
			||||
							<label>{{.i18n.Tr "auth.remember_me"}}</label> | 
				
			||||
							<input name="remember" type="checkbox"> | 
				
			||||
						</div> | 
				
			||||
					</div> | 
				
			||||
					{{end}} | 
				
			||||
 | 
				
			||||
					<div class="inline field"> | 
				
			||||
						<label></label> | 
				
			||||
						<button class="ui green button">{{.i18n.Tr "sign_in"}}</button> | 
				
			||||
						<a href="{{AppSubUrl}}/user/forget_password">{{.i18n.Tr "auth.forget_password"}}</a> | 
				
			||||
					</div> | 
				
			||||
 | 
				
			||||
					{{if .ShowRegistrationButton}} | 
				
			||||
						<div class="inline field"> | 
				
			||||
							<label></label> | 
				
			||||
							<a href="{{AppSubUrl}}/user/sign_up">{{.i18n.Tr "auth.sign_up_now" | Str2html}}</a> | 
				
			||||
						</div> | 
				
			||||
					{{end}} | 
				
			||||
 | 
				
			||||
					{{if .OAuth2Providers}} | 
				
			||||
					<div class="ui attached segment"> | 
				
			||||
						<div class="oauth2 center"> | 
				
			||||
							<div> | 
				
			||||
								<p>{{.i18n.Tr "sign_in_with"}}</p>{{range $key, $value := .OAuth2Providers}}<a href="{{AppSubUrl}}/user/oauth2/{{$key}}"><img alt="{{$value.DisplayName}}" title="{{$value.DisplayName}}" src="{{AppSubUrl}}{{$value.Image}}"></a>{{end}} | 
				
			||||
							</div> | 
				
			||||
						</div> | 
				
			||||
					</div> | 
				
			||||
					{{end}} | 
				
			||||
				</div> | 
				
			||||
			</form> | 
				
			||||
		</div> | 
				
			||||
	</div> | 
				
			||||
</div> | 
				
			||||
@ -1,56 +1,3 @@ | 
				
			||||
{{template "base/head" .}} | 
				
			||||
<div class="user signup"> | 
				
			||||
	<div class="ui middle very relaxed page grid"> | 
				
			||||
		<div class="column"> | 
				
			||||
			<form class="ui form" action="{{.Link}}" method="post"> | 
				
			||||
				{{.CsrfTokenHtml}} | 
				
			||||
				<h3 class="ui top attached header"> | 
				
			||||
					{{if .IsSocialLogin}}{{.i18n.Tr "social_sign_in" | Str2html}}{{else}}{{.i18n.Tr "sign_up"}}{{end}} | 
				
			||||
				</h3> | 
				
			||||
				<div class="ui attached segment"> | 
				
			||||
					{{template "base/alert" .}} | 
				
			||||
					{{if .DisableRegistration}} | 
				
			||||
						<p>{{.i18n.Tr "auth.disable_register_prompt"}}</p> | 
				
			||||
					{{else}} | 
				
			||||
						<div class="required inline field {{if .Err_UserName}}error{{end}}"> | 
				
			||||
							<label for="user_name">{{.i18n.Tr "username"}}</label> | 
				
			||||
							<input id="user_name" name="user_name" value="{{.user_name}}" autofocus required> | 
				
			||||
						</div> | 
				
			||||
						<div class="required inline field {{if .Err_Email}}error{{end}}"> | 
				
			||||
							<label for="email">{{.i18n.Tr "email"}}</label> | 
				
			||||
							<input id="email" name="email" type="email" value="{{.email}}" required> | 
				
			||||
						</div> | 
				
			||||
						<div class="required inline field {{if .Err_Password}}error{{end}}"> | 
				
			||||
							<label for="password">{{.i18n.Tr "password"}}</label> | 
				
			||||
							<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required> | 
				
			||||
						</div> | 
				
			||||
						<div class="required inline field {{if .Err_Password}}error{{end}}"> | 
				
			||||
							<label for="retype">{{.i18n.Tr "re_type"}}</label> | 
				
			||||
							<input id="retype" name="retype" type="password" value="{{.retype}}" autocomplete="off" required> | 
				
			||||
						</div> | 
				
			||||
						{{if .EnableCaptcha}} | 
				
			||||
							<div class="inline field"> | 
				
			||||
								<label></label> | 
				
			||||
								{{.Captcha.CreateHtml}} | 
				
			||||
							</div> | 
				
			||||
							<div class="required inline field {{if .Err_Captcha}}error{{end}}"> | 
				
			||||
								<label for="captcha">{{.i18n.Tr "captcha"}}</label> | 
				
			||||
								<input id="captcha" name="captcha" value="{{.captcha}}" autocomplete="off"> | 
				
			||||
							</div> | 
				
			||||
						{{end}} | 
				
			||||
 | 
				
			||||
						<div class="inline field"> | 
				
			||||
							<label></label> | 
				
			||||
							<button class="ui green button">{{.i18n.Tr "auth.create_new_account"}}</button> | 
				
			||||
						</div> | 
				
			||||
						<div class="inline field"> | 
				
			||||
							<label></label> | 
				
			||||
							<a href="{{AppSubUrl}}/user/login">{{if .IsSocialLogin}}{{.i18n.Tr "auth.social_register_helper_msg"}}{{else}}{{.i18n.Tr "auth.register_helper_msg"}}{{end}}</a> | 
				
			||||
						</div> | 
				
			||||
					{{end}} | 
				
			||||
				</div> | 
				
			||||
			</form> | 
				
			||||
		</div> | 
				
			||||
	</div> | 
				
			||||
</div> | 
				
			||||
{{template "user/auth/signup_inner" .}} | 
				
			||||
{{template "base/footer" .}} | 
				
			||||
 | 
				
			||||
@ -0,0 +1,59 @@ | 
				
			||||
<div class="user signup{{if .LinkAccountMode}} icon{{end}}"> | 
				
			||||
	<div class="ui middle very relaxed page grid"> | 
				
			||||
		<div class="column"> | 
				
			||||
			<form class="ui form" action="{{if not .LinkAccountMode}}{{.Link}}{{else}}{{.SignUpLink}}{{end}}" method="post"> | 
				
			||||
				{{.CsrfTokenHtml}} | 
				
			||||
				<h3 class="ui top attached header"> | 
				
			||||
					{{.i18n.Tr "sign_up"}} | 
				
			||||
				</h3> | 
				
			||||
				<div class="ui attached segment"> | 
				
			||||
					{{if or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister)}} | 
				
			||||
					{{template "base/alert" .}} | 
				
			||||
					{{end}} | 
				
			||||
					{{if .DisableRegistration}} | 
				
			||||
						<p>{{.i18n.Tr "auth.disable_register_prompt"}}</p> | 
				
			||||
					{{else}} | 
				
			||||
						<div class="required inline field {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}"> | 
				
			||||
							<label for="user_name">{{.i18n.Tr "username"}}</label> | 
				
			||||
							<input id="user_name" name="user_name" value="{{.user_name}}" autofocus required> | 
				
			||||
						</div> | 
				
			||||
						<div class="required inline field {{if .Err_Email}}error{{end}}"> | 
				
			||||
							<label for="email">{{.i18n.Tr "email"}}</label> | 
				
			||||
							<input id="email" name="email" type="email" value="{{.email}}" required> | 
				
			||||
						</div> | 
				
			||||
						<div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}"> | 
				
			||||
							<label for="password">{{.i18n.Tr "password"}}</label> | 
				
			||||
							<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required> | 
				
			||||
						</div> | 
				
			||||
						<div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}"> | 
				
			||||
							<label for="retype">{{.i18n.Tr "re_type"}}</label> | 
				
			||||
							<input id="retype" name="retype" type="password" value="{{.retype}}" autocomplete="off" required> | 
				
			||||
						</div> | 
				
			||||
						{{if .EnableCaptcha}} | 
				
			||||
							<div class="inline field"> | 
				
			||||
								<label></label> | 
				
			||||
								{{.Captcha.CreateHtml}} | 
				
			||||
							</div> | 
				
			||||
							<div class="required inline field {{if .Err_Captcha}}error{{end}}"> | 
				
			||||
								<label for="captcha">{{.i18n.Tr "captcha"}}</label> | 
				
			||||
								<input id="captcha" name="captcha" value="{{.captcha}}" autocomplete="off"> | 
				
			||||
							</div> | 
				
			||||
						{{end}} | 
				
			||||
 | 
				
			||||
						<div class="inline field"> | 
				
			||||
							<label></label> | 
				
			||||
							<button class="ui green button">{{.i18n.Tr "auth.create_new_account"}}</button> | 
				
			||||
						</div> | 
				
			||||
 | 
				
			||||
						{{if not .LinkAccountMode}} | 
				
			||||
						<div class="inline field"> | 
				
			||||
							<label></label> | 
				
			||||
							<a href="{{AppSubUrl}}/user/login">{{.i18n.Tr "auth.register_helper_msg"}}</a> | 
				
			||||
						</div> | 
				
			||||
						{{end}} | 
				
			||||
					{{end}} | 
				
			||||
				</div> | 
				
			||||
			</form> | 
				
			||||
		</div> | 
				
			||||
	</div> | 
				
			||||
</div> | 
				
			||||
@ -0,0 +1,48 @@ | 
				
			||||
{{template "base/head" .}} | 
				
			||||
<div class="user settings account_link"> | 
				
			||||
	<div class="ui container"> | 
				
			||||
		<div class="ui grid"> | 
				
			||||
			{{template "user/settings/navbar" .}} | 
				
			||||
			<div class="twelve wide column content"> | 
				
			||||
				{{template "base/alert" .}} | 
				
			||||
				<h4 class="ui top attached header"> | 
				
			||||
					{{.i18n.Tr "settings.manage_account_links"}} | 
				
			||||
				</h4> | 
				
			||||
				<div class="ui attached segment"> | 
				
			||||
					<div class="ui key list"> | 
				
			||||
						<div class="item"> | 
				
			||||
							{{.i18n.Tr "settings.manage_account_links_desc"}} | 
				
			||||
						</div> | 
				
			||||
						{{if .AccountLinks}} | 
				
			||||
						{{range $loginSource, $provider := .AccountLinks}} | 
				
			||||
							<div class="item ui grid"> | 
				
			||||
								<div class="column"> | 
				
			||||
									<strong>{{$provider}}</strong> | 
				
			||||
									{{if $loginSource.IsActived}}<span class="text red">{{$.i18n.Tr "settings.active"}}</span>{{end}} | 
				
			||||
									<div class="ui right"> | 
				
			||||
										<button class="ui red tiny button delete-button" data-url="{{$.Link}}" data-id="{{$loginSource.ID}}"> | 
				
			||||
											{{$.i18n.Tr "settings.delete_key"}} | 
				
			||||
										</button> | 
				
			||||
									</div> | 
				
			||||
								</div> | 
				
			||||
							</div> | 
				
			||||
						{{end}} | 
				
			||||
						{{end}} | 
				
			||||
					</div> | 
				
			||||
				</div> | 
				
			||||
			</div> | 
				
			||||
		</div> | 
				
			||||
	</div> | 
				
			||||
</div> | 
				
			||||
 | 
				
			||||
<div class="ui small basic delete modal"> | 
				
			||||
	<div class="ui icon header"> | 
				
			||||
		<i class="trash icon"></i> | 
				
			||||
		{{.i18n.Tr "settings.remove_account_link"}} | 
				
			||||
	</div> | 
				
			||||
	<div class="content"> | 
				
			||||
		<p>{{.i18n.Tr "settings.remove_account_link_desc"}}</p> | 
				
			||||
	</div> | 
				
			||||
	{{template "base/delete_modal_actions" .}} | 
				
			||||
</div> | 
				
			||||
{{template "base/footer" .}} | 
				
			||||
@ -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. | 
				
			||||
@ -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 | 
				
			||||
@ -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) | 
				
			||||
	}) | 
				
			||||
} | 
				
			||||
@ -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 | 
				
			||||
@ -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. | 
				
			||||
@ -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. | 
				
			||||
@ -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) | 
				
			||||
} | 
				
			||||
@ -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 | 
				
			||||
} | 
				
			||||
@ -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 | 
				
			||||
@ -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 | 
				
			||||
} | 
				
			||||
@ -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]] | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -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 | 
				
			||||
} | 
				
			||||
@ -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. | 
				
			||||
@ -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. | 
				
			||||
@ -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 | 
				
			||||
@ -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 | 
				
			||||
} | 
				
			||||
@ -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 | 
				
			||||
} | 
				
			||||
@ -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. | 
				
			||||
@ -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. | 
				
			||||
@ -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 | 
				
			||||
@ -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 | 
				
			||||
} | 
				
			||||
@ -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) | 
				
			||||
} | 
				
			||||
@ -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 | 
				
			||||
} | 
				
			||||
@ -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. | 
				
			||||
@ -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 | 
				
			||||
@ -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 | 
				
			||||
@ -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 | 
				
			||||
} | 
				
			||||
@ -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 | 
				
			||||
} | 
				
			||||
@ -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 | 
				
			||||
} | 
				
			||||
@ -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 | 
				
			||||
} | 
				
			||||
@ -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) | 
				
			||||
} | 
				
			||||
@ -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 | 
				
			||||
} | 
				
			||||
@ -0,0 +1,3 @@ | 
				
			||||
# This source code refers to The Go Authors for copyright purposes. | 
				
			||||
# The master list of authors is in the main Go distribution, | 
				
			||||
# visible at http://tip.golang.org/AUTHORS. | 
				
			||||
@ -0,0 +1,31 @@ | 
				
			||||
# Contributing to Go | 
				
			||||
 | 
				
			||||
Go is an open source project. | 
				
			||||
 | 
				
			||||
It is the work of hundreds of contributors. We appreciate your help! | 
				
			||||
 | 
				
			||||
 | 
				
			||||
## Filing issues | 
				
			||||
 | 
				
			||||
When [filing an issue](https://github.com/golang/oauth2/issues), make sure to answer these five questions: | 
				
			||||
 | 
				
			||||
1. What version of Go are you using (`go version`)? | 
				
			||||
2. What operating system and processor architecture are you using? | 
				
			||||
3. What did you do? | 
				
			||||
4. What did you expect to see? | 
				
			||||
5. What did you see instead? | 
				
			||||
 | 
				
			||||
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker. | 
				
			||||
The gophers there will answer or ask you to file an issue if you've tripped over a bug. | 
				
			||||
 | 
				
			||||
## Contributing code | 
				
			||||
 | 
				
			||||
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html) | 
				
			||||
before sending patches. | 
				
			||||
 | 
				
			||||
**We do not accept GitHub pull requests** | 
				
			||||
(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review). | 
				
			||||
 | 
				
			||||
Unless otherwise noted, the Go source files are distributed under | 
				
			||||
the BSD-style license found in the LICENSE file. | 
				
			||||
 | 
				
			||||
@ -0,0 +1,3 @@ | 
				
			||||
# This source code was written by the Go contributors. | 
				
			||||
# The master list of contributors is in the main Go distribution, | 
				
			||||
# visible at http://tip.golang.org/CONTRIBUTORS. | 
				
			||||
@ -0,0 +1,27 @@ | 
				
			||||
Copyright (c) 2009 The oauth2 Authors. 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. | 
				
			||||
@ -0,0 +1,65 @@ | 
				
			||||
# OAuth2 for Go | 
				
			||||
 | 
				
			||||
[](https://travis-ci.org/golang/oauth2) | 
				
			||||
[](https://godoc.org/golang.org/x/oauth2) | 
				
			||||
 | 
				
			||||
oauth2 package contains a client implementation for OAuth 2.0 spec. | 
				
			||||
 | 
				
			||||
## Installation | 
				
			||||
 | 
				
			||||
~~~~ | 
				
			||||
go get golang.org/x/oauth2 | 
				
			||||
~~~~ | 
				
			||||
 | 
				
			||||
See godoc for further documentation and examples. | 
				
			||||
 | 
				
			||||
* [godoc.org/golang.org/x/oauth2](http://godoc.org/golang.org/x/oauth2) | 
				
			||||
* [godoc.org/golang.org/x/oauth2/google](http://godoc.org/golang.org/x/oauth2/google) | 
				
			||||
 | 
				
			||||
 | 
				
			||||
## App Engine | 
				
			||||
 | 
				
			||||
In change 96e89be (March 2015) we removed the `oauth2.Context2` type in favor | 
				
			||||
of the [`context.Context`](https://golang.org/x/net/context#Context) type from | 
				
			||||
the `golang.org/x/net/context` package | 
				
			||||
 | 
				
			||||
This means its no longer possible to use the "Classic App Engine" | 
				
			||||
`appengine.Context` type with the `oauth2` package. (You're using | 
				
			||||
Classic App Engine if you import the package `"appengine"`.) | 
				
			||||
 | 
				
			||||
To work around this, you may use the new `"google.golang.org/appengine"` | 
				
			||||
package. This package has almost the same API as the `"appengine"` package, | 
				
			||||
but it can be fetched with `go get` and used on "Managed VMs" and well as | 
				
			||||
Classic App Engine. | 
				
			||||
 | 
				
			||||
See the [new `appengine` package's readme](https://github.com/golang/appengine#updating-a-go-app-engine-app) | 
				
			||||
for information on updating your app. | 
				
			||||
 | 
				
			||||
If you don't want to update your entire app to use the new App Engine packages, | 
				
			||||
you may use both sets of packages in parallel, using only the new packages | 
				
			||||
with the `oauth2` package. | 
				
			||||
 | 
				
			||||
	import ( | 
				
			||||
		"golang.org/x/net/context" | 
				
			||||
		"golang.org/x/oauth2" | 
				
			||||
		"golang.org/x/oauth2/google" | 
				
			||||
		newappengine "google.golang.org/appengine" | 
				
			||||
		newurlfetch "google.golang.org/appengine/urlfetch" | 
				
			||||
 | 
				
			||||
		"appengine" | 
				
			||||
	) | 
				
			||||
 | 
				
			||||
	func handler(w http.ResponseWriter, r *http.Request) { | 
				
			||||
		var c appengine.Context = appengine.NewContext(r) | 
				
			||||
		c.Infof("Logging a message with the old package") | 
				
			||||
 | 
				
			||||
		var ctx context.Context = newappengine.NewContext(r) | 
				
			||||
		client := &http.Client{ | 
				
			||||
			Transport: &oauth2.Transport{ | 
				
			||||
				Source: google.AppEngineTokenSource(ctx, "scope"), | 
				
			||||
				Base:   &newurlfetch.Transport{Context: ctx}, | 
				
			||||
			}, | 
				
			||||
		} | 
				
			||||
		client.Get("...") | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
@ -0,0 +1,25 @@ | 
				
			||||
// Copyright 2014 The Go Authors. All rights reserved.
 | 
				
			||||
// Use of this source code is governed by a BSD-style
 | 
				
			||||
// license that can be found in the LICENSE file.
 | 
				
			||||
 | 
				
			||||
// +build appengine
 | 
				
			||||
 | 
				
			||||
// App Engine hooks.
 | 
				
			||||
 | 
				
			||||
package oauth2 | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"net/http" | 
				
			||||
 | 
				
			||||
	"golang.org/x/net/context" | 
				
			||||
	"golang.org/x/oauth2/internal" | 
				
			||||
	"google.golang.org/appengine/urlfetch" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
func init() { | 
				
			||||
	internal.RegisterContextClientFunc(contextClientAppEngine) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func contextClientAppEngine(ctx context.Context) (*http.Client, error) { | 
				
			||||
	return urlfetch.Client(ctx), nil | 
				
			||||
} | 
				
			||||
@ -0,0 +1,76 @@ | 
				
			||||
// Copyright 2014 The Go Authors. All rights reserved.
 | 
				
			||||
// Use of this source code is governed by a BSD-style
 | 
				
			||||
// license that can be found in the LICENSE file.
 | 
				
			||||
 | 
				
			||||
// Package internal contains support packages for oauth2 package.
 | 
				
			||||
package internal | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"bufio" | 
				
			||||
	"crypto/rsa" | 
				
			||||
	"crypto/x509" | 
				
			||||
	"encoding/pem" | 
				
			||||
	"errors" | 
				
			||||
	"fmt" | 
				
			||||
	"io" | 
				
			||||
	"strings" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// ParseKey converts the binary contents of a private key file
 | 
				
			||||
// to an *rsa.PrivateKey. It detects whether the private key is in a
 | 
				
			||||
// PEM container or not. If so, it extracts the the private key
 | 
				
			||||
// from PEM container before conversion. It only supports PEM
 | 
				
			||||
// containers with no passphrase.
 | 
				
			||||
func ParseKey(key []byte) (*rsa.PrivateKey, error) { | 
				
			||||
	block, _ := pem.Decode(key) | 
				
			||||
	if block != nil { | 
				
			||||
		key = block.Bytes | 
				
			||||
	} | 
				
			||||
	parsedKey, err := x509.ParsePKCS8PrivateKey(key) | 
				
			||||
	if err != nil { | 
				
			||||
		parsedKey, err = x509.ParsePKCS1PrivateKey(key) | 
				
			||||
		if err != nil { | 
				
			||||
			return nil, fmt.Errorf("private key should be a PEM or plain PKSC1 or PKCS8; parse error: %v", err) | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	parsed, ok := parsedKey.(*rsa.PrivateKey) | 
				
			||||
	if !ok { | 
				
			||||
		return nil, errors.New("private key is invalid") | 
				
			||||
	} | 
				
			||||
	return parsed, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func ParseINI(ini io.Reader) (map[string]map[string]string, error) { | 
				
			||||
	result := map[string]map[string]string{ | 
				
			||||
		"": map[string]string{}, // root section
 | 
				
			||||
	} | 
				
			||||
	scanner := bufio.NewScanner(ini) | 
				
			||||
	currentSection := "" | 
				
			||||
	for scanner.Scan() { | 
				
			||||
		line := strings.TrimSpace(scanner.Text()) | 
				
			||||
		if strings.HasPrefix(line, ";") { | 
				
			||||
			// comment.
 | 
				
			||||
			continue | 
				
			||||
		} | 
				
			||||
		if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") { | 
				
			||||
			currentSection = strings.TrimSpace(line[1 : len(line)-1]) | 
				
			||||
			result[currentSection] = map[string]string{} | 
				
			||||
			continue | 
				
			||||
		} | 
				
			||||
		parts := strings.SplitN(line, "=", 2) | 
				
			||||
		if len(parts) == 2 && parts[0] != "" { | 
				
			||||
			result[currentSection][strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	if err := scanner.Err(); err != nil { | 
				
			||||
		return nil, fmt.Errorf("error scanning ini: %v", err) | 
				
			||||
	} | 
				
			||||
	return result, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func CondVal(v string) []string { | 
				
			||||
	if v == "" { | 
				
			||||
		return nil | 
				
			||||
	} | 
				
			||||
	return []string{v} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,227 @@ | 
				
			||||
// Copyright 2014 The Go Authors. All rights reserved.
 | 
				
			||||
// Use of this source code is governed by a BSD-style
 | 
				
			||||
// license that can be found in the LICENSE file.
 | 
				
			||||
 | 
				
			||||
// Package internal contains support packages for oauth2 package.
 | 
				
			||||
package internal | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"encoding/json" | 
				
			||||
	"fmt" | 
				
			||||
	"io" | 
				
			||||
	"io/ioutil" | 
				
			||||
	"mime" | 
				
			||||
	"net/http" | 
				
			||||
	"net/url" | 
				
			||||
	"strconv" | 
				
			||||
	"strings" | 
				
			||||
	"time" | 
				
			||||
 | 
				
			||||
	"golang.org/x/net/context" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// Token represents the crendentials used to authorize
 | 
				
			||||
// the requests to access protected resources on the OAuth 2.0
 | 
				
			||||
// provider's backend.
 | 
				
			||||
//
 | 
				
			||||
// This type is a mirror of oauth2.Token and exists to break
 | 
				
			||||
// an otherwise-circular dependency. Other internal packages
 | 
				
			||||
// should convert this Token into an oauth2.Token before use.
 | 
				
			||||
type Token struct { | 
				
			||||
	// AccessToken is the token that authorizes and authenticates
 | 
				
			||||
	// the requests.
 | 
				
			||||
	AccessToken string | 
				
			||||
 | 
				
			||||
	// TokenType is the type of token.
 | 
				
			||||
	// The Type method returns either this or "Bearer", the default.
 | 
				
			||||
	TokenType string | 
				
			||||
 | 
				
			||||
	// RefreshToken is a token that's used by the application
 | 
				
			||||
	// (as opposed to the user) to refresh the access token
 | 
				
			||||
	// if it expires.
 | 
				
			||||
	RefreshToken string | 
				
			||||
 | 
				
			||||
	// Expiry is the optional expiration time of the access token.
 | 
				
			||||
	//
 | 
				
			||||
	// If zero, TokenSource implementations will reuse the same
 | 
				
			||||
	// token forever and RefreshToken or equivalent
 | 
				
			||||
	// mechanisms for that TokenSource will not be used.
 | 
				
			||||
	Expiry time.Time | 
				
			||||
 | 
				
			||||
	// Raw optionally contains extra metadata from the server
 | 
				
			||||
	// when updating a token.
 | 
				
			||||
	Raw interface{} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// tokenJSON is the struct representing the HTTP response from OAuth2
 | 
				
			||||
// providers returning a token in JSON form.
 | 
				
			||||
type tokenJSON struct { | 
				
			||||
	AccessToken  string         `json:"access_token"` | 
				
			||||
	TokenType    string         `json:"token_type"` | 
				
			||||
	RefreshToken string         `json:"refresh_token"` | 
				
			||||
	ExpiresIn    expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
 | 
				
			||||
	Expires      expirationTime `json:"expires"`    // broken Facebook spelling of expires_in
 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (e *tokenJSON) expiry() (t time.Time) { | 
				
			||||
	if v := e.ExpiresIn; v != 0 { | 
				
			||||
		return time.Now().Add(time.Duration(v) * time.Second) | 
				
			||||
	} | 
				
			||||
	if v := e.Expires; v != 0 { | 
				
			||||
		return time.Now().Add(time.Duration(v) * time.Second) | 
				
			||||
	} | 
				
			||||
	return | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type expirationTime int32 | 
				
			||||
 | 
				
			||||
func (e *expirationTime) UnmarshalJSON(b []byte) error { | 
				
			||||
	var n json.Number | 
				
			||||
	err := json.Unmarshal(b, &n) | 
				
			||||
	if err != nil { | 
				
			||||
		return err | 
				
			||||
	} | 
				
			||||
	i, err := n.Int64() | 
				
			||||
	if err != nil { | 
				
			||||
		return err | 
				
			||||
	} | 
				
			||||
	*e = expirationTime(i) | 
				
			||||
	return nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
var brokenAuthHeaderProviders = []string{ | 
				
			||||
	"https://accounts.google.com/", | 
				
			||||
	"https://api.dropbox.com/", | 
				
			||||
	"https://api.dropboxapi.com/", | 
				
			||||
	"https://api.instagram.com/", | 
				
			||||
	"https://api.netatmo.net/", | 
				
			||||
	"https://api.odnoklassniki.ru/", | 
				
			||||
	"https://api.pushbullet.com/", | 
				
			||||
	"https://api.soundcloud.com/", | 
				
			||||
	"https://api.twitch.tv/", | 
				
			||||
	"https://app.box.com/", | 
				
			||||
	"https://connect.stripe.com/", | 
				
			||||
	"https://login.microsoftonline.com/", | 
				
			||||
	"https://login.salesforce.com/", | 
				
			||||
	"https://oauth.sandbox.trainingpeaks.com/", | 
				
			||||
	"https://oauth.trainingpeaks.com/", | 
				
			||||
	"https://oauth.vk.com/", | 
				
			||||
	"https://openapi.baidu.com/", | 
				
			||||
	"https://slack.com/", | 
				
			||||
	"https://test-sandbox.auth.corp.google.com", | 
				
			||||
	"https://test.salesforce.com/", | 
				
			||||
	"https://user.gini.net/", | 
				
			||||
	"https://www.douban.com/", | 
				
			||||
	"https://www.googleapis.com/", | 
				
			||||
	"https://www.linkedin.com/", | 
				
			||||
	"https://www.strava.com/oauth/", | 
				
			||||
	"https://www.wunderlist.com/oauth/", | 
				
			||||
	"https://api.patreon.com/", | 
				
			||||
	"https://sandbox.codeswholesale.com/oauth/token", | 
				
			||||
	"https://api.codeswholesale.com/oauth/token", | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func RegisterBrokenAuthHeaderProvider(tokenURL string) { | 
				
			||||
	brokenAuthHeaderProviders = append(brokenAuthHeaderProviders, tokenURL) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
 | 
				
			||||
// implements the OAuth2 spec correctly
 | 
				
			||||
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
 | 
				
			||||
// In summary:
 | 
				
			||||
// - Reddit only accepts client secret in the Authorization header
 | 
				
			||||
// - Dropbox accepts either it in URL param or Auth header, but not both.
 | 
				
			||||
// - Google only accepts URL param (not spec compliant?), not Auth header
 | 
				
			||||
// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
 | 
				
			||||
func providerAuthHeaderWorks(tokenURL string) bool { | 
				
			||||
	for _, s := range brokenAuthHeaderProviders { | 
				
			||||
		if strings.HasPrefix(tokenURL, s) { | 
				
			||||
			// Some sites fail to implement the OAuth2 spec fully.
 | 
				
			||||
			return false | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	// Assume the provider implements the spec properly
 | 
				
			||||
	// otherwise. We can add more exceptions as they're
 | 
				
			||||
	// discovered. We will _not_ be adding configurable hooks
 | 
				
			||||
	// to this package to let users select server bugs.
 | 
				
			||||
	return true | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values) (*Token, error) { | 
				
			||||
	hc, err := ContextClient(ctx) | 
				
			||||
	if err != nil { | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
	v.Set("client_id", clientID) | 
				
			||||
	bustedAuth := !providerAuthHeaderWorks(tokenURL) | 
				
			||||
	if bustedAuth && clientSecret != "" { | 
				
			||||
		v.Set("client_secret", clientSecret) | 
				
			||||
	} | 
				
			||||
	req, err := http.NewRequest("POST", tokenURL, strings.NewReader(v.Encode())) | 
				
			||||
	if err != nil { | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded") | 
				
			||||
	if !bustedAuth { | 
				
			||||
		req.SetBasicAuth(clientID, clientSecret) | 
				
			||||
	} | 
				
			||||
	r, err := hc.Do(req) | 
				
			||||
	if err != nil { | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
	defer r.Body.Close() | 
				
			||||
	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20)) | 
				
			||||
	if err != nil { | 
				
			||||
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) | 
				
			||||
	} | 
				
			||||
	if code := r.StatusCode; code < 200 || code > 299 { | 
				
			||||
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	var token *Token | 
				
			||||
	content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type")) | 
				
			||||
	switch content { | 
				
			||||
	case "application/x-www-form-urlencoded", "text/plain": | 
				
			||||
		vals, err := url.ParseQuery(string(body)) | 
				
			||||
		if err != nil { | 
				
			||||
			return nil, err | 
				
			||||
		} | 
				
			||||
		token = &Token{ | 
				
			||||
			AccessToken:  vals.Get("access_token"), | 
				
			||||
			TokenType:    vals.Get("token_type"), | 
				
			||||
			RefreshToken: vals.Get("refresh_token"), | 
				
			||||
			Raw:          vals, | 
				
			||||
		} | 
				
			||||
		e := vals.Get("expires_in") | 
				
			||||
		if e == "" { | 
				
			||||
			// TODO(jbd): Facebook's OAuth2 implementation is broken and
 | 
				
			||||
			// returns expires_in field in expires. Remove the fallback to expires,
 | 
				
			||||
			// when Facebook fixes their implementation.
 | 
				
			||||
			e = vals.Get("expires") | 
				
			||||
		} | 
				
			||||
		expires, _ := strconv.Atoi(e) | 
				
			||||
		if expires != 0 { | 
				
			||||
			token.Expiry = time.Now().Add(time.Duration(expires) * time.Second) | 
				
			||||
		} | 
				
			||||
	default: | 
				
			||||
		var tj tokenJSON | 
				
			||||
		if err = json.Unmarshal(body, &tj); err != nil { | 
				
			||||
			return nil, err | 
				
			||||
		} | 
				
			||||
		token = &Token{ | 
				
			||||
			AccessToken:  tj.AccessToken, | 
				
			||||
			TokenType:    tj.TokenType, | 
				
			||||
			RefreshToken: tj.RefreshToken, | 
				
			||||
			Expiry:       tj.expiry(), | 
				
			||||
			Raw:          make(map[string]interface{}), | 
				
			||||
		} | 
				
			||||
		json.Unmarshal(body, &token.Raw) // no error checks for optional fields
 | 
				
			||||
	} | 
				
			||||
	// Don't overwrite `RefreshToken` with an empty value
 | 
				
			||||
	// if this was a token refreshing request.
 | 
				
			||||
	if token.RefreshToken == "" { | 
				
			||||
		token.RefreshToken = v.Get("refresh_token") | 
				
			||||
	} | 
				
			||||
	return token, nil | 
				
			||||
} | 
				
			||||
@ -0,0 +1,69 @@ | 
				
			||||
// Copyright 2014 The Go Authors. All rights reserved.
 | 
				
			||||
// Use of this source code is governed by a BSD-style
 | 
				
			||||
// license that can be found in the LICENSE file.
 | 
				
			||||
 | 
				
			||||
// Package internal contains support packages for oauth2 package.
 | 
				
			||||
package internal | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"net/http" | 
				
			||||
 | 
				
			||||
	"golang.org/x/net/context" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// HTTPClient is the context key to use with golang.org/x/net/context's
 | 
				
			||||
// WithValue function to associate an *http.Client value with a context.
 | 
				
			||||
var HTTPClient ContextKey | 
				
			||||
 | 
				
			||||
// ContextKey is just an empty struct. It exists so HTTPClient can be
 | 
				
			||||
// an immutable public variable with a unique type. It's immutable
 | 
				
			||||
// because nobody else can create a ContextKey, being unexported.
 | 
				
			||||
type ContextKey struct{} | 
				
			||||
 | 
				
			||||
// ContextClientFunc is a func which tries to return an *http.Client
 | 
				
			||||
// given a Context value. If it returns an error, the search stops
 | 
				
			||||
// with that error.  If it returns (nil, nil), the search continues
 | 
				
			||||
// down the list of registered funcs.
 | 
				
			||||
type ContextClientFunc func(context.Context) (*http.Client, error) | 
				
			||||
 | 
				
			||||
var contextClientFuncs []ContextClientFunc | 
				
			||||
 | 
				
			||||
func RegisterContextClientFunc(fn ContextClientFunc) { | 
				
			||||
	contextClientFuncs = append(contextClientFuncs, fn) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func ContextClient(ctx context.Context) (*http.Client, error) { | 
				
			||||
	if ctx != nil { | 
				
			||||
		if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok { | 
				
			||||
			return hc, nil | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	for _, fn := range contextClientFuncs { | 
				
			||||
		c, err := fn(ctx) | 
				
			||||
		if err != nil { | 
				
			||||
			return nil, err | 
				
			||||
		} | 
				
			||||
		if c != nil { | 
				
			||||
			return c, nil | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	return http.DefaultClient, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func ContextTransport(ctx context.Context) http.RoundTripper { | 
				
			||||
	hc, err := ContextClient(ctx) | 
				
			||||
	// This is a rare error case (somebody using nil on App Engine).
 | 
				
			||||
	if err != nil { | 
				
			||||
		return ErrorTransport{err} | 
				
			||||
	} | 
				
			||||
	return hc.Transport | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// ErrorTransport returns the specified error on RoundTrip.
 | 
				
			||||
// This RoundTripper should be used in rare error cases where
 | 
				
			||||
// error handling can be postponed to response handling time.
 | 
				
			||||
type ErrorTransport struct{ Err error } | 
				
			||||
 | 
				
			||||
func (t ErrorTransport) RoundTrip(*http.Request) (*http.Response, error) { | 
				
			||||
	return nil, t.Err | 
				
			||||
} | 
				
			||||
@ -0,0 +1,341 @@ | 
				
			||||
// Copyright 2014 The Go Authors. All rights reserved.
 | 
				
			||||
// Use of this source code is governed by a BSD-style
 | 
				
			||||
// license that can be found in the LICENSE file.
 | 
				
			||||
 | 
				
			||||
// Package oauth2 provides support for making
 | 
				
			||||
// OAuth2 authorized and authenticated HTTP requests.
 | 
				
			||||
// It can additionally grant authorization with Bearer JWT.
 | 
				
			||||
package oauth2 // import "golang.org/x/oauth2"
 | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"bytes" | 
				
			||||
	"errors" | 
				
			||||
	"net/http" | 
				
			||||
	"net/url" | 
				
			||||
	"strings" | 
				
			||||
	"sync" | 
				
			||||
 | 
				
			||||
	"golang.org/x/net/context" | 
				
			||||
	"golang.org/x/oauth2/internal" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// NoContext is the default context you should supply if not using
 | 
				
			||||
// your own context.Context (see https://golang.org/x/net/context).
 | 
				
			||||
//
 | 
				
			||||
// Deprecated: Use context.Background() or context.TODO() instead.
 | 
				
			||||
var NoContext = context.TODO() | 
				
			||||
 | 
				
			||||
// RegisterBrokenAuthHeaderProvider registers an OAuth2 server
 | 
				
			||||
// identified by the tokenURL prefix as an OAuth2 implementation
 | 
				
			||||
// which doesn't support the HTTP Basic authentication
 | 
				
			||||
// scheme to authenticate with the authorization server.
 | 
				
			||||
// Once a server is registered, credentials (client_id and client_secret)
 | 
				
			||||
// will be passed as query parameters rather than being present
 | 
				
			||||
// in the Authorization header.
 | 
				
			||||
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
 | 
				
			||||
func RegisterBrokenAuthHeaderProvider(tokenURL string) { | 
				
			||||
	internal.RegisterBrokenAuthHeaderProvider(tokenURL) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Config describes a typical 3-legged OAuth2 flow, with both the
 | 
				
			||||
// client application information and the server's endpoint URLs.
 | 
				
			||||
// For the client credentials 2-legged OAuth2 flow, see the clientcredentials
 | 
				
			||||
// package (https://golang.org/x/oauth2/clientcredentials).
 | 
				
			||||
type Config struct { | 
				
			||||
	// ClientID is the application's ID.
 | 
				
			||||
	ClientID string | 
				
			||||
 | 
				
			||||
	// ClientSecret is the application's secret.
 | 
				
			||||
	ClientSecret string | 
				
			||||
 | 
				
			||||
	// Endpoint contains the resource server's token endpoint
 | 
				
			||||
	// URLs. These are constants specific to each server and are
 | 
				
			||||
	// often available via site-specific packages, such as
 | 
				
			||||
	// google.Endpoint or github.Endpoint.
 | 
				
			||||
	Endpoint Endpoint | 
				
			||||
 | 
				
			||||
	// RedirectURL is the URL to redirect users going through
 | 
				
			||||
	// the OAuth flow, after the resource owner's URLs.
 | 
				
			||||
	RedirectURL string | 
				
			||||
 | 
				
			||||
	// Scope specifies optional requested permissions.
 | 
				
			||||
	Scopes []string | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// A TokenSource is anything that can return a token.
 | 
				
			||||
type TokenSource interface { | 
				
			||||
	// Token returns a token or an error.
 | 
				
			||||
	// Token must be safe for concurrent use by multiple goroutines.
 | 
				
			||||
	// The returned Token must not be modified.
 | 
				
			||||
	Token() (*Token, error) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Endpoint contains the OAuth 2.0 provider's authorization and token
 | 
				
			||||
// endpoint URLs.
 | 
				
			||||
type Endpoint struct { | 
				
			||||
	AuthURL  string | 
				
			||||
	TokenURL string | 
				
			||||
} | 
				
			||||
 | 
				
			||||
var ( | 
				
			||||
	// AccessTypeOnline and AccessTypeOffline are options passed
 | 
				
			||||
	// to the Options.AuthCodeURL method. They modify the
 | 
				
			||||
	// "access_type" field that gets sent in the URL returned by
 | 
				
			||||
	// AuthCodeURL.
 | 
				
			||||
	//
 | 
				
			||||
	// Online is the default if neither is specified. If your
 | 
				
			||||
	// application needs to refresh access tokens when the user
 | 
				
			||||
	// is not present at the browser, then use offline. This will
 | 
				
			||||
	// result in your application obtaining a refresh token the
 | 
				
			||||
	// first time your application exchanges an authorization
 | 
				
			||||
	// code for a user.
 | 
				
			||||
	AccessTypeOnline  AuthCodeOption = SetAuthURLParam("access_type", "online") | 
				
			||||
	AccessTypeOffline AuthCodeOption = SetAuthURLParam("access_type", "offline") | 
				
			||||
 | 
				
			||||
	// ApprovalForce forces the users to view the consent dialog
 | 
				
			||||
	// and confirm the permissions request at the URL returned
 | 
				
			||||
	// from AuthCodeURL, even if they've already done so.
 | 
				
			||||
	ApprovalForce AuthCodeOption = SetAuthURLParam("approval_prompt", "force") | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// An AuthCodeOption is passed to Config.AuthCodeURL.
 | 
				
			||||
type AuthCodeOption interface { | 
				
			||||
	setValue(url.Values) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type setParam struct{ k, v string } | 
				
			||||
 | 
				
			||||
func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) } | 
				
			||||
 | 
				
			||||
// SetAuthURLParam builds an AuthCodeOption which passes key/value parameters
 | 
				
			||||
// to a provider's authorization endpoint.
 | 
				
			||||
func SetAuthURLParam(key, value string) AuthCodeOption { | 
				
			||||
	return setParam{key, value} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
 | 
				
			||||
// that asks for permissions for the required scopes explicitly.
 | 
				
			||||
//
 | 
				
			||||
// State is a token to protect the user from CSRF attacks. You must
 | 
				
			||||
// always provide a non-zero string and validate that it matches the
 | 
				
			||||
// the state query parameter on your redirect callback.
 | 
				
			||||
// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
 | 
				
			||||
//
 | 
				
			||||
// Opts may include AccessTypeOnline or AccessTypeOffline, as well
 | 
				
			||||
// as ApprovalForce.
 | 
				
			||||
func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string { | 
				
			||||
	var buf bytes.Buffer | 
				
			||||
	buf.WriteString(c.Endpoint.AuthURL) | 
				
			||||
	v := url.Values{ | 
				
			||||
		"response_type": {"code"}, | 
				
			||||
		"client_id":     {c.ClientID}, | 
				
			||||
		"redirect_uri":  internal.CondVal(c.RedirectURL), | 
				
			||||
		"scope":         internal.CondVal(strings.Join(c.Scopes, " ")), | 
				
			||||
		"state":         internal.CondVal(state), | 
				
			||||
	} | 
				
			||||
	for _, opt := range opts { | 
				
			||||
		opt.setValue(v) | 
				
			||||
	} | 
				
			||||
	if strings.Contains(c.Endpoint.AuthURL, "?") { | 
				
			||||
		buf.WriteByte('&') | 
				
			||||
	} else { | 
				
			||||
		buf.WriteByte('?') | 
				
			||||
	} | 
				
			||||
	buf.WriteString(v.Encode()) | 
				
			||||
	return buf.String() | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// PasswordCredentialsToken converts a resource owner username and password
 | 
				
			||||
// pair into a token.
 | 
				
			||||
//
 | 
				
			||||
// Per the RFC, this grant type should only be used "when there is a high
 | 
				
			||||
// degree of trust between the resource owner and the client (e.g., the client
 | 
				
			||||
// is part of the device operating system or a highly privileged application),
 | 
				
			||||
// and when other authorization grant types are not available."
 | 
				
			||||
// See https://tools.ietf.org/html/rfc6749#section-4.3 for more info.
 | 
				
			||||
//
 | 
				
			||||
// The HTTP client to use is derived from the context.
 | 
				
			||||
// If nil, http.DefaultClient is used.
 | 
				
			||||
func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) { | 
				
			||||
	return retrieveToken(ctx, c, url.Values{ | 
				
			||||
		"grant_type": {"password"}, | 
				
			||||
		"username":   {username}, | 
				
			||||
		"password":   {password}, | 
				
			||||
		"scope":      internal.CondVal(strings.Join(c.Scopes, " ")), | 
				
			||||
	}) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Exchange converts an authorization code into a token.
 | 
				
			||||
//
 | 
				
			||||
// It is used after a resource provider redirects the user back
 | 
				
			||||
// to the Redirect URI (the URL obtained from AuthCodeURL).
 | 
				
			||||
//
 | 
				
			||||
// The HTTP client to use is derived from the context.
 | 
				
			||||
// If a client is not provided via the context, http.DefaultClient is used.
 | 
				
			||||
//
 | 
				
			||||
// The code will be in the *http.Request.FormValue("code"). Before
 | 
				
			||||
// calling Exchange, be sure to validate FormValue("state").
 | 
				
			||||
func (c *Config) Exchange(ctx context.Context, code string) (*Token, error) { | 
				
			||||
	return retrieveToken(ctx, c, url.Values{ | 
				
			||||
		"grant_type":   {"authorization_code"}, | 
				
			||||
		"code":         {code}, | 
				
			||||
		"redirect_uri": internal.CondVal(c.RedirectURL), | 
				
			||||
		"scope":        internal.CondVal(strings.Join(c.Scopes, " ")), | 
				
			||||
	}) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Client returns an HTTP client using the provided token.
 | 
				
			||||
// The token will auto-refresh as necessary. The underlying
 | 
				
			||||
// HTTP transport will be obtained using the provided context.
 | 
				
			||||
// The returned client and its Transport should not be modified.
 | 
				
			||||
func (c *Config) Client(ctx context.Context, t *Token) *http.Client { | 
				
			||||
	return NewClient(ctx, c.TokenSource(ctx, t)) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// TokenSource returns a TokenSource that returns t until t expires,
 | 
				
			||||
// automatically refreshing it as necessary using the provided context.
 | 
				
			||||
//
 | 
				
			||||
// Most users will use Config.Client instead.
 | 
				
			||||
func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource { | 
				
			||||
	tkr := &tokenRefresher{ | 
				
			||||
		ctx:  ctx, | 
				
			||||
		conf: c, | 
				
			||||
	} | 
				
			||||
	if t != nil { | 
				
			||||
		tkr.refreshToken = t.RefreshToken | 
				
			||||
	} | 
				
			||||
	return &reuseTokenSource{ | 
				
			||||
		t:   t, | 
				
			||||
		new: tkr, | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token"
 | 
				
			||||
// HTTP requests to renew a token using a RefreshToken.
 | 
				
			||||
type tokenRefresher struct { | 
				
			||||
	ctx          context.Context // used to get HTTP requests
 | 
				
			||||
	conf         *Config | 
				
			||||
	refreshToken string | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// WARNING: Token is not safe for concurrent access, as it
 | 
				
			||||
// updates the tokenRefresher's refreshToken field.
 | 
				
			||||
// Within this package, it is used by reuseTokenSource which
 | 
				
			||||
// synchronizes calls to this method with its own mutex.
 | 
				
			||||
func (tf *tokenRefresher) Token() (*Token, error) { | 
				
			||||
	if tf.refreshToken == "" { | 
				
			||||
		return nil, errors.New("oauth2: token expired and refresh token is not set") | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	tk, err := retrieveToken(tf.ctx, tf.conf, url.Values{ | 
				
			||||
		"grant_type":    {"refresh_token"}, | 
				
			||||
		"refresh_token": {tf.refreshToken}, | 
				
			||||
	}) | 
				
			||||
 | 
				
			||||
	if err != nil { | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
	if tf.refreshToken != tk.RefreshToken { | 
				
			||||
		tf.refreshToken = tk.RefreshToken | 
				
			||||
	} | 
				
			||||
	return tk, err | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// reuseTokenSource is a TokenSource that holds a single token in memory
 | 
				
			||||
// and validates its expiry before each call to retrieve it with
 | 
				
			||||
// Token. If it's expired, it will be auto-refreshed using the
 | 
				
			||||
// new TokenSource.
 | 
				
			||||
type reuseTokenSource struct { | 
				
			||||
	new TokenSource // called when t is expired.
 | 
				
			||||
 | 
				
			||||
	mu sync.Mutex // guards t
 | 
				
			||||
	t  *Token | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Token returns the current token if it's still valid, else will
 | 
				
			||||
// refresh the current token (using r.Context for HTTP client
 | 
				
			||||
// information) and return the new one.
 | 
				
			||||
func (s *reuseTokenSource) Token() (*Token, error) { | 
				
			||||
	s.mu.Lock() | 
				
			||||
	defer s.mu.Unlock() | 
				
			||||
	if s.t.Valid() { | 
				
			||||
		return s.t, nil | 
				
			||||
	} | 
				
			||||
	t, err := s.new.Token() | 
				
			||||
	if err != nil { | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
	s.t = t | 
				
			||||
	return t, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// StaticTokenSource returns a TokenSource that always returns the same token.
 | 
				
			||||
// Because the provided token t is never refreshed, StaticTokenSource is only
 | 
				
			||||
// useful for tokens that never expire.
 | 
				
			||||
func StaticTokenSource(t *Token) TokenSource { | 
				
			||||
	return staticTokenSource{t} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// staticTokenSource is a TokenSource that always returns the same Token.
 | 
				
			||||
type staticTokenSource struct { | 
				
			||||
	t *Token | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s staticTokenSource) Token() (*Token, error) { | 
				
			||||
	return s.t, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// HTTPClient is the context key to use with golang.org/x/net/context's
 | 
				
			||||
// WithValue function to associate an *http.Client value with a context.
 | 
				
			||||
var HTTPClient internal.ContextKey | 
				
			||||
 | 
				
			||||
// NewClient creates an *http.Client from a Context and TokenSource.
 | 
				
			||||
// The returned client is not valid beyond the lifetime of the context.
 | 
				
			||||
//
 | 
				
			||||
// As a special case, if src is nil, a non-OAuth2 client is returned
 | 
				
			||||
// using the provided context. This exists to support related OAuth2
 | 
				
			||||
// packages.
 | 
				
			||||
func NewClient(ctx context.Context, src TokenSource) *http.Client { | 
				
			||||
	if src == nil { | 
				
			||||
		c, err := internal.ContextClient(ctx) | 
				
			||||
		if err != nil { | 
				
			||||
			return &http.Client{Transport: internal.ErrorTransport{Err: err}} | 
				
			||||
		} | 
				
			||||
		return c | 
				
			||||
	} | 
				
			||||
	return &http.Client{ | 
				
			||||
		Transport: &Transport{ | 
				
			||||
			Base:   internal.ContextTransport(ctx), | 
				
			||||
			Source: ReuseTokenSource(nil, src), | 
				
			||||
		}, | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// ReuseTokenSource returns a TokenSource which repeatedly returns the
 | 
				
			||||
// same token as long as it's valid, starting with t.
 | 
				
			||||
// When its cached token is invalid, a new token is obtained from src.
 | 
				
			||||
//
 | 
				
			||||
// ReuseTokenSource is typically used to reuse tokens from a cache
 | 
				
			||||
// (such as a file on disk) between runs of a program, rather than
 | 
				
			||||
// obtaining new tokens unnecessarily.
 | 
				
			||||
//
 | 
				
			||||
// The initial token t may be nil, in which case the TokenSource is
 | 
				
			||||
// wrapped in a caching version if it isn't one already. This also
 | 
				
			||||
// means it's always safe to wrap ReuseTokenSource around any other
 | 
				
			||||
// TokenSource without adverse effects.
 | 
				
			||||
func ReuseTokenSource(t *Token, src TokenSource) TokenSource { | 
				
			||||
	// Don't wrap a reuseTokenSource in itself. That would work,
 | 
				
			||||
	// but cause an unnecessary number of mutex operations.
 | 
				
			||||
	// Just build the equivalent one.
 | 
				
			||||
	if rt, ok := src.(*reuseTokenSource); ok { | 
				
			||||
		if t == nil { | 
				
			||||
			// Just use it directly.
 | 
				
			||||
			return rt | 
				
			||||
		} | 
				
			||||
		src = rt.new | 
				
			||||
	} | 
				
			||||
	return &reuseTokenSource{ | 
				
			||||
		t:   t, | 
				
			||||
		new: src, | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,158 @@ | 
				
			||||
// Copyright 2014 The Go Authors. All rights reserved.
 | 
				
			||||
// Use of this source code is governed by a BSD-style
 | 
				
			||||
// license that can be found in the LICENSE file.
 | 
				
			||||
 | 
				
			||||
package oauth2 | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"net/http" | 
				
			||||
	"net/url" | 
				
			||||
	"strconv" | 
				
			||||
	"strings" | 
				
			||||
	"time" | 
				
			||||
 | 
				
			||||
	"golang.org/x/net/context" | 
				
			||||
	"golang.org/x/oauth2/internal" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// expiryDelta determines how earlier a token should be considered
 | 
				
			||||
// expired than its actual expiration time. It is used to avoid late
 | 
				
			||||
// expirations due to client-server time mismatches.
 | 
				
			||||
const expiryDelta = 10 * time.Second | 
				
			||||
 | 
				
			||||
// Token represents the crendentials used to authorize
 | 
				
			||||
// the requests to access protected resources on the OAuth 2.0
 | 
				
			||||
// provider's backend.
 | 
				
			||||
//
 | 
				
			||||
// Most users of this package should not access fields of Token
 | 
				
			||||
// directly. They're exported mostly for use by related packages
 | 
				
			||||
// implementing derivative OAuth2 flows.
 | 
				
			||||
type Token struct { | 
				
			||||
	// AccessToken is the token that authorizes and authenticates
 | 
				
			||||
	// the requests.
 | 
				
			||||
	AccessToken string `json:"access_token"` | 
				
			||||
 | 
				
			||||
	// TokenType is the type of token.
 | 
				
			||||
	// The Type method returns either this or "Bearer", the default.
 | 
				
			||||
	TokenType string `json:"token_type,omitempty"` | 
				
			||||
 | 
				
			||||
	// RefreshToken is a token that's used by the application
 | 
				
			||||
	// (as opposed to the user) to refresh the access token
 | 
				
			||||
	// if it expires.
 | 
				
			||||
	RefreshToken string `json:"refresh_token,omitempty"` | 
				
			||||
 | 
				
			||||
	// Expiry is the optional expiration time of the access token.
 | 
				
			||||
	//
 | 
				
			||||
	// If zero, TokenSource implementations will reuse the same
 | 
				
			||||
	// token forever and RefreshToken or equivalent
 | 
				
			||||
	// mechanisms for that TokenSource will not be used.
 | 
				
			||||
	Expiry time.Time `json:"expiry,omitempty"` | 
				
			||||
 | 
				
			||||
	// raw optionally contains extra metadata from the server
 | 
				
			||||
	// when updating a token.
 | 
				
			||||
	raw interface{} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Type returns t.TokenType if non-empty, else "Bearer".
 | 
				
			||||
func (t *Token) Type() string { | 
				
			||||
	if strings.EqualFold(t.TokenType, "bearer") { | 
				
			||||
		return "Bearer" | 
				
			||||
	} | 
				
			||||
	if strings.EqualFold(t.TokenType, "mac") { | 
				
			||||
		return "MAC" | 
				
			||||
	} | 
				
			||||
	if strings.EqualFold(t.TokenType, "basic") { | 
				
			||||
		return "Basic" | 
				
			||||
	} | 
				
			||||
	if t.TokenType != "" { | 
				
			||||
		return t.TokenType | 
				
			||||
	} | 
				
			||||
	return "Bearer" | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// SetAuthHeader sets the Authorization header to r using the access
 | 
				
			||||
// token in t.
 | 
				
			||||
//
 | 
				
			||||
// This method is unnecessary when using Transport or an HTTP Client
 | 
				
			||||
// returned by this package.
 | 
				
			||||
func (t *Token) SetAuthHeader(r *http.Request) { | 
				
			||||
	r.Header.Set("Authorization", t.Type()+" "+t.AccessToken) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// WithExtra returns a new Token that's a clone of t, but using the
 | 
				
			||||
// provided raw extra map. This is only intended for use by packages
 | 
				
			||||
// implementing derivative OAuth2 flows.
 | 
				
			||||
func (t *Token) WithExtra(extra interface{}) *Token { | 
				
			||||
	t2 := new(Token) | 
				
			||||
	*t2 = *t | 
				
			||||
	t2.raw = extra | 
				
			||||
	return t2 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Extra returns an extra field.
 | 
				
			||||
// Extra fields are key-value pairs returned by the server as a
 | 
				
			||||
// part of the token retrieval response.
 | 
				
			||||
func (t *Token) Extra(key string) interface{} { | 
				
			||||
	if raw, ok := t.raw.(map[string]interface{}); ok { | 
				
			||||
		return raw[key] | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	vals, ok := t.raw.(url.Values) | 
				
			||||
	if !ok { | 
				
			||||
		return nil | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	v := vals.Get(key) | 
				
			||||
	switch s := strings.TrimSpace(v); strings.Count(s, ".") { | 
				
			||||
	case 0: // Contains no "."; try to parse as int
 | 
				
			||||
		if i, err := strconv.ParseInt(s, 10, 64); err == nil { | 
				
			||||
			return i | 
				
			||||
		} | 
				
			||||
	case 1: // Contains a single "."; try to parse as float
 | 
				
			||||
		if f, err := strconv.ParseFloat(s, 64); err == nil { | 
				
			||||
			return f | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return v | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// expired reports whether the token is expired.
 | 
				
			||||
// t must be non-nil.
 | 
				
			||||
func (t *Token) expired() bool { | 
				
			||||
	if t.Expiry.IsZero() { | 
				
			||||
		return false | 
				
			||||
	} | 
				
			||||
	return t.Expiry.Add(-expiryDelta).Before(time.Now()) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Valid reports whether t is non-nil, has an AccessToken, and is not expired.
 | 
				
			||||
func (t *Token) Valid() bool { | 
				
			||||
	return t != nil && t.AccessToken != "" && !t.expired() | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// tokenFromInternal maps an *internal.Token struct into
 | 
				
			||||
// a *Token struct.
 | 
				
			||||
func tokenFromInternal(t *internal.Token) *Token { | 
				
			||||
	if t == nil { | 
				
			||||
		return nil | 
				
			||||
	} | 
				
			||||
	return &Token{ | 
				
			||||
		AccessToken:  t.AccessToken, | 
				
			||||
		TokenType:    t.TokenType, | 
				
			||||
		RefreshToken: t.RefreshToken, | 
				
			||||
		Expiry:       t.Expiry, | 
				
			||||
		raw:          t.Raw, | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// retrieveToken takes a *Config and uses that to retrieve an *internal.Token.
 | 
				
			||||
// This token is then mapped from *internal.Token into an *oauth2.Token which is returned along
 | 
				
			||||
// with an error..
 | 
				
			||||
func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) { | 
				
			||||
	tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v) | 
				
			||||
	if err != nil { | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
	return tokenFromInternal(tk), nil | 
				
			||||
} | 
				
			||||
@ -0,0 +1,132 @@ | 
				
			||||
// Copyright 2014 The Go Authors. All rights reserved.
 | 
				
			||||
// Use of this source code is governed by a BSD-style
 | 
				
			||||
// license that can be found in the LICENSE file.
 | 
				
			||||
 | 
				
			||||
package oauth2 | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"errors" | 
				
			||||
	"io" | 
				
			||||
	"net/http" | 
				
			||||
	"sync" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// Transport is an http.RoundTripper that makes OAuth 2.0 HTTP requests,
 | 
				
			||||
// wrapping a base RoundTripper and adding an Authorization header
 | 
				
			||||
// with a token from the supplied Sources.
 | 
				
			||||
//
 | 
				
			||||
// Transport is a low-level mechanism. Most code will use the
 | 
				
			||||
// higher-level Config.Client method instead.
 | 
				
			||||
type Transport struct { | 
				
			||||
	// Source supplies the token to add to outgoing requests'
 | 
				
			||||
	// Authorization headers.
 | 
				
			||||
	Source TokenSource | 
				
			||||
 | 
				
			||||
	// Base is the base RoundTripper used to make HTTP requests.
 | 
				
			||||
	// If nil, http.DefaultTransport is used.
 | 
				
			||||
	Base http.RoundTripper | 
				
			||||
 | 
				
			||||
	mu     sync.Mutex                      // guards modReq
 | 
				
			||||
	modReq map[*http.Request]*http.Request // original -> modified
 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// RoundTrip authorizes and authenticates the request with an
 | 
				
			||||
// access token. If no token exists or token is expired,
 | 
				
			||||
// tries to refresh/fetch a new token.
 | 
				
			||||
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { | 
				
			||||
	if t.Source == nil { | 
				
			||||
		return nil, errors.New("oauth2: Transport's Source is nil") | 
				
			||||
	} | 
				
			||||
	token, err := t.Source.Token() | 
				
			||||
	if err != nil { | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	req2 := cloneRequest(req) // per RoundTripper contract
 | 
				
			||||
	token.SetAuthHeader(req2) | 
				
			||||
	t.setModReq(req, req2) | 
				
			||||
	res, err := t.base().RoundTrip(req2) | 
				
			||||
	if err != nil { | 
				
			||||
		t.setModReq(req, nil) | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
	res.Body = &onEOFReader{ | 
				
			||||
		rc: res.Body, | 
				
			||||
		fn: func() { t.setModReq(req, nil) }, | 
				
			||||
	} | 
				
			||||
	return res, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// CancelRequest cancels an in-flight request by closing its connection.
 | 
				
			||||
func (t *Transport) CancelRequest(req *http.Request) { | 
				
			||||
	type canceler interface { | 
				
			||||
		CancelRequest(*http.Request) | 
				
			||||
	} | 
				
			||||
	if cr, ok := t.base().(canceler); ok { | 
				
			||||
		t.mu.Lock() | 
				
			||||
		modReq := t.modReq[req] | 
				
			||||
		delete(t.modReq, req) | 
				
			||||
		t.mu.Unlock() | 
				
			||||
		cr.CancelRequest(modReq) | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (t *Transport) base() http.RoundTripper { | 
				
			||||
	if t.Base != nil { | 
				
			||||
		return t.Base | 
				
			||||
	} | 
				
			||||
	return http.DefaultTransport | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (t *Transport) setModReq(orig, mod *http.Request) { | 
				
			||||
	t.mu.Lock() | 
				
			||||
	defer t.mu.Unlock() | 
				
			||||
	if t.modReq == nil { | 
				
			||||
		t.modReq = make(map[*http.Request]*http.Request) | 
				
			||||
	} | 
				
			||||
	if mod == nil { | 
				
			||||
		delete(t.modReq, orig) | 
				
			||||
	} else { | 
				
			||||
		t.modReq[orig] = mod | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// cloneRequest returns a clone of the provided *http.Request.
 | 
				
			||||
// The clone is a shallow copy of the struct and its Header map.
 | 
				
			||||
func cloneRequest(r *http.Request) *http.Request { | 
				
			||||
	// shallow copy of the struct
 | 
				
			||||
	r2 := new(http.Request) | 
				
			||||
	*r2 = *r | 
				
			||||
	// deep copy of the Header
 | 
				
			||||
	r2.Header = make(http.Header, len(r.Header)) | 
				
			||||
	for k, s := range r.Header { | 
				
			||||
		r2.Header[k] = append([]string(nil), s...) | 
				
			||||
	} | 
				
			||||
	return r2 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type onEOFReader struct { | 
				
			||||
	rc io.ReadCloser | 
				
			||||
	fn func() | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (r *onEOFReader) Read(p []byte) (n int, err error) { | 
				
			||||
	n, err = r.rc.Read(p) | 
				
			||||
	if err == io.EOF { | 
				
			||||
		r.runFunc() | 
				
			||||
	} | 
				
			||||
	return | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (r *onEOFReader) Close() error { | 
				
			||||
	err := r.rc.Close() | 
				
			||||
	r.runFunc() | 
				
			||||
	return err | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (r *onEOFReader) runFunc() { | 
				
			||||
	if fn := r.fn; fn != nil { | 
				
			||||
		fn() | 
				
			||||
		r.fn = nil | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue