umbrella

package module
v0.13.0 Latest Latest
Warning

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

Go to latest
Published: Dec 10, 2021 License: MIT Imports: 20 Imported by: 0

README

umbrella

test codecov go.dev reference go report card license

This package provides 20+ middleware that can be used in various frameworks compatible with Go standard net/http ecosystem.

Installation

go get -u github.com/kenkyu392/umbrella

Usage

The middleware provided in this package can be used by intuitively combining individual functions.
Documentation for all middleware can be found here.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/kenkyu392/umbrella"
)

func init() {
	os.Setenv("DEBUG", "true")
}

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)

	m := http.NewServeMux()

	// Create a MetricsRecorder and start recording using the middleware.
	mr := umbrella.NewMetricsRecorder(
		umbrella.WithRequestMetricsHookFunc(func(rm *umbrella.RequestMetrics) {
			// You can use hook functions to output request metrics to a log
			// or send them to a monitoring service.
			raw, err := json.Marshal(rm)
			if err != nil {
				log.Println(err)
			}
			log.Printf("%s", raw)
		}),
	)

	// Create the middleware.
	mw := umbrella.Use(
		// Recover panic.
		umbrella.Recover(os.Stderr),
		// Enables the recording of metrics.
		mr.Middleware(),
		// Only accessible in Firefox and Chrome.
		umbrella.AllowUserAgent("Firefox", "Chrome"),
		// Limit the display of iframe to mitigate clickjacking attacks.
		umbrella.Clickjacking("deny"),
		// It implements a countermeasure for Content-Type snuffing vulnerability,
		// which is a problem in old Internet Explorer, for example.
		umbrella.ContentSniffing(),
		// Enable browser cache for 120s.
		umbrella.CacheControl("public", "max-age=120", "s-maxage=120"),
		// Set the timeout at 800ms.
		umbrella.Timeout(time.Millisecond*800),
		// Enable caching for 2s.
		umbrella.Stampede(time.Second*2),
		// Limit the number of requests from the same IP address to 10 per second.
		umbrella.RateLimitPerIP(10),
	)

	// Enable the handler for debugging using the value set in the environment variable.
	debug, _ := strconv.ParseBool(os.Getenv("DEBUG"))
	debugMW := umbrella.Use(
		umbrella.Recover(os.Stderr),
		umbrella.Debug(debug),
	)

	m.Handle("/search", mw(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		q := r.URL.Query().Get("q")
		log.Printf("Search: %s", q)
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w, "Search: %s", q)
	})))

	// You can use MetricsRecorder.Handler to view the metrics.
	// ~$ curl -s http://localhost:3000/metrics | jq .
	m.Handle("/metrics", debugMW(http.HandlerFunc(mr.Handler)))

	http.ListenAndServe(":3000", m)
}

This is an example of middleware used to deliver static files such as images.

mw := umbrella.Use(
  // Sets the ETag.
  umbrella.ETag(),
  // Add headers for basic cache control, etc.
  umbrella.ResponseHeader(
    umbrella.ClickjackingHeaderFunc("deny"),
    umbrella.ContentSniffingHeaderFunc(),
    umbrella.XSSFilteringHeaderFunc("1; mode=block"),
    umbrella.CacheControlHeaderFunc("public", "max-age=86400", "no-transform"),
    umbrella.ExpiresHeaderFunc(time.Second*86400),
  ),
  // Set the timeout at 2s.
  umbrella.Timeout(time.Second*2),
  // Enable caching for 60s.
  umbrella.Stampede(time.Second*60),
)

Middleware

Middleware Description
Use Creates a single middleware that executes multiple middleware.
RealIP Override the RemoteAddr in http.Request with an X-Forwarded-For or X-Real-IP header.
Recover Recover from panic and record a stack trace and return a 500 Internal Server Error status.
Timeout Timeout cancels the context at the given time.
Context Context is middleware that manipulates request scope context.
Stampede Stampede provides a simple cache middleware that is valid for a specified amount of time.
RateLimit/RateLimitPerIP RateLimit provides middleware that limits the number of requests processed per second.
MetricsRecorder MetricsRecorder provides simple metrics such as request/response size and request duration.
HSTS HSTS adds the Strict-Transport-Security header.
Clickjacking Clickjacking mitigates clickjacking attacks by limiting the display of iframe.
ContentSniffing ContentSniffing adds a header for Content-Type sniffing vulnerability countermeasures.
XSSFiltering XSSFiltering provides middleware that enables the ability to stop a page from loading when a cross-site scripting attack is detected.
CacheControl/NoCache CacheControl/NoCache adds the Cache-Control header.
AllowUserAgent/DisallowUserAgent Allow/DisallowUserAgent middleware controls the request based on the User-Agent header of the request.
AllowContentType/DisallowContentType Allow/DisallowContentType middleware controls the request based on the Content-Type header of the request.
AllowAccept/DisallowAccept Allow/DisallowAccept middleware controls the request based on the Accept header of the request.
AllowMethod/DisallowMethod Create an access control using the request method.
RequestHeader/ResponseHeader Request/ResponseHeader is middleware that edits request and response headers.
Debug Debug provides middleware that executes the handler only if d is true.
Switch Switch provides a middleware that executes the next handler if the result of f is true, and executes h if it is false.
ETag ETag provides middleware that calculates MD5 from the response data and sets it in the ETag header.
Expires Expires provides middleware for adding response expiration dates.
Static Provides a handler to deliver static files.
BasicAuth BasicAuth provides basic authentication with minimal functionality.
Use

Creates a single middleware that executes multiple middleware.

Example :
package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/kenkyu392/umbrella"
)

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		for k := range w.Header() {
			fmt.Fprintf(w, "%s: %s\n", k, w.Header().Get(k))
		}
	})

	m := http.NewServeMux()

	// Creates a single middleware that executes multiple middleware.
	mw := umbrella.Use(
		umbrella.AllowUserAgent("Firefox", "Chrome"),
		umbrella.Clickjacking("deny"),
		umbrella.ContentSniffing(),
		umbrella.NoCache(),
		umbrella.Timeout(time.Millisecond*800),
	)
	m.Handle("/", mw(handler))

	http.ListenAndServe(":3000", m)
}
RealIP

Override the RemoteAddr in http.Request with an X-Forwarded-For or X-Real-IP header.

Example :
package main

import (
	"fmt"
	"net/http"

	"github.com/kenkyu392/umbrella"
)

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		// If an X-Forwarded-For or X-Real-IP header is received,
		// RemoteAddr will be overwritten.
		fmt.Fprintf(w, "RemoteAddr: %v\n", r.RemoteAddr)
		r.Write(w)
	})

	m := http.NewServeMux()

	mw := umbrella.RealIP()
	m.Handle("/", mw(handler))

	http.ListenAndServe(":3000", m)
}
Recover

Recover from panic and record a stack trace and return a 500 Internal Server Error status.

Example :
package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/kenkyu392/umbrella"
)

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		now := time.Now()
		if now.Unix()%2 == 0 {
			panic(fmt.Sprintf("panic: %v\n", now))
		}
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w, "Time: %v\n", now)
		r.Write(w)
	})

	m := http.NewServeMux()

	// If you give nil, it will be output to os.Stderr.
	mw := umbrella.Recover(nil)
	m.Handle("/", mw(handler))

	http.ListenAndServe(":3000", m)
}
Timeout

Timeout cancels the context at the given time.

Example :
package main

import (
	"fmt"
	"math/rand"
	"net/http"
	"time"

	"github.com/kenkyu392/umbrella"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		d := time.Millisecond * time.Duration(rand.Intn(500)+500)
		ctx := r.Context()
		select {
		case <-ctx.Done():
			return
		case <-time.After(d):
		}
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w, "duration: %v", d)
	})

	m := http.NewServeMux()

	// This handler times out in 800ms.
	mw := umbrella.Timeout(time.Millisecond * 800)
	m.Handle("/", mw(handler))

	http.ListenAndServe(":3000", m)
}
Stampede

Stampede provides a simple cache middleware that is valid for a specified amount of time.

Example :
package main

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

	"github.com/kenkyu392/umbrella"
)

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		q := r.URL.Query().Get("q")
		log.Printf("Search: %s", q)
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w, "Search: %s", q)
	})

	m := http.NewServeMux()

	// Create a middleware with a cache that expires in 5 seconds.
	mw := umbrella.Stampede(time.Second * 5)
	m.Handle("/search", mw(handler))

	http.ListenAndServe(":3000", m)
}
RateLimit/RateLimitPerIP

RateLimit provides middleware that limits the number of requests processed per second.

Example :
package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/kenkyu392/umbrella"
)

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		q := r.URL.Query().Get("q")
		log.Printf("Search: %s", q)
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w, "Search: %s", q)
	})

	m := http.NewServeMux()

	// Create a middleware that processes 5 requests per second.
	mw := umbrella.RateLimit(5) // or umbrella.RateLimitPerIP(5)
	m.Handle("/search", mw(handler))

	http.ListenAndServe(":3000", m)
}
MetricsRecorder

MetricsRecorder provides simple metrics such as request/response size and request duration.

Example :
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"

	"github.com/kenkyu392/umbrella"
)

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		q := r.URL.Query().Get("q")
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w, "Search: %s", q)
	})

	m := http.NewServeMux()

	// Create a MetricsRecorder and start recording using the middleware.
	mr := umbrella.NewMetricsRecorder(
		umbrella.WithRequestMetricsHookFunc(func(rm *umbrella.RequestMetrics) {
			// You can use hook functions to output request metrics to a log
			// or send them to a monitoring service.
			raw, err := json.Marshal(rm)
			if err != nil {
				log.Println(err)
			}
			log.Printf("%s", raw)
		}),
	)
	m.Handle("/search", mr.Middleware()(handler))
	// You can use MetricsRecorder.Handler to view the metrics.
	// ~$ curl -s http://localhost:3000/metrics | jq .
	m.Handle("/metrics", http.HandlerFunc(mr.Handler))

	http.ListenAndServe(":3000", m)
}
HSTS

HSTS adds the Strict-Transport-Security header.
Proper use of this header will mitigate stripping attacks.

Example :
package main

import (
	"fmt"
	"net/http"

	"github.com/kenkyu392/umbrella"
)

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w,
			"Strict-Transport-Security: %v",
			w.Header().Get("Strict-Transport-Security"),
		)
	})

	m := http.NewServeMux()

	// Tells the browser to use HTTPS instead of HTTP to connect to a domain
	// (including subdomains).
	mw := umbrella.HSTS(60, "includeSubDomains")
	m.Handle("/", mw(handler))

	http.ListenAndServe(":3000", m)
}
Clickjacking

Clickjacking mitigates clickjacking attacks by limiting the display of iframe.

Example :
package main

import (
	"net/http"

	"github.com/kenkyu392/umbrella"
)

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		// This iframe is not displayed.
		w.Write([]byte(`<iframe src="https://www.google.com/"></iframe>`))
	})

	m := http.NewServeMux()

	// Limit the display of iframe to mitigate clickjacking attacks.
	mw := umbrella.Clickjacking("deny")
	m.Handle("/", mw(handler))

	http.ListenAndServe(":3000", m)
}
ContentSniffing

ContentSniffing adds a header for Content-Type sniffing vulnerability countermeasures.

Example :
package main

import (
	"fmt"
	"net/http"

	"github.com/kenkyu392/umbrella"
)

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w,
			"X-Content-Type-Options: %v",
			w.Header().Get("X-Content-Type-Options"),
		)
	})

	m := http.NewServeMux()

	// It implements a countermeasure for Content-Type snuffing vulnerability,
	// which is a problem in old Internet Explorer, for example.
	mw := umbrella.ContentSniffing()
	m.Handle("/", mw(handler))

	http.ListenAndServe(":3000", m)
}
XSSFiltering

XSSFiltering provides middleware that enables the ability to stop a page from loading when a cross-site scripting attack is detected.

Example :
package main

import (
	"fmt"
	"net/http"

	"github.com/kenkyu392/umbrella"
)

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w,
			"X-XSS-Protection: %v",
			w.Header().Get("X-XSS-Protection"),
		)
	})

	m := http.NewServeMux()

	// Enable the filter function (stop page loading) for cross-site scripting (XSS).
	// This header is for browsers that do not support Content-Security-Policy.
	mw := umbrella.XSSFiltering("0") // If empty, "1; mode=block" will be set.
	m.Handle("/", mw(handler))

	http.ListenAndServe(":3000", m)
}
CacheControl/NoCache

CacheControl/NoCache adds the Cache-Control header.

Example :
package main

import (
	"crypto/md5"
	"fmt"
	"net/http"
	"strings"

	"github.com/kenkyu392/umbrella"
)

func main() {
	data := []byte(`<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
	<circle cx="50" cy="50" r="40" stroke="#6a737d" stroke-width="4" fill="#1b1f23" />
	</svg>`)
	etag := fmt.Sprintf(`"%x"`, md5.Sum(data))
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if match := r.Header.Get("If-None-Match"); strings.Contains(match, etag) {
			w.WriteHeader(http.StatusNotModified)
			return
		}
		w.Header().Set("Content-Type", "image/svg+xml")
		w.Header().Set("ETag", etag)
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(data))
	})

	m := http.NewServeMux()

	// Enable browser cache for 2 days.
	// mw := umbrella.NoCache()
	mw := umbrella.CacheControl("public", "max-age=172800", "s-maxage=172800")
	m.Handle("/", mw(handler))

	http.ListenAndServe(":3000", m)
}
Context

Context is middleware that manipulates request scope context.

Example :
package main

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"github.com/kenkyu392/umbrella"
)

type key struct{}

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w, "context: %v", r.Context().Value(key{}))
	})

	m := http.NewServeMux()

	// You can embed the value in the request context.
	mw := umbrella.Context(func(ctx context.Context) context.Context {
		return context.WithValue(ctx, key{}, time.Now().UnixNano())
	})
	m.Handle("/", mw(handler))

	http.ListenAndServe(":3000", m)
}
AllowUserAgent/DisallowUserAgent

Allow/DisallowUserAgent middleware controls the request based on the User-Agent header of the request.

Example :
package main

import (
	"fmt"
	"net/http"

	"github.com/kenkyu392/umbrella"
)

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w, "ua: %v", r.UserAgent())
	})

	m := http.NewServeMux()

  // Only accessible in Firefox and Chrome.
  allows := umbrella.AllowUserAgent("Firefox", "Chrome")
	m.Handle("/allows",
		allows(handler),
  )

  // Not accessible in Edge and Internet Explorer.
  disallows := umbrella.DisallowUserAgent("Edg", "MSIE")
	m.Handle("/disallows",
		disallows(handler),
	)

	http.ListenAndServe(":3000", m)
}
AllowContentType/DisallowContentType

Allow/DisallowContentType middleware controls the request based on the Content-Type header of the request.

Example :
package main

import (
	"fmt"
	"net/http"

	"github.com/kenkyu392/umbrella"
)

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w, "ua: %v", r.UserAgent())
	})

	m := http.NewServeMux()

	allows := umbrella.AllowContentType(
		"application/json", "text/json",
		"application/xml", "text/xml",
	)
	disallows := umbrella.DisallowContentType(
		"text/plain", "application/octet-stream",
	)

	// Only accessible in JSON and XML.
	m.Handle("/allows",
		allows(handler),
	)
	// Not accessible in Plain text and Binary data.
	m.Handle("/disallows",
		disallows(handler),
	)

	http.ListenAndServe(":3000", m)
}
AllowAccept/DisallowAccept

Allow/DisallowAccept middleware controls the request based on the Accept header of the request.

Example :
package main

import (
	"fmt"
	"net/http"

	"github.com/kenkyu392/umbrella"
)

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w, "ua: %v", r.UserAgent())
	})

	m := http.NewServeMux()

	allows := umbrella.AllowAccept(
		"application/json", "text/json",
	)
	disallows := umbrella.DisallowAccept(
		"text/plain", "text/html",
	)

	// Only accessible in JSON.
	m.Handle("/allows",
		allows(handler),
	)
	// Not accessible in Plain text and HTML data.
	m.Handle("/disallows",
		disallows(handler),
	)

	http.ListenAndServe(":3000", m)
}
AllowMethod/DisallowMethod

Create an access control using the request method.

Example :
package main

import (
	"net/http"

	"github.com/kenkyu392/umbrella"
)

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		r.Write(w)
	})

	m := http.NewServeMux()

	// Create an access control using the request method.
	mw1 := umbrella.DisallowMethod(http.MethodGet)
	mw2 := umbrella.AllowMethod(http.MethodGet)
	m.Handle("/mw1", mw1(handler))
	m.Handle("/mw2", mw2(handler))

	http.ListenAndServe(":3000", m)
}
RequestHeader/ResponseHeader

Request/ResponseHeader is middleware that edits request and response headers.

Example :
package main

import (
	"fmt"
	"math/rand"
	"net/http"
	"strings"
	"time"

	"github.com/kenkyu392/umbrella"
)

const Version = "1.0.0"

func init() {
	rand.Seed(time.Now().UnixNano())
}

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w, "version:%s request:%s response:%s",
			w.Header().Get("X-Version"),
			r.Header.Get("X-Request-Id"),
			w.Header().Get("X-Response-Id"),
		)
	})

	m := http.NewServeMux()

	// You can embed values in request and response headers.
	mw1 := umbrella.RequestHeader(
		func(h http.Header) {
			h.Set("X-Request-Id", generateID())
		},
	)
	mw2 := umbrella.ResponseHeader(
		func(h http.Header) {
			h.Set("X-Response-Id", generateID())
		},
		umbrella.AddHeaderFunc("X-Version", Version),
	)
	m.Handle("/", mw1(mw2(handler)))

	http.ListenAndServe(":3000", m)
}

func generateID() string {
	var chars = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
	var b strings.Builder
	for i := 0; i < 6; i++ {
		b.WriteRune(chars[rand.Intn(len(chars))])
	}
	return fmt.Sprintf("%s%x", b.String(), time.Now().UnixNano())
}
Debug

Debug provides middleware that executes the handler only if d is true.

Example :
package main

import (
	"net/http"
	"os"
	"strconv"

	"github.com/kenkyu392/umbrella"
)

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Debug handler implementation.
		w.WriteHeader(http.StatusOK)
	})

	m := http.NewServeMux()

	// Enable the handler for debugging using the value set in the environment variable.
	debug, _ := strconv.ParseBool(os.Getenv("DEBUG"))
	mw := umbrella.Debug(debug)
	m.Handle("/debug", mw(handler))

	http.ListenAndServe(":3000", m)
}
Switch

Switch provides a middleware that executes the next handler if the result of f is true, and executes h if it is false.

Example :
package main

import (
	"net/http"

	"github.com/kenkyu392/umbrella"
)

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
	})
	forbidden := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
	})

	m := http.NewServeMux()

	mw := umbrella.Switch(func(r *http.Request) bool {
		// e.g. checking the prerequisites for a request such as authentication information...
		return true
	}, forbidden)

	m.Handle("/", mw(handler))

	http.ListenAndServe(":3000", m)
}
ETag

ETag provides middleware that calculates MD5 from the response data and sets it in the ETag header.

Example :
package main

import (
	"crypto/md5"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/kenkyu392/umbrella"
)

func main() {
	data := []byte(`<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
	<circle cx="50" cy="50" r="40" stroke="#6a737d" stroke-width="4" fill="#1b1f23" />
	</svg>`)
	etag := fmt.Sprintf(`"%x"`, md5.Sum(data))
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if match := r.Header.Get("If-None-Match"); strings.Contains(match, etag) {
			w.WriteHeader(http.StatusNotModified)
			return
		}
		w.Header().Set("Content-Type", "image/svg+xml")
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(data))
	})

	m := http.NewServeMux()

	mw := umbrella.Use(
		// Automatically calculates and sets the ETag.
		umbrella.ETag(),
		umbrella.ResponseHeader(
			umbrella.CacheControlHeaderFunc("public", "max-age=86400", "no-transform"),
			umbrella.ExpiresHeaderFunc(time.Second*86400),
		),
	)
	m.Handle("/", mw(handler))

	http.ListenAndServe(":3000", m)
}
Expires

Expires provides middleware for adding response expiration dates.

Example :
package main

import (
	"crypto/md5"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/kenkyu392/umbrella"
)

func main() {
	data := []byte(`<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
	<circle cx="50" cy="50" r="40" stroke="#6a737d" stroke-width="4" fill="#1b1f23" />
	</svg>`)
	etag := fmt.Sprintf(`"%x"`, md5.Sum(data))
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if match := r.Header.Get("If-None-Match"); strings.Contains(match, etag) {
			w.WriteHeader(http.StatusNotModified)
			return
		}
		w.Header().Set("Content-Type", "image/svg+xml")
		w.Header().Set("ETag", etag)
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(data))
	})

	m := http.NewServeMux()

	// Enable browser cache for 2 days.
	mw := umbrella.Expires(time.Second * 172800)
	m.Handle("/", mw(handler))

	http.ListenAndServe(":3000", m)
}
Static

Provides a handler to deliver static files.

Example :
package main

import (
	"net/http"
	"time"

	"github.com/kenkyu392/umbrella"
)

func main() {
	m := http.NewServeMux()

	// Add a handler to ServeMux to deliver static files.
	umbrella.Static(m, "/static", "./static",
		// Any middleware can be passed as a variable-length argument.
		// * Sets the ETag.
		umbrella.ETag(),
		// * Add headers for basic cache control, etc.
		umbrella.ResponseHeader(
			umbrella.ClickjackingHeaderFunc("deny"),
			umbrella.ContentSniffingHeaderFunc(),
			umbrella.XSSFilteringHeaderFunc("1; mode=block"),
			umbrella.CacheControlHeaderFunc("public", "max-age=86400", "no-transform"),
			umbrella.ExpiresHeaderFunc(time.Second*86400),
		),
		// * Set the timeout at 2s.
		umbrella.Timeout(time.Second*2),
		// * Enable caching for 60s.
		umbrella.Stampede(time.Second*60),
	)
	// You can also use StaticHandler to create a http.Handler.
	// m.Handle(umbrella.StaticHandler("/static", "./static"))

	http.ListenAndServe(":3000", m)
}
BasicAuth

BasicAuth provides basic authentication with minimal functionality.

Example :
package main

import (
	"net/http"

	"github.com/kenkyu392/umbrella"
)

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		r.Write(w)
	})

	// You can either return a preloaded username/password pair
	// or implement logic to reload the .htpasswd file.
	loadPairsFn := func() map[string]string {
		return map[string]string{
			"user1": "$2a$10$XfDWaLYmr3shVnaT1f06GeApug0dtssFrUsVtQofRiU0B8y63oKJm", // pass1
			"user2": "$2a$10$8JIfR1afqCwmCeO1NvKhW./JHLQfhQzeo7VBCHDwCdPddVaycdmYW", // pass2
			"user3": "$2a$10$95D11Af6vmKb8SjAAd89eOJoG8QXqeXrAo2lWpkz3btBCClMplGcq", // pass3
		}
	}

	const realm = "My Authentication"
	mw := umbrella.BasicAuth(realm, loadPairsFn)

	m := http.NewServeMux()
	m.Handle("/", mw(handler))
	http.ListenAndServe(":3000", m)
}

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

View Source
var HoneypotUserAgents = []string{
	"0xSCANNER",
	"20010801",
	"AhrefsBot",
	"Alprazolam",
	"BLEXBot",
	"BOT for JCE",
	"Baiduspider",
	"Gecko/20100115",
	"Gemini",
	"Hakai",
	"Hello",
	"Indy Library",
	"Indy-Library",
	"JDatabaseDriverMysqli",
	"Jorgee",
	"LMAO",
	"MJ12bot",
	"Mappy",
	"Morfeus",
	"NYU",
	"Nessus",
	"Nikto",
	"OpenVAS",
	"Ronin",
	"SemrushBot",
	"Shinka",
	"WPScan",
	"ZmEu",
	"aiohttp",
	"masscan",
	"muhstik",
	"sqlmap",
	"sysscan",
	"union select",
	"yandex",
	"zgrab",
}

HoneypotUserAgents ...

Functions

func AllowAccept added in v0.4.0

func AllowAccept(contentTypes ...string) func(http.Handler) http.Handler

AllowAccept is middleware that allows a request only if any of the specified strings is included in the Accept header. Returns 406 Not Acceptable status if the request has a type that is not allowed.

func AllowContentType added in v0.2.0

func AllowContentType(contentTypes ...string) func(http.Handler) http.Handler

AllowContentType is middleware that allows a request only if any of the specified strings is included in the Content-Type header. Returns 415 Unsupported Media Type status if the request has a type that is not allowed.

func AllowHTTPHeader

func AllowHTTPHeader(badStatus int, name string, values ...string) func(http.Handler) http.Handler

AllowHTTPHeader is middleware that allows a request only when one of the specified strings is included in the specified request header.

func AllowMethod added in v0.3.0

func AllowMethod(methods ...string) func(http.Handler) http.Handler

AllowMethod is a middleware that returns a 405 Method Not Allowed status if the request method is not one of the given methods.

func AllowUserAgent

func AllowUserAgent(userAgents ...string) func(http.Handler) http.Handler

AllowUserAgent is middleware that allows a request only if any of the specified strings is included in the User-Agent header. Returns 403 Forbidden status if the request has a user-agent that is not allowed.

func BasicAuth added in v0.13.0

func BasicAuth(realm string, loadPairsFn func() map[string]string) func(http.Handler) http.Handler

BasicAuth provides basic authentication with minimal functionality. loadPairsFn is used to load the username and hashed password (bcrypt) pairs used for authentication.

func CacheControl added in v0.3.0

func CacheControl(opts ...string) func(http.Handler) http.Handler

CacheControl adds the Cache-Control header.

func Clickjacking added in v0.2.0

func Clickjacking(opt string) func(http.Handler) http.Handler

Clickjacking mitigates clickjacking attacks by limiting the display of iframe.

func ContentSniffing added in v0.2.0

func ContentSniffing() func(http.Handler) http.Handler

ContentSniffing adds a header for Content-Type sniffing vulnerability countermeasures.

func Context

func Context(f ContextFunc) func(http.Handler) http.Handler

Context is middleware that edits the context of the request.

func Debug added in v0.9.0

func Debug(b bool) func(http.Handler) http.Handler

Debug provides middleware that executes the handler only if b is true. If b is false, it will return 404.

func DisallowAccept added in v0.4.0

func DisallowAccept(contentTypes ...string) func(http.Handler) http.Handler

DisallowAccept is middleware that disallow a request only if any of the specified strings is included in the Accept header. Returns 406 Not Acceptable status if the request has a type that is not allowed.

func DisallowContentType added in v0.2.0

func DisallowContentType(contentTypes ...string) func(http.Handler) http.Handler

DisallowContentType is middleware that disallow a request only if any of the specified strings is included in the Content-Type header. Returns 415 Unsupported Media Type status if the request has a type that is not allowed.

func DisallowHTTPHeader

func DisallowHTTPHeader(badStatus int, name string, values ...string) func(http.Handler) http.Handler

DisallowHTTPHeader is middleware that disallows a request only when one of the specified strings is included in the specified request header.

func DisallowMethod added in v0.3.0

func DisallowMethod(methods ...string) func(http.Handler) http.Handler

DisallowMethod is a middleware that returns a 405 Method Not Allowed status if the request method is one of the given methods.

func DisallowUserAgent

func DisallowUserAgent(userAgents ...string) func(http.Handler) http.Handler

DisallowUserAgent is middleware that disallow a request only if any of the specified strings is included in the User-Agent header. Returns 403 Forbidden status if the request has a user-agent that is not allowed.

func ETag added in v0.10.0

func ETag() func(http.Handler) http.Handler

ETag provides middleware that calculates MD5 from the response data and sets it in the ETag header.

func Expires added in v0.10.0

func Expires(d time.Duration) func(http.Handler) http.Handler

Expires provides middleware for adding response expiration dates. The expiration time is set to the current time plus d.

func HSTS added in v0.2.0

func HSTS(maxAge int, opt string) func(http.Handler) http.Handler

HSTS adds the Strict-Transport-Security header. Proper use of this header will mitigate stripping attacks.

func NoCache added in v0.3.0

func NoCache() func(http.Handler) http.Handler

NoCache adds the Cache-Control to disable the cache.

func RateLimit added in v0.8.0

func RateLimit(rl int) func(http.Handler) http.Handler

RateLimit provides middleware that limits the number of requests processed per second.

func RateLimitPerIP added in v0.8.0

func RateLimitPerIP(rl int) func(http.Handler) http.Handler

RateLimitPerIP provides middleware that limits the number of requests processed per second per IP.

func RealIP added in v0.3.0

func RealIP() func(http.Handler) http.Handler

RealIP is middleware that overwrites RemoteAddr of http.Request with X-Forwarded-For or X-Real-IP header. Validation of the X-Forwarded-For header is done from right to left.

func Recover added in v0.3.0

func Recover(out io.Writer) func(http.Handler) http.Handler

Recover is a middleware that recovers from panic and records a stack trace and returns a 500 Internal Server Error status.

func Reject added in v0.13.0

func Reject(badStatus int, f func(r *http.Request) bool) func(http.Handler) http.Handler

Reject provides middleware that returns badStatus if the result of f is false.

func RequestHeader added in v0.2.0

func RequestHeader(fs ...HeaderFunc) func(http.Handler) http.Handler

RequestHeader is middleware that edits the header of the request.

func ResponseHeader added in v0.2.0

func ResponseHeader(fs ...HeaderFunc) func(http.Handler) http.Handler

ResponseHeader is middleware that edits the header of the response.

func Stampede added in v0.5.0

func Stampede(d time.Duration) func(http.Handler) http.Handler

Stampede provides a simple cache middleware that is valid for a specified amount of time. It uses singleflight for caching to prevent thundering-herd and cache-stampede. If this middleware is requested at the same time, it executes the handler only once and shares the execution result with all requests.

func Static added in v0.12.0

func Static(m ServeMux, pattern, root string, middlewares ...func(http.Handler) http.Handler)

Static calls StaticFS internally.

func StaticFS added in v0.12.0

func StaticFS(m ServeMux, pattern string, fs http.FileSystem, middlewares ...func(http.Handler) http.Handler)

StaticFS adds an endpoint for static files to ServeMux.

func StaticHandler added in v0.12.0

func StaticHandler(pattern, root string) (string, http.Handler)

StaticHandler calls StaticFileHandlerFS internally.

func StaticHandlerFS added in v0.12.0

func StaticHandlerFS(pattern string, fs http.FileSystem) (string, http.Handler)

StaticHandlerFS returns a normalized pattern and Handler for static file delivery.

func Switch added in v0.10.0

func Switch(f func(r *http.Request) bool, h http.Handler) func(http.Handler) http.Handler

Switch provides a middleware that executes the next handler if the result of f is true, and executes h if it is false.

func Timeout added in v0.2.0

func Timeout(d time.Duration) func(http.Handler) http.Handler

Timeout cancels the context at the given time. Returns 504 Gateway Timeout status if a timeout occurs.

func Use added in v0.3.0

func Use(middlewares ...func(http.Handler) http.Handler) func(http.Handler) http.Handler

Use creates a single middleware that executes multiple middleware.

func XSSFiltering added in v0.10.0

func XSSFiltering(opt string) func(http.Handler) http.Handler

XSSFiltering provides middleware that enables the ability to stop a page from loading when a cross-site scripting attack is detected.

Types

type ContextFunc

type ContextFunc func(ctx context.Context) context.Context

ContextFunc ...

type HeaderFunc added in v0.2.0

type HeaderFunc func(header http.Header)

HeaderFunc ...

func AddHeaderFunc added in v0.11.0

func AddHeaderFunc(key, value string) HeaderFunc

AddHeaderFunc adds the key, value pair to the header.

func CacheControlHeaderFunc added in v0.3.0

func CacheControlHeaderFunc(opts ...string) HeaderFunc

CacheControlHeaderFunc returns a HeaderFunc that adds a Cache-Control header.

func ClickjackingHeaderFunc added in v0.2.0

func ClickjackingHeaderFunc(opt string) HeaderFunc

ClickjackingHeaderFunc returns a HeaderFunc to mitigate a clickjacking vulnerability.

func ContentSniffingHeaderFunc added in v0.2.0

func ContentSniffingHeaderFunc() HeaderFunc

ContentSniffingHeaderFunc returns a HeaderFunc for Content-Type sniffing vulnerability countermeasure.

func ExpiresHeaderFunc added in v0.10.0

func ExpiresHeaderFunc(d time.Duration) HeaderFunc

ExpiresHeaderFunc returns a HeaderFunc that adds an expiration date. The expiration time is set to the current time plus d.

func HSTSHeaderFunc added in v0.2.0

func HSTSHeaderFunc(maxAge int, opt string) HeaderFunc

HSTSHeaderFunc returns a HeaderFunc that adds a Strict-Transport-Security header.

func NoCacheHeaderFunc added in v0.3.0

func NoCacheHeaderFunc() HeaderFunc

NoCacheHeaderFunc returns the HeaderFunc to add the Cache-Control header that disables the cache.

func XSSFilteringHeaderFunc added in v0.10.0

func XSSFilteringHeaderFunc(opt string) HeaderFunc

XSSFilteringHeaderFunc returns a HeaderFunc to enable XSS filtering.

type Metrics added in v0.6.0

type Metrics struct {
	UptimeDurationNanoseconds  int64 `json:"uptimeDurationNanoseconds"`
	UptimeDurationMilliseconds int64 `json:"uptimeDurationMilliseconds"`

	GoroutinesTotalCount int64 `json:"goroutinesTotalCount"`
	MaxGoroutinesCount   int64 `json:"maxGoroutinesCount"`
	MinGoroutinesCount   int64 `json:"minGoroutinesCount"`
	AvgGoroutinesCount   int64 `json:"avgGoroutinesCount"`

	RequestsTotalCount int64 `json:"requestsTotalCount"`

	TotalRequestDurationNanoseconds int64 `json:"totalRequestDurationNanoseconds"`
	MaxRequestDurationNanoseconds   int64 `json:"maxRequestDurationNanoseconds"`
	MinRequestDurationNanoseconds   int64 `json:"minRequestDurationNanoseconds"`
	AvgRequestDurationNanoseconds   int64 `json:"avgRequestDurationNanoseconds"`

	TotalRequestDurationMilliseconds int64 `json:"totalRequestDurationMilliseconds"`
	MaxRequestDurationMilliseconds   int64 `json:"maxRequestDurationMilliseconds"`
	MinRequestDurationMilliseconds   int64 `json:"minRequestDurationMilliseconds"`
	AvgRequestDurationMilliseconds   int64 `json:"avgRequestDurationMilliseconds"`

	MaxRequestBytesCount  int64 `json:"maxRequestBytesCount"`
	MinRequestBytesCount  int64 `json:"minRequestBytesCount"`
	MaxResponseBytesCount int64 `json:"maxResponseBytesCount"`
	MinResponseBytesCount int64 `json:"minResponseBytesCount"`

	MethodCount      map[string]int64 `json:"methodCount"`
	StatusCount      map[int]int64    `json:"statusCount"`
	StatusClassCount map[string]int64 `json:"statusClassCount"`
}

Metrics ...

func (*Metrics) Clone added in v0.6.0

func (m *Metrics) Clone() *Metrics

Clone returns a new Metrics with the same value.

type MetricsRecorder added in v0.6.0

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

MetricsRecorder provides features for recording and retrieving metrics.

func NewMetricsRecorder added in v0.6.0

func NewMetricsRecorder(opts ...MetricsRecorderOption) *MetricsRecorder

NewMetricsRecorder creates and returns a new MetricsRecorder.

func (*MetricsRecorder) Handler added in v0.6.0

func (mr *MetricsRecorder) Handler(w http.ResponseWriter, r *http.Request)

Handler returns metrics in JSON.

func (*MetricsRecorder) Metrics added in v0.6.0

func (mr *MetricsRecorder) Metrics() *Metrics

Metrics ...

func (*MetricsRecorder) Middleware added in v0.6.0

func (mr *MetricsRecorder) Middleware() func(http.Handler) http.Handler

Middleware records metrics. If an error occurs, call the next handler without recording any metrics.

type MetricsRecorderOption added in v0.7.0

type MetricsRecorderOption func(mr *MetricsRecorder)

MetricsRecorderOption ...

func WithRequestMetricsHookFunc added in v0.7.0

func WithRequestMetricsHookFunc(fn func(*RequestMetrics)) MetricsRecorderOption

WithRequestMetricsHookFunc sets the hook function to be called for each request. The hook function receives the request metrics as an argument.

type RequestMetrics added in v0.7.0

type RequestMetrics struct {
	StartTime                   time.Time `json:"startTime"`
	EndTime                     time.Time `json:"endTime"`
	Method                      string    `json:"method"`
	Status                      int       `json:"status"`
	UserAgent                   string    `json:"userAgent"`
	Referer                     string    `json:"referer"`
	GoroutinesCount             int64     `json:"goroutinesCount"`
	RequestDurationNanoseconds  int64     `json:"requestDurationNanoseconds"`
	RequestDurationMilliseconds int64     `json:"requestDurationMilliseconds"`
	RequestBytesCount           int64     `json:"requestBytesCount"`
	ResponseBytesCount          int64     `json:"responseBytesCount"`
}

RequestMetrics ...

func (*RequestMetrics) Clone added in v0.7.0

func (r *RequestMetrics) Clone() *RequestMetrics

Clone returns a new RequestMetrics with the same value.

type ServeMux added in v0.12.0

type ServeMux interface {
	Handle(pattern string, handler http.Handler)
	HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))
}

ServeMux ...

Jump to

Keyboard shortcuts

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