API reference¶
Every exported symbol in gitlab.com/phpboyscout/go/browser, what it does, and
what it guarantees. The package is deliberately small: one function that opens a
URL, one option that swaps the opener, one accessor, one constant, and two
sentinel errors.
For the generated godoc — including runnable examples — see pkg.go.dev. This page covers the behaviour the signatures do not show.
The complete exported surface¶
const MaxURLLength = 8192
var ErrInvalidURL error
var ErrDisallowedScheme error
type Opener func(rawURL string) error
type Option func(*options)
func AllowedSchemes() []string
func OpenURL(ctx context.Context, rawURL string, opts ...Option) error
func WithOpener(opener Opener) Option
That is the whole API. There is no config struct, no Client to construct, and
nothing to initialise before the first call.
OpenURL — validate a URL, then open it¶
Validates rawURL and, if it passes, hands it to the opener — by default the
operating system's URL handler for the user's browser or mail client.
| Parameter | Notes |
|---|---|
ctx |
Must not be nil. Checked after validation and before the open; a cancelled or expired context means the open is skipped and ctx.Err() is returned. A nil context panics with a nil-pointer dereference. |
rawURL |
The URL exactly as it will be handed to the opener. OpenURL never rewrites, normalises, or re-encodes it — the opener receives the identical string. |
opts |
Zero or more options. Today WithOpener is the only one. |
Returns nil once the opener returns nil. For everything else see
Errors; for exactly which URLs pass, see
Validation rules.
OpenURL blocks until the opener returns. With the default opener that means it
waits for the platform launcher process to exit — see
Platform behaviour.
WithOpener — replace the opener that performs the open¶
Replaces the function that performs the actual open. The intended use is testing — see Test without launching a browser — but it is also the seam for an unusual deployment that needs its own handler.
Three behaviours worth knowing, all of them observable:
- Validation still runs. A custom opener sits behind the gate, not in front of it. A rejected URL never reaches it.
- Last non-nil option wins. Supply
WithOpenertwice and the second one is used. WithOpener(nil)is ignored. It leaves whatever opener is already configured in place — the default if no earlier option set one, or an earlierWithOpener's function if one did. It is not a way to reset to the default, and it is not a way to disable opening.
The option is scoped to the single OpenURL call it is passed to. There is no
global opener to set or restore, so parallel tests cannot interfere with each
other.
Opener — the opener function signature¶
Receives the validated URL and returns an error if the open failed. OpenURL
wraps a non-nil return with invoking URL opener, preserving the original for
errors.Is.
An Opener is called at most once per OpenURL call, and only when validation
and the context check have both passed. It receives no context: the signature has
no cancellation channel because the platform handlers behind it have none either.
Option — why you cannot write your own¶
Option is exported but the options struct it configures is not, so Option
values can only be produced by constructors in this package. Adding an option is
a change to this module, not something a caller can do from outside. That is
deliberate: the same reasoning that keeps the scheme allowlist unexported keeps
the option set closed.
AllowedSchemes — read the permitted schemes at runtime¶
Returns []string{"https", "http", "mailto"} — the schemes OpenURL permits, in
the order they are checked (the order has no effect on the outcome).
The slice is a fresh copy on every call. Mutating it, appending to it, or
replacing its elements changes nothing about validation: the allowlist backing
OpenURL is an unexported package variable and there is no exported way to
widen it. Adding a scheme requires a change to this module and a security review.
Use it to build an error message or a help string rather than hardcoding the list where it can drift:
fmt.Printf("only these URL schemes can be opened: %v\n", browser.AllowedSchemes())
// only these URL schemes can be opened: [https http mailto]
MaxURLLength — the byte cap on a URL¶
The largest URL OpenURL accepts, in bytes — len(rawURL), not runes and
not characters. A URL of 8192 bytes is accepted; 8193 bytes is rejected with
ErrInvalidURL.
The byte/rune distinction bites with non-ASCII URLs. A URL of 4,110 characters
that is mostly two-byte runes measures 8,200 bytes and is rejected, even though
it is only half the cap in characters. If you need to check a URL against the
cap yourself, use len(), not len([]rune(...)).
It is a const, so it can be used in array sizes and constant expressions, and it
cannot be changed by a caller at runtime. Why 8192 and not something larger is
covered in the threat model.
ErrInvalidURL and ErrDisallowedScheme — the two sentinels¶
The two failure classes OpenURL distinguishes. Match them with errors.Is,
never by comparing strings. Full details, including the hint attached to each
case, are on the Errors page.
Is OpenURL safe to call concurrently?¶
Yes. OpenURL holds no package-level mutable state: the allowlist is read-only
after initialisation, options are built into a fresh struct per call, and
AllowedSchemes returns a copy. Concurrent calls from multiple goroutines are
safe, including under -race.
What is not guaranteed is what happens on the other side of the opener. Launching several browser windows at once is the operating system's business, and the default opener has no queue or rate limit.
What the package does not export¶
There is no way, from outside this module, to:
- add a scheme to the allowlist or remove one from it;
- change
MaxURLLength; - disable validation for a single call;
- install a global opener that applies to every call;
- ask "would this URL be accepted?" without also opening it — validation is not exported separately.
The last one is worth planning for: if you want to check a URL early (say, to
disable a menu item), replicate the check by matching the sentinel from an
OpenURL call against a no-op opener, or apply your own scheme check first.
See also¶
- Validation rules — exactly which URLs are accepted.
- Errors — every error, and what to do about each.
- Platform behaviour — what the default opener runs on each OS.
- What
browserdoes not do — the limits of the guarantee.