protocol

package
v0.0.1-alpha.22 Latest Latest
Warning

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

Go to latest
Published: Jul 22, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package protocol provides shared AWS protocol helpers used by all service handlers: error serialisation, request IDs, ARN construction, and response writing. Nothing in this package is service-specific.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotImplemented is returned for endpoints that exist in the routing
	// table but have not yet been implemented. The x-emulator-unsupported
	// header is added automatically by WriteXMLError / WriteJSONError.
	ErrNotImplemented = &AWSError{
		Code:       "NotImplemented",
		Message:    "This operation is not yet emulated. Check docs/services/ for the support matrix.",
		HTTPStatus: http.StatusNotImplemented,
	}

	// ErrInternalError is returned when the emulator itself encounters an
	// unexpected failure (state backend error, serialisation failure, etc.).
	// Always Wrap() this with the underlying error for log context.
	ErrInternalError = &AWSError{
		Code:       "InternalError",
		Message:    "An internal error occurred.",
		HTTPStatus: http.StatusInternalServerError,
	}

	// ErrServiceDisabled is returned when a request targets a service that is
	// known to the emulator but not enabled in the current configuration.
	// Callers should add the service name to OVERCAST_SERVICES to enable it.
	ErrServiceDisabled = &AWSError{
		Code:       "ServiceDisabled",
		Message:    "This service is not enabled in this emulator. Add it to OVERCAST_SERVICES to enable it.",
		HTTPStatus: http.StatusServiceUnavailable,
	}
)

Functions

func APIV2ARN

func APIV2ARN(region, apiID string) string

APIV2ARN builds an API Gateway v2 (HTTP/WebSocket) API ARN. Format: arn:aws:apigateway:{region}::/apis/{apiId}.

func ARN

func ARN(region, accountID, service, resource string) string

ARN builds an AWS ARN string. Format: arn:aws:<service>:<region>:<accountID>:<resource>

Examples:

ARN("us-east-1", "000000000000", "s3", "my-bucket")
  → "arn:aws:s3:::my-bucket"  (S3 omits region and account)

ARN("us-east-1", "000000000000", "sqs", "my-queue")
  → "arn:aws:sqs:us-east-1:000000000000:my-queue"

func Cause

func Cause(aerr *AWSError) error

Cause returns the underlying cause of aerr, or nil if none was set. Prefer errors.Is / errors.As over Cause for most use cases — they traverse arbitrarily deep chains. Use Cause only when you need the immediate cause.

func ContextWithRequestID

func ContextWithRequestID(ctx context.Context, id string) context.Context

ContextWithRequestID returns a new context carrying the given request ID. Called by the request-ID middleware on every incoming request.

func DistributionARN

func DistributionARN(accountID, distributionID string) string

DistributionARN builds a CloudFront distribution ARN. CloudFront ARNs omit the region — this is an AWS quirk. Format: arn:aws:cloudfront::{accountID}:distribution/{distributionID}.

func LambdaARN

func LambdaARN(region, accountID, functionName string) string

LambdaARN builds a Lambda function ARN.

func LambdaVersionARN

func LambdaVersionARN(region, accountID, functionName string, version int) string

LambdaVersionARN builds a Lambda function ARN with a numeric version qualifier. Format: arn:aws:lambda:{region}:{account}:function:{name}:{version}.

func LayerARN

func LayerARN(region, accountID, layerName string) string

LayerARN builds the unversioned Lambda layer ARN (no version suffix). Format: arn:aws:lambda:{region}:{account}:layer:{name}.

func LayerVersionARN

func LayerVersionARN(region, accountID, layerName string, version int) string

LayerVersionARN builds a Lambda layer version ARN. Format: arn:aws:lambda:{region}:{account}:layer:{name}:{version}.

func LogGroupARN

func LogGroupARN(region, accountID, groupName string) string

LogGroupARN builds a CloudWatch Logs log group ARN.

func LogStreamARN

func LogStreamARN(region, accountID, groupName, streamName string) string

LogStreamARN builds a CloudWatch Logs log stream ARN.

func NewRequestID

func NewRequestID() string

NewRequestID generates a new unique AWS-style request ID. AWS uses UUID v4 format without hyphens in some services, with hyphens in others. We always use with-hyphens — both forms are accepted by SDKs.

func NotImplementedEC2QueryXML

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

NotImplementedEC2QueryXML writes a 501 for unimplemented EC2 Query operations.

func NotImplementedJSON

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

NotImplementedJSON is a convenience handler for unimplemented JSON endpoints.

func NotImplementedQueryXML

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

NotImplementedQueryXML writes a 501 for unimplemented Query-protocol operations.

func NotImplementedXML

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

NotImplementedXML is a convenience handler for unimplemented S3 endpoints.

func QueueARN

func QueueARN(region, accountID, queueName string) string

QueueARN builds an SQS queue ARN from its components.

func RequestIDFromContext

func RequestIDFromContext(ctx context.Context) string

RequestIDFromContext retrieves the request ID stored in ctx. Returns a freshly generated ID if none was set — this is a safety net and should not happen in normal operation (the middleware always sets one).

func RestAPIARN

func RestAPIARN(region, apiID string) string

RestAPIARN builds an API Gateway REST API ARN. API Gateway ARNs omit the account ID — this is an AWS quirk. Format: arn:aws:apigateway:{region}::/restapis/{apiId}.

func ServiceFromARN

func ServiceFromARN(arn string) string

ServiceFromARN extracts the service name (e.g. "appsync", "kafka") from an ARN's third colon-delimited segment ("arn:partition:service:..."). Returns "" if arn is not a well-formed ARN string.

This is the authoritative way to identify which service owns a resource when a path is shared between services (e.g. /v1/tags/{resourceArn}) — the ARN is self-describing, unlike the request's SigV4 credential scope, which callers may not always set to match (hand-built requests, or resource-group-style tagging tools that sign generically).

func TableARN

func TableARN(region, accountID, tableName string) string

TableARN builds a DynamoDB table ARN.

func TopicARN

func TopicARN(region, accountID, topicName string) string

TopicARN builds an SNS topic ARN.

func WriteAWSJSON

func WriteAWSJSON(w http.ResponseWriter, r *http.Request, status int, v any, contentType string)

WriteAWSJSON serialises v as JSON and writes it with the provided AWS JSON content type and request ID headers.

Pass an explicit content type per service protocol, for example:

  • application/x-amz-json-1.0
  • application/x-amz-json-1.1

If contentType is empty, application/x-amz-json-1.0 is used.

func WriteEC2QueryXMLError

func WriteEC2QueryXMLError(w http.ResponseWriter, r *http.Request, aerr *AWSError)

WriteEC2QueryXMLError writes an EC2 Query-protocol XML error response.

func WriteEmpty

func WriteEmpty(w http.ResponseWriter, r *http.Request, status int)

WriteEmpty writes a response with no body and the standard request ID header. Used for operations like DeleteObject which return 204 or an empty 200.

func WriteJSON

func WriteJSON(w http.ResponseWriter, r *http.Request, status int, v any)

WriteJSON serialises v as JSON and writes it with the correct Content-Type and request ID headers. Services call this for successful responses.

func WriteJSONError

func WriteJSONError(w http.ResponseWriter, r *http.Request, aerr *AWSError)

WriteJSONError writes an AWS JSON-protocol error response. The cause, if any, is NOT included in the response body.

func WriteQueryXML

func WriteQueryXML(w http.ResponseWriter, r *http.Request, status int, v any)

WriteQueryXML serialises v as XML with text/xml content type. Used by Query-protocol services (SNS, STS, IAM). The request ID is set as both a response header and should be embedded in the response struct's ResponseMetadata. The request body is drained so the HTTP/1.1 connection can be reused by the SDK client.

func WriteQueryXMLError

func WriteQueryXMLError(w http.ResponseWriter, r *http.Request, aerr *AWSError)

WriteQueryXMLError writes an AWS Query-protocol XML error response (SNS format).

func WriteXML

func WriteXML(w http.ResponseWriter, r *http.Request, status int, v any)

WriteXML serialises v as XML and writes it with the correct Content-Type and request ID headers. Services call this for successful responses.

func WriteXMLError

func WriteXMLError(w http.ResponseWriter, r *http.Request, aerr *AWSError)

WriteXMLError writes an AWS REST-XML error response (S3 format). The cause, if any, is NOT included in the response — it stays server-side for logging. The x-emulator-unsupported header is set automatically for 501.

Types

type AWSError

type AWSError struct {
	// Code is the AWS error code string, e.g. "NoSuchBucket", "QueueDoesNotExist".
	Code string
	// Message is the human-readable description sent to the client.
	Message string
	// HTTPStatus is the HTTP status code to send, e.g. 404, 400, 500.
	HTTPStatus int
	// contains filtered or unexported fields
}

AWSError represents a structured AWS API error that maps to an HTTP response.

It implements the standard error interface and supports Go's error wrapping convention, equivalent to JavaScript's `new Error("msg", { cause: err })`.

Wrapping pattern — use Wrap() to attach an underlying cause while presenting a clean AWS error to callers:

// Service code:
raw, found, err := s.store.Get(ctx, ns, key)
if err != nil {
    return protocol.Wrap(protocol.ErrInternalError, err)
}

// The HTTP layer sees ErrInternalError (clean AWS error code + message).
// The original err is preserved in the chain for logging and debugging:
logger.Error("state read failed", zap.Error(aerr))
// → logs both "InternalError" and the underlying storage error

Inspection pattern — anywhere in the call chain:

var aerr *protocol.AWSError
if errors.As(err, &aerr) {
    // aerr.Code, aerr.HTTPStatus available
}

func AsAWSError

func AsAWSError(err error) *AWSError

AsAWSError extracts the first *AWSError from the error chain. Returns nil if no *AWSError is found.

This is a convenience wrapper around errors.As for the common case of checking whether an error from a helper function is an AWSError:

aerr := protocol.AsAWSError(err)
if aerr != nil {
    protocol.WriteJSONError(w, r, aerr)
    return
}

func ErrInvalidArgument

func ErrInvalidArgument(msg string) *AWSError

ErrInvalidArgument returns a 400 error for malformed input.

func ErrMissingParameter

func ErrMissingParameter(param string) *AWSError

ErrMissingParameter returns a 400 error for a missing required parameter.

func Wrap

func Wrap(template *AWSError, cause error) *AWSError

Wrap returns a new *AWSError with the same Code, Message, and HTTPStatus as template, but with cause attached as the underlying error.

This is the primary way to preserve error context in service code:

func (s *s3Store) getBucket(ctx context.Context, name string) (*Bucket, *AWSError) {
    raw, found, err := s.store.Get(ctx, nsBuckets, name)
    if err != nil {
        // ErrInternalError is shown to the client.
        // err (e.g. "sqlite: no such table") is preserved for logging.
        return nil, protocol.Wrap(ErrInternalError, err)
    }
    ...
}

Wrap never modifies the template — it always returns a new AWSError value.

func (*AWSError) Error

func (e *AWSError) Error() string

Error implements the error interface. Returns the AWS error code and message; the cause is accessible via Unwrap.

func (*AWSError) Unwrap

func (e *AWSError) Unwrap() error

Unwrap returns the underlying cause, enabling errors.Is / errors.As to traverse the full error chain. This is the Go equivalent of error.cause in JavaScript.

// Check if a specific underlying error occurred:
if errors.Is(aerr, sql.ErrNoRows) { ... }

// Extract a specific error type from anywhere in the chain:
var pgErr *pgconn.PgError
if errors.As(aerr, &pgErr) { ... }

type ResponseMetadata

type ResponseMetadata struct {
	RequestID string `xml:"RequestId"`
}

ResponseMetadata is embedded in AWS Query-protocol XML responses. It carries the request ID that SDKs surface as response.ResultMetadata.

func QueryResponseMetadata

func QueryResponseMetadata(r *http.Request) ResponseMetadata

QueryResponseMetadata returns a ResponseMetadata populated from the request context.

Directories

Path Synopsis
Package codec defines the wire-protocol abstraction used by Overcast's typed operation dispatcher.
Package codec defines the wire-protocol abstraction used by Overcast's typed operation dispatcher.
Package op defines the typed operation dispatcher used by Overcast's Smithy-aligned services.
Package op defines the typed operation dispatcher used by Overcast's Smithy-aligned services.

Jump to

Keyboard shortcuts

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