* 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)
		
			
				
	
	
		
			300 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			300 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| 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.
 |