Documentation
¶
Overview ¶
Package edge adapts AWS API Gateway Lambda proxy events to net/http handlers. It supports raw JSON v2 and already typed AWS events with optional native gateway identity. Buffered and REST streaming responses share request and identity policies; each has its own response and invocation boundary.
Index ¶
- Variables
- type Adapter
- func (a *Adapter) HandleV1(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error)
- func (a *Adapter) HandleV2(ctx context.Context, event events.APIGatewayV2HTTPRequest) (events.APIGatewayV2HTTPResponse, error)
- func (a *Adapter) Invoke(ctx context.Context, payload []byte) ([]byte, error)
- type InvocationError
- type Operation
- type Option
- type Resource
- type StreamingAdapter
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInvalidInvocation indicates an invalid direct invocation argument or adapter. ErrInvalidInvocation = errors.New("edge: invalid invocation") // ErrInvalidEvent indicates a malformed event or failed HTTP request conversion. ErrInvalidEvent = errors.New("edge: invalid event") // ErrUnsupportedEvent indicates an unsupported event family or payload version. ErrUnsupportedEvent = errors.New("edge: unsupported event") // ErrIdentity indicates failure to establish or isolate invocation identity. ErrIdentity = errors.New("edge: identity failure") // ErrResponse indicates an invalid or unrepresentable HTTP response. ErrResponse = errors.New("edge: response failure") // ErrLimitExceeded indicates an adapter-enforced resource limit. ErrLimitExceeded = errors.New("edge: resource limit exceeded") // ErrCleanup indicates failure to clean up invocation-owned request resources. ErrCleanup = errors.New("edge: request cleanup failure") // ErrStream indicates a stream lifecycle failure or a recovered late panic. ErrStream = errors.New("edge: stream failure") )
Invocation failure categories support errors.Is through wrapping and joined errors. Application HTTP responses, including 4xx and 5xx, are not invocation failures when the transport completes successfully.
Functions ¶
This section is empty.
Types ¶
type Adapter ¶
type Adapter struct {
// contains filtered or unexported fields
}
Adapter holds an HTTP handler and its API Gateway adaptation configuration. Construct an Adapter with New; its zero value is not usable. Configuration is fixed at construction. Calls own separate request state; a shared handler must be safe for concurrent use.
func New ¶
New validates handler and opts and returns an Adapter without performing I/O or invoking handler. Nil handlers, including typed nil handlers, and nil options are rejected. On error, the returned Adapter is nil.
The caller retains ownership of handler and is responsible for its concurrency safety when sharing it. New does not use http.DefaultServeMux.
func (*Adapter) HandleV1 ¶
func (a *Adapter) HandleV1(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error)
HandleV1 adapts an already decoded REST/HTTP API 1.0 event without serializing envelopes inside edge. It can be registered as lambda.Start(adapter.HandleV1). The upstream decoder owns wire validation and numeric fidelity; the caller or runtime owns final response serialization and its complete envelope size. On failure it returns a zero response. Inputs must not change during the call.
func (*Adapter) HandleV2 ¶
func (a *Adapter) HandleV2(ctx context.Context, event events.APIGatewayV2HTTPRequest) (events.APIGatewayV2HTTPResponse, error)
HandleV2 adapts an already decoded HTTP API 2.0 event. Its ownership, error and serialization contract matches HandleV1. V2 SDK string claims cannot recover arrays or exact numeric values discarded by an upstream decoder.
Example ¶
package main
import (
"context"
"fmt"
"net/http"
"github.com/aws/aws-lambda-go/events"
edge "github.com/asteroid-computing/go-lambda-edge"
)
func main() {
adapter, err := edge.New(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusAccepted)
}))
if err != nil {
panic(err)
}
// Production main may call lambda.Start(adapter.HandleV2).
// Its runtime then owns envelope JSON.
// This direct typed call performs no envelope JSON work.
response, err := adapter.HandleV2(context.Background(), events.APIGatewayV2HTTPRequest{
Version: "2.0",
RawPath: "/",
RequestContext: events.APIGatewayV2HTTPRequestContext{APIID: "example", HTTP: events.APIGatewayV2HTTPRequestContextHTTPDescription{Method: "POST"}},
})
fmt.Println(response.StatusCode, err)
}
Output: 202 <nil>
func (*Adapter) Invoke ¶
Invoke adapts a REST proxy, HTTP API 1.0 or HTTP API 2.0 event using JSON v2 directly. Register the Adapter object with lambda.Start(adapter), not its Invoke method as a reflected function. Transport/identity failures return no usable payload; ordinary HTTP error responses remain successful invocations. Panics propagate after cleanup. The caller must not mutate payload during use.
Example ¶
package main
import (
"context"
"fmt"
"net/http"
"github.com/aws/aws-lambda-go/lambda"
edge "github.com/asteroid-computing/go-lambda-edge"
)
func main() {
adapter, err := edge.New(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
}))
if err != nil {
panic(err)
}
// Production main calls lambda.Start(adapter).
// Passing adapter.Invoke as a reflected function would give []byte ordinary JSON/base64 semantics.
handler := lambda.NewHandler(adapter)
response, err := handler.Invoke(context.Background(), []byte(`{"httpMethod":"GET","path":"/","requestContext":{"apiId":"example"}}`))
fmt.Println(len(response) > 0, err)
}
Output: true <nil>
type InvocationError ¶
type InvocationError struct {
// contains filtered or unexported fields
}
InvocationError describes a sanitized transport failure. Use errors.Is to inspect its category and errors.AsType[*InvocationError] for its diagnostics. Its text is diagnostic, not a stable identifier or an HTTP response body. It does not expose event values, application data, or unsafe underlying errors. A zero InvocationError describes an unclassified failure.
Operation failures precede any joined cleanup failures. Parent cancellation is returned as the standard context error when there is no operation or cleanup failure. Application panics propagated to the runtime are not InvocationErrors.
func (*InvocationError) Error ¶
func (e *InvocationError) Error() string
Error returns a sanitized description of the failed operation.
func (*InvocationError) Limit ¶
func (e *InvocationError) Limit() (resource Resource, maximum int64, ok bool)
Limit reports the exhausted resource, as one of the Resource constants, and its effective maximum in bytes for a limit failure. A response header or identity claims budget measures the documented charge; raw claims also have a wire-length limit. Neither budget measures heap use. Other failures return "", 0, false.
func (*InvocationError) Operation ¶
func (e *InvocationError) Operation() Operation
Operation identifies the failed operation as one of the Operation constants. A zero error returns "".
func (*InvocationError) Unwrap ¶
func (e *InvocationError) Unwrap() error
Unwrap exposes the failure category and any explicitly preserved safe causes.
type Operation ¶
type Operation string
Operation identifies the invocation stage that failed.
const ( OperationValidate Operation = "validate" // adapter, argument or event envelope validation OperationDecode Operation = "decode" // event JSON decoding OperationRequest Operation = "request" // HTTP request conversion OperationIdentity Operation = "identity" // gateway identity construction or isolation OperationResponse Operation = "response" // HTTP response capture OperationEncode Operation = "encode" // response envelope or stream prefix encoding OperationCleanup Operation = "cleanup" // invocation-owned request cleanup OperationStream Operation = "stream" // stream lifecycle after handoff )
Invocation stages reported by InvocationError.Operation.
type Option ¶
type Option func(*config)
Option configures an Adapter or StreamingAdapter during construction. Use the option functions provided by this package. A nil Option is invalid. Options apply in argument order; the last assignment to a scalar setting wins.
func WithGatewayIdentity ¶
WithGatewayIdentity configures whether gateway assertions establish identity. The default is false. When enabled, the policy covers native IAM and JWT/Cognito assertions; it does not configure authorization in AWS or enable local token verification. Custom-authorizer contexts need an explicit mapper. Unsupported or conflicting assertions fail before running the HTTP handler.
func WithIdentityClaimsBudget ¶
WithIdentityClaimsBudget sets the gateway identity claims allowance in bytes. The default is 256 KiB; New and NewStreaming reject settings outside 1..6 MiB. Each claim value costs 64 bytes plus name, string and exact-number text bytes. Dedicated gateway scopes consume the same allowance. Raw claim JSON is also limited to this byte length. This is a resource policy, not a heap cap or AWS quota. It does not enable gateway identity.
func WithResponseHeaderBudget ¶
WithResponseHeaderBudget sets the response header resource budget in bytes. The default is 256 KiB. Both constructors reject settings outside 1..6 MiB; zero does not disable the bound. Each original value costs its name length, value length and 32 bytes; nil/empty slices cost their name length and 32. Generated headers also consume the budget. This is a library resource policy, not an AWS quota or a bound on total heap usage. It does not change the separate buffered envelope or streaming metadata limits.
func WithStreamErrorReporter ¶
WithStreamErrorReporter reports a sanitized terminal failure once after producer cleanup and before stream completion. It applies only to NewStreaming; New rejects it. A final nil callback is invalid. The callback receives the invocation context, possibly canceled, and must return promptly. Pre-handoff failures are returned directly and not reported. A callback panic is recovered and adds a sanitized ErrStream fault without replacing the original failure. It must not read or close the same stream: reporting precedes its completion.
type Resource ¶
type Resource string
Resource names an adapter-enforced limit reported by InvocationError.Limit.
const ( ResourceResponseHeaders Resource = "response_headers" // weighted response header budget ResourceBufferedBody Resource = "buffered_body" // buffered body, including base64 expansion and HEAD byte counts ResourceBufferedEnvelope Resource = "buffered_envelope" // complete serialized buffered response ResourceStreamMetadata Resource = "stream_metadata" // streaming metadata prefix ResourceIdentityClaims Resource = "identity_claims" // weighted gateway identity claims allowance )
Resources whose limits the adapter enforces.
type StreamingAdapter ¶
type StreamingAdapter struct {
// contains filtered or unexported fields
}
StreamingAdapter serves REST API proxy events through an ordinary HTTP handler and returns a metadata-prefixed body stream. Construct it with NewStreaming; its zero value is unusable. Configuration is immutable; the application owns handler concurrency safety. HTTP APIs and request streaming are unsupported.
Deployment requires a REST STREAM integration using InvokeWithResponseStream. SDK Runtime API compatibility remains unverified against live AWS; see the module's streaming guide for the documented SDK/header/connection qualification.
func NewStreaming ¶
func NewStreaming(handler http.Handler, opts ...Option) (*StreamingAdapter, error)
NewStreaming validates handler and options without starting I/O or goroutines. It shares New's handler, header-budget, identity-budget and gateway defaults. Streaming never infers Content-Length; supply it explicitly when needed. Authenticate and authorize before writing or flushing any response.
Example (Binary) ¶
adapter, err := edge.NewStreaming(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/octet-stream")
_, _ = w.Write([]byte{0, 255, 1})
}))
if err != nil {
panic(err)
}
stream, err := adapter.HandleV1(context.Background(), exampleRESTEvent())
if err != nil {
panic(err)
}
fmt.Printf("%v\n", readExampleStream(stream))
Output: [0 255 1]
Example (Gzip) ¶
adapter, err := edge.NewStreaming(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.Header().Set("Content-Encoding", "gzip")
// The example always emits gzip;
// a real route negotiates Accept-Encoding.
compressed := gzip.NewWriter(w)
defer compressed.Close()
for _, chunk := range []string{"first\n", "last\n"} {
if r.Context().Err() != nil {
return
}
if _, err := io.WriteString(compressed, chunk); err != nil {
return
}
// Flush the encoder first, then the underlying HTTP response.
if err := compressed.Flush(); err != nil {
return
}
if err := http.NewResponseController(w).Flush(); err != nil {
return
}
}
}))
if err != nil {
panic(err)
}
stream, err := adapter.HandleV1(context.Background(), exampleRESTEvent())
if err != nil {
panic(err)
}
reader, err := gzip.NewReader(bytes.NewReader(readExampleStream(stream)))
if err != nil {
panic(err)
}
defer reader.Close()
data, err := io.ReadAll(reader)
if err != nil {
panic(err)
}
fmt.Print(string(data))
Output: first last
func (*StreamingAdapter) Handle ¶
func (a *StreamingAdapter) Handle(ctx context.Context, event jsontext.Value) (io.ReadCloser, error)
Handle validates raw REST event JSON with JSON v2 and returns a stream that callers must consume and Close. Register lambda.Start(adapter.Handle), not the adapter object or the buffered Handler.Invoke boundary. The SDK owns its initial jsontext.Value capture; edge owns strict event decoding and prefix encoding. Explicit HTTP API version envelopes are rejected. Do not mutate event during the call. Request resources remain owned until producer cleanup completes.
Pre-handoff failures return a nil stream. Early producer panics propagate after cleanup; late panics become sanitized terminal errors. After handoff, failures cannot replace status or bytes. Close cancels and joins the producer; a handler ignoring cancellation/write errors can delay Close until invocation timeout.
Example ¶
adapter, err := edge.NewStreaming(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Protected applications authenticate and authorize before this point.
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
controller := http.NewResponseController(w)
for _, message := range []string{"ready", "done"} {
if r.Context().Err() != nil {
return
}
if _, err := fmt.Fprintf(w, "data: %s\n\n", message); err != nil {
return
}
if err := controller.Flush(); err != nil {
return
}
}
}))
if err != nil {
panic(err)
}
// A Lambda main registers lambda.Start(adapter.Handle).
// This direct call demonstrates reader ownership locally;
// it makes no AWS calls.
stream, err := adapter.Handle(context.Background(), jsontext.Value(`{"httpMethod":"GET","path":"/","requestContext":{"apiId":"example"}}`))
if err != nil {
panic(err)
}
fmt.Print(string(readExampleStream(stream)))
Output: data: ready data: done
func (*StreamingAdapter) HandleV1 ¶
func (a *StreamingAdapter) HandleV1(ctx context.Context, event events.APIGatewayProxyRequest) (io.ReadCloser, error)
HandleV1 accepts an already typed REST event, avoiding request envelope JSON work inside edge. Register lambda.Start(adapter.HandleV1). The caller/upstream codec owns wire validation and numeric fidelity. The V1 type also represents HTTP API 1.0; it cannot prove the deployment is a REST streaming integration. Its stream ownership and error contract match Handle. Inputs must not change during the call. Streaming metadata still requires direct JSON v2 encoding.
Example ¶
adapter, err := edge.NewStreaming(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/x-ndjson")
for _, value := range []string{"ready", "done"} {
if r.Context().Err() != nil {
return
}
if err := json.MarshalWrite(w, struct {
State string `json:"state"`
}{value}); err != nil {
return
}
if _, err := io.WriteString(w, "\n"); err != nil {
return
}
if err := http.NewResponseController(w).Flush(); err != nil {
return
}
}
}))
if err != nil {
panic(err)
}
// A Lambda main may register lambda.Start(adapter.HandleV1).
// Input envelope decoding then belongs to the SDK;
// output metadata still uses JSON v2 in edge.
stream, err := adapter.HandleV1(context.Background(), exampleRESTEvent())
if err != nil {
panic(err)
}
fmt.Print(string(readExampleStream(stream)))
Output: {"state":"ready"} {"state":"done"}
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package actionheader selects one application action from a configured HTTP request header.
|
Package actionheader selects one application action from a configured HTTP request header. |
|
Package authn selects one configured Bearer or EdgeIAM verifier from an HTTP Authorization header and transports the verified caller to protected handlers.
|
Package authn selects one configured Bearer or EdgeIAM verifier from an HTTP Authorization header and transports the verified caller to protected handlers. |
|
Package authz authorizes explicit caller, action and resource facts using immutable rules.
|
Package authz authorizes explicit caller, action and resource facts using immutable rules. |
|
examples
|
|
|
dispatcher/buffered
command
Command buffered registers the orders application for REST or HTTP API events.
|
Command buffered registers the orders application for REST or HTTP API events. |
|
dispatcher/internal/orders
Package orders is application code for the dispatcher example, not an edge API.
|
Package orders is application code for the dispatcher example, not an edge API. |
|
dispatcher/native
command
Command native serves the buffered orders application on loopback.
|
Command native serves the buffered orders application on loopback. |
|
dispatcher/streaming
command
Command streaming registers the orders application for REST response streaming.
|
Command streaming registers the orders application for REST response streaming. |
|
Package iamproof generates short-lived, audience-bound GetCallerIdentity proofs and verifies them online with regional AWS STS.
|
Package iamproof generates short-lived, audience-bound GetCallerIdentity proofs and verifies them online with regional AWS STS. |
|
Package identity provides immutable callers, owned claims and context transport.
|
Package identity provides immutable callers, owned claims and context transport. |