taldir

Directory service to resolve wallet mailboxes by messenger addresses
Log | Files | Refs | Submodules | README | LICENSE

README.md (25673B)


      1 # gorilla/mux
      2 
      3 ![testing](https://github.com/gorilla/mux/actions/workflows/test.yml/badge.svg)
      4 [![codecov](https://codecov.io/github/gorilla/mux/branch/main/graph/badge.svg)](https://codecov.io/github/gorilla/mux)
      5 [![godoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux)
      6 [![sourcegraph](https://sourcegraph.com/github.com/gorilla/mux/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/mux?badge)
      7 
      8 
      9 ![Gorilla Logo](https://github.com/gorilla/.github/assets/53367916/d92caabf-98e0-473e-bfbf-ab554ba435e5)
     10 
     11 Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to
     12 their respective handler.
     13 
     14 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:
     15 
     16 * It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`.
     17 * Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers.
     18 * URL hosts, paths and query values can have variables with an optional regular expression.
     19 * Registered URLs can be built, or "reversed", which helps maintaining references to resources.
     20 * 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.
     21 
     22 ---
     23 
     24 * [Install](#install)
     25 * [Examples](#examples)
     26 * [Matching Routes](#matching-routes)
     27 * [Static Files](#static-files)
     28 * [Serving Single Page Applications](#serving-single-page-applications) (e.g. React, Vue, Ember.js, etc.)
     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 ### Serving Single Page Applications
    216 
    217 Most of the time it makes sense to serve your SPA on a separate web server from your API,
    218 but sometimes it's desirable to serve them both from one place. It's possible to write a simple
    219 handler for serving your SPA (for use with React Router's [BrowserRouter](https://reacttraining.com/react-router/web/api/BrowserRouter) for example), and leverage
    220 mux's powerful routing for your API endpoints.
    221 
    222 ```go
    223 package main
    224 
    225 import (
    226 	"encoding/json"
    227 	"log"
    228 	"net/http"
    229 	"os"
    230 	"path/filepath"
    231 	"time"
    232 
    233 	"github.com/gorilla/mux"
    234 )
    235 
    236 // spaHandler implements the http.Handler interface, so we can use it
    237 // to respond to HTTP requests. The path to the static directory and
    238 // path to the index file within that static directory are used to
    239 // serve the SPA in the given static directory.
    240 type spaHandler struct {
    241 	staticPath string
    242 	indexPath  string
    243 }
    244 
    245 // ServeHTTP inspects the URL path to locate a file within the static dir
    246 // on the SPA handler. If a file is found, it will be served. If not, the
    247 // file located at the index path on the SPA handler will be served. This
    248 // is suitable behavior for serving an SPA (single page application).
    249 func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    250 	// Join internally call path.Clean to prevent directory traversal
    251 	path := filepath.Join(h.staticPath, r.URL.Path)
    252 
    253 	// check whether a file exists or is a directory at the given path
    254 	fi, err := os.Stat(path)
    255 	if os.IsNotExist(err) || fi.IsDir() {
    256 		// file does not exist or path is a directory, serve index.html
    257 		http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath))
    258 		return
    259 	}
    260 
    261 	if err != nil {
    262 		// if we got an error (that wasn't that the file doesn't exist) stating the
    263 		// file, return a 500 internal server error and stop
    264 		http.Error(w, err.Error(), http.StatusInternalServerError)
    265         return
    266 	}
    267 
    268 	// otherwise, use http.FileServer to serve the static file
    269 	http.FileServer(http.Dir(h.staticPath)).ServeHTTP(w, r)
    270 }
    271 
    272 func main() {
    273 	router := mux.NewRouter()
    274 
    275 	router.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) {
    276 		// an example API handler
    277 		json.NewEncoder(w).Encode(map[string]bool{"ok": true})
    278 	})
    279 
    280 	spa := spaHandler{staticPath: "build", indexPath: "index.html"}
    281 	router.PathPrefix("/").Handler(spa)
    282 
    283 	srv := &http.Server{
    284 		Handler: router,
    285 		Addr:    "127.0.0.1:8000",
    286 		// Good practice: enforce timeouts for servers you create!
    287 		WriteTimeout: 15 * time.Second,
    288 		ReadTimeout:  15 * time.Second,
    289 	}
    290 
    291 	log.Fatal(srv.ListenAndServe())
    292 }
    293 ```
    294 
    295 ### Registered URLs
    296 
    297 Now let's see how to build registered URLs.
    298 
    299 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:
    300 
    301 ```go
    302 r := mux.NewRouter()
    303 r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
    304   Name("article")
    305 ```
    306 
    307 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:
    308 
    309 ```go
    310 url, err := r.Get("article").URL("category", "technology", "id", "42")
    311 ```
    312 
    313 ...and the result will be a `url.URL` with the following path:
    314 
    315 ```
    316 "/articles/technology/42"
    317 ```
    318 
    319 This also works for host and query value variables:
    320 
    321 ```go
    322 r := mux.NewRouter()
    323 r.Host("{subdomain}.example.com").
    324   Path("/articles/{category}/{id:[0-9]+}").
    325   Queries("filter", "{filter}").
    326   HandlerFunc(ArticleHandler).
    327   Name("article")
    328 
    329 // url.String() will be "http://news.example.com/articles/technology/42?filter=gorilla"
    330 url, err := r.Get("article").URL("subdomain", "news",
    331                                  "category", "technology",
    332                                  "id", "42",
    333                                  "filter", "gorilla")
    334 ```
    335 
    336 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.
    337 
    338 Regex support also exists for matching Headers within a route. For example, we could do:
    339 
    340 ```go
    341 r.HeadersRegexp("Content-Type", "application/(text|json)")
    342 ```
    343 
    344 ...and the route will match both requests with a Content-Type of `application/json` as well as `application/text`
    345 
    346 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:
    347 
    348 ```go
    349 // "http://news.example.com/"
    350 host, err := r.Get("article").URLHost("subdomain", "news")
    351 
    352 // "/articles/technology/42"
    353 path, err := r.Get("article").URLPath("category", "technology", "id", "42")
    354 ```
    355 
    356 And if you use subrouters, host and path defined separately can be built as well:
    357 
    358 ```go
    359 r := mux.NewRouter()
    360 s := r.Host("{subdomain}.example.com").Subrouter()
    361 s.Path("/articles/{category}/{id:[0-9]+}").
    362   HandlerFunc(ArticleHandler).
    363   Name("article")
    364 
    365 // "http://news.example.com/articles/technology/42"
    366 url, err := r.Get("article").URL("subdomain", "news",
    367                                  "category", "technology",
    368                                  "id", "42")
    369 ```
    370 
    371 To find all the required variables for a given route when calling `URL()`, the method `GetVarNames()` is available:
    372 ```go
    373 r := mux.NewRouter()
    374 r.Host("{domain}").
    375     Path("/{group}/{item_id}").
    376     Queries("some_data1", "{some_data1}").
    377     Queries("some_data2", "{some_data2}").
    378     Name("article")
    379 
    380 // Will print [domain group item_id some_data1 some_data2] <nil>
    381 fmt.Println(r.Get("article").GetVarNames())
    382 
    383 ```
    384 ### Walking Routes
    385 
    386 The `Walk` function on `mux.Router` can be used to visit all of the routes that are registered on a router. For example,
    387 the following prints all of the registered routes:
    388 
    389 ```go
    390 package main
    391 
    392 import (
    393 	"fmt"
    394 	"net/http"
    395 	"strings"
    396 
    397 	"github.com/gorilla/mux"
    398 )
    399 
    400 func handler(w http.ResponseWriter, r *http.Request) {
    401 	return
    402 }
    403 
    404 func main() {
    405 	r := mux.NewRouter()
    406 	r.HandleFunc("/", handler)
    407 	r.HandleFunc("/products", handler).Methods("POST")
    408 	r.HandleFunc("/articles", handler).Methods("GET")
    409 	r.HandleFunc("/articles/{id}", handler).Methods("GET", "PUT")
    410 	r.HandleFunc("/authors", handler).Queries("surname", "{surname}")
    411 	err := r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
    412 		pathTemplate, err := route.GetPathTemplate()
    413 		if err == nil {
    414 			fmt.Println("ROUTE:", pathTemplate)
    415 		}
    416 		pathRegexp, err := route.GetPathRegexp()
    417 		if err == nil {
    418 			fmt.Println("Path regexp:", pathRegexp)
    419 		}
    420 		queriesTemplates, err := route.GetQueriesTemplates()
    421 		if err == nil {
    422 			fmt.Println("Queries templates:", strings.Join(queriesTemplates, ","))
    423 		}
    424 		queriesRegexps, err := route.GetQueriesRegexp()
    425 		if err == nil {
    426 			fmt.Println("Queries regexps:", strings.Join(queriesRegexps, ","))
    427 		}
    428 		methods, err := route.GetMethods()
    429 		if err == nil {
    430 			fmt.Println("Methods:", strings.Join(methods, ","))
    431 		}
    432 		fmt.Println()
    433 		return nil
    434 	})
    435 
    436 	if err != nil {
    437 		fmt.Println(err)
    438 	}
    439 
    440 	http.Handle("/", r)
    441 }
    442 ```
    443 
    444 ### Graceful Shutdown
    445 
    446 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`:
    447 
    448 ```go
    449 package main
    450 
    451 import (
    452     "context"
    453     "flag"
    454     "log"
    455     "net/http"
    456     "os"
    457     "os/signal"
    458     "time"
    459 
    460     "github.com/gorilla/mux"
    461 )
    462 
    463 func main() {
    464     var wait time.Duration
    465     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")
    466     flag.Parse()
    467 
    468     r := mux.NewRouter()
    469     // Add your routes as needed
    470 
    471     srv := &http.Server{
    472         Addr:         "0.0.0.0:8080",
    473         // Good practice to set timeouts to avoid Slowloris attacks.
    474         WriteTimeout: time.Second * 15,
    475         ReadTimeout:  time.Second * 15,
    476         IdleTimeout:  time.Second * 60,
    477         Handler: r, // Pass our instance of gorilla/mux in.
    478     }
    479 
    480     // Run our server in a goroutine so that it doesn't block.
    481     go func() {
    482         if err := srv.ListenAndServe(); err != nil {
    483             log.Println(err)
    484         }
    485     }()
    486 
    487     c := make(chan os.Signal, 1)
    488     // We'll accept graceful shutdowns when quit via SIGINT (Ctrl+C)
    489     // SIGKILL, SIGQUIT or SIGTERM (Ctrl+/) will not be caught.
    490     signal.Notify(c, os.Interrupt)
    491 
    492     // Block until we receive our signal.
    493     <-c
    494 
    495     // Create a deadline to wait for.
    496     ctx, cancel := context.WithTimeout(context.Background(), wait)
    497     defer cancel()
    498     // Doesn't block if no connections, but will otherwise wait
    499     // until the timeout deadline.
    500     srv.Shutdown(ctx)
    501     // Optionally, you could run srv.Shutdown in a goroutine and block on
    502     // <-ctx.Done() if your application should wait for other services
    503     // to finalize based on context cancellation.
    504     log.Println("shutting down")
    505     os.Exit(0)
    506 }
    507 ```
    508 
    509 ### Middleware
    510 
    511 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.
    512 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.
    513 
    514 Mux middlewares are defined using the de facto standard type:
    515 
    516 ```go
    517 type MiddlewareFunc func(http.Handler) http.Handler
    518 ```
    519 
    520 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.
    521 
    522 A very basic middleware which logs the URI of the request being handled could be written as:
    523 
    524 ```go
    525 func loggingMiddleware(next http.Handler) http.Handler {
    526     return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    527         // Do stuff here
    528         log.Println(r.RequestURI)
    529         // Call the next handler, which can be another middleware in the chain, or the final handler.
    530         next.ServeHTTP(w, r)
    531     })
    532 }
    533 ```
    534 
    535 Middlewares can be added to a router using `Router.Use()`:
    536 
    537 ```go
    538 r := mux.NewRouter()
    539 r.HandleFunc("/", handler)
    540 r.Use(loggingMiddleware)
    541 ```
    542 
    543 A more complex authentication middleware, which maps session token to users, could be written as:
    544 
    545 ```go
    546 // Define our struct
    547 type authenticationMiddleware struct {
    548 	tokenUsers map[string]string
    549 }
    550 
    551 // Initialize it somewhere
    552 func (amw *authenticationMiddleware) Populate() {
    553 	amw.tokenUsers["00000000"] = "user0"
    554 	amw.tokenUsers["aaaaaaaa"] = "userA"
    555 	amw.tokenUsers["05f717e5"] = "randomUser"
    556 	amw.tokenUsers["deadbeef"] = "user0"
    557 }
    558 
    559 // Middleware function, which will be called for each request
    560 func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler {
    561     return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    562         token := r.Header.Get("X-Session-Token")
    563 
    564         if user, found := amw.tokenUsers[token]; found {
    565         	// We found the token in our map
    566         	log.Printf("Authenticated user %s\n", user)
    567         	// Pass down the request to the next middleware (or final handler)
    568         	next.ServeHTTP(w, r)
    569         } else {
    570         	// Write an error and stop the handler chain
    571         	http.Error(w, "Forbidden", http.StatusForbidden)
    572         }
    573     })
    574 }
    575 ```
    576 
    577 ```go
    578 r := mux.NewRouter()
    579 r.HandleFunc("/", handler)
    580 
    581 amw := authenticationMiddleware{tokenUsers: make(map[string]string)}
    582 amw.Populate()
    583 
    584 r.Use(amw.Middleware)
    585 ```
    586 
    587 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.
    588 
    589 ### Handling CORS Requests
    590 
    591 [CORSMethodMiddleware](https://godoc.org/github.com/gorilla/mux#CORSMethodMiddleware) intends to make it easier to strictly set the `Access-Control-Allow-Methods` response header.
    592 
    593 * You will still need to use your own CORS handler to set the other CORS headers such as `Access-Control-Allow-Origin`
    594 * 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
    595 * If you do not specify any methods, then:
    596 > _Important_: there must be an `OPTIONS` method matcher for the middleware to set the headers.
    597 
    598 Here is an example of using `CORSMethodMiddleware` along with a custom `OPTIONS` handler to set all the required CORS headers:
    599 
    600 ```go
    601 package main
    602 
    603 import (
    604 	"net/http"
    605 	"github.com/gorilla/mux"
    606 )
    607 
    608 func main() {
    609     r := mux.NewRouter()
    610 
    611     // IMPORTANT: you must specify an OPTIONS method matcher for the middleware to set CORS headers
    612     r.HandleFunc("/foo", fooHandler).Methods(http.MethodGet, http.MethodPut, http.MethodPatch, http.MethodOptions)
    613     r.Use(mux.CORSMethodMiddleware(r))
    614     
    615     http.ListenAndServe(":8080", r)
    616 }
    617 
    618 func fooHandler(w http.ResponseWriter, r *http.Request) {
    619     w.Header().Set("Access-Control-Allow-Origin", "*")
    620     if r.Method == http.MethodOptions {
    621         return
    622     }
    623 
    624     w.Write([]byte("foo"))
    625 }
    626 ```
    627 
    628 And an request to `/foo` using something like:
    629 
    630 ```bash
    631 curl localhost:8080/foo -v
    632 ```
    633 
    634 Would look like:
    635 
    636 ```bash
    637 *   Trying ::1...
    638 * TCP_NODELAY set
    639 * Connected to localhost (::1) port 8080 (#0)
    640 > GET /foo HTTP/1.1
    641 > Host: localhost:8080
    642 > User-Agent: curl/7.59.0
    643 > Accept: */*
    644 > 
    645 < HTTP/1.1 200 OK
    646 < Access-Control-Allow-Methods: GET,PUT,PATCH,OPTIONS
    647 < Access-Control-Allow-Origin: *
    648 < Date: Fri, 28 Jun 2019 20:13:30 GMT
    649 < Content-Length: 3
    650 < Content-Type: text/plain; charset=utf-8
    651 < 
    652 * Connection #0 to host localhost left intact
    653 foo
    654 ```
    655 
    656 ### Testing Handlers
    657 
    658 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_.
    659 
    660 First, our simple HTTP handler:
    661 
    662 ```go
    663 // endpoints.go
    664 package main
    665 
    666 func HealthCheckHandler(w http.ResponseWriter, r *http.Request) {
    667     // A very simple health check.
    668     w.Header().Set("Content-Type", "application/json")
    669     w.WriteHeader(http.StatusOK)
    670 
    671     // In the future we could report back on the status of our DB, or our cache
    672     // (e.g. Redis) by performing a simple PING, and include them in the response.
    673     io.WriteString(w, `{"alive": true}`)
    674 }
    675 
    676 func main() {
    677     r := mux.NewRouter()
    678     r.HandleFunc("/health", HealthCheckHandler)
    679 
    680     log.Fatal(http.ListenAndServe("localhost:8080", r))
    681 }
    682 ```
    683 
    684 Our test code:
    685 
    686 ```go
    687 // endpoints_test.go
    688 package main
    689 
    690 import (
    691     "net/http"
    692     "net/http/httptest"
    693     "testing"
    694 )
    695 
    696 func TestHealthCheckHandler(t *testing.T) {
    697     // Create a request to pass to our handler. We don't have any query parameters for now, so we'll
    698     // pass 'nil' as the third parameter.
    699     req, err := http.NewRequest("GET", "/health", nil)
    700     if err != nil {
    701         t.Fatal(err)
    702     }
    703 
    704     // We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
    705     rr := httptest.NewRecorder()
    706     handler := http.HandlerFunc(HealthCheckHandler)
    707 
    708     // Our handlers satisfy http.Handler, so we can call their ServeHTTP method
    709     // directly and pass in our Request and ResponseRecorder.
    710     handler.ServeHTTP(rr, req)
    711 
    712     // Check the status code is what we expect.
    713     if status := rr.Code; status != http.StatusOK {
    714         t.Errorf("handler returned wrong status code: got %v want %v",
    715             status, http.StatusOK)
    716     }
    717 
    718     // Check the response body is what we expect.
    719     expected := `{"alive": true}`
    720     if rr.Body.String() != expected {
    721         t.Errorf("handler returned unexpected body: got %v want %v",
    722             rr.Body.String(), expected)
    723     }
    724 }
    725 ```
    726 
    727 In the case that our routes have [variables](#examples), we can pass those in the request. We could write
    728 [table-driven tests](https://dave.cheney.net/2013/06/09/writing-table-driven-tests-in-go) to test multiple
    729 possible route variables as needed.
    730 
    731 ```go
    732 // endpoints.go
    733 func main() {
    734     r := mux.NewRouter()
    735     // A route with a route variable:
    736     r.HandleFunc("/metrics/{type}", MetricsHandler)
    737 
    738     log.Fatal(http.ListenAndServe("localhost:8080", r))
    739 }
    740 ```
    741 
    742 Our test file, with a table-driven test of `routeVariables`:
    743 
    744 ```go
    745 // endpoints_test.go
    746 func TestMetricsHandler(t *testing.T) {
    747     tt := []struct{
    748         routeVariable string
    749         shouldPass bool
    750     }{
    751         {"goroutines", true},
    752         {"heap", true},
    753         {"counters", true},
    754         {"queries", true},
    755         {"adhadaeqm3k", false},
    756     }
    757 
    758     for _, tc := range tt {
    759         path := fmt.Sprintf("/metrics/%s", tc.routeVariable)
    760         req, err := http.NewRequest("GET", path, nil)
    761         if err != nil {
    762             t.Fatal(err)
    763         }
    764 
    765         rr := httptest.NewRecorder()
    766 	
    767 	// To add the vars to the context, 
    768 	// we need to create a router through which we can pass the request.
    769 	router := mux.NewRouter()
    770         router.HandleFunc("/metrics/{type}", MetricsHandler)
    771         router.ServeHTTP(rr, req)
    772 
    773         // In this case, our MetricsHandler returns a non-200 response
    774         // for a route variable it doesn't know about.
    775         if rr.Code == http.StatusOK && !tc.shouldPass {
    776             t.Errorf("handler should have failed on routeVariable %s: got %v want %v",
    777                 tc.routeVariable, rr.Code, http.StatusOK)
    778         }
    779     }
    780 }
    781 ```
    782 
    783 ## Full Example
    784 
    785 Here's a complete, runnable example of a small `mux` based server:
    786 
    787 ```go
    788 package main
    789 
    790 import (
    791     "net/http"
    792     "log"
    793     "github.com/gorilla/mux"
    794 )
    795 
    796 func YourHandler(w http.ResponseWriter, r *http.Request) {
    797     w.Write([]byte("Gorilla!\n"))
    798 }
    799 
    800 func main() {
    801     r := mux.NewRouter()
    802     // Routes consist of a path and a handler function.
    803     r.HandleFunc("/", YourHandler)
    804 
    805     // Bind to a port and pass our router in
    806     log.Fatal(http.ListenAndServe(":8000", r))
    807 }
    808 ```
    809 
    810 ## License
    811 
    812 BSD licensed. See the LICENSE file for details.