garcon

package module
v0.23.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 19, 2022 License: MIT Imports: 52 Imported by: 7

README

Teal.Finance/Garcon

logo Garcon works with all HTTP routers ans middleware respecting the Go HTTP standards. Garcon provides the batteries: static website server, contact-form backend, API helpers, debugging helpers (PProf), Git version, metrics server (Prometheus), URI sanitization and middleware: rate-limiter, JWT cookies, CORS, traffic logs, OPA…
Go Reference Go Report Card

Motivation

Many projects often start with one of the many nice HTTP routers and middleware already available and develop their own middleware, debugging server, API helpers... At Teal.Finance, we decided to share in one place (here) all our stuff in the idea to let other projects go faster.

Middleware

Our Middleware are very easy to setup. They respect the Go standards. Thus you can easily use them with the HTTP router of your choice and chained them with other middleware:

  • MiddlewareLogRequest Log incoming requests (with or without browser fingerprint)
  • MiddlewareLogDuration Log processing time
  • MiddlewareExportTrafficMetrics Export web traffic metrics
  • MiddlewareRejectUnprintableURI Reject request with unwanted characters
  • MiddlewareRateLimiter Limit incoming request to prevent flooding
  • MiddlewareServerHeader Add the "Server" HTTP header in the response
  • JWTChecker JWT management using HttpOnly cookie or Authorization header
  • IncorruptibleChecker Session cookie with Incorruptible token
  • MiddlewareCORS Cross-Origin Resource Sharing (CORS)
  • MiddlewareOPA Authenticate from Datalog/Rego files using Open Policy Agent
  • MiddlewareSecureHTTPHeader Set some HTTP header to increase the web security
g := garcon.New()
middleware = garcon.NewChain(
    g.MiddlewareRejectUnprintableURI(),
    g.MiddlewareLogRequest(),
    g.MiddlewareRateLimiter())
router  := ... you choose
handler := middleware.Then(router)
server  := http.Server{Addr: ":8080", Handler: handler}
server.ListenAndServe()

Other features

  • Static web files server supporting Brotli and AVIF
  • Metrics server exporting data to Prometheus (or other compatible monitoring tool)
  • PProf server for debugging purpose
  • Serialize JSON responses, including the error messages
  • Chained middleware (fork of justinas/alice)
  • Chained round trip handlers
  • Retrieve Git version, branch and commit from build flags and Go module information

Basic example

g := garcon.New()

// chain some middleware
middleware = garcon.NewChain(
    g.MiddlewareRejectUnprintableURI(),
    g.MiddlewareLogRequests(),
    g.MiddlewareRateLimiter())

// use the HTTP router library of your choice, here we use Chi 
router := chi.NewRouter()

// static website, automatically sends the Brotli-compressed file if present and supported by the browser
ws := g.NewStaticWebServer("/var/www")
router.Get("/", ws.ServeFile("index.html", "text/html; charset=utf-8"))
router.Get("/favicon.ico", ws.ServeFile("favicon.ico", "image/x-icon"))
router.Get("/js/*", ws.ServeDir("text/javascript; charset=utf-8"))
router.Get("/css/*", ws.ServeDir("text/css; charset=utf-8"))
router.Get("/images/*", ws.ServeImages()) // automatically sends AVIF if present and supported by the browser

// receive contact-forms on your chat channel on the fly
cf := g.NewContactForm("/")
router.Post("/", cf.Notify("https://mattermost.com/hooks/qite178czotd5"))

// Git version and last commit date (HTML or JSON depending on the "Accept" header)
router.Get("/version", garcon.ServeVersion())

// return a JSON message
router.Get("/reserved", g.Writer.NotImplemented)
router.NotFound(g.Writer.InvalidPath)

handler := middleware.Then(router)
server := http.Server{Addr: ":8080", Handler: handler}
server.ListenAndServe()

Incorruptible middleware

Garcon uses the Incorruptible package to create/verify session cookie.

package main

import "github.com/teal-finance/garcon"

func main() {
    g, _ := garcon.New(
        garcon.WithURLs("https://my-company.com"),
        garcon.WithDev())

    aes128Key = "00112233445566778899aabbccddeeff"
    maxAge := 3600
    setIP := true
    ck := g.IncorruptibleChecker(aes128Key, maxAge, setIP)

    router := chi.NewRouter()

    // website with static files directory
    ws := g.NewStaticWebServer("/var/www")

    // ck.Set => set the cookie when visiting /
    router.With(ck.Set).Get("/", ws.ServeFile("index.html", "text/html; charset=utf-8"))

    // ck.Chk => reject request with invalid JWT cookie
    router.With(ck.Chk).Get("/js/*", ws.ServeDir("text/javascript; charset=utf-8"))
    router.With(ck.Chk).Get("/assets/*", ws.ServeAssets())

    // ck.Vet => accepts valid JWT in either the cookie or the Authorization header
    router.With(ck.Vet).Post("/api/items", myFunctionHandler)

    server := http.Server{Addr: ":8080", Handler: router}
    server.ListenAndServe()
}

JWT middleware

The JWT and Incorruptible checkers share a common interface, TokenChecker, providing the same middleware: Set(), Chk() and Vet().

package main

import "github.com/teal-finance/garcon"

func main() {
    g, _ := garcon.New(
        garcon.WithURLs("https://my-company.com"),
        garcon.WithDev())

    hmacSHA256Key := "9d2e0a02121179a3c3de1b035ae1355b1548781c8ce8538a1dc0853a12dfb13d"
    ck := g.JWTChecker(hmacSHA256Key, "FreePlan", 10, "PremiumPlan", 100)

    router := chi.NewRouter()

    // website with static files directory
    ws := g.NewStaticWebServer("/var/www")

    // ck.Set => set the cookie when visiting /
    router.With(ck.Set).Get("/", ws.ServeFile("index.html", "text/html; charset=utf-8"))

    // ck.Chk => reject request with invalid JWT cookie
    router.With(ck.Chk).Get("/js/*", ws.ServeDir("text/javascript; charset=utf-8"))
    router.With(ck.Chk).Get("/assets/*", ws.ServeAssets())

    // ck.Vet => accepts valid JWT in either the cookie or the Authorization header
    router.With(ck.Vet).Post("/api/items", myFunctionHandler)

    server := http.Server{Addr: ":8080", Handler: router}
    server.ListenAndServe()
}

Who use Garcon

In production, this library is used by Rainbow, Quid and other internal projects at Teal.Finance.

Please propose a Pull Request to add here your project that also uses Garcon.

See a complete real example in the repo github.com/teal-finance/rainbow.

CPU profiling

Moreover, Garcon simplifies investigation on CPU and memory consumption issues thanks to https://github.com/pkg/profile.

In your code, add defer garcon.ProbeCPU.Stop() that will write the cpu.pprof file.

import "github.com/teal-finance/garcon"

func myFunctionConsumingLotsOfCPU() {
    defer garcon.ProbeCPU.Stop()

    // ... lots of sub-functions
}

Run pprof and browse your cpu.pprof file:

go run github.com/google/pprof@latest -http=: cpu.pprof

Complete example

See the complete example enabling almost of the Garcon features. Below is a simplified extract:

package main

import "github.com/teal-finance/garcon"

func main() {
    defer garcon.ProbeCPU().Stop() // collects the CPU-profile and writes it in the file "cpu.pprof"
    
    garcon.LogVersion()     // log the Git version
    garcon.SetVersionFlag() // the -version flag prints the Git version
    jwt := flag.Bool("jwt", false, "Use JWT in lieu of the Incorruptible token")
    flag.Parse()

    g := garcon.New(
        garcon.WithURLs("https://my-company.co"),
        garcon.WithDocURL("/doc"),
        garcon.WithPProf(8093))

    ic := g.IncorruptibleChecker(aes128Key, 60, true)
    jc := g.JWTChecker(hmacSHA256Key, "FreePlan", 10, "PremiumPlan", 100)

    middleware, connState := g.StartMetricsServer(9093)
    middleware = middleware.Append(
        g.MiddlewareRejectUnprintableURI(),
        g.MiddlewareLogRequests("fingerprint"),
        g.MiddlewareRateLimiter(10, 30),
        g.MiddlewareServerHeader("MyApp"),
        g.MiddlewareCORS(),
        g.MiddlewareOPA("auth.rego"),
        g.MiddlewareLogDuration(true()))

    router := chi.NewRouter()

    // website with static files directory
    ws := g.NewStaticWebServer("/var/www")
    router.With(ic.Set).With(jc.Set).Get("/", ws.ServeFile("index.html", "text/html; charset=utf-8"))
    router.With(ic.Chk).Get("/assets/*", ws.ServeAssets())

    // Contact-form
    cf := g.NewContactForm("/")
    router.Post("/", cf.Notify("https://mattermost.com/hooks/qite178czotd5"))

    // API
    router.With(jc.Vet).Post("/api/items", myFunctionHandler)

    handler := middleware.Then(router)
    server := garcon.Server(handler, 8080, connState)
    garcon.ListenAndServe(&server)
}
1. Run the complete example
cd garcon
go build -race ./examples/complete && ./complete
2022/01/29 17:31:26 Prometheus export http://localhost:9093
2022/01/29 17:31:26 CORS: Set origin prefixes: [http://localhost:8080 http://localhost: http://192.168.1.]
2022/01/29 17:31:26 CORS: Methods=[GET POST] Headers=[Origin Accept Content-Type Authorization Cookie] Credentials=true MaxAge=86400
2022/01/29 17:31:26 Enable PProf endpoints: http://localhost:8093/debug/pprof
2022/01/29 17:31:26 Create cookie plan=FreePlan domain=localhost secure=false myapp=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuIjoiRnJlZVBsYW4iLCJleHAiOjE2NzUwMDk4ODZ9.hiQQuFxNghrrCvvzEsXzN1lWTavL09Plx0dhFynrBxc
2022/01/29 17:31:26 Create cookie plan=PremiumPlan domain=localhost secure=false myapp=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuIjoiUHJlbWl1bVBsYW4iLCJleHAiOjE2NzUwMDk4ODZ9.iP587iHjhLmX_8yMhuQfKu9q7qLbLE7UX-UgkL_VYhE
2022/01/29 17:31:26 JWT not required for dev. origins: [http://localhost:8080 http://localhost: http://192.168.1.]
2022/01/29 17:31:26 Middleware response HTTP header: Set Server MyApp-1.2.0
2022/01/29 17:31:26 Middleware MiddlewareRateLimiter: burst=100 rate=5/s
2022/01/29 17:31:26 Middleware logger: requester IP and requested URL
2022/01/29 17:31:26 Server listening on http://localhost:8080
2. Embedded PProf server

Visit the PProf server at http://localhost:8093/debug/pprof providing the following endpoints:

PProf is easy to use with curl or wget:

( cd ~ ; go get -u github.com/google/pprof )

curl http://localhost:8093/debug/pprof/allocs > allocs.pprof
pprof -http=: allocs.pprof

wget http://localhost:8093/debug/pprof/heap
pprof -http=: heap

wget http://localhost:8093/debug/pprof/goroutine
pprof -http=: goroutine

See the PProf post (2013) for further explanations.

3. Embedded metrics server

The export port http://localhost:9093/metrics is for the monitoring tools like Prometheus.

4. Static website server

WARNING: This section is outdated!

The complete example is running.

Open http://localhost:8080/myapp with your browser, and play with the API endpoints.

The resources and API endpoints are protected with a HttpOnly cookie. The complete example sets the cookie to browsers visiting the index.html.

func handler(gw garcon.Writer, jc *jwtperm.Checker) http.Handler {
    r := chi.NewRouter()

    // Static website files
    ws := garcon.WebServer{Dir: "examples/www", Writer: gw}
    r.With(jc.SetCookie).Get("/", ws.ServeFile("index.html", "text/html; charset=utf-8"))
    r.With(jc.SetCookie).Get("/favicon.ico", ws.ServeFile("favicon.ico", "image/x-icon"))
    r.With(jc.ChkCookie).Get("/js/*", ws.ServeDir("text/javascript; charset=utf-8"))
    r.With(jc.ChkCookie).Get("/css/*", ws.ServeDir("text/css; charset=utf-8"))
    r.With(jc.ChkCookie).Get("/images/*", ws.ServeImages())

    // API
    r.With(jc.ChkJWT).Get("/api/v1/items", items)
    r.With(jc.ChkJWT).Get("/api/v1/ducks", gw.NotImplemented)

    // Other endpoints
    r.NotFound(gw.InvalidPath)

    return r
}
5. Enable Authentication

WARNING: This section is outdated!

Restart again the complete example with authentication enabled.

Attention, in this example we use two redundant middleware pieces using the same JWT: jwtperm and opa. This is just an example, don't be confused.

go build -race ./examples/complete && ./complete -auth
2021/12/02 08:09:47 Prometheus export http://localhost:9093
2021/12/02 08:09:47 CORS: Set origin prefixes: [http://localhost:8080 http://localhost: http://192.168.1.]
2021/12/02 08:09:47 CORS: Methods=[GET] Headers=[Origin Accept Content-Type Authorization Cookie] Credentials=true MaxAge=86400
2021/12/02 08:09:47 JWT not required for dev. origins: [http://localhost:8080 http://localhost: http://192.168.1.]
2021/12/02 08:09:47 Enable PProf endpoints: http://localhost:8093/debug/pprof
2021/12/02 08:09:47 Create cookie plan=FreePlan domain=localhost secure=false jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuIjoiRnJlZVBsYW4iLCJleHAiOjE2Njk5NjQ5ODd9.5tJk2NoHxkG0o_owtMleBcUaR8z1vRx4rxRRqtZUc_Q
2021/12/02 08:09:47 Create cookie plan=PremiumPlan domain=localhost secure=false jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuIjoiUHJlbWl1bVBsYW4iLCJleHAiOjE2Njk5NjQ5ODd9.ifKhbmxQQ64NweL5aQDb_42tvKHwqiEKD-vxHO3KzsM
2021/12/02 08:09:47 OPA: load "examples/sample-auth.rego"
2021/12/02 08:09:47 Middleware OPA: map[sample-auth.rego:package auth

default allow = false
tokens := {"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuIjoiRnJlZVBsYW4iLCJleHAiOjE2Njk5NjQ0ODh9.elDm_t4vezVgEmS8UFFo_spLJTts7JWybzbyO_aYV3Y"} { true }
allow = true { __local0__ = input.token; data.auth.tokens[__local0__] }]
2021/12/02 08:09:47 Middleware response HTTP header: Set Server MyBackendName-1.2.0
2021/12/02 08:09:47 Middleware MiddlewareRateLimiter: burst=100 rate=5/s
2021/12/02 08:09:47 Middleware logger: requester IP and requested URL
2021/12/02 08:09:47 Server listening on http://localhost:8080
6. Default HTTP request headers

Test the API with curl:

curl -D - http://localhost:8080/myapp/api/v1/items
HTTP/1.1 401 Unauthorized
Content-Type: application/json
Server: MyBackendName-1.2.0
Vary: Origin
Date: Thu, 02 Dec 2021 07:06:20 GMT
Content-Length: 84

{"error":"Unauthorized",
"path":"/api/v1/items",
"doc":"http://localhost:8080/myapp/doc"}

The corresponding garcon logs:

2021/12/02 08:06:20 in  127.0.0.1:42888 GET /api/v1/items
[cors] 2021/12/02 08:06:20 Handler: Actual request
[cors] 2021/12/02 08:06:20   Actual request no headers added: missing origin
2021/12/02 08:06:20 OPA unauthorize 127.0.0.1:42888 /api/v1/items
2021/12/02 08:06:20 out 127.0.0.1:42888 GET /api/v1/items 1.426916ms c=1

The CORS logs can be disabled by passing debug=false in cors.Handler(origins, false).

The value c=1 measures the web traffic (current active HTTP connections).

7. With Authorization header
curl -D - http://localhost:8080/myapp/api/v1/items -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuIjoiRnJlZVBsYW4iLCJleHAiOjE2Njk5NjQ0ODh9.elDm_t4vezVgEmS8UFFo_spLJTts7JWybzbyO_aYV3Y'
HTTP/1.1 200 OK
Content-Type: application/json
Server: MyBackendName-1.2.0
Vary: Origin
Date: Thu, 02 Dec 2021 07:10:37 GMT
Content-Length: 25

["item1","item2","item3"]

The corresponding garcon logs:

2021/12/02 08:10:37 in  127.0.0.1:42892 GET /api/v1/items
[cors] 2021/12/02 08:10:37 Handler: Actual request
[cors] 2021/12/02 08:10:37   Actual request no headers added: missing origin
2021/12/02 08:10:37 Authorization header has JWT: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuIjoiRnJlZVBsYW4iLCJleHAiOjE2Njk5NjQ0ODh9.elDm_t4vezVgEmS8UFFo_spLJTts7JWybzbyO_aYV3Y
2021/12/02 08:10:37 JWT Claims: {FreePlan  {  [] 2022-12-02 08:01:28 +0100 CET <nil> <nil> invalid cookie}}
2021/12/02 08:10:37 JWT has the FreePlan Namespace
2021/12/02 08:10:37 JWT Permission: {10}
2021/12/02 08:10:37 out 127.0.0.1:42892 GET /api/v1/items 1.984568ms c=1 a=1 i=0 h=0

Low-level example

WARNING: This section is outdated!

See the low-level example.

The following code is a bit different to the stuff done by the complete function Garcon.Run() presented in the previous chapter. The following code is intended to show Garcon can be customized to meet your specific requirements.

package main

import (
    "log"
    "net"
    "net/http"
    "time"

    "github.com/go-chi/chi/v5"
    "github.com/teal-finance/garcon"
)

// Garcon settings
const apiDoc = "https://my-dns.co/doc"
const allowedProdOrigin = "https://my-dns.co"
const allowedDevOrigins = "http://localhost: http://192.168.1."
const serverHeader = "MyBackendName-1.2.0"
const authCfg = "examples/sample-auth.rego"
const pprofPort = 8093
const expPort = 9093
const burst, reqMinute = 10, 30
const devMode = true

func main() {
    if devMode {
        // the following line collects the CPU-profile and writes it in the file "cpu.pprof"
        defer garcon.ProbeCPU().Stop()
    }

    garcon.StartPProfServer(pprofPort)

    // Uniformize error responses with API doc
    gw := garcon.NewWriter(apiDoc)

    middleware, connState := setMiddlewares(gw)

    // Handles both REST API and static web files
    h := handler(gw)
    h = middleware.Then(h)

    runServer(h, connState)
}

func setMiddlewares(gw garcon.Writer) (middleware garcon.Chain, connState func(net.Conn, http.ConnState)) {
    // Start a metrics server in background if export port > 0.
    // The metrics server is for use with Prometheus or another compatible monitoring tool.
    metrics := garcon.Metrics{}
    middleware, connState = garcon.StartMetricsServer(expPort, devMode)

    // Limit the input request rate per IP
    reqLimiter := garcon.NewReqLimiter(gw, burst, reqMinute, devMode)

    corsConfig := allowedProdOrigin
    if devMode {
        corsConfig += " " + allowedDevOrigins
    }

    allowedOrigins := garcon.SplitClean(corsConfig)

    middleware = middleware.Append(
        reqLimiter.Limit,
        garcon.ServerHeader(serverHeader),
        cors.Handler(allowedOrigins, devMode),
    )

    // Endpoint authentication rules (Open Policy Agent)
    files := garcon.SplitClean(authCfg)
    policy, err := garcon.NewPolicy(gw, files)
    if err != nil {
        log.Fatal(err)
    }

    if policy.Ready() {
        middleware = middleware.Append(policy.Auth)
    }

    return middleware, connState
}

// runServer runs in foreground the main server.
func runServer(h http.Handler, connState func(net.Conn, http.ConnState)) {
    const mainPort = "8080"

    server := http.Server{
        Addr:              ":" + mainPort,
        Handler:           h,
        TLSConfig:         nil,
        ReadTimeout:       1 * time.Second,
        ReadHeaderTimeout: 1 * time.Second,
        WriteTimeout:      1 * time.Second,
        IdleTimeout:       1 * time.Second,
        MaxHeaderBytes:    222,
        TLSNextProto:      nil,
        ConnState:         connState,
        ErrorLog:          log.Default(),
        BaseContext:       nil,
        ConnContext:       nil,
    }

    log.Print("Server listening on http://localhost", server.Addr)

    log.Fatal(server.ListenAndServe())
}

// handler creates the mapping between the endpoints and the handler functions.
func handler(gw garcon.Writer) http.Handler {
    r := chi.NewRouter()

    // Website with static files
    ws := g.NewStaticWebServer("/var/www")
    r.Get("/", ws.ServeFile("index.html", "text/html; charset=utf-8"))
    r.Get("/js/*", ws.ServeDir("text/javascript; charset=utf-8"))
    r.Get("/css/*", ws.ServeDir("text/css; charset=utf-8"))
    r.Get("/images/*", ws.ServeImages())

    // API
    r.Get("/api/v1/items", items)
    r.Get("/api/v1/ducks", gw.NotImplemented)

    // Other endpoints
    r.NotFound(gw.InvalidPath)

    return r
}

func items(w http.ResponseWriter, _ *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    _, _ = w.Write([]byte(`["item1","item2","item3"]`))
}

KeyStore example

The example KeyStore implements a key/value datastore providing private storage for each client identified by its unique IP.

cd garcon
go build ./examples/keystore
./keystore

Then open http://localhost:8080 to learn more about the implemented features.

✨ Contributions Welcome

This project needs your help to become better. Please propose your enhancements, or even a further refactoring.

We welcome contributions in many forms, and there's always plenty to do!

🗣️ Feedback

If you have some suggestions, or need a new feature, please contact us, using the issues, or at Teal.Finance@pm.me or @TealFinance.

Feel free to propose a Pull Request, your contributions are welcome. 😉

Copyright (c) 2021 Teal.Finance/Garcon contributors

Teal.Finance/Garcon is free software, and can be redistributed and/or modified under the terms of the MIT License. SPDX-License-Identifier: MIT

Teal.Finance/Garcon is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the LICENSE file (alongside the source files) or https://opensource.org/licenses/MIT.

See also

Documentation

Overview

Package garcon is a server for API and static website including middlewares to manage rate-limit, Cookies, JWT, CORS, OPA, web traffic, Prometheus export and PProf.

Index

Constants

View Source
const (

	// DefaultPlan is the plan name in absence of "permissions" parametric parameters.
	DefaultPlan = "VIP"
	// DefaultPerm is the perm value in absence of "permissions" parametric parameters.
	DefaultPerm = 1
)
View Source
const FingerprintExplanation = `` /* 683-byte string literal not displayed */

FingerprintExplanation provides a description of the logged HTTP headers.

Variables

View Source
var (
	ErrExpiredToken    = errors.New("expired or invalid refresh token")
	ErrJWTSignature    = errors.New("JWT signature mismatch")
	ErrNoAuthorization = errors.New("provide your JWT within the 'Authorization Bearer' HTTP header")
	ErrNoBase64JWT     = errors.New("the token claims (second part of the JWT) is not base64-valid")
	ErrNoBearer        = errors.New("malformed HTTP Authorization, must be Bearer")
	ErrNoValidJWT      = errors.New("cannot find a valid JWT in either the cookie or the first 'Authorization' HTTP header")
)
View Source
var ErrEmptyOPAFilename = errors.New("OPA: missing filename")
View Source
var ErrNonPrintable = errors.New("non-printable")

V is set at build time using the `-ldflags` build flag:

v="$(git describe --tags --always --broken)"
go build -ldflags="-X 'github.com/teal-finance/garcon.V=$v'" ./cmd/main/package

The following commands provide a semver-like version format such as "v1.2.0-my-branch+3" where "+3" is the number of commits since "v1.2.0". If no tag in the Git repo, $t is the long SHA1 of the last commit.

t="$(git describe --tags --abbrev=0 --always)"
b="$(git branch --show-current)"
[ _$b = _main ] && b="" || b="-$b"
n="$(git rev-list --count "$t"..)"
[ "$n" -eq 0 ] && n="" || n="+$n"
go build -ldflags="-X 'github.com/teal-finance/garcon.V=$t$b$n'" ./cmd/main/package

Functions

func AppendPrefixes added in v0.5.1

func AppendPrefixes(origins []string, prefixes ...string) []string

func AppendURLs added in v0.9.0

func AppendURLs(urls []*url.URL, prefixes ...*url.URL) []*url.URL

func ConvertSize added in v0.17.0

func ConvertSize(sizeInBytes int) string

ConvertSize converts a size in bytes into the most appropriate unit among KiB, MiB, GiB, TiB, PiB and EiB. 1 KiB is 1024 bytes as defined by the ISO/IEC 80000-13:2008 standard. See: https://wikiless.org/wiki/ISO%2FIEC_80000#Units_of_the_ISO_and_IEC_80000_series

func ConvertSize64 added in v0.17.0

func ConvertSize64(sizeInBytes int64) string

ConvertSize64 is similar ConvertSize but takes in input an int64.

func DecodeJSONBody deprecated added in v0.21.0

func DecodeJSONBody[T json.Unmarshaler](r *http.Request, msg T) error

DecodeJSONBody unmarshals the JSON from the request body.

Deprecated: Please use garcon.UnmarshalJSONRequest(w, r, msg) instead.

func DecodeJSONRequest added in v0.23.5

func DecodeJSONRequest(w http.ResponseWriter, r *http.Request, msg any, maxBytes ...int64) error

DecodeJSONRequest decodes the JSON from the request body.

func DecodeJSONResponse added in v0.23.5

func DecodeJSONResponse(resp *http.Response, msg any, maxBytes ...int64) error

DecodeJSONResponse decodes the JSON from the request body.

func DefaultContactSettings added in v0.16.0

func DefaultContactSettings() map[string][2]int

DefaultContactSettings is compliant with standard names for web form input fields: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#inappropriate-for-the-control

func DefaultFileSettings added in v0.16.0

func DefaultFileSettings() map[string][2]int

DefaultFileSettings sets FileLimits with only "file".

func DevOrigins added in v0.4.3

func DevOrigins() []*url.URL

DevOrigins provides the development origins: - yarn run vite --port 3000 - yarn run vite preview --port 5000 - localhost:8085 on multi devices: web auto-reload using https://github.com/synw/fwr - flutter run --web-port=8080 - 192.168.1.x + any port on tablet: mobile app using fast builtin auto-reload.

func FingerprintMD added in v0.17.0

func FingerprintMD(r *http.Request) string

FingerprintMD provide the browser fingerprint in markdown format. Attention: read the .

func InvalidPath added in v0.17.0

func InvalidPath(w http.ResponseWriter, r *http.Request)

func ListenAndServe added in v0.23.0

func ListenAndServe(server *http.Server) error

ListenAndServe runs the HTTP server(s) in foreground. Optionally it also starts a metrics server in background (if export port > 0). The metrics server is for use with Prometheus or another compatible monitoring tool.

func LoadPolicy added in v0.17.0

func LoadPolicy(filenames []string) (*ast.Compiler, error)

LoadPolicy checks the Rego filenames and loads them to build the OPA compiler.

func LogVersion added in v0.20.0

func LogVersion()

LogVersion logs the version and (Git) commit information.

func MiddlewareCORS added in v0.23.0

func MiddlewareCORS(origins, methods, headers []string, debug bool) func(next http.Handler) http.Handler

MiddlewareCORS uses restrictive CORS values.

func MiddlewareLogDuration added in v0.23.0

func MiddlewareLogDuration(next http.Handler) http.Handler

MiddlewareLogDuration logs the requested URL along with the time to handle it.

func MiddlewareLogDurationSafe added in v0.23.0

func MiddlewareLogDurationSafe(next http.Handler) http.Handler

MiddlewareLogDurationSafe is similar to MiddlewareLogDurations but also sanitizes the URL.

func MiddlewareLogFingerprint added in v0.23.0

func MiddlewareLogFingerprint(next http.Handler) http.Handler

MiddlewareLogFingerprint is the middleware to log incoming HTTP request and browser fingerprint.

func MiddlewareLogFingerprintSafe added in v0.23.0

func MiddlewareLogFingerprintSafe(next http.Handler) http.Handler

MiddlewareLogFingerprintSafe is similar to MiddlewareLogFingerprints but sanitize the URL.

func MiddlewareLogRequest added in v0.23.0

func MiddlewareLogRequest(next http.Handler) http.Handler

MiddlewareLogRequest is the middleware to log the requester IP and the requested URL.

func MiddlewareLogRequestSafe added in v0.23.0

func MiddlewareLogRequestSafe(next http.Handler) http.Handler

MiddlewareLogRequestSafe is similar to LogRequest but sanitize the URL.

func MiddlewareRejectUnprintableURI added in v0.23.0

func MiddlewareRejectUnprintableURI(next http.Handler) http.Handler

MiddlewareRejectUnprintableURI is a middleware rejecting HTTP requests having a Carriage Return "\r" or a Line Feed "\n" within the URI to prevent log injection.

func MiddlewareSecureHTTPHeader added in v0.23.0

func MiddlewareSecureHTTPHeader(secure bool) func(next http.Handler) http.Handler

MiddlewareSecureHTTPHeader is a middleware adding recommended HTTP response headers to secure the web application.

func MiddlewareServerHeader added in v0.23.0

func MiddlewareServerHeader(version string) func(next http.Handler) http.Handler

MiddlewareServerHeader is the middleware setting the Server HTTP header in the response.

func NewHash added in v0.17.0

func NewHash() (hash.Hash, error)

NewHash is based on HighwayHash, a hashing algorithm enabling high speed (especially on AMD64). See the study on HighwayHash and some other hash functions: https://github.com/fwessels/HashCompare

func NotImplemented added in v0.17.0

func NotImplemented(w http.ResponseWriter, r *http.Request)

func Obfuscate added in v0.17.0

func Obfuscate(str string) (string, error)

Obfuscate hashes the input string to prevent logging sensitive information.

func OriginsFromURLs added in v0.9.0

func OriginsFromURLs(urls []*url.URL) []string

func OverwriteBufferContent added in v0.22.0

func OverwriteBufferContent(b []byte)

OverwriteBufferContent is to erase a secret when it is no longer required.

func ParseURLs added in v0.9.3

func ParseURLs(origins []string) []*url.URL

func PrintVersion added in v0.17.0

func PrintVersion(program string)

PrintVersion prints the version and (Git) commit information.

func Printable added in v0.17.0

func Printable(array ...string) int

Printable returns -1 when all the strings are safely printable else returns the position of the rejected character.

The non printable characters are:

  • Carriage Return "\r"
  • Line Feed "\n"
  • other ASCII control codes (except space)
  • invalid UTF-8 codes

Printable can be used to preventing log injection.

When multiple strings are passed, the returned position is sum with the string index multiplied by 1000.

func PrintableRune added in v0.17.0

func PrintableRune(r rune) bool

PrintableRune returns false if rune is a Carriage Return "\r", a Line Feed "\n", another ASCII control code (except space), or an invalid UTF-8 code. PrintableRune can be used to prevent log injection.

func ProbeCPU added in v0.17.0

func ProbeCPU() interface{ Stop() }

ProbeCPU is used like the following:

defer pprof.ProbeCPU.Stop()

When the caller reaches its function end, the defer executes Stop() that writes the file "cpu.pprof". To visualize "cpu.pprof" use the pprof tool:

cd ~/go
go get -u github.com/google/pprof
cd -
pprof -http=: cpu.pprof

or using one single command line:

go run github.com/google/pprof@latest -http=: cpu.pprof

func RandomBytes added in v0.21.0

func RandomBytes(n int) []byte

func SafeHeader added in v0.17.0

func SafeHeader(r *http.Request, header string) string

SafeHeader stringifies a safe list of HTTP header values.

func Sanitize added in v0.17.0

func Sanitize(str string) string

Sanitize replaces control codes by the tofu symbol and invalid UTF-8 codes by the replacement character. Sanitize can be used to prevent log injection.

Inspired from: - https://wikiless.org/wiki/Replacement_character#Replacement_character - https://graphicdesign.stackexchange.com/q/108297

func ServeVersion added in v0.22.0

func ServeVersion() func(w http.ResponseWriter, r *http.Request)

ServeVersion send HTML or JSON depending on Accept header.

func Server added in v0.23.0

func Server(h http.Handler, port int, connState ...func(net.Conn, http.ConnState)) http.Server

Server returns a default http.Server ready to handle API endpoints, static web pages...

func SetCustomVersionFlag added in v0.20.0

func SetCustomVersionFlag(fs *flag.FlagSet, flagName, program string)

SetCustomVersionFlag register PrintVersion() for the version flag.

Example with default values:

import "github.com/teal-finance/garcon"

func main() {
     garcon.SetCustomVersionFlag(nil, "", "")
     flag.Parse()
}

Example with custom values values:

import "github.com/teal-finance/garcon"

func main() {
     garcon.SetCustomVersionFlag(nil, "v", "MyApp")
     flag.Parse()
}

func SetVersionFlag added in v0.17.0

func SetVersionFlag()

SetVersionFlag defines -version flag to print the version stored in V. See SetCustomVersionFlag for a more flexibility.

func SplitClean

func SplitClean(values string) []string

SplitClean splits the values and trim them.

func StartPProfServer added in v0.17.0

func StartPProfServer(port int)

StartPProfServer starts a PProf server in background. Endpoints usage example:

curl http://localhost:6063/debug/pprof/allocs > allocs.pprof
pprof -http=: allocs.pprof

wget http://localhost:31415/debug/pprof/goroutine
pprof -http=: goroutine

wget http://localhost:31415/debug/pprof/heap
pprof -http=: heap

wget http://localhost:31415/debug/pprof/trace
pprof -http=: trace

func TraversalPath added in v0.17.0

func TraversalPath(w http.ResponseWriter, r *http.Request) bool

TraversalPath returns true when path contains ".." to prevent path traversal attack.

func UnmarshalJSONRequest added in v0.23.5

func UnmarshalJSONRequest[T json.Unmarshaler](w http.ResponseWriter, r *http.Request, msg T, maxBytes ...int64) error

UnmarshalJSONRequest unmarshals the JSON from the request body.

func UnmarshalJSONResponse added in v0.23.5

func UnmarshalJSONResponse[T json.Unmarshaler](resp *http.Response, msg T, maxBytes ...int64) error

UnmarshalJSONResponse unmarshals the JSON from the request body.

func Value added in v0.12.3

func Value(r *http.Request, key, header string) (string, error)

Value returns the /endpoint/{key} (URL path) else the "key" form (HTTP body) else the "key" query string (URL) else the HTTP header. Value requires chi.URLParam().

func Values added in v0.12.3

func Values(r *http.Request, key string) ([]string, error)

Values requires chi.URLParam().

func Version added in v0.17.0

func Version(serverName string) string

Version format is "Program-1.2.3". If the program argument is empty, the format is "v1.2.3". If V is empty, Version uses the main module version.

func WriteErr added in v0.21.0

func WriteErr(w http.ResponseWriter, r *http.Request, statusCode int, kv ...any)

func WriteErrSafe added in v0.21.0

func WriteErrSafe(w http.ResponseWriter, r *http.Request, statusCode int, kv ...any)

func WriteOK added in v0.21.0

func WriteOK(w http.ResponseWriter, kv ...any)

Types

type Chain added in v0.17.0

type Chain []Middleware

Chain acts as a list of http.Handler middleware. Chain is effectively immutable: once created, it will always hold the same set of middleware in the same order.

func NewChain added in v0.17.0

func NewChain(chain ...Middleware) Chain

NewChain creates a new chain, memorizing the given list of middleware. NewChain serves no other function, middleware is only constructed upon a call to chain.Then().

func StartMetricsServer added in v0.17.0

func StartMetricsServer(port int, namespace ServerName) (Chain, func(net.Conn, http.ConnState))

StartMetricsServer creates and starts the Prometheus export server.

func (Chain) Append added in v0.17.0

func (c Chain) Append(chain ...Middleware) Chain

Append extends a chain, adding the provided middleware as the last ones in the request flow.

chain := garcon.NewChain(m1, m2)
chain = chain.Append(m3, m4)
// requests in chain go m1 -> m2 -> m3 -> m4

func (Chain) Then added in v0.17.0

func (c Chain) Then(handler http.Handler) http.Handler

Then chains the middlewares and returns the final http.Handler.

garcon.NewChain(m1, m2, m3).Then(h)

is equivalent to:

m1(m2(m3(h)))

When the request comes in, it will be passed to m1, then m2, then m3 and finally, the given handler (assuming every middleware calls the following one).

A chain can be safely reused by calling Then() several times.

chain := garcon.NewChain(rateLimitHandler, csrfHandler)
indexPipe = chain.Then(indexHandler)
authPipe  = chain.Then(authHandler)

Note: every call to Then() calls all middleware pieces. Thus several instances of the same middleware will be created when a chain is reused in this previous example. For proper middleware, this should cause no problem.

Then() treats nil as http.DefaultServeMux.

func (Chain) ThenFunc added in v0.17.0

func (c Chain) ThenFunc(fn http.HandlerFunc) http.Handler

ThenFunc works identically to Then, but takes a HandlerFunc instead of a Handler.

The following two statements are equivalent:

c.Then(http.HandlerFunc(fn))
c.ThenFunc(fn)

ThenFunc provides all the guarantees of Then.

type Garcon

type Garcon struct {
	ServerName ServerName
	Writer     Writer
	// contains filtered or unexported fields
}

func New added in v0.6.0

func New(opts ...Option) *Garcon

func (*Garcon) IncorruptibleChecker added in v0.23.0

func (g *Garcon) IncorruptibleChecker(secretKeyHex string, maxAge int, setIP bool) *incorruptible.Incorruptible

IncorruptibleChecker uses cookies based the fast and tiny Incorruptible token. IncorruptibleChecker requires g.WithURLs() to set the Cookie secure, domain and path.

func (*Garcon) JWTChecker added in v0.6.0

func (g *Garcon) JWTChecker(secretKeyHex string, planPerm ...any) *JWTChecker

JWTChecker requires WithURLs() to set the Cookie name, secure, domain and path.

func (*Garcon) MiddlewareCORS added in v0.23.0

func (g *Garcon) MiddlewareCORS() Middleware

MiddlewareCORS is a middleware to handle Cross-Origin Resource Sharing (CORS).

func (*Garcon) MiddlewareCORSWithMethodsHeaders added in v0.23.3

func (g *Garcon) MiddlewareCORSWithMethodsHeaders(methods, headers []string) Middleware

MiddlewareCORSWithMethodsHeaders is a middleware to handle Cross-Origin Resource Sharing (CORS).

func (*Garcon) MiddlewareLogDuration added in v0.23.0

func (g *Garcon) MiddlewareLogDuration(safe ...bool) Middleware

MiddlewareLogDuration logs the requested URL along with its handling time. When the optional parameter safe is true, this middleware sanitizes the URL before printing it.

func (*Garcon) MiddlewareLogRequest added in v0.23.0

func (g *Garcon) MiddlewareLogRequest(settings ...string) Middleware

MiddlewareLogRequest logs the incoming request URL. If one of its optional parameter is "fingerprint", this middleware also logs the browser fingerprint. If the other optional parameter is "safe", this middleware sanitizes the URL before printing it.

func (*Garcon) MiddlewareOPA added in v0.23.0

func (g *Garcon) MiddlewareOPA(opaFilenames ...string) Middleware

MiddlewareOPA creates the middleware for Authentication rules (Open Policy Agent).

func (*Garcon) MiddlewareRateLimiter added in v0.23.0

func (g *Garcon) MiddlewareRateLimiter(settings ...int) Middleware

func (Garcon) MiddlewareRejectUnprintableURI added in v0.23.0

func (Garcon) MiddlewareRejectUnprintableURI() Middleware

MiddlewareRejectUnprintableURI is a middleware rejecting HTTP requests having a Carriage Return "\r" or a Line Feed "\n" within the URI to prevent log injection.

func (*Garcon) MiddlewareServerHeader added in v0.23.0

func (g *Garcon) MiddlewareServerHeader(serverName ...string) Middleware

func (*Garcon) NewContactForm added in v0.22.0

func (g *Garcon) NewContactForm(redirectURL string) WebForm

func (*Garcon) NewStaticWebServer added in v0.23.0

func (g *Garcon) NewStaticWebServer(dir string) StaticWebServer

NewStaticWebServer creates a StaticWebServer.

func (*Garcon) StartMetricsServer added in v0.22.0

func (g *Garcon) StartMetricsServer(expPort int) (Chain, func(net.Conn, http.ConnState))

StartMetricsServer creates and starts the Prometheus export server.

type JWTChecker added in v0.20.0

type JWTChecker struct {
	// contains filtered or unexported fields
}

func NewJWTChecker added in v0.20.0

func NewJWTChecker(gw Writer, urls []*url.URL, secretKey []byte, permissions ...any) *JWTChecker

func (*JWTChecker) Chk added in v0.20.0

func (ck *JWTChecker) Chk(next http.Handler) http.Handler

Chk is a middleware to accept only HTTP requests having a valid cookie. Then, Chk puts the permission (of the JWT) in the request context.

func (*JWTChecker) Cookie added in v0.20.0

func (ck *JWTChecker) Cookie(i int) *http.Cookie

Cookie returns a default cookie to facilitate testing.

func (*JWTChecker) NewCookie added in v0.20.0

func (ck *JWTChecker) NewCookie(name, plan, user string, secure bool, dns, dir string) http.Cookie

func (*JWTChecker) PermFromBearerOrCookie added in v0.20.0

func (ck *JWTChecker) PermFromBearerOrCookie(r *http.Request) (Perm, []any)

func (*JWTChecker) PermFromCookie added in v0.20.0

func (ck *JWTChecker) PermFromCookie(r *http.Request) (Perm, []any)

func (*JWTChecker) PermFromJWT added in v0.20.0

func (ck *JWTChecker) PermFromJWT(JWT string) (Perm, []any)

func (*JWTChecker) Set added in v0.20.0

func (ck *JWTChecker) Set(next http.Handler) http.Handler

Set is a middleware putting a HttpOnly cookie in the HTTP response header when no valid cookie is present. The new cookie conveys the JWT of the first plan. Set also puts the permission from the JWT in the request context.

func (*JWTChecker) Vet added in v0.20.0

func (ck *JWTChecker) Vet(next http.Handler) http.Handler

Vet is a middleware to accept only the HTTP request having a valid JWT. The JWT can be either in the cookie or in the first "Authorization" header. Then, Vet puts the permission (of the JWT) in the request context.

type Middleware added in v0.17.0

type Middleware func(http.Handler) http.Handler

Middleware is a constructor function returning a http.Handler.

type Muter added in v0.21.0

type Muter struct {
	// Threshold is the level enabling the muted state.
	Threshold int

	// NoAlertDuration allows to consider the verbosity is back to normal.
	NoAlertDuration time.Duration

	// RemindMuteState allows to remind the state is still muted.
	// Set value 100 to send this reminder every 100 increments.
	// Set to zero to disable this feature,
	RemindMuteState int
	// contains filtered or unexported fields
}

Muter can be used to limit the logger/alerting verbosity. Muter detects when its internal counter is over the Threshold and waits the counter goes back to zero or after NoAlertDuration to return to normal situation. Muter uses the Hysteresis principle: https://wikiless.org/wiki/Hysteresis Similar wording: quieter, stopper, limiter, reducer, inhibitor, mouth-closer.

func (*Muter) Decrement added in v0.21.0

func (m *Muter) Decrement() (ok bool, _ time.Time, dropped int)

Decrement decrements the internal counter and switches to un-muted state when counter reaches zero or after NoAlertDuration.

func (*Muter) Increment added in v0.21.0

func (m *Muter) Increment() (ok bool, dropped int)

Increment increments the internal counter and returns false when in muted state. Every RemindMuteState calls, Increment also returns the number of times Increment has been called.

type Option added in v0.6.0

type Option func(*Garcon)

func WithDev added in v0.6.0

func WithDev(enable ...bool) Option

func WithDocURL added in v0.6.0

func WithDocURL(docURL string) Option

func WithPProf added in v0.6.0

func WithPProf(port int) Option

func WithServerName added in v0.23.0

func WithServerName(str string) Option

func WithURLs added in v0.9.0

func WithURLs(addresses ...string) Option

type Perm added in v0.17.0

type Perm struct {
	Value int
}

func PermFromCtx added in v0.20.0

func PermFromCtx(r *http.Request) Perm

PermFromCtx gets the permission information from the request context.

func (Perm) PutInCtx added in v0.20.0

func (perm Perm) PutInCtx(r *http.Request) *http.Request

PutInCtx stores the permission info within the request context.

type Policy added in v0.17.0

type Policy struct {
	// contains filtered or unexported fields
}

Policy manages the Open Policy Agent (OPA). see https://www.openpolicyagent.org/docs/edge/integration/#integrating-with-the-go-api

func NewPolicy added in v0.17.0

func NewPolicy(gw Writer, filenames []string) (Policy, error)

NewPolicy creates a new Policy by loading rego files.

func (Policy) MiddlewareOPA added in v0.23.0

func (opa Policy) MiddlewareOPA(next http.Handler) http.Handler

MiddlewareOPA is the HTTP middleware to check endpoint authorization.

type RTChain added in v0.21.0

type RTChain []RTMiddleware

RTChain acts as a list of http.RoundTripper middlewares. RTChain is effectively immutable: once created, it will always hold the same set of middleware in the same order.

func NewRTChain added in v0.21.0

func NewRTChain(chain ...RTMiddleware) RTChain

NewRTChain creates a new chain of HTTP round trippers, memorizing the given list of RoundTrip middlewares. NewRTChain serves no other function, middlewares are only called upon a call to Then().

func (RTChain) Append added in v0.21.0

func (c RTChain) Append(chain ...RTMiddleware) RTChain

Append extends a chain, adding the specified middlewares as the last ones in the request flow.

Append returns a new chain, leaving the original one untouched.

Example #1

stdChain := garcon.NewRTChain(m1, m2)
extChain := stdChain.Append(m3, m4)
// requests in stdChain go m1 -> m2
// requests in extChain go m1 -> m2 -> m3 -> m4

Example #2

stdChain := garcon.NewRTChain(m1, m2)
ext1Chain := garcon.NewRTChain(m3, m4)
ext2Chain := stdChain.Append(ext1Chain...)
// requests in stdChain go  m1 -> m2
// requests in ext1Chain go m3 -> m4
// requests in ext2Chain go m1 -> m2 -> m3 -> m4

Example #3

aHtmlAfterNosurf := garcon.NewRTChain(m2)
aHtml := garcon.NewRTChain(m1, func(rt http.RoundTripper) http.RoundTripper {
	csrf := nosurf.New(rt)
	csrf.SetFailureHandler(aHtmlAfterNosurf.ThenFunc(csrfFail))
	return csrf
}).Append(aHtmlAfterNosurf)
// requests to aHtml hitting nosurfs success handler go: m1 -> nosurf -> m2 -> rt
// requests to aHtml hitting nosurfs failure handler go: m1 -> nosurf -> m2 -> csrfFail

func (RTChain) Then added in v0.21.0

Then chains the middleware and returns the final http.RoundTripper.

garcon.NewRTChain(m1, m2, m3).Then(rt)

is equivalent to:

m1(m2(m3(rt)))

When the request goes out, it will be passed to m1, then m2, then m3 and finally, the given round tripper (assuming every middleware calls the following one).

A chain can be safely reused by calling Then() several times.

stdStack := garcon.NewRTChain(rateLimitHandler, csrfHandler)
indexPipe = stdStack.Then(indexHandler)
authPipe = stdStack.Then(authHandler)

Note that middlewares are called on every call to Then() and thus several instances of the same middleware will be created when a chain is reused in this way. For proper middleware, this should cause no problems.

Then() treats nil as http.DefaultTransport.

func (RTChain) ThenFunc added in v0.21.0

func (c RTChain) ThenFunc(fn RoundTripperFunc) http.RoundTripper

ThenFunc works identically to Then, but takes a RoundTripperFunc instead of a RoundTripper.

The following two statements are equivalent:

c.Then(http.RoundTripperFunc(fn))
c.ThenFunc(fn)

ThenFunc provides all the guarantees of Then.

type RTMiddleware added in v0.21.0

type RTMiddleware func(http.RoundTripper) http.RoundTripper

RTMiddleware for a piece of RoundTrip middleware. Some middleware uses this out of the box, so in most cases you can just use somepackage.New().

type ReqLimiter added in v0.17.0

type ReqLimiter struct {
	// contains filtered or unexported fields
}

func NewRateLimiter added in v0.23.0

func NewRateLimiter(gw Writer, maxReqBurst, maxReqPerMinute int, devMode bool) ReqLimiter

func (*ReqLimiter) MiddlewareRateLimiter added in v0.23.0

func (rl *ReqLimiter) MiddlewareRateLimiter(next http.Handler) http.Handler

type RoundTripperFunc added in v0.21.0

type RoundTripperFunc func(*http.Request) (*http.Response, error)

RoundTripperFunc is to RoundTripper what HandlerFunc is to Handler. It is a higher-order function that enables chaining of RoundTrippers with the middleware pattern.

func (RoundTripperFunc) RoundTrip added in v0.21.0

func (f RoundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error)

RoundTrip calls the function itself.

type ServerName added in v0.23.0

type ServerName string

ServerName is used in multiple parts in Garcon: - the HTTP Server header, - the Prometheus namespace.

func ExtractName added in v0.23.0

func ExtractName(str string) ServerName

ExtractName extracts the wider "[a-zA-Z0-9_]+" string from the end of str. If str is a path or an URL, keep the last basename. Example: keep "myapp" from "https://example.com/path/myapp/" ExtractName also removes all punctuation characters except "_".

func (ServerName) MiddlewareExportTrafficMetrics added in v0.23.0

func (ns ServerName) MiddlewareExportTrafficMetrics(next http.Handler) http.Handler

MiddlewareExportTrafficMetrics measures the time to handle a request.

func (*ServerName) SetPromNamingRule added in v0.23.0

func (ns *ServerName) SetPromNamingRule()

SetPromNamingRule verifies Prom naming rules for namespace and fixes it if necessary. valid namespace = [a-zA-Z][a-zA-Z0-9_]* https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels

func (ServerName) String added in v0.23.0

func (ns ServerName) String() string

type StaticWebServer added in v0.16.0

type StaticWebServer struct {
	Writer Writer
	Dir    string
}

StaticWebServer is a webserver serving static files among HTML, CSS, JS and popular image formats.

func NewStaticWebServer added in v0.16.0

func NewStaticWebServer(gw Writer, dir string) StaticWebServer

NewStaticWebServer creates a StaticWebServer.

func (*StaticWebServer) ServeAssets added in v0.16.0

func (ws *StaticWebServer) ServeAssets() func(w http.ResponseWriter, r *http.Request)

ServeAssets detects the Content-Type depending on the asset extension.

func (*StaticWebServer) ServeDir added in v0.16.0

func (ws *StaticWebServer) ServeDir(contentType string) func(w http.ResponseWriter, r *http.Request)

ServeDir handles the static files using the same Content-Type.

func (*StaticWebServer) ServeFile added in v0.16.0

func (ws *StaticWebServer) ServeFile(urlPath, contentType string) func(w http.ResponseWriter, r *http.Request)

ServeFile handles one specific file (and its specific Content-Type).

func (*StaticWebServer) ServeImages added in v0.16.0

func (ws *StaticWebServer) ServeImages() func(w http.ResponseWriter, r *http.Request)

ServeImages detects the Content-Type depending on the image extension.

type TokenChecker added in v0.13.0

type TokenChecker interface {
	// Set is a middleware setting a cookie in the response when the request has no valid token.
	// Set searches the token in a cookie and in the first "Authorization" header.
	// Finally, Set stores the decoded token fields within the request context.
	Set(next http.Handler) http.Handler

	// Chk is a middleware accepting requests only if it has a valid cookie:
	// other requests are rejected with http.StatusUnauthorized.
	// Chk does not verify the "Authorization" header.
	// See also the Vet() function if the token should also be verified in the "Authorization" header.
	// Finally, Chk stores the decoded token fields within the request context.
	// In dev. mode, Chk accepts any request but does not store invalid tokens.
	Chk(next http.Handler) http.Handler

	// Vet is a middleware accepting accepting requests having a valid token
	// either in the cookie or in the first "Authorization" header:
	// other requests are rejected with http.StatusUnauthorized.
	// Vet also stores the decoded token in the request context.
	// In dev. mode, Vet accepts any request but does not store invalid tokens.
	Vet(next http.Handler) http.Handler

	// Cookie returns a default cookie to facilitate testing.
	Cookie(i int) *http.Cookie
}

TokenChecker is the common interface to Incorruptible and JWTChecker.

type WebForm added in v0.16.0

type WebForm struct {
	Writer   Writer
	Notifier notifier.Notifier
	Redirect string

	// TextLimits are used as security limits
	// to avoid being flooded by large web forms
	// and unexpected field names.
	// The map key is the input field name.
	// The map value is a pair of integers:
	// the max length and the max line breaks.
	// Use 0 to disable any limit.
	TextLimits map[string][2]int

	// FileLimits is similar to TextLimits
	// but for uploaded files.
	// The map value is a pair of integers:
	// the max size in runes of one file
	// and the max occurrences having same field name.
	// Use 0 to disable any limit.
	FileLimits map[string][2]int

	// MaxTotalLength includes the
	// form fields and browser fingerprints.
	MaxTotalLength int
	// contains filtered or unexported fields
}

func NewContactForm added in v0.16.0

func NewContactForm(gw Writer, redirectURL string) WebForm

NewContactForm initializes a new WebForm with the default contact-form settings.

func (*WebForm) Notify added in v0.23.0

func (form *WebForm) Notify(notifierURL string) func(w http.ResponseWriter, r *http.Request)

Notify returns a handler that converts the received web-form into markdown format and sends it to the notifierURL.

type Writer added in v0.21.0

type Writer string

Writer enables writing useful JSON error message in the HTTP response body.

func NewWriter added in v0.21.0

func NewWriter(docURL string) Writer

NewWriter creates a Writer structure.

func (Writer) InvalidPath added in v0.21.0

func (gw Writer) InvalidPath(w http.ResponseWriter, r *http.Request)

func (Writer) NotImplemented added in v0.21.0

func (gw Writer) NotImplemented(w http.ResponseWriter, r *http.Request)

func (Writer) WriteErr added in v0.21.0

func (gw Writer) WriteErr(w http.ResponseWriter, r *http.Request, statusCode int, kv ...any)

WriteErr is a fast pretty-JSON marshaler dedicated to the HTTP error response. WriteErr extends the JSON content when more than two key-values (kv) are provided.

func (Writer) WriteErrSafe added in v0.21.0

func (gw Writer) WriteErrSafe(w http.ResponseWriter, r *http.Request, statusCode int, kv ...any)

WriteErrSafe is a safe alternative to Write, may be slower despite the easyjson generated code. Disadvantage: WriteErrSafe concatenates all key-values (kv) in "message" field.

func (Writer) WriteOK added in v0.21.0

func (gw Writer) WriteOK(w http.ResponseWriter, kv ...any)

WriteOK is a fast pretty-JSON marshaler dedicated to the HTTP successful response.

Directories

Path Synopsis
examples
chi
Package timex extends the standard package time.
Package timex extends the standard package time.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL