Help us shape the future of Go! Take the 2026 Go Developer Survey

push

package
v0.18.2 Latest Latest
Warning

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

Go to latest
Published: Oct 17, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package push implements the FCM v1 sender (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

View Source
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.

View Source
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.

View Source
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-pushgw passes it.

Variables

View Source
var ErrUnregistered = errors.New("push: token is no longer registered with the provider")

ErrUnregistered reports that a token no longer belongs to a live installation.

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

func NewFCM(cfg FCMConfig) (*FCM, error)

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

func (f *FCM) Push(ctx context.Context, token string, ciphertext []byte) error

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 ErrUnregistered so the gateway can prune.

type FCMConfig

type FCMConfig struct {
	Account *ServiceAccount // parsed credential; required
	// ProjectID + AuthorizedHTTPClient are the ADC/workload-identity alternative
	// to Account. The client must already attach a bearer with the Firebase
	// Messaging scope. Exactly one authorization mode may be configured.
	ProjectID            string
	AuthorizedHTTPClient *http.Client
	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

func (a *ServiceAccount) SignJWT(scope string, now time.Time) (string, error)

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.

Jump to

Keyboard shortcuts

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