├── .hound.yml
├── mux
├── doc.go
├── route.go
├── benchmark_test.go
├── example_test.go
├── tree_test.go
├── tree.go
├── node.go
└── node_test.go
├── path
├── doc.go
├── benchmark_test.go
├── path.go
└── path_test.go
├── context
├── doc.go
├── context.go
├── context_test.go
├── param.go
└── param_test.go
├── middleware
├── doc.go
├── middleware_test.go
├── collection.go
├── middleware.go
└── collection_test.go
├── website
├── src
│ ├── static
│ │ ├── img
│ │ │ ├── logo.png
│ │ │ ├── fasthttp.png
│ │ │ ├── gopher_files.png
│ │ │ ├── gopher_http2.png
│ │ │ ├── gopher_search.png
│ │ │ ├── apple-touch-icon.png
│ │ │ ├── gopher_lowmemory.png
│ │ │ ├── gopher_middleware.png
│ │ │ ├── gpher_multidomain.png
│ │ │ ├── gopher_authentication.png
│ │ │ ├── android-chrome-192x192.png
│ │ │ ├── android-chrome-512x512.png
│ │ │ └── gopher.svg
│ │ ├── favicon
│ │ │ ├── favicon.ico
│ │ │ ├── favicon-16x16.png
│ │ │ └── favicon-32x32.png
│ │ ├── benchmarks
│ │ │ ├── fasthttp
│ │ │ │ ├── benchmark.png
│ │ │ │ ├── concurrency.png
│ │ │ │ ├── benchmark_alloc.png
│ │ │ │ ├── benchmark_latency.png
│ │ │ │ ├── concurrency_alloc.png
│ │ │ │ ├── benchmark-pipeline.png
│ │ │ │ ├── concurrency-pipeline.png
│ │ │ │ ├── concurrency_latency.png
│ │ │ │ ├── cpubound_benchmark.png
│ │ │ │ └── cpubound_concurrency.png
│ │ │ └── nethttp
│ │ │ │ ├── benchmark.png
│ │ │ │ ├── concurrency.png
│ │ │ │ ├── benchmark_alloc.png
│ │ │ │ ├── benchmark-pipeline.png
│ │ │ │ ├── benchmark_latency.png
│ │ │ │ ├── concurrency_alloc.png
│ │ │ │ ├── cpubound_benchmark.png
│ │ │ │ ├── concurrency-pipeline.png
│ │ │ │ ├── concurrency_latency.png
│ │ │ │ └── cpubound_concurrency.png
│ │ ├── site.webmanifest
│ │ └── css
│ │ │ └── custom.css
│ ├── .gitignore
│ ├── package.json
│ ├── sidebars.json
│ ├── pages
│ │ └── en
│ │ │ ├── users.js
│ │ │ ├── help.js
│ │ │ └── index.js
│ ├── core
│ │ └── Footer.js
│ ├── siteConfig.js
│ └── README.md
└── docs
│ ├── installation.md
│ ├── http2.md
│ ├── sub-router.md
│ ├── apphandler.md
│ ├── basic-example.md
│ ├── static-files.md
│ ├── panic.md
│ ├── https.md
│ ├── routing.md
│ ├── basic-authentication.md
│ ├── multidomain.md
│ ├── benchmark.md
│ └── middleware.md
├── .travis.yml
├── go.mod
├── route.go
├── .gitignore
├── .github
├── workflows
│ ├── test.yml
│ └── website-publish.yml
├── FUNDING.yml
├── CONTRIBUTING.md
└── CODE_OF_CONDUCT.md
├── go.sum
├── tree.go
├── LICENSE.md
├── doc.go
├── route_test.go
├── mocks_test.go
├── README.md
├── router.go
├── benchmark_test.go
├── nethttp.go
├── fasthttp.go
├── example_test.go
└── fasthttp_test.go
/.hound.yml:
--------------------------------------------------------------------------------
1 | go:
2 | enabled: true
3 |
--------------------------------------------------------------------------------
/mux/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Package mux provide route tree
3 | */
4 | package mux
5 |
--------------------------------------------------------------------------------
/path/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Package path provide path utils
3 | */
4 | package path
5 |
--------------------------------------------------------------------------------
/context/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Package context provide router context
3 | */
4 | package context
5 |
--------------------------------------------------------------------------------
/middleware/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Package middleware provide router middleware
3 | */
4 | package middleware
5 |
--------------------------------------------------------------------------------
/website/src/static/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/img/logo.png
--------------------------------------------------------------------------------
/website/src/static/img/fasthttp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/img/fasthttp.png
--------------------------------------------------------------------------------
/website/src/static/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/favicon/favicon.ico
--------------------------------------------------------------------------------
/website/src/static/img/gopher_files.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/img/gopher_files.png
--------------------------------------------------------------------------------
/website/src/static/img/gopher_http2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/img/gopher_http2.png
--------------------------------------------------------------------------------
/website/src/static/img/gopher_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/img/gopher_search.png
--------------------------------------------------------------------------------
/website/src/static/img/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/img/apple-touch-icon.png
--------------------------------------------------------------------------------
/website/src/static/img/gopher_lowmemory.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/img/gopher_lowmemory.png
--------------------------------------------------------------------------------
/mux/route.go:
--------------------------------------------------------------------------------
1 | package mux
2 |
3 | // Route is an handler aware route interface
4 | type Route interface {
5 | Handler() interface{}
6 | }
7 |
--------------------------------------------------------------------------------
/website/src/static/favicon/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/favicon/favicon-16x16.png
--------------------------------------------------------------------------------
/website/src/static/favicon/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/favicon/favicon-32x32.png
--------------------------------------------------------------------------------
/website/src/static/img/gopher_middleware.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/img/gopher_middleware.png
--------------------------------------------------------------------------------
/website/src/static/img/gpher_multidomain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/img/gpher_multidomain.png
--------------------------------------------------------------------------------
/website/src/static/img/gopher_authentication.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/img/gopher_authentication.png
--------------------------------------------------------------------------------
/website/src/static/img/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/img/android-chrome-192x192.png
--------------------------------------------------------------------------------
/website/src/static/img/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/img/android-chrome-512x512.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/fasthttp/benchmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/fasthttp/benchmark.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/nethttp/benchmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/nethttp/benchmark.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/nethttp/concurrency.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/nethttp/concurrency.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/fasthttp/concurrency.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/fasthttp/concurrency.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/fasthttp/benchmark_alloc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/fasthttp/benchmark_alloc.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/nethttp/benchmark_alloc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/nethttp/benchmark_alloc.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/fasthttp/benchmark_latency.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/fasthttp/benchmark_latency.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/fasthttp/concurrency_alloc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/fasthttp/concurrency_alloc.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/nethttp/benchmark-pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/nethttp/benchmark-pipeline.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/nethttp/benchmark_latency.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/nethttp/benchmark_latency.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/nethttp/concurrency_alloc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/nethttp/concurrency_alloc.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/nethttp/cpubound_benchmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/nethttp/cpubound_benchmark.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/fasthttp/benchmark-pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/fasthttp/benchmark-pipeline.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/fasthttp/concurrency-pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/fasthttp/concurrency-pipeline.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/fasthttp/concurrency_latency.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/fasthttp/concurrency_latency.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/fasthttp/cpubound_benchmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/fasthttp/cpubound_benchmark.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/fasthttp/cpubound_concurrency.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/fasthttp/cpubound_concurrency.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/nethttp/concurrency-pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/nethttp/concurrency-pipeline.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/nethttp/concurrency_latency.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/nethttp/concurrency_latency.png
--------------------------------------------------------------------------------
/website/src/static/benchmarks/nethttp/cpubound_concurrency.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vardius/gorouter/HEAD/website/src/static/benchmarks/nethttp/cpubound_concurrency.png
--------------------------------------------------------------------------------
/website/src/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | node_modules
4 | build
5 |
6 | lib/core/metadata.js
7 | lib/core/MetadataBlog.js
8 |
9 | /translated_docs
10 | /build/
11 | /node_modules
12 | /i18n/*
13 |
14 | yarn.lock
15 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 | go:
3 | - "1.15"
4 | - tip
5 | script:
6 | - go build
7 | - go test ./... -v -race -cover -coverprofile=coverage.txt -covermode=atomic
8 | - go test -bench=. -run=^$ -cpu=4 -benchmem
9 | after_script:
10 | - bash <(curl -s https://codecov.io/bash)
11 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/vardius/gorouter/v4
2 |
3 | go 1.20
4 |
5 | require github.com/valyala/fasthttp v1.50.0
6 |
7 | require (
8 | github.com/andybalholm/brotli v1.0.6 // indirect
9 | github.com/klauspost/compress v1.17.2 // indirect
10 | github.com/valyala/bytebufferpool v1.0.0 // indirect
11 | )
12 |
--------------------------------------------------------------------------------
/route.go:
--------------------------------------------------------------------------------
1 | package gorouter
2 |
3 | type route struct {
4 | handler interface{}
5 | }
6 |
7 | func newRoute(h interface{}) *route {
8 | if h == nil {
9 | panic("Handler can not be nil.")
10 | }
11 |
12 | return &route{
13 | handler: h,
14 | }
15 | }
16 |
17 | func (r *route) Handler() interface{} {
18 | // returns already cached computed handler
19 | return r.handler
20 | }
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.dll
4 | *.so
5 | *.dylib
6 |
7 | # Test binary, build with `go test -c`
8 | *.test
9 |
10 | # Output of the go coverage tool, specifically when used with LiteIDE
11 | *.out
12 |
13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
14 | .glide/
15 |
16 | .vscode
17 | .idea
18 |
19 | vendor/
20 |
21 | .history/
22 |
--------------------------------------------------------------------------------
/website/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "examples": "docusaurus-examples",
4 | "start": "docusaurus-start",
5 | "build": "docusaurus-build",
6 | "publish-gh-pages": "docusaurus-publish",
7 | "write-translations": "docusaurus-write-translations",
8 | "version": "docusaurus-version",
9 | "rename-version": "docusaurus-rename-version"
10 | },
11 | "devDependencies": {
12 | "docusaurus": "^1.14.4"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/context/context.go:
--------------------------------------------------------------------------------
1 | package context
2 |
3 | import (
4 | "context"
5 | )
6 |
7 | type key struct{}
8 |
9 | // WithParams stores params in context
10 | func WithParams(ctx context.Context, params Params) context.Context {
11 | return context.WithValue(ctx, key{}, params)
12 | }
13 |
14 | // Parameters extracts the request Params ctx, if present.
15 | func Parameters(ctx context.Context) (Params, bool) {
16 | params, ok := ctx.Value(key{}).(Params)
17 | return params, ok
18 | }
19 |
--------------------------------------------------------------------------------
/website/docs/installation.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: installation
3 | title: Installation
4 | sidebar_label: Installation
5 | ---
6 |
7 | Package **gorouter** provides request router with middleware.
8 |
9 | ## Installation
10 |
11 | Install the [gorouter](https://github.com/vardius/gorouter) package by calling the following command:
12 |
13 | ```bash
14 | go get -u github.com/vardius/gorouter
15 | ```
16 |
17 | Import package as follow
18 |
19 | ```go
20 | import "github.com/vardius/gorouter/v4"
21 | ```
22 |
--------------------------------------------------------------------------------
/website/src/static/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name":"vardius/gorouter",
3 | "short_name":"gorouter",
4 | "theme_color":"#ffffff",
5 | "background_color":"#ffffff",
6 | "display":"standalone",
7 | "icons":[
8 | {"src":"/img/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},
9 | {"src":"/img/android-chrome-512x512.png","sizes":"512x512","type":"image/png"},
10 | {"src": "img/apple-touch-icon.png","sizes": "48x48","type": "image/png"},
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/context/context_test.go:
--------------------------------------------------------------------------------
1 | package context
2 |
3 | import (
4 | "net/http"
5 | "testing"
6 | )
7 |
8 | func TestContext(t *testing.T) {
9 | req, err := http.NewRequest("GET", "/x", nil)
10 | if err != nil {
11 | t.Fatal(err)
12 | }
13 |
14 | p := Param{"test", "test"}
15 | params := Params{p}
16 |
17 | req = req.WithContext(WithParams(req.Context(), params))
18 | cParams, ok := Parameters(req.Context())
19 | if !ok {
20 | t.Fatal("Error while getting context")
21 | }
22 |
23 | if params.Value("test") != cParams.Value("test") {
24 | t.Error("Request returned invalid context")
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/context/param.go:
--------------------------------------------------------------------------------
1 | package context
2 |
3 | type (
4 | // Param object to hold request parameter
5 | Param struct {
6 | Key string
7 | Value string
8 | }
9 | // Params slice returned from request context
10 | Params []Param
11 | )
12 |
13 | // Value of the request parameter by name
14 | func (p Params) Value(key string) string {
15 | for i := range p {
16 | if p[i].Key == key {
17 | return p[i].Value
18 | }
19 | }
20 | return ""
21 | }
22 |
23 | // Set key value pair at index
24 | func (p Params) Set(index uint8, key string, value string) {
25 | p[index].Value = value
26 | p[index].Key = key
27 | }
28 |
--------------------------------------------------------------------------------
/website/src/sidebars.json:
--------------------------------------------------------------------------------
1 | {
2 | "docs": {
3 | "Quick Start": ["installation", "basic-example"],
4 | "Router": ["routing", "middleware", "sub-router"],
5 | "Examples": [
6 | {
7 | "type": "subcategory",
8 | "label": "Authentication",
9 | "ids": [
10 | "basic-authentication"
11 | ]
12 | },
13 | {
14 | "type": "subcategory",
15 | "label": "Serving Files",
16 | "ids": ["static-files"]
17 | },
18 | "https",
19 | "http2",
20 | "multidomain",
21 | "panic",
22 | "apphandler"
23 | ],
24 | "Benchmark": ["benchmark"]
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a golang project
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
3 |
4 | name: Go
5 |
6 | on:
7 | push:
8 | branches: [ "master" ]
9 | pull_request:
10 | branches: [ "master" ]
11 |
12 | jobs:
13 |
14 | build:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: actions/checkout@v3
18 |
19 | - name: Set up Go
20 | uses: actions/setup-go@v4
21 | with:
22 | go-version: '1.20'
23 |
24 | - name: Build
25 | run: go build -v ./...
26 |
27 | - name: Test
28 | run: go test -v ./...
29 |
--------------------------------------------------------------------------------
/website/src/static/css/custom.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present, Facebook, Inc.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | /* your custom css */
9 |
10 | @media only screen and (min-device-width: 360px) and (max-device-width: 736px) {
11 | }
12 |
13 | @media only screen and (min-width: 1024px) {
14 | }
15 |
16 | @media only screen and (max-width: 1023px) {
17 | }
18 |
19 | @media only screen and (min-width: 1400px) {
20 | }
21 |
22 | @media only screen and (min-width: 1500px) {
23 | }
24 |
25 | .codeSample {
26 | text-align: initial !important;
27 | }
28 |
--------------------------------------------------------------------------------
/path/benchmark_test.go:
--------------------------------------------------------------------------------
1 | package path
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func BenchmarkTrimSlash(b *testing.B) {
8 | path := "/pl/blog/comments/123/new/"
9 |
10 | b.ResetTimer()
11 | b.RunParallel(func(pb *testing.PB) {
12 | for pb.Next() {
13 | p := TrimSlash(path)
14 |
15 | if p != "pl/blog/comments/123/new" {
16 | b.Fatalf("%s", p)
17 | }
18 | }
19 | })
20 | }
21 |
22 | func BenchmarkGetPart(b *testing.B) {
23 | path := "pl/blog/comments/123/new"
24 |
25 | b.ResetTimer()
26 | b.RunParallel(func(pb *testing.PB) {
27 | for pb.Next() {
28 | p, _ := GetPart(path)
29 |
30 | if p != "pl" {
31 | b.Fatalf("%s", p)
32 | }
33 | }
34 | })
35 | }
36 |
--------------------------------------------------------------------------------
/middleware/middleware_test.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestMiddleware_WithPriority(t *testing.T) {
8 | type test struct {
9 | name string
10 | middleware Middleware
11 | priority uint
12 | }
13 | tests := []test{
14 | {"Zero", mockMiddleware("Zero"), 0},
15 | {"Positive", mockMiddleware("Positive"), 1},
16 | {"Positive Large", mockMiddleware("Positive Large"), 999},
17 | }
18 | for _, tt := range tests {
19 | t.Run(tt.name, func(t *testing.T) {
20 | m := WithPriority(tt.middleware, tt.priority)
21 | if got := m.Priority(); got != tt.priority {
22 | t.Errorf("Priority() = %v, want %v", got, tt.priority)
23 | }
24 | })
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
2 | github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
3 | github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
4 | github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
5 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
6 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
7 | github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
8 | github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
9 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [vardius] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with a single custom sponsorship URL
13 |
--------------------------------------------------------------------------------
/middleware/collection.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "sort"
5 | )
6 |
7 | // Collection is a slice of handler wrappers functions
8 | type Collection []Middleware
9 |
10 | // NewCollection provides new middleware
11 | func NewCollection(ms ...Middleware) Collection {
12 | return ms
13 | }
14 |
15 | // Merge merges another middleware
16 | func (c Collection) Merge(m Collection) Collection {
17 | return append(c, m...)
18 | }
19 |
20 | // Compose returns middleware composed to single WrapperFunc
21 | func (c Collection) Compose(h Handler) Handler {
22 | if h == nil {
23 | return nil
24 | }
25 |
26 | for i := range c {
27 | h = c[len(c)-1-i].Wrap(h)
28 | }
29 |
30 | return h
31 | }
32 |
33 | // Sort sorts collection by priority
34 | func (c Collection) Sort() Collection {
35 | sort.SliceStable(c, func(i, j int) bool {
36 | return c[i].Priority() < c[j].Priority()
37 | })
38 |
39 | return c
40 | }
41 |
--------------------------------------------------------------------------------
/.github/workflows/website-publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish website to GitHub Pages
2 | on:
3 | # Trigger the workflow on push or pull request,
4 | # but only for the master branch
5 | push:
6 | branches:
7 | - master
8 | jobs:
9 | publish:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Copy Files
13 | uses: actions/checkout@v2
14 | - name: Setup Git
15 | run: |
16 | git config --global user.name "$GITHUB_ACTOR"
17 | git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com"
18 | - name: install
19 | run: cd website/src && yarn install
20 | - name: build
21 | run: cd website/src && yarn build
22 | - name: Publish website
23 | env:
24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
25 | run: cd website/src && GIT_USER="$GITHUB_ACTOR:$GITHUB_TOKEN" CURRENT_BRANCH=master yarn run publish-gh-pages
26 |
--------------------------------------------------------------------------------
/tree.go:
--------------------------------------------------------------------------------
1 | package gorouter
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/vardius/gorouter/v4/mux"
7 | )
8 |
9 | func allowed(t mux.Tree, method, path string) (allow string) {
10 | if path == "*" {
11 | // tree roots should be http method nodes only
12 | for _, root := range t {
13 | if root.Name() == http.MethodOptions {
14 | continue
15 | }
16 | if len(allow) == 0 {
17 | allow = root.Name()
18 | } else {
19 | allow += ", " + root.Name()
20 | }
21 | }
22 | } else {
23 | // tree roots should be http method nodes only
24 | for _, root := range t {
25 | if root.Name() == method || root.Name() == http.MethodOptions {
26 | continue
27 | }
28 |
29 | if route, _ := root.Tree().MatchRoute(path); route != nil {
30 | if len(allow) == 0 {
31 | allow = root.Name()
32 | } else {
33 | allow += ", " + root.Name()
34 | }
35 | }
36 | }
37 | }
38 | if len(allow) > 0 {
39 | allow += ", " + http.MethodOptions
40 | }
41 | return allow
42 | }
43 |
--------------------------------------------------------------------------------
/context/param_test.go:
--------------------------------------------------------------------------------
1 | package context
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestParamValue(t *testing.T) {
8 | key := "key"
9 | value := "value"
10 |
11 | p := Param{key, value}
12 | params := Params{p}
13 |
14 | if params.Value(key) != value {
15 | t.Error("Invalid parameter value")
16 | }
17 | }
18 |
19 | func TestParamsSet(t *testing.T) {
20 | p := make(Params, 5)
21 | type args struct {
22 | index uint8
23 | key string
24 | value string
25 | }
26 | tests := []struct {
27 | name string
28 | p Params
29 | args args
30 | }{
31 | {"one", p, args{0, "one", "one"}},
32 | {"two", p, args{0, "two", "two"}},
33 | {"three", p, args{0, "three", "three"}},
34 | {"four", p, args{0, "four", "four"}},
35 | {"five", p, args{0, "five", "five"}},
36 | }
37 | for _, tt := range tests {
38 | t.Run(tt.name, func(t *testing.T) {
39 | tt.p.Set(tt.args.index, tt.args.key, tt.args.value)
40 |
41 | if p.Value(tt.args.key) != tt.args.value {
42 | t.Error("Invalid parameter value")
43 | }
44 | })
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017-present Rafał Lorenz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/website/docs/http2.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: http2
3 | title: HTTP2
4 | sidebar_label: HTTP2
5 | ---
6 |
7 | ## [The Go Blog - HTTP/2 Server Push](https://blog.golang.org/h2push)
8 |
9 | ## Pusher
10 |
11 |
12 | ```go
13 | package main
14 |
15 | import (
16 | "log"
17 | "net/http"
18 |
19 | "golang.org/x/net/http2"
20 | "github.com/vardius/gorouter/v4"
21 | )
22 |
23 | func Pusher(w http.ResponseWriter, r *http.Request) {
24 | if pusher, ok := w.(http.Pusher); ok {
25 | // Push is supported.
26 | options := &http.PushOptions{
27 | Header: http.Header{
28 | "Accept-Encoding": r.Header["Accept-Encoding"],
29 | },
30 | }
31 | if err := pusher.Push("/script.js", options); err != nil {
32 | log.Printf("Failed to push: %v", err)
33 | }
34 | }
35 | // ...
36 | }
37 |
38 | func main() {
39 | router := gorouter.New()
40 | router.GET("/", http.HandlerFunc(Pusher))
41 |
42 | http2.ConfigureServer(router, &http2.Server{})
43 | log.Fatal(router.ListenAndServeTLS("router.crt", "router.key"))
44 | }
45 | ```
46 |
47 | HTTP/2 implementation for fasthttp is [under construction...](https://github.com/fasthttp/http2)
48 |
--------------------------------------------------------------------------------
/middleware/middleware.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | // Handler represents wrapped function
4 | type Handler interface{}
5 |
6 | // Middleware wraps Handler
7 | type Middleware interface {
8 | // Wrap Handler with middleware
9 | Wrap(Handler) Handler
10 | // Priority provides a value for sorting Collection, lower values come first
11 | Priority() uint
12 | }
13 |
14 | // WrapperFunc is an adapter to allow the use of
15 | // handler wrapper functions as middleware functions.
16 | type WrapperFunc func(Handler) Handler
17 |
18 | // Wrap implements Wrapper interface
19 | func (f WrapperFunc) Wrap(h Handler) Handler {
20 | return f(h)
21 | }
22 |
23 | // Priority provides a value for sorting Collection, lower values come first
24 | func (f WrapperFunc) Priority() (priority uint) {
25 | return
26 | }
27 |
28 | // Middleware is a slice of handler wrappers functions
29 | type sortableMiddleware struct {
30 | Middleware
31 | priority uint
32 | }
33 |
34 | // Priority provides a value for sorting Collection, lower values come first
35 | func (m *sortableMiddleware) Priority() uint {
36 | return m.priority
37 | }
38 |
39 | // WithPriority provides new Middleware with priority
40 | func WithPriority(middleware Middleware, priority uint) Middleware {
41 | return &sortableMiddleware{
42 | Middleware: middleware,
43 | priority: priority,
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/website/docs/sub-router.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: sub-router
3 | title: Mounting Sub-Router
4 | sidebar_label: Mounting Sub-Router
5 | ---
6 |
7 | When having multiple instance of a router you might want to mount one as a sub router of another under some route path, still keeping all middleware.
8 |
9 | It doesn't have to be [gorouter](github.com/vardius/gorouter). You can mount other routers as well as long they implement `http.Handler` interface.
10 |
11 | ## Mount
12 |
13 |
14 |
15 | ```go
16 | package main
17 |
18 | import (
19 | "log"
20 | "net/http"
21 |
22 | "github.com/vardius/gorouter/v4"
23 | )
24 |
25 | func main() {
26 | router := gorouter.New()
27 | subrouter := gorouter.New()
28 |
29 | router.Mount("/{param}", subrouter)
30 |
31 | log.Fatal(http.ListenAndServe(":8080", router))
32 | }
33 | ```
34 |
35 | ```go
36 | package main
37 |
38 | import (
39 | "log"
40 |
41 | "github.com/valyala/fasthttp"
42 | "github.com/vardius/gorouter/v4"
43 | )
44 |
45 | func main() {
46 | router := gorouter.NewFastHTTPRouter()
47 | subrouter := gorouter.NewFastHTTPRouter()
48 |
49 | router.Mount("/{param}", subrouter)
50 |
51 | log.Fatal(fasthttp.ListenAndServe(":8080", router.HandleFastHTTP))
52 | }
53 | ```
54 |
55 |
56 | Given example will result in all routes of a `subrouter` being available under paths prefixed with a mount path.
--------------------------------------------------------------------------------
/path/path.go:
--------------------------------------------------------------------------------
1 | package path
2 |
3 | import "strings"
4 |
5 | // TrimSlash trims '/' URL path
6 | func TrimSlash(path string) string {
7 | pathLen := len(path)
8 | if pathLen > 0 && path[0] == '/' {
9 | path = path[1:]
10 | pathLen--
11 | }
12 |
13 | if pathLen > 0 && path[pathLen-1] == '/' {
14 | path = path[:pathLen-1]
15 | }
16 |
17 | return path
18 | }
19 |
20 | // GetPart returns first path part and next path as a second argument
21 | func GetPart(path string) (part string, nextPath string) {
22 | if j := strings.IndexByte(path, '/'); j > 0 {
23 | part = path[:j]
24 | nextPath = path[j+1:]
25 | } else {
26 | part = path
27 | nextPath = ""
28 | }
29 |
30 | return
31 | }
32 |
33 | // GetNameFromPart gets node name from path part
34 | func GetNameFromPart(pathPart string) (name string, exp string) {
35 | name = pathPart
36 |
37 | if pathPart[0] == '{' {
38 | name = pathPart[1 : len(pathPart)-1]
39 |
40 | if parts := strings.Split(name, ":"); len(parts) == 2 {
41 | name = parts[0]
42 | exp = parts[1]
43 | }
44 |
45 | if name == "" {
46 | panic("Empty wildcard name")
47 | }
48 |
49 | return
50 | }
51 |
52 | return
53 | }
54 |
55 | func StripLeadingSlashes(path string, stripSlashes int) string {
56 | for stripSlashes > 0 && len(path) > 0 {
57 | n := strings.IndexByte(path[1:], '/')
58 | if n < 0 {
59 | path = path[:0]
60 | break
61 | }
62 | path = path[n+1:]
63 | stripSlashes--
64 | }
65 | return path
66 | }
67 |
--------------------------------------------------------------------------------
/website/src/pages/en/users.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present, Facebook, Inc.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | const React = require("react");
9 |
10 | const CompLibrary = require("../../core/CompLibrary.js");
11 |
12 | const Container = CompLibrary.Container;
13 |
14 | class Users extends React.Component {
15 | render() {
16 | const { config: siteConfig } = this.props;
17 | if ((siteConfig.users || []).length === 0) {
18 | return null;
19 | }
20 |
21 | const editUrl = `${siteConfig.repoUrl}/edit/master/website/siteConfig.js`;
22 | const showcase = siteConfig.users.map(user => (
23 |
24 |
25 |
26 | ));
27 |
28 | return (
29 |
This project is used by many folks
35 |Are you using this project?
38 | 39 | Add your company 40 | 41 |This project is maintained by a dedicated group of people.
59 |
11 |
12 | Go Server/API micro framework, HTTP request router, multiplexer, mux.
13 |
14 | 📖 ABOUT
15 | ==================================================
16 | Contributors:
17 |
18 | * [Rafał Lorenz](https://rafallorenz.com)
19 |
20 | Want to contribute ? Feel free to send pull requests!
21 |
22 | Have problems, bugs, feature ideas?
23 | We are using the github [issue tracker](https://github.com/vardius/gorouter/issues) to manage them.
24 |
25 | ## 📚 Documentation
26 |
27 | For **documentation** (_including examples_), **visit [rafallorenz.com/gorouter](https://rafallorenz.com/gorouter)**
28 |
29 | For **GoDoc** reference, **visit [pkg.go.dev](https://pkg.go.dev/github.com/vardius/gorouter)**
30 |
31 | ## 🚅 Benchmark
32 |
33 | ```go
34 | ➜ gorouter git:(master) ✗ go test -bench=. -cpu=4 -benchmem
35 | test
36 | goos: darwin
37 | goarch: amd64
38 | pkg: github.com/vardius/gorouter/v4
39 | BenchmarkNetHTTP-4 65005786 17.9 ns/op 0 B/op 0 allocs/op
40 | BenchmarkFastHTTP-4 69810878 16.5 ns/op 0 B/op 0 allocs/op
41 | PASS
42 | ok github.com/vardius/gorouter/v4 3.808s
43 | ```
44 |
45 | 👉 **[Click here](https://rafallorenz.com/gorouter/docs/benchmark)** to see all benchmark results.
46 |
47 | ## Features
48 | - Routing System
49 | - Middleware System
50 | - Authentication
51 | - Fast HTTP
52 | - Serving Files
53 | - Multidomain
54 | - HTTP2 Support
55 | - Low memory usage
56 | - [Documentation](https://rafallorenz.com/gorouter/)
57 |
58 | 🚏 HOW TO USE
59 | ==================================================
60 |
61 | - [Basic example](https://rafallorenz.com/gorouter/docs/basic-example)
62 | - [net/http](https://rafallorenz.com/gorouter/docs/basic-example#nethttp)
63 | - [valyala/fasthttp](https://rafallorenz.com/gorouter/docs/basic-example#fasthttp)
64 |
65 | ## 🖥️ API example setup
66 |
67 | - **[Go Server/API boilerplate](https://github.com/vardius/go-api-boilerplate)** using best practices DDD CQRS ES.
68 |
69 | 📜 [License](LICENSE.md)
70 | -------
71 |
72 | This package is released under the MIT license. See the complete license in the package:
73 |
74 | [](https://app.fossa.io/projects/git%2Bgithub.com%2Fvardius%2Fgorouter?ref=badge_large)
75 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at vardius@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/website/docs/basic-authentication.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: basic-authentication
3 | title: Basic Authentication
4 | sidebar_label: Basic Authentication
5 | ---
6 |
7 | ## Basic Authentication
8 |
9 |
10 |
11 | ```go
12 | package main
13 |
14 | import (
15 | "fmt"
16 | "log"
17 | "net/http"
18 | "crypto/subtle"
19 |
20 | "github.com/vardius/gorouter/v4"
21 | )
22 |
23 | var (
24 | requiredUser = []byte("gordon")
25 | requiredPassword = []byte("secret!")
26 | )
27 |
28 | func BasicAuth(next http.Handler) http.Handler {
29 | fn := func(w http.ResponseWriter, r *http.Request) {
30 | // Get the Basic Authentication credentials
31 | user, password, hasAuth := r.BasicAuth()
32 |
33 | if !hasAuth || subtle.ConstantTimeCompare(requiredUser, []byte(user)) != 1 || subtle.ConstantTimeCompare(requiredPassword, []byte(password)) != 1 {
34 | w.Header().Set("WWW-Authenticate", "Basic realm=Restricted")
35 | http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
36 | return
37 | }
38 |
39 | next.ServeHTTP(w, r)
40 | }
41 |
42 | return http.HandlerFunc(fn)
43 | }
44 |
45 | func index(w http.ResponseWriter, r *http.Request) {
46 | fmt.Fprint(w, "Not protected!\n")
47 | }
48 |
49 | func protected(w http.ResponseWriter, r *http.Request) {
50 | fmt.Fprint(w, "Protected!\n")
51 | }
52 |
53 | func main() {
54 | router := gorouter.New()
55 | router.GET("/", http.HandlerFunc(index))
56 | router.GET("/protected", http.HandlerFunc(protected))
57 |
58 | router.USE("GET", "/protected", BasicAuth)
59 |
60 | log.Fatal(http.ListenAndServe(":8080", router))
61 | }
62 | ```
63 |
64 | ```go
65 | package main
66 |
67 | import (
68 | "crypto/subtle"
69 | "bytes"
70 | "encoding/base64"
71 | "fmt"
72 | "log"
73 |
74 | "github.com/valyala/fasthttp"
75 | "github.com/vardius/gorouter/v4"
76 | )
77 |
78 | var (
79 | basicAuthPrefix = []byte("Basic ")
80 | requiredUser = []byte("gordon")
81 | requiredPassword = []byte("secret!")
82 | )
83 |
84 | func BasicAuth(next fasthttp.RequestHandler) fasthttp.RequestHandler {
85 | fn := func(ctx *fasthttp.RequestCtx) {
86 | // Get the Basic Authentication credentials
87 | auth := ctx.Request.Header.Peek("Authorization")
88 | if bytes.HasPrefix(auth, basicAuthPrefix) {
89 | // Check credentials
90 | payload, err := base64.StdEncoding.DecodeString(string(auth[len(basicAuthPrefix):]))
91 | if err == nil {
92 | pair := bytes.SplitN(payload, []byte(":"), 2)
93 | if len(pair) == 2 && subtle.ConstantTimeCompare(requiredUser, pair[0]) == 1 && subtle.ConstantTimeCompare(requiredPassword, pair[1]) == 1 {
94 | // Delegate request to the given handle
95 | next(ctx)
96 | return
97 | }
98 | }
99 | }
100 |
101 | // Request Basic Authentication otherwise
102 | ctx.Response.Header.Set("WWW-Authenticate", "Basic realm=Restricted")
103 | ctx.Error(fasthttp.StatusMessage(fasthttp.StatusUnauthorized), fasthttp.StatusUnauthorized)
104 | }
105 |
106 | return fn
107 | }
108 |
109 | func index(_ *fasthttp.RequestCtx) {
110 | fmt.Print("Not Protected!\n")
111 | }
112 |
113 | func protected(_ *fasthttp.RequestCtx) {
114 | fmt.Print("Protected!\n")
115 | }
116 |
117 | func main() {
118 | router := gorouter.NewFastHTTPRouter()
119 | router.GET("/", index)
120 | router.GET("/protected", protected)
121 |
122 | router.USE("GET", "/protected", BasicAuth)
123 |
124 | log.Fatal(fasthttp.ListenAndServe(":8080", router.HandleFastHTTP))
125 | }
126 | ```
127 |
128 |
--------------------------------------------------------------------------------
/website/src/core/Footer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present, Facebook, Inc.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | const React = require("react");
9 |
10 | class Footer extends React.Component {
11 | docUrl(doc, language) {
12 | const baseUrl = this.props.config.baseUrl;
13 | const docsUrl = this.props.config.docsUrl;
14 | const docsPart = `${docsUrl ? `${docsUrl}/` : ""}`;
15 | const langPart = `${language ? `${language}/` : ""}`;
16 | return `${baseUrl}${docsPart}${langPart}${doc}`;
17 | }
18 |
19 | pageUrl(doc, language) {
20 | const baseUrl = this.props.config.baseUrl;
21 | return baseUrl + (language ? `${language}/` : "") + doc;
22 | }
23 |
24 | render() {
25 | return (
26 |
96 | );
97 | }
98 | }
99 |
100 | module.exports = Footer;
101 |
--------------------------------------------------------------------------------
/website/docs/multidomain.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: multidomain
3 | title: Multidomain
4 | sidebar_label: Multidomain
5 | ---
6 |
7 | ## HostSwitch
8 |
9 |
10 |
11 | ```go
12 | import (
13 | "fmt"
14 | "log"
15 | "net/http"
16 |
17 | "github.com/vardius/gorouter/v4"
18 | "github.com/vardius/gorouter/v4/context"
19 | )
20 |
21 | // We need an object that implements the http.Handler interface.
22 | // Therefore we need a type for which we implement the ServeHTTP method.
23 | // We just use a map here, in which we map host names (with port) to http.Handlers
24 | type HostSwitch map[string]http.Handler
25 |
26 | // Implement the ServerHTTP method on our new type
27 | func (hs HostSwitch) ServeHTTP(w http.ResponseWriter, r *http.Request) {
28 | // Check if a http.Handler is registered for the given host.
29 | // If yes, use it to handle the request.
30 | if handler := hs[r.Host]; handler != nil {
31 | handler.ServeHTTP(w, r)
32 | } else {
33 | // Handle host names for wich no handler is registered
34 | http.Error(w, "Forbidden", 403) // Or Redirect?
35 | }
36 | }
37 |
38 | func index(w http.ResponseWriter, _ *http.Request) {
39 | if _, err := fmt.Fprint(w, "Welcome!\n"); err != nil {
40 | panic(err)
41 | }
42 | }
43 |
44 | func hello(w http.ResponseWriter, r *http.Request) {
45 | params, _ := context.Parameters(r.Context())
46 | if _, err := fmt.Fprintf(w, "hello, %s!\n", params.Value("name")); err != nil {
47 | panic(err)
48 | }
49 | }
50 |
51 | func main() {
52 | // Initialize a router as usual
53 | router := gorouter.New()
54 | router.GET("/", http.HandlerFunc(Index))
55 | router.GET("/hello/{name}", http.HandlerFunc(Hello))
56 |
57 | // Make a new HostSwitch and insert the router (our http handler)
58 | // for example.com and port 12345
59 | hs := make(HostSwitch)
60 | hs["example.com:12345"] = router
61 |
62 | // Use the HostSwitch to listen and serve on port 12345
63 | log.Fatal(http.ListenAndServe(":12345", hs))
64 | }
65 | ```
66 |
67 | ```go
68 | package main
69 |
70 | import (
71 | "fmt"
72 | "log"
73 |
74 | "github.com/valyala/fasthttp"
75 | "github.com/vardius/gorouter/v4"
76 | "github.com/vardius/gorouter/v4/context"
77 | )
78 |
79 | // HostSwitch is the host-handler map
80 | // We need an object that implements the fasthttp.RequestHandler interface.
81 | // We just use a map here, in which we map host names (with port) to fasthttp.RequestHandlers
82 | type HostSwitch map[string]fasthttp.RequestHandler
83 |
84 | // CheckHost Implement a CheckHost method on our new type
85 | func (hs HostSwitch) CheckHost(ctx *fasthttp.RequestCtx) {
86 | // Check if a http.Handler is registered for the given host.
87 | // If yes, use it to handle the request.
88 | if handler := hs[string(ctx.Host())]; handler != nil {
89 | handler(ctx)
90 | } else {
91 | // Handle host names for wich no handler is registered
92 | ctx.Error("Forbidden", 403) // Or Redirect?
93 | }
94 | }
95 |
96 | func index(_ *fasthttp.RequestCtx) {
97 | fmt.Print("Welcome!\n")
98 | }
99 |
100 | func hello(ctx *fasthttp.RequestCtx) {
101 | params := ctx.UserValue("params").(context.Params)
102 | fmt.Printf("Hello, %s!\n", params.Value("name"))
103 | }
104 |
105 | func main() {
106 | router := gorouter.NewFastHTTPRouter()
107 | router.GET("/", index)
108 | router.GET("/hello/{name}", hello)
109 |
110 | // Make a new HostSwitch and insert the router (our http handler)
111 | // for example.com and port 12345
112 | hs := make(HostSwitch)
113 | hs["example.com:12345"] = router.HandleFastHTTP
114 |
115 | // Use the HostSwitch to listen and serve on port 12345
116 | log.Fatal(fasthttp.ListenAndServe(":12345", hs.CheckHost))
117 | }
118 | ```
119 |
120 |
--------------------------------------------------------------------------------
/middleware/collection_test.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "net/http"
5 | "net/http/httptest"
6 | "testing"
7 | )
8 |
9 | func mockMiddleware(body string) Middleware {
10 | fn := func(h Handler) Handler {
11 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
12 | if _, err := w.Write([]byte(body)); err != nil {
13 | panic(err)
14 | }
15 | h.(http.Handler).ServeHTTP(w, r)
16 | })
17 | }
18 |
19 | return WrapperFunc(fn)
20 | }
21 |
22 | func TestCollection(t *testing.T) {
23 | middlewareFactory := func(body string, priority uint) Middleware {
24 | fn := func(h Handler) Handler {
25 | return func() string { return body + h.(func() string)() }
26 | }
27 |
28 | return WithPriority(WrapperFunc(fn), priority)
29 | }
30 | type test struct {
31 | name string
32 | m Collection
33 | output string
34 | sortedOutput string
35 | }
36 | tests := []test{
37 | {"Empty", NewCollection(), "h", "h"},
38 | {"Single middleware", NewCollection(middlewareFactory("0", 0)), "0h", "0h"},
39 | {"Multiple unsorted middleware", NewCollection(middlewareFactory("3", 3), middlewareFactory("1", 1), middlewareFactory("2", 2)), "312h", "123h"},
40 | {"Multiple unsorted middleware 2", NewCollection(middlewareFactory("2", 2), middlewareFactory("1", 1), middlewareFactory("3", 3)), "213h", "123h"},
41 | {"Multiple unsorted middleware 3", NewCollection(middlewareFactory("1", 1), middlewareFactory("3", 3), middlewareFactory("2", 2)), "132h", "123h"},
42 | {"Multiple sorted middleware", NewCollection(middlewareFactory("1", 1), middlewareFactory("2", 2), middlewareFactory("3", 3)), "123h", "123h"},
43 | }
44 | for _, tt := range tests {
45 | t.Run(tt.name, func(t *testing.T) {
46 | m := NewCollection(tt.m...)
47 | h := m.Compose(func() string { return "h" })
48 |
49 | result := h.(func() string)()
50 |
51 | if h.(func() string)() != tt.output {
52 | t.Errorf("NewCollection: h() = %v, want %v", result, tt.output)
53 | }
54 |
55 | h = m.Sort().Compose(func() string { return "h" })
56 |
57 | result = h.(func() string)()
58 |
59 | if h.(func() string)() != tt.sortedOutput {
60 | t.Errorf("NewCollection: h() = %v, want %v", result, tt.sortedOutput)
61 | }
62 | })
63 | }
64 | }
65 |
66 | func TestWithPriority(t *testing.T) {
67 | m1 := WithPriority(mockMiddleware("1"), 3)
68 | m2 := WithPriority(mockMiddleware("2"), 2)
69 | m3 := WithPriority(mockMiddleware("3"), 1)
70 | fn := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
71 | if _, err := w.Write([]byte("4")); err != nil {
72 | t.Fatal(err)
73 | }
74 | })
75 |
76 | m := NewCollection(m1, m2, m3)
77 | h := m.Sort().Compose(fn).(http.Handler)
78 |
79 | w := httptest.NewRecorder()
80 | r, err := http.NewRequest("GET", "/", nil)
81 | if err != nil {
82 | t.Fatal(err)
83 | }
84 |
85 | h.ServeHTTP(w, r)
86 |
87 | if w.Body.String() != "3214" {
88 | t.Error("The order is incorrect")
89 | }
90 | }
91 |
92 | func TestMerge(t *testing.T) {
93 | m1 := mockMiddleware("1")
94 | m2 := mockMiddleware("2")
95 | m3 := mockMiddleware("3")
96 | fn := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
97 | if _, err := w.Write([]byte("4")); err != nil {
98 | t.Fatal(err)
99 | }
100 | })
101 |
102 | m := NewCollection(m1)
103 | m = m.Merge(NewCollection(m2, m3))
104 | h := m.Compose(fn).(http.Handler)
105 |
106 | w := httptest.NewRecorder()
107 | r, err := http.NewRequest("GET", "/", nil)
108 | if err != nil {
109 | t.Fatal(err)
110 | }
111 |
112 | h.ServeHTTP(w, r)
113 |
114 | if w.Body.String() != "1234" {
115 | t.Errorf("The order is incorrect expected: 1234 actual: %s", w.Body.String())
116 | }
117 | }
118 |
119 | func TestCompose(t *testing.T) {
120 | m := NewCollection(mockMiddleware("1"))
121 | h := m.Compose(nil)
122 |
123 | if h != nil {
124 | t.Fail()
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/website/src/siteConfig.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017-present, Facebook, Inc.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | // See https://docusaurus.io/docs/site-config for all the possible
9 | // site configuration options.
10 |
11 | // List of projects/orgs using your project for the users page.
12 | const users = [
13 | // {
14 | // caption: "go-api-boilerplate",
15 | // // You will need to prepend the image path with your baseUrl
16 | // // if it is not '/', like: '/test-site/img/image.jpg'.
17 | // image: "/img/undraw_open_source.svg",
18 | // infoLink: "https://github.com/vardius/go-api-boilerplate",
19 | // pinned: true
20 | // }
21 | ];
22 |
23 | const siteConfig = {
24 | title: "gorouter", // Title for your website.
25 | tagline:
26 | "Go Server/API micro framework, HTTP request router, multiplexer, mux",
27 | url: "https://rafallorenz.com/", // Your website URL
28 | baseUrl: "/gorouter/", // Base URL for your project */
29 | // For github.io type URLs, you would set the url and baseUrl like:
30 | // url: 'https://facebook.github.io',
31 | // baseUrl: '/test-site/',
32 |
33 | // Used for publishing and more
34 | projectName: "gorouter",
35 | organizationName: "vardius",
36 | // For top-level user or org sites, the organization is still the same.
37 | // e.g., for the https://JoelMarcey.github.io site, it would be set like...
38 | // organizationName: 'JoelMarcey'
39 |
40 | // For no header links in the top nav bar -> headerLinks: [],
41 | headerLinks: [
42 | { doc: "installation", label: "Docs" },
43 | { page: "help", label: "Help" },
44 | // { page: "users", label: "Users" },
45 | { href: "https://github.com/vardius/gorouter", label: "GitHub" }
46 | // { blog: true, label: 'Blog'},
47 | // { languages: true },
48 | // { search: true }
49 | ],
50 |
51 | docsSideNavCollapsible: true,
52 |
53 | // If you have users set above, you add it here:
54 | users,
55 |
56 | /* path to images for header/footer */
57 | headerIcon: "img/apple-touch-icon.png",
58 | footerIcon: "img/apple-touch-icon.png",
59 | favicon: "favicon/favicon.ico",
60 |
61 | /* Colors for website */
62 | colors: {
63 | primaryColor: "#3e4f4e",
64 | secondaryColor: "#6e8d7c"
65 | },
66 |
67 | /* Custom fonts for website */
68 | /*
69 | fonts: {
70 | myFont: [
71 | "Times New Roman",
72 | "Serif"
73 | ],
74 | myOtherFont: [
75 | "-apple-system",
76 | "system-ui"
77 | ]
78 | },
79 | */
80 |
81 | // This copyright info is used in /core/Footer.js and blog RSS/Atom feeds.
82 | copyright: `Copyright © ${new Date().getFullYear()} Rafał Lorenz`,
83 |
84 | highlight: {
85 | // Highlight.js theme to use for syntax highlighting in code blocks.
86 | theme: "darcula"
87 | },
88 |
89 | // Add custom scripts here that would be placed in