wrap

package
v0.0.0-...-c5cf874 Latest Latest
Warning

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

Go to latest
Published: May 20, 2024 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package wrap provides wrappers for [http.Handler]s.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Context

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

Context wraps handler, replacing the context of any request using the given function.

Example
package main

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"

	"github.com/tkw1536/pkglib/httpx/wrap"
)

type responseKeyType struct{}

var responseKey = responseKeyType{}

func main() {
	handler := wrap.Context(
		// Create a new handler that extracts a given context key
		// and writes it to the response
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			content, ok := r.Context().Value(responseKey).(string)
			if !ok {
				return
			}

			_, _ = w.Write([]byte(content))
		}),

		// Wrap it using a function that automatically sets the key
		func(r *http.Request) (context.Context, context.CancelFunc) {
			return context.WithValue(r.Context(), responseKey, "this response got set in a wrapper"), nil
		},
	)

	// create a new request
	req, err := http.NewRequest(http.MethodGet, "/", nil)
	if err != nil {
		panic(err)
	}

	// serve the http request
	rr := httptest.NewRecorder()
	handler.ServeHTTP(rr, req)

	if status := rr.Result().StatusCode; status != http.StatusOK {
		fmt.Println("Expected http.StatusOK")
	}

	result, _ := io.ReadAll(rr.Result().Body)
	fmt.Println(string(result))

}
Output:

this response got set in a wrapper
Example (Cancel)
package main

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"

	"github.com/tkw1536/pkglib/httpx/wrap"
)

func main() {
	handler := wrap.Context(
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			fmt.Println("calling handler")
			_, _ = w.Write(nil)
		}),

		// Wrap it using a function that automatically sets the key
		func(r *http.Request) (context.Context, context.CancelFunc) {
			return r.Context(), func() { fmt.Printf("calling CancelFunc") }
		},
	)

	// create a new request
	req, err := http.NewRequest(http.MethodGet, "/", nil)
	if err != nil {
		panic(err)
	}

	// serve the http request
	rr := httptest.NewRecorder()
	handler.ServeHTTP(rr, req)

	if status := rr.Result().StatusCode; status != http.StatusOK {
		fmt.Println("Expected http.StatusOK")
	}

	result, _ := io.ReadAll(rr.Result().Body)
	fmt.Println(string(result))

}
Output:

calling handler
calling CancelFunc

func Methods

func Methods(handler http.Handler, methods ...string) http.Handler

Methods wraps handler, rejecting requests not using any of the provided methods. Requests with rejected methods return a generic "Method Not Allowed" response with appropriate status code.

Example
package main

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"

	"github.com/tkw1536/pkglib/httpx/wrap"
)

func main() {

	handler := wrap.Methods(
		// Create a new handler that echoes the appropriate method
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			_, _ = w.Write([]byte(r.Method))
		}),

		// and permit only the GET and POST methods
		http.MethodGet, http.MethodPost,
	)

	// A simple function to make a request with a specific method
	makeRequest := func(method string) {
		req, err := http.NewRequest(method, "/", nil)
		if err != nil {
			panic(err)
		}

		rr := httptest.NewRecorder()
		handler.ServeHTTP(rr, req)

		result, _ := io.ReadAll(rr.Result().Body)
		fmt.Printf("%s returned code %d with content %q\n", method, rr.Result().StatusCode, string(result))
	}

	// and do a couple of the requests
	makeRequest(http.MethodGet)
	makeRequest(http.MethodPost)
	makeRequest(http.MethodHead)

}
Output:

GET returned code 200 with content "GET"
POST returned code 200 with content "POST"
HEAD returned code 405 with content "Method Not Allowed"

func Time

func Time(h http.Handler) http.Handler

Time wraps an http.Handler, storing the time a request was started within it. To retrieve stored time, see TimeStart and TimeSince.

Example
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"time"

	"github.com/tkw1536/pkglib/httpx/wrap"
)

func main() {

	// delay used during this example
	var delay = 50 * time.Millisecond

	// Create a new HandlerFunc that sleeps for the delay.
	handler := wrap.Time(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		time.Sleep(delay)         // sleep for a bit
		took := wrap.TimeSince(r) // record how long it took

		_ = json.NewEncoder(w).Encode(took)
	}))

	// create a new request
	req, err := http.NewRequest(http.MethodGet, "/", nil)
	if err != nil {
		panic(err)
	}

	// serve the http request
	rr := httptest.NewRecorder()
	handler.ServeHTTP(rr, req)

	if status := rr.Code; status != http.StatusOK {
		fmt.Println("Expected http.StatusOK")
	}

	// decode the amount of time taken from the request
	var took time.Duration
	_ = json.NewDecoder(rr.Result().Body).Decode(&took)

	if took >= delay {
		fmt.Println("Handler returned correct delay")
	}

}
Output:

Handler returned correct delay

func TimeSince

func TimeSince(r *http.Request) time.Duration

TimeSince returns the time since the request r was started. Must be called from within a handler wrapped with Time to be effective. If no time is stored in the request, returns 0.

func TimeStart

func TimeStart(r *http.Request) time.Time

TimeStart returns the time that the request r was started. Must be called from within a handler wrapped with Time. If no time is stored, returns the current time.

Types

type ContextFunc

type ContextFunc = func(r *http.Request) (context.Context, context.CancelFunc)

ContextFunc is a function that replaces contexts for a given request. A nil ContextFunc leaves the original context intact.

- the returned context, if non-nil, is used to replace the context of the request. - the returned CancelFunc is called once the request ends.

Jump to

Keyboard shortcuts

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