Documentation
¶
Overview ¶
Package actionheader selects one application action from a configured HTTP request header. It uses only the standard library, so native HTTP servers and Lambda adapters can share the same selection rules. Consumers own action metadata, registry lookup, authorization and HTTP responses.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrMissing means the configured header has no represented values. // An absent header and a nil or empty value slice are all missing. ErrMissing = errors.New("actionheader: missing action") // ErrAmbiguous means the header has multiple represented values or a value containing a comma. // Gateway payload 2.0 combines duplicates with commas; // a literal comma cannot be distinguished from combined values. ErrAmbiguous = errors.New("actionheader: ambiguous action") // ErrInvalid means the single action value is empty after trimming outer spaces and tabs, or does not satisfy the HTTP token grammar. ErrInvalid = errors.New("actionheader: invalid action") )
Functions ¶
This section is empty.
Types ¶
type Selector ¶
type Selector struct {
// contains filtered or unexported fields
}
Selector selects an action from a configured request header. Construct it with NewSelector; its zero value is unconfigured. A Selector is immutable, safe to copy, and safe for concurrent use.
Example ¶
package main
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"github.com/asteroid-computing/go-lambda-edge/actionheader"
)
// These are consumer-owned middleware and metadata, not actionheader API types.
type actionKey struct{}
func processActionHeaders(selector actionheader.Selector, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
action, err := selector.Parse(r.Header)
if err != nil {
http.Error(w, "invalid action selection", http.StatusBadRequest)
return
}
ctx := context.WithValue(r.Context(), actionKey{}, action)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func main() {
selector, err := actionheader.NewSelector("Action")
if err != nil {
fmt.Println(err)
return
}
dispatcher := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
action, ok := r.Context().Value(actionKey{}).(string)
if !ok {
http.Error(w, "missing action metadata", http.StatusInternalServerError)
return
}
// This example operation is public.
// Protected operations must authorize this same selection before executing or publishing streaming output.
switch action {
case "service.ping":
fmt.Fprintln(w, "pong")
default:
http.Error(w, "unknown action", http.StatusBadRequest)
}
})
// The composed http.Handler serves native HTTP directly or goes to an edge adapter unchanged.
handler := processActionHeaders(selector, dispatcher)
r := httptest.NewRequest(http.MethodPost, "/", nil)
r.Header.Set("Action", "service.ping")
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
fmt.Println(w.Code)
fmt.Print(w.Body.String())
}
Output: 200 pong
func NewSelector ¶
NewSelector validates name as an HTTP field name and returns a selector. The name is required and matched case-insensitively; no default is inferred. On error, the returned selector is the zero value. Configuration errors are separate from ErrMissing, ErrAmbiguous and ErrInvalid.
func (Selector) Parse ¶
Parse returns the single action in headers, trimming only outer SP/HTAB and preserving case. An action must be a nonempty HTTP token (RFC 9110 §5.6.2). Parse inspects all case-insensitive aliases, including noncanonical map keys. Multiple values are ambiguous even if identical; a comma is also ambiguous. Multiplicity and comma checks take precedence over token validation. Duplicates already lost by a gateway or earlier proxy cannot be detected.
On failure Parse returns an empty string and ErrMissing, ErrAmbiguous or ErrInvalid, matchable with errors.Is. An unconfigured selector instead returns a configuration error. Errors do not include supplied header values. Parse does not modify headers or write an HTTP response. Callers must not mutate headers concurrently with Parse. Select once and use that same action for authorization and execution.