Use vendored dependencies
[bloat] / vendor / github.com / gorilla / mux / README.md
1 # gorilla/mux
2
3 [![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux)
4 [![Build Status](https://travis-ci.org/gorilla/mux.svg?branch=master)](https://travis-ci.org/gorilla/mux)
5 [![CircleCI](https://circleci.com/gh/gorilla/mux.svg?style=svg)](https://circleci.com/gh/gorilla/mux)
6 [![Sourcegraph](https://sourcegraph.com/github.com/gorilla/mux/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/mux?badge)
7
8 ![Gorilla Logo](http://www.gorillatoolkit.org/static/images/gorilla-icon-64.png)
9
10 https://www.gorillatoolkit.org/pkg/mux
11
12 Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to
13 their respective handler.
14
15 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:
16
17 * It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`.
18 * Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers.
19 * URL hosts, paths and query values can have variables with an optional regular expression.
20 * Registered URLs can be built, or "reversed", which helps maintaining references to resources.
21 * 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.
22
23 ---
24
25 * [Install](#install)
26 * [Examples](#examples)
27 * [Matching Routes](#matching-routes)
28 * [Static Files](#static-files)
29 * [Registered URLs](#registered-urls)
30 * [Walking Routes](#walking-routes)
31 * [Graceful Shutdown](#graceful-shutdown)
32 * [Middleware](#middleware)
33 * [Handling CORS Requests](#handling-cors-requests)
34 * [Testing Handlers](#testing-handlers)
35 * [Full Example](#full-example)
36
37 ---
38
39 ## Install
40
41 With a [correctly configured](https://golang.org/doc/install#testing) Go toolchain:
42
43 ```sh
44 go get -u github.com/gorilla/mux
45 ```
46
47 ## Examples
48
49 Let's start registering a couple of URL paths and handlers:
50
51 ```go
52 func main() {
53     r := mux.NewRouter()
54     r.HandleFunc("/", HomeHandler)
55     r.HandleFunc("/products", ProductsHandler)
56     r.HandleFunc("/articles", ArticlesHandler)
57     http.Handle("/", r)
58 }
59 ```
60
61 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.
62
63 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:
64
65 ```go
66 r := mux.NewRouter()
67 r.HandleFunc("/products/{key}", ProductHandler)
68 r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
69 r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
70 ```
71
72 The names are used to create a map of route variables which can be retrieved calling `mux.Vars()`:
73
74 ```go
75 func ArticlesCategoryHandler(w http.ResponseWriter, r *http.Request) {
76     vars := mux.Vars(r)
77     w.WriteHeader(http.StatusOK)
78     fmt.Fprintf(w, "Category: %v\n", vars["category"])
79 }
80 ```
81
82 And this is all you need to know about the basic usage. More advanced options are explained below.
83
84 ### Matching Routes
85
86 Routes can also be restricted to a domain or subdomain. Just define a host pattern to be matched. They can also have variables:
87
88 ```go
89 r := mux.NewRouter()
90 // Only matches if domain is "www.example.com".
91 r.Host("www.example.com")
92 // Matches a dynamic subdomain.
93 r.Host("{subdomain:[a-z]+}.example.com")
94 ```
95
96 There are several other matchers that can be added. To match path prefixes:
97
98 ```go
99 r.PathPrefix("/products/")
100 ```
101
102 ...or HTTP methods:
103
104 ```go
105 r.Methods("GET", "POST")
106 ```
107
108 ...or URL schemes:
109
110 ```go
111 r.Schemes("https")
112 ```
113
114 ...or header values:
115
116 ```go
117 r.Headers("X-Requested-With", "XMLHttpRequest")
118 ```
119
120 ...or query values:
121
122 ```go
123 r.Queries("key", "value")
124 ```
125
126 ...or to use a custom matcher function:
127
128 ```go
129 r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
130     return r.ProtoMajor == 0
131 })
132 ```
133
134 ...and finally, it is possible to combine several matchers in a single route:
135
136 ```go
137 r.HandleFunc("/products", ProductsHandler).
138   Host("www.example.com").
139   Methods("GET").
140   Schemes("http")
141 ```
142
143 Routes are tested in the order they were added to the router. If two routes match, the first one wins:
144
145 ```go
146 r := mux.NewRouter()
147 r.HandleFunc("/specific", specificHandler)
148 r.PathPrefix("/").Handler(catchAllHandler)
149 ```
150
151 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".
152
153 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:
154
155 ```go
156 r := mux.NewRouter()
157 s := r.Host("www.example.com").Subrouter()
158 ```
159
160 Then register routes in the subrouter:
161
162 ```go
163 s.HandleFunc("/products/", ProductsHandler)
164 s.HandleFunc("/products/{key}", ProductHandler)
165 s.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
166 ```
167
168 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.
169
170 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.
171
172 There's one more thing about subroutes. When a subrouter has a path prefix, the inner routes use it as base for their paths:
173
174 ```go
175 r := mux.NewRouter()
176 s := r.PathPrefix("/products").Subrouter()
177 // "/products/"
178 s.HandleFunc("/", ProductsHandler)
179 // "/products/{key}/"
180 s.HandleFunc("/{key}/", ProductHandler)
181 // "/products/{key}/details"
182 s.HandleFunc("/{key}/details", ProductDetailsHandler)
183 ```
184
185
186 ### Static Files
187
188 Note that the path provided to `PathPrefix()` represents a "wildcard": calling
189 `PathPrefix("/static/").Handler(...)` means that the handler will be passed any
190 request that matches "/static/\*". This makes it easy to serve static files with mux:
191
192 ```go
193 func main() {
194     var dir string
195
196     flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir")
197     flag.Parse()
198     r := mux.NewRouter()
199
200     // This will serve files under http://localhost:8000/static/<filename>
201     r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir))))
202
203     srv := &http.Server{
204         Handler:      r,
205         Addr:         "127.0.0.1:8000",
206         // Good practice: enforce timeouts for servers you create!
207         WriteTimeout: 15 * time.Second,
208         ReadTimeout:  15 * time.Second,
209     }
210
211     log.Fatal(srv.ListenAndServe())
212 }
213 ```
214
215 ### Registered URLs
216
217 Now let's see how to build registered URLs.
218
219 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:
220
221 ```go
222 r := mux.NewRouter()
223 r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
224   Name("article")
225 ```
226
227 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:
228
229 ```go
230 url, err := r.Get("article").URL("category", "technology", "id", "42")
231 ```
232
233 ...and the result will be a `url.URL` with the following path:
234
235 ```
236 "/articles/technology/42"
237 ```
238
239 This also works for host and query value variables:
240
241 ```go
242 r := mux.NewRouter()
243 r.Host("{subdomain}.example.com").
244   Path("/articles/{category}/{id:[0-9]+}").
245   Queries("filter", "{filter}").
246   HandlerFunc(ArticleHandler).
247   Name("article")
248
249 // url.String() will be "http://news.example.com/articles/technology/42?filter=gorilla"
250 url, err := r.Get("article").URL("subdomain", "news",
251                                  "category", "technology",
252                                  "id", "42",
253                                  "filter", "gorilla")
254 ```
255
256 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.
257
258 Regex support also exists for matching Headers within a route. For example, we could do:
259
260 ```go
261 r.HeadersRegexp("Content-Type", "application/(text|json)")
262 ```
263
264 ...and the route will match both requests with a Content-Type of `application/json` as well as `application/text`
265
266 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:
267
268 ```go
269 // "http://news.example.com/"
270 host, err := r.Get("article").URLHost("subdomain", "news")
271
272 // "/articles/technology/42"
273 path, err := r.Get("article").URLPath("category", "technology", "id", "42")
274 ```
275
276 And if you use subrouters, host and path defined separately can be built as well:
277
278 ```go
279 r := mux.NewRouter()
280 s := r.Host("{subdomain}.example.com").Subrouter()
281 s.Path("/articles/{category}/{id:[0-9]+}").
282   HandlerFunc(ArticleHandler).
283   Name("article")
284
285 // "http://news.example.com/articles/technology/42"
286 url, err := r.Get("article").URL("subdomain", "news",
287                                  "category", "technology",
288                                  "id", "42")
289 ```
290
291 ### Walking Routes
292
293 The `Walk` function on `mux.Router` can be used to visit all of the routes that are registered on a router. For example,
294 the following prints all of the registered routes:
295
296 ```go
297 package main
298
299 import (
300         "fmt"
301         "net/http"
302         "strings"
303
304         "github.com/gorilla/mux"
305 )
306
307 func handler(w http.ResponseWriter, r *http.Request) {
308         return
309 }
310
311 func main() {
312         r := mux.NewRouter()
313         r.HandleFunc("/", handler)
314         r.HandleFunc("/products", handler).Methods("POST")
315         r.HandleFunc("/articles", handler).Methods("GET")
316         r.HandleFunc("/articles/{id}", handler).Methods("GET", "PUT")
317         r.HandleFunc("/authors", handler).Queries("surname", "{surname}")
318         err := r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
319                 pathTemplate, err := route.GetPathTemplate()
320                 if err == nil {
321                         fmt.Println("ROUTE:", pathTemplate)
322                 }
323                 pathRegexp, err := route.GetPathRegexp()
324                 if err == nil {
325                         fmt.Println("Path regexp:", pathRegexp)
326                 }
327                 queriesTemplates, err := route.GetQueriesTemplates()
328                 if err == nil {
329                         fmt.Println("Queries templates:", strings.Join(queriesTemplates, ","))
330                 }
331                 queriesRegexps, err := route.GetQueriesRegexp()
332                 if err == nil {
333                         fmt.Println("Queries regexps:", strings.Join(queriesRegexps, ","))
334                 }
335                 methods, err := route.GetMethods()
336                 if err == nil {
337                         fmt.Println("Methods:", strings.Join(methods, ","))
338                 }
339                 fmt.Println()
340                 return nil
341         })
342
343         if err != nil {
344                 fmt.Println(err)
345         }
346
347         http.Handle("/", r)
348 }
349 ```
350
351 ### Graceful Shutdown
352
353 Go 1.8 introduced the ability to [gracefully shutdown](https://golang.org/doc/go1.8#http_shutdown) a `*http.Server`. Here's how to do that alongside `mux`:
354
355 ```go
356 package main
357
358 import (
359     "context"
360     "flag"
361     "log"
362     "net/http"
363     "os"
364     "os/signal"
365     "time"
366
367     "github.com/gorilla/mux"
368 )
369
370 func main() {
371     var wait time.Duration
372     flag.DurationVar(&wait, "graceful-timeout", time.Second * 15, "the duration for which the server gracefully wait for existing connections to finish - e.g. 15s or 1m")
373     flag.Parse()
374
375     r := mux.NewRouter()
376     // Add your routes as needed
377
378     srv := &http.Server{
379         Addr:         "0.0.0.0:8080",
380         // Good practice to set timeouts to avoid Slowloris attacks.
381         WriteTimeout: time.Second * 15,
382         ReadTimeout:  time.Second * 15,
383         IdleTimeout:  time.Second * 60,
384         Handler: r, // Pass our instance of gorilla/mux in.
385     }
386
387     // Run our server in a goroutine so that it doesn't block.
388     go func() {
389         if err := srv.ListenAndServe(); err != nil {
390             log.Println(err)
391         }
392     }()
393
394     c := make(chan os.Signal, 1)
395     // We'll accept graceful shutdowns when quit via SIGINT (Ctrl+C)
396     // SIGKILL, SIGQUIT or SIGTERM (Ctrl+/) will not be caught.
397     signal.Notify(c, os.Interrupt)
398
399     // Block until we receive our signal.
400     <-c
401
402     // Create a deadline to wait for.
403     ctx, cancel := context.WithTimeout(context.Background(), wait)
404     defer cancel()
405     // Doesn't block if no connections, but will otherwise wait
406     // until the timeout deadline.
407     srv.Shutdown(ctx)
408     // Optionally, you could run srv.Shutdown in a goroutine and block on
409     // <-ctx.Done() if your application should wait for other services
410     // to finalize based on context cancellation.
411     log.Println("shutting down")
412     os.Exit(0)
413 }
414 ```
415
416 ### Middleware
417
418 Mux supports the addition of middlewares to a [Router](https://godoc.org/github.com/gorilla/mux#Router), which are executed in the order they are added if a match is found, including its subrouters.
419 Middlewares are (typically) small pieces of code which take one request, do something with it, and pass it down to another middleware or the final handler. Some common use cases for middleware are request logging, header manipulation, or `ResponseWriter` hijacking.
420
421 Mux middlewares are defined using the de facto standard type:
422
423 ```go
424 type MiddlewareFunc func(http.Handler) http.Handler
425 ```
426
427 Typically, the returned handler is a closure which does something with the http.ResponseWriter and http.Request passed to it, and then calls the handler passed as parameter to the MiddlewareFunc. This takes advantage of closures being able access variables from the context where they are created, while retaining the signature enforced by the receivers.
428
429 A very basic middleware which logs the URI of the request being handled could be written as:
430
431 ```go
432 func loggingMiddleware(next http.Handler) http.Handler {
433     return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
434         // Do stuff here
435         log.Println(r.RequestURI)
436         // Call the next handler, which can be another middleware in the chain, or the final handler.
437         next.ServeHTTP(w, r)
438     })
439 }
440 ```
441
442 Middlewares can be added to a router using `Router.Use()`:
443
444 ```go
445 r := mux.NewRouter()
446 r.HandleFunc("/", handler)
447 r.Use(loggingMiddleware)
448 ```
449
450 A more complex authentication middleware, which maps session token to users, could be written as:
451
452 ```go
453 // Define our struct
454 type authenticationMiddleware struct {
455         tokenUsers map[string]string
456 }
457
458 // Initialize it somewhere
459 func (amw *authenticationMiddleware) Populate() {
460         amw.tokenUsers["00000000"] = "user0"
461         amw.tokenUsers["aaaaaaaa"] = "userA"
462         amw.tokenUsers["05f717e5"] = "randomUser"
463         amw.tokenUsers["deadbeef"] = "user0"
464 }
465
466 // Middleware function, which will be called for each request
467 func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler {
468     return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
469         token := r.Header.Get("X-Session-Token")
470
471         if user, found := amw.tokenUsers[token]; found {
472                 // We found the token in our map
473                 log.Printf("Authenticated user %s\n", user)
474                 // Pass down the request to the next middleware (or final handler)
475                 next.ServeHTTP(w, r)
476         } else {
477                 // Write an error and stop the handler chain
478                 http.Error(w, "Forbidden", http.StatusForbidden)
479         }
480     })
481 }
482 ```
483
484 ```go
485 r := mux.NewRouter()
486 r.HandleFunc("/", handler)
487
488 amw := authenticationMiddleware{}
489 amw.Populate()
490
491 r.Use(amw.Middleware)
492 ```
493
494 Note: The handler chain will be stopped if your middleware doesn't call `next.ServeHTTP()` with the corresponding parameters. This can be used to abort a request if the middleware writer wants to. Middlewares _should_ write to `ResponseWriter` if they _are_ going to terminate the request, and they _should not_ write to `ResponseWriter` if they _are not_ going to terminate it.
495
496 ### Handling CORS Requests
497
498 [CORSMethodMiddleware](https://godoc.org/github.com/gorilla/mux#CORSMethodMiddleware) intends to make it easier to strictly set the `Access-Control-Allow-Methods` response header.
499
500 * You will still need to use your own CORS handler to set the other CORS headers such as `Access-Control-Allow-Origin`
501 * The middleware will set the `Access-Control-Allow-Methods` header to all the method matchers (e.g. `r.Methods(http.MethodGet, http.MethodPut, http.MethodOptions)` -> `Access-Control-Allow-Methods: GET,PUT,OPTIONS`) on a route
502 * If you do not specify any methods, then:
503 > _Important_: there must be an `OPTIONS` method matcher for the middleware to set the headers.
504
505 Here is an example of using `CORSMethodMiddleware` along with a custom `OPTIONS` handler to set all the required CORS headers:
506
507 ```go
508 package main
509
510 import (
511         "net/http"
512         "github.com/gorilla/mux"
513 )
514
515 func main() {
516     r := mux.NewRouter()
517
518     // IMPORTANT: you must specify an OPTIONS method matcher for the middleware to set CORS headers
519     r.HandleFunc("/foo", fooHandler).Methods(http.MethodGet, http.MethodPut, http.MethodPatch, http.MethodOptions)
520     r.Use(mux.CORSMethodMiddleware(r))
521     
522     http.ListenAndServe(":8080", r)
523 }
524
525 func fooHandler(w http.ResponseWriter, r *http.Request) {
526     w.Header().Set("Access-Control-Allow-Origin", "*")
527     if r.Method == http.MethodOptions {
528         return
529     }
530
531     w.Write([]byte("foo"))
532 }
533 ```
534
535 And an request to `/foo` using something like:
536
537 ```bash
538 curl localhost:8080/foo -v
539 ```
540
541 Would look like:
542
543 ```bash
544 *   Trying ::1...
545 * TCP_NODELAY set
546 * Connected to localhost (::1) port 8080 (#0)
547 > GET /foo HTTP/1.1
548 > Host: localhost:8080
549 > User-Agent: curl/7.59.0
550 > Accept: */*
551
552 < HTTP/1.1 200 OK
553 < Access-Control-Allow-Methods: GET,PUT,PATCH,OPTIONS
554 < Access-Control-Allow-Origin: *
555 < Date: Fri, 28 Jun 2019 20:13:30 GMT
556 < Content-Length: 3
557 < Content-Type: text/plain; charset=utf-8
558
559 * Connection #0 to host localhost left intact
560 foo
561 ```
562
563 ### Testing Handlers
564
565 Testing handlers in a Go web application is straightforward, and _mux_ doesn't complicate this any further. Given two files: `endpoints.go` and `endpoints_test.go`, here's how we'd test an application using _mux_.
566
567 First, our simple HTTP handler:
568
569 ```go
570 // endpoints.go
571 package main
572
573 func HealthCheckHandler(w http.ResponseWriter, r *http.Request) {
574     // A very simple health check.
575     w.Header().Set("Content-Type", "application/json")
576     w.WriteHeader(http.StatusOK)
577
578     // In the future we could report back on the status of our DB, or our cache
579     // (e.g. Redis) by performing a simple PING, and include them in the response.
580     io.WriteString(w, `{"alive": true}`)
581 }
582
583 func main() {
584     r := mux.NewRouter()
585     r.HandleFunc("/health", HealthCheckHandler)
586
587     log.Fatal(http.ListenAndServe("localhost:8080", r))
588 }
589 ```
590
591 Our test code:
592
593 ```go
594 // endpoints_test.go
595 package main
596
597 import (
598     "net/http"
599     "net/http/httptest"
600     "testing"
601 )
602
603 func TestHealthCheckHandler(t *testing.T) {
604     // Create a request to pass to our handler. We don't have any query parameters for now, so we'll
605     // pass 'nil' as the third parameter.
606     req, err := http.NewRequest("GET", "/health", nil)
607     if err != nil {
608         t.Fatal(err)
609     }
610
611     // We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
612     rr := httptest.NewRecorder()
613     handler := http.HandlerFunc(HealthCheckHandler)
614
615     // Our handlers satisfy http.Handler, so we can call their ServeHTTP method
616     // directly and pass in our Request and ResponseRecorder.
617     handler.ServeHTTP(rr, req)
618
619     // Check the status code is what we expect.
620     if status := rr.Code; status != http.StatusOK {
621         t.Errorf("handler returned wrong status code: got %v want %v",
622             status, http.StatusOK)
623     }
624
625     // Check the response body is what we expect.
626     expected := `{"alive": true}`
627     if rr.Body.String() != expected {
628         t.Errorf("handler returned unexpected body: got %v want %v",
629             rr.Body.String(), expected)
630     }
631 }
632 ```
633
634 In the case that our routes have [variables](#examples), we can pass those in the request. We could write
635 [table-driven tests](https://dave.cheney.net/2013/06/09/writing-table-driven-tests-in-go) to test multiple
636 possible route variables as needed.
637
638 ```go
639 // endpoints.go
640 func main() {
641     r := mux.NewRouter()
642     // A route with a route variable:
643     r.HandleFunc("/metrics/{type}", MetricsHandler)
644
645     log.Fatal(http.ListenAndServe("localhost:8080", r))
646 }
647 ```
648
649 Our test file, with a table-driven test of `routeVariables`:
650
651 ```go
652 // endpoints_test.go
653 func TestMetricsHandler(t *testing.T) {
654     tt := []struct{
655         routeVariable string
656         shouldPass bool
657     }{
658         {"goroutines", true},
659         {"heap", true},
660         {"counters", true},
661         {"queries", true},
662         {"adhadaeqm3k", false},
663     }
664
665     for _, tc := range tt {
666         path := fmt.Sprintf("/metrics/%s", tc.routeVariable)
667         req, err := http.NewRequest("GET", path, nil)
668         if err != nil {
669             t.Fatal(err)
670         }
671
672         rr := httptest.NewRecorder()
673         
674         // Need to create a router that we can pass the request through so that the vars will be added to the context
675         router := mux.NewRouter()
676         router.HandleFunc("/metrics/{type}", MetricsHandler)
677         router.ServeHTTP(rr, req)
678
679         // In this case, our MetricsHandler returns a non-200 response
680         // for a route variable it doesn't know about.
681         if rr.Code == http.StatusOK && !tc.shouldPass {
682             t.Errorf("handler should have failed on routeVariable %s: got %v want %v",
683                 tc.routeVariable, rr.Code, http.StatusOK)
684         }
685     }
686 }
687 ```
688
689 ## Full Example
690
691 Here's a complete, runnable example of a small `mux` based server:
692
693 ```go
694 package main
695
696 import (
697     "net/http"
698     "log"
699     "github.com/gorilla/mux"
700 )
701
702 func YourHandler(w http.ResponseWriter, r *http.Request) {
703     w.Write([]byte("Gorilla!\n"))
704 }
705
706 func main() {
707     r := mux.NewRouter()
708     // Routes consist of a path and a handler function.
709     r.HandleFunc("/", YourHandler)
710
711     // Bind to a port and pass our router in
712     log.Fatal(http.ListenAndServe(":8000", r))
713 }
714 ```
715
716 ## License
717
718 BSD licensed. See the LICENSE file for details.