Documentation
¶
Overview ¶
Package push implements the FCM v1 sender behind relay.PushSink (PB-PUSH-2): it turns a generic alert plus an opaque ciphertext envelope into an HTTP/2 request Google's messaging API accepts, and turns Google's answers back into the two verdicts the relay acts on — retry, or prune this token.
SCOPE HONESTY. This package models the FCM v1 PROTOCOL: the request emitted, the OAuth exchange performed, which failures are retried, and which response means a dead token. It models NOTHING about delivery. There is no Google account in this project, every test request goes to a loopback httptest.Server, and PB-E2E-5 — the physical-handset gate covering real delivery, real Doze behaviour and a real device — remains DEFERRED. Nothing here may be read as evidence that a handset would receive anything.
Index ¶
Constants ¶
const DefaultFCMBaseURL = "https://fcm.googleapis.com"
DefaultFCMBaseURL is Google's messaging endpoint. Tests point BaseURL at a loopback httptest.Server instead; nothing in this package ever reaches Google.
const DefaultMaxAttempts = 3
DefaultMaxAttempts bounds how many times ONE push is sent before it is reported failed. A provider outage must end in an error rather than an unbounded loop: the relay calls this synchronously on its push path, so a sender that retried forever would hold the connection that triggered it.
const DefaultRetryDelay = 500 * time.Millisecond
DefaultRetryDelay is the spacing a caller should ask for between retries. It is NOT the zero-value default: FCMConfig.RetryDelay of 0 means no delay at all, so a test can drive the retry path without putting a real sleep in the suite. What bounds a retry storm is DefaultMaxAttempts (and, above it, the relay's own per-target push quota), never the delay — so a caller that forgets this gets three back-to-back HTTP round trips, not a spin. cmd/swarm-relay passes it.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type FCM ¶
type FCM struct {
// contains filtered or unexported fields
}
FCM sends wakes through Firebase Cloud Messaging v1.
func NewFCM ¶
NewFCM builds a sender over cfg, REFUSING an invalid credential rather than deferring the failure to the first wake (PB-PUSH-5). LoadServiceAccount does the parsing; this catches the assembly mistake — a nil or half-built account reaching the constructor.
func (*FCM) Push ¶
Push delivers one wake to token.
It retries only what is worth retrying: a 5xx or a transport failure is the provider having a bad moment, while a 4xx is a request Google will refuse identically forever and retrying it is quota spent to reproduce a refusal. The one 4xx with a consequence is UNREGISTERED, which is reported as relay.ErrPushUnregistered so the relay can prune.
type FCMConfig ¶
type FCMConfig struct {
Account *ServiceAccount // parsed credential; required
BaseURL string // messaging endpoint (nil/"" => DefaultFCMBaseURL)
Now func() time.Time
RetryDelay time.Duration // spacing between retries; <= 0 means none (see DefaultRetryDelay)
HTTPClient *http.Client
}
FCMConfig configures an FCM sender.
type ServiceAccount ¶
type ServiceAccount struct {
ProjectID string
ClientEmail string
TokenURI string
PrivateKeyID string
// contains filtered or unexported fields
}
ServiceAccount is a parsed Google service-account credential: the fields the OAuth assertion is built from, plus the RSA key it is signed with.
func LoadServiceAccount ¶
func LoadServiceAccount(doc []byte) (*ServiceAccount, error)
LoadServiceAccount parses and VALIDATES a service-account JSON document.
It validates at load rather than at first send on purpose (PB-PUSH-5). A sender that constructs happily from a broken credential and fails on every push is a relay that looks healthy while push is dead, and the operator finds out from a user who missed a hand-off — hours later, with nothing in any log tying the two together.
func (*ServiceAccount) SignJWT ¶
SignJWT builds the RS256 assertion the token endpoint verifies against the service account's registered public key, requesting exactly scope and nothing more.
WHY IT TAKES THE SCOPE. Two APIs in this tree are asserted against with the same credential SHAPE and different scopes -- messaging here, androidpublisher in internal/play. Copying a signer to vary one string would put the credential crypto in two places, and a signing bug fixed in one copy is a signing bug still shipped in the other. The narrow-scope discipline messagingScope documents is preserved by each caller passing its own single scope, not by fixing one here.