awss3

package
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Sep 11, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package awss3 reads what S3 said in the body of a refusal.

IT IS NOT AN S3 CLIENT. billet has two of those — internal/store/ebss3 for the site cache and internal/archivestore for the off-box deployment archive — and this is the one thing they have to agree about. It lives beside internal/awssig for the reason internal/awsquota and internal/awssts do: a store and a transport are siblings that may not import each other, and both need this answer.

WHAT IT EXISTS FOR is that a 404 from S3 is TWO DIFFERENT FACTS. `NoSuchKey` says the object is not there, which is an ordinary answer that a cold cache and a bucket nobody has written to yet both produce. `NoSuchBucket` says billet is addressing a bucket that does not exist, which is a misconfiguration no amount of retrying fixes. Both readers used to treat every 404 as the first, so a cache pointed at a bucket S3 had never heard of was indistinguishable from a cold one and nothing reported a fault.

SO ONLY `NoSuchKey` MEANS ABSENCE, and a 404 carrying anything else — another code, no code, a body billet could not read — stays an error. That is the direction the house rule points: could-not-tell never collapses into no.

MEASURED AGAINST REAL S3 IN us-east-1 ON 2026-09-04, because a rule about somebody else's API written from its documentation agrees with whatever billet already believes. WHAT WAS OBSERVED, exactly, is two unauthenticated GETs, both answering HTTP 404 with a body ending at </Error> and no trailing byte:

one key that was not there, in a public bucket that was:
  <Error><Code>NoSuchKey</Code><Message>The specified key does not
  exist.</Message><Key>…</Key><RequestId>…</RequestId><HostId>…</HostId></Error>

one key in a randomly named bucket that could not exist:
  <Error><Code>NoSuchBucket</Code><Message>The specified bucket does not
  exist</Message><BucketName>…</BucketName><RequestId>…</RequestId>…</Error>

TWO INFERENCES, LABELLED AS THAT. That the first answered 404 rather than 403 is consistent with that bucket granting anonymous s3:ListBucket, which is the condition internal/store/ebss3's CheckAccess already names — no policy was read. And that the second answered without a credential suggests the name is resolved before the request is authorized, which is a reading of one response rather than a measurement of S3's ordering. Neither inference is what the code rests on: it rests on the two codes.

reals3_test.go is the same pair SIGNED, against a bucket an operator points it at, which is the run that would contradict any of this.

Index

Constants

View Source
const (
	// CodeNoSuchKey is the ONLY 404 that means an object is absent.
	CodeNoSuchKey = "NoSuchKey"
	// CodeNoSuchBucket is a bucket that does not exist at the address billet
	// built out of the configured bucket name and region.
	CodeNoSuchBucket = "NoSuchBucket"
)

The two codes billet acts on by name. Every other code travels as itself.

Variables

This section is empty.

Functions

func RegionHint

func RegionHint(response *http.Response) string

RegionHint reports the bucket's real region when S3 named one.

S3 answers a wrong-region request with a redirect carrying this header, and an operator staring at a bare 301 has no other way to see that their region is wrong. It is empty when the header is absent or is not something billet will repeat.

func StatusOf

func StatusOf(err error) int

StatusOf reports the status of the S3 refusal in err's chain, or zero.

ZERO IS "NOT AN S3 REFUSAL", which is what a transport failure, a credential that would not resolve and a nil error all are. A caller branching on a particular status therefore falls through on all three rather than treating them as that status.

Types

type Refusal

type Refusal struct {
	// Status is the HTTP status S3 answered with.
	Status int
	// Code is the <Code> element of S3's error document, empty when S3 named
	// none or billet could not read one. Empty is could-not-tell.
	Code string
}

Refusal is one non-2xx answer from S3, as an error.

AN ERROR RATHER THAN A VALUE so a caller can wrap it with %w and anything above can ask what S3 actually said without matching on prose. `billet check`'s cache probe classifies a 403 as inconclusive, and it used to do that by looking for the substring "HTTP 403" in a rendered message — which makes a reworded diagnostic silently change the verdict.

func ParseRefusal

func ParseRefusal(status int, body []byte) *Refusal

ParseRefusal is ReadRefusal for a caller that already holds the body — the listing paths read theirs before they look at the status.

func ReadRefusal

func ReadRefusal(response *http.Response) *Refusal

ReadRefusal consumes a bounded prefix of a non-2xx response and reports what S3 said. The caller still closes the body.

func (*Refusal) Absent

func (r *Refusal) Absent() bool

Absent reports the one 404 that is a fact rather than a failure.

THE STATUS IS CHECKED AS WELL AS THE CODE. A code alone would let a body that arrived with some other status decide that an object is missing, and the two halves of the answer come from different places in the response.

func (*Refusal) Error

func (r *Refusal) Error() string

Error renders the code and the status, or the status alone.

Jump to

Keyboard shortcuts

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