Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: gorilla/mux
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.7.3
Choose a base ref
...
head repository: gorilla/mux
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v1.7.4
Choose a head ref
  • 16 commits
  • 15 files changed
  • 10 contributors

Commits on Jul 1, 2019

  1. Update config.yml (#495)

    * Update config.yml
    
    * Update config.yml
    elithrar authored Jul 1, 2019
    Copy the full SHA
    d83b6ff View commit details

Commits on Jul 20, 2019

  1. Avoid unnecessary conversion (#502)

    No need to convert here.
    muesli authored and elithrar committed Jul 20, 2019
    Copy the full SHA
    50fbc3e View commit details
  2. Simplify code (#501)

    Use a single append call instead of a ranged for loop.
    muesli authored and elithrar committed Jul 20, 2019
    Copy the full SHA
    eab9c4f View commit details
  3. [docs] Add documentation for using mux to serve a SPA (#493)

    * Add documentation for using mux to serve a SPA
    
    * r -> router to prevent shadowing
    
    * Expand SPA acronym
    
    * BrowserRouter link
    
    * Add more comments to explain how the spaHandler.ServeHTTP method works
    fharding1 authored and elithrar committed Jul 20, 2019
    Copy the full SHA
    7a1bf40 View commit details
  4. Copy the full SHA
    e67b3c0 View commit details

Commits on Aug 24, 2019

  1. Copy the full SHA
    e0cdff4 View commit details

Commits on Aug 26, 2019

  1. Copy the full SHA
    9536e40 View commit details

Commits on Aug 27, 2019

  1. Copy the full SHA
    e1863a6 View commit details

Commits on Aug 30, 2019

  1. Copy the full SHA
    884b5ff View commit details

Commits on Oct 18, 2019

  1. Guess the scheme if r.URL.Scheme is unset (#474)

    * Guess the scheme if r.URL.Scheme is unset
    It's not expected that the request's URL is fully populated when used on
    the server-side (it's more of a client-side field), so we shouldn't
    expect it to be present.
    
    In practice, it's only rarely set at all on the server, making mux's
    `Schemes` matcher tricky to use as it is.
    
    This commit adds a test which would have failed before demonstrating the
    problem, as well as a fix which I think makes `.Schemes` match what
    users expect.
    
    * [doc] Add more detail to Schemes and URL godocs
    
    * Add route url test for schemes
    
    * Make httpserver test use more specific scheme matchers
    
    * Update test to have different responses per route
    euank authored and elithrar committed Oct 18, 2019
    Copy the full SHA
    ff4e71f View commit details

Commits on Oct 24, 2019

  1. Remove/cleanup request context helpers (#525)

    * Remove context helpers in context.go
    * Update request context funcs to take concrete types
    * Move TestNativeContextMiddleware to mux_test.go
    * Clarify KeepContext Go 1.7+ comment
    
    Mux doesn't build on Go < 1.7 so the comment doesn't really need to
    clarify anymore.
    fharding1 authored and elithrar committed Oct 24, 2019
    Copy the full SHA
    f395758 View commit details

Commits on Nov 15, 2019

  1. Fix the CORSMethodMiddleware bug with subrouters

    * Adds a test case for the repro given in issue #534
    * Fixes the logic in CORSMethodMiddleware to handle matching routes
    better
    fharding1 committed Nov 15, 2019
    Copy the full SHA
    946b623 View commit details

Commits on Nov 17, 2019

  1. Merge pull request #535 from fharding1/cors-subrouter-bug

    Fix the CORSMethodMiddleware bug with subrouters
    elithrar authored Nov 17, 2019
    Copy the full SHA
    2854a05 View commit details

Commits on Nov 19, 2019

  1. fix headers regexp test (#536)

    icattlecoder authored and elithrar committed Nov 19, 2019
    Copy the full SHA
    4de8a5a View commit details

Commits on Nov 21, 2019

  1. lint: Remove golint warning (#526)

    kveselkov authored and elithrar committed Nov 21, 2019
    Copy the full SHA
    49c0148 View commit details

Commits on Jan 12, 2020

  1. perf: reduce allocations in (*routeRegexp).getURLQuery (#544)

    A production server is seeing a significant amount of allocations in (*routeRegexp).getURLQuery
    
    Since it is only interested in a single value and only the first value we create a specialized function for that.
    
    Comparing a few parameter parsing scenarios:
    
    ```
    Benchmark_findQueryKey/0-8 	 7184014	       168 ns/op	       0 B/op	       0 allocs/op
    Benchmark_findQueryKey/1-8 	 5307873	       227 ns/op	      48 B/op	       3 allocs/op
    Benchmark_findQueryKey/2-8 	 1560836	       770 ns/op	     483 B/op	      10 allocs/op
    Benchmark_findQueryKey/3-8 	 1296200	       931 ns/op	     559 B/op	      11 allocs/op
    Benchmark_findQueryKey/4-8 	  666502	      1769 ns/op	       3 B/op	       1 allocs/op
    
    Benchmark_findQueryKeyGoLib/0-8 	 1740973	       690 ns/op	     864 B/op	       8 allocs/op
    Benchmark_findQueryKeyGoLib/1-8 	 3029618	       393 ns/op	     432 B/op	       4 allocs/op
    Benchmark_findQueryKeyGoLib/2-8 	  461427	      2511 ns/op	    1542 B/op	      24 allocs/op
    Benchmark_findQueryKeyGoLib/3-8 	  324252	      3804 ns/op	    1984 B/op	      28 allocs/op
    Benchmark_findQueryKeyGoLib/4-8 	   69348	     14928 ns/op	   12716 B/op	     130 allocs/op
    ```
    klauspost authored and elithrar committed Jan 12, 2020
    Copy the full SHA
    75dcda0 View commit details
Showing with 480 additions and 136 deletions.
  1. +26 −14 .circleci/config.yml
  2. +89 −2 README.md
  3. +0 −18 context.go
  4. +0 −30 context_test.go
  5. +2 −0 go.mod
  6. +10 −15 middleware.go
  7. +20 −0 middleware_test.go
  8. +14 −14 mux.go
  9. +49 −0 mux_httpserver_test.go
  10. +90 −10 mux_test.go
  11. +5 −12 old_test.go
  12. +51 −14 regexp.go
  13. +91 −0 regexp_test.go
  14. +32 −6 route.go
  15. +1 −1 test_helpers.go
40 changes: 26 additions & 14 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -8,23 +8,35 @@ jobs:
- image: circleci/golang:latest
working_directory: /go/src/github.com/gorilla/mux
steps: &steps
# Our build steps: we checkout the repo, fetch our deps, lint, and finally
# run "go test" on the package.
- checkout
# Logs the version in our build logs, for posterity
- run: go version
- run: go get -t -v ./...
- run:
name: "Fetch dependencies"
command: >
go get -t -v ./...
# Only run gofmt, vet & lint against the latest Go version
- run: >
if [[ "$LATEST" = true ]]; then
go get -u golang.org/x/lint/golint
golint ./...
fi
- run: >
if [[ "$LATEST" = true ]]; then
diff -u <(echo -n) <(gofmt -d .)
fi
- run: >
if [[ "$LATEST" = true ]]; then
go vet -v .
fi
- run:
name: "Run golint"
command: >
if [ "${LATEST}" = true ] && [ -z "${SKIP_GOLINT}" ]; then
go get -u golang.org/x/lint/golint
golint ./...
fi
- run:
name: "Run gofmt"
command: >
if [[ "${LATEST}" = true ]]; then
diff -u <(echo -n) <(gofmt -d -e .)
fi
- run:
name: "Run go vet"
command: >
if [[ "${LATEST}" = true ]]; then
go vet -v ./...
fi
- run: go test -v -race ./...

"latest":
91 changes: 89 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# gorilla/mux

[![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux)
[![Build Status](https://travis-ci.org/gorilla/mux.svg?branch=master)](https://travis-ci.org/gorilla/mux)
[![CircleCI](https://circleci.com/gh/gorilla/mux.svg?style=svg)](https://circleci.com/gh/gorilla/mux)
[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/mux/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/mux?badge)

![Gorilla Logo](http://www.gorillatoolkit.org/static/images/gorilla-icon-64.png)
![Gorilla Logo](https://cloud-cdn.questionable.services/gorilla-icon-64.png)

https://www.gorillatoolkit.org/pkg/mux

@@ -26,6 +25,7 @@ The name mux stands for "HTTP request multiplexer". Like the standard `http.Serv
* [Examples](#examples)
* [Matching Routes](#matching-routes)
* [Static Files](#static-files)
* [Serving Single Page Applications](#serving-single-page-applications) (e.g. React, Vue, Ember.js, etc.)
* [Registered URLs](#registered-urls)
* [Walking Routes](#walking-routes)
* [Graceful Shutdown](#graceful-shutdown)
@@ -212,6 +212,93 @@ func main() {
}
```

### Serving Single Page Applications

Most of the time it makes sense to serve your SPA on a separate web server from your API,
but sometimes it's desirable to serve them both from one place. It's possible to write a simple
handler for serving your SPA (for use with React Router's [BrowserRouter](https://reacttraining.com/react-router/web/api/BrowserRouter) for example), and leverage
mux's powerful routing for your API endpoints.

```go
package main

import (
"encoding/json"
"log"
"net/http"
"os"
"path/filepath"
"time"

"github.com/gorilla/mux"
)

// spaHandler implements the http.Handler interface, so we can use it
// to respond to HTTP requests. The path to the static directory and
// path to the index file within that static directory are used to
// serve the SPA in the given static directory.
type spaHandler struct {
staticPath string
indexPath string
}

// ServeHTTP inspects the URL path to locate a file within the static dir
// on the SPA handler. If a file is found, it will be served. If not, the
// file located at the index path on the SPA handler will be served. This
// is suitable behavior for serving an SPA (single page application).
func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// get the absolute path to prevent directory traversal
path, err := filepath.Abs(r.URL.Path)
if err != nil {
// if we failed to get the absolute path respond with a 400 bad request
// and stop
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

// prepend the path with the path to the static directory
path = filepath.Join(h.staticPath, path)

// check whether a file exists at the given path
_, err = os.Stat(path)
if os.IsNotExist(err) {
// file does not exist, serve index.html
http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath))
return
} else if err != nil {
// if we got an error (that wasn't that the file doesn't exist) stating the
// file, return a 500 internal server error and stop
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

// otherwise, use http.FileServer to serve the static dir
http.FileServer(http.Dir(h.staticPath)).ServeHTTP(w, r)
}

func main() {
router := mux.NewRouter()

router.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) {
// an example API handler
json.NewEncoder(w).Encode(map[string]bool{"ok": true})
})

spa := spaHandler{staticPath: "build", indexPath: "index.html"}
router.PathPrefix("/").Handler(spa)

srv := &http.Server{
Handler: router,
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.
18 changes: 0 additions & 18 deletions context.go

This file was deleted.

30 changes: 0 additions & 30 deletions context_test.go

This file was deleted.

2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
module github.com/gorilla/mux

go 1.12
25 changes: 10 additions & 15 deletions middleware.go
Original file line number Diff line number Diff line change
@@ -58,22 +58,17 @@ func CORSMethodMiddleware(r *Router) MiddlewareFunc {
func getAllMethodsForRoute(r *Router, req *http.Request) ([]string, error) {
var allMethods []string

err := r.Walk(func(route *Route, _ *Router, _ []*Route) error {
for _, m := range route.matchers {
if _, ok := m.(*routeRegexp); ok {
if m.Match(req, &RouteMatch{}) {
methods, err := route.GetMethods()
if err != nil {
return err
}

allMethods = append(allMethods, methods...)
}
break
for _, route := range r.routes {
var match RouteMatch
if route.Match(req, &match) || match.MatchErr == ErrMethodMismatch {
methods, err := route.GetMethods()
if err != nil {
return nil, err
}

allMethods = append(allMethods, methods...)
}
return nil
})
}

return allMethods, err
return allMethods, nil
}
20 changes: 20 additions & 0 deletions middleware_test.go
Original file line number Diff line number Diff line change
@@ -478,6 +478,26 @@ func TestCORSMethodMiddleware(t *testing.T) {
}
}

func TestCORSMethodMiddlewareSubrouter(t *testing.T) {
router := NewRouter().StrictSlash(true)

subrouter := router.PathPrefix("/test").Subrouter()
subrouter.HandleFunc("/hello", stringHandler("a")).Methods(http.MethodGet, http.MethodOptions, http.MethodPost)
subrouter.HandleFunc("/hello/{name}", stringHandler("b")).Methods(http.MethodGet, http.MethodOptions)

subrouter.Use(CORSMethodMiddleware(subrouter))

rw := NewRecorder()
req := newRequest("GET", "/test/hello/asdf")
router.ServeHTTP(rw, req)

actualMethods := rw.Header().Get("Access-Control-Allow-Methods")
expectedMethods := "GET,OPTIONS"
if actualMethods != expectedMethods {
t.Fatalf("expected methods %q but got: %q", expectedMethods, actualMethods)
}
}

func TestMiddlewareOnMultiSubrouter(t *testing.T) {
first := "first"
second := "second"
28 changes: 14 additions & 14 deletions mux.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
package mux

import (
"context"
"errors"
"fmt"
"net/http"
@@ -58,8 +59,7 @@ type Router struct {

// If true, do not clear the request context after handling the request.
//
// Deprecated: No effect when go1.7+ is used, since the context is stored
// on the request itself.
// Deprecated: No effect, since the context is stored on the request itself.
KeepContext bool

// Slice of middlewares to be called after a match is found
@@ -111,10 +111,8 @@ func copyRouteConf(r routeConf) routeConf {
c.regexp.queries = append(c.regexp.queries, copyRouteRegexp(q))
}

c.matchers = make([]matcher, 0, len(r.matchers))
for _, m := range r.matchers {
c.matchers = append(c.matchers, m)
}
c.matchers = make([]matcher, len(r.matchers))
copy(c.matchers, r.matchers)

return c
}
@@ -197,8 +195,8 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
var handler http.Handler
if r.Match(req, &match) {
handler = match.Handler
req = setVars(req, match.Vars)
req = setCurrentRoute(req, match.Route)
req = requestWithVars(req, match.Vars)
req = requestWithRoute(req, match.Route)
}

if handler == nil && match.MatchErr == ErrMethodMismatch {
@@ -428,7 +426,7 @@ const (

// Vars returns the route variables for the current request, if any.
func Vars(r *http.Request) map[string]string {
if rv := contextGet(r, varsKey); rv != nil {
if rv := r.Context().Value(varsKey); rv != nil {
return rv.(map[string]string)
}
return nil
@@ -440,18 +438,20 @@ func Vars(r *http.Request) map[string]string {
// after the handler returns, unless the KeepContext option is set on the
// Router.
func CurrentRoute(r *http.Request) *Route {
if rv := contextGet(r, routeKey); rv != nil {
if rv := r.Context().Value(routeKey); rv != nil {
return rv.(*Route)
}
return nil
}

func setVars(r *http.Request, val interface{}) *http.Request {
return contextSet(r, varsKey, val)
func requestWithVars(r *http.Request, vars map[string]string) *http.Request {
ctx := context.WithValue(r.Context(), varsKey, vars)
return r.WithContext(ctx)
}

func setCurrentRoute(r *http.Request, val interface{}) *http.Request {
return contextSet(r, routeKey, val)
func requestWithRoute(r *http.Request, route *Route) *http.Request {
ctx := context.WithValue(r.Context(), routeKey, route)
return r.WithContext(ctx)
}

// ----------------------------------------------------------------------------
Loading