github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/gorilla/mux/README.md (about)

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