Skip to content

feat: send OS context headers on update check requests#52

Open
ranganath42 wants to merge 10 commits into
masterfrom
feat/add-os-headers
Open

feat: send OS context headers on update check requests#52
ranganath42 wants to merge 10 commits into
masterfrom
feat/add-os-headers

Conversation

@ranganath42

Copy link
Copy Markdown

The updater now sends X-OS-Type, X-OS-Version and X-OS-Arch with every update check, giving the update server the context to select a build compatible with this host.

X-OS-Type and X-OS-Arch carry runtime.GOOS and runtime.GOARCH verbatim, so no OS/arch mapping code ships in the client; the server owns the vocabulary.

X-OS-Version is best effort and omitted if detection fails:

  • Windows: RtlGetVersion (major.minor.build, unaffected by compatibility shims)
  • macOS: sysctl kern.osproductversion
  • Linux: kernel release via uname

The updater now sends X-OS-Type, X-OS-Version and X-OS-Arch with every
update check, giving the update server the context to select a build
compatible with this host.

X-OS-Type and X-OS-Arch carry runtime.GOOS and runtime.GOARCH verbatim,
so no OS/arch mapping code ships in the client; the server owns the
vocabulary.

X-OS-Version is best effort and omitted if detection fails:
- Windows: RtlGetVersion (major.minor.build, unaffected by
  compatibility shims)
- macOS: sysctl kern.osproductversion
- Linux: kernel release via uname
@ranganath42 ranganath42 self-assigned this Jul 6, 2026
@ranganath42 ranganath42 requested review from priyankasuduge and removed request for thejanasatan July 6, 2026 00:38
Comment thread updater/update/osinfo.go Outdated
req.Header.Set(headerOSTypeKey, runtime.GOOS)
req.Header.Set(headerOSArchKey, runtime.GOARCH)
// Best effort: an unknown OS version is better than a failed update check.
if version, err := osVersion(); err == nil && version != "" {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: shouldn't we log the error here?

Comment thread updater/update/osinfo_test.go Outdated
// SILVER - Service Wrapper
// Auto Updater
//
// Copyright (c) 2014-2021 PaperCut Software http://www.papercut.com/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/2014-2021/2026/

Comment thread updater/update/osinfo_test.go Outdated
// See the project's LICENSE file for more information.
//

package update

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/update/update_test/

Comment thread updater/update/osinfo.go Outdated
// addOSContextToRequestHeader adds OS identity headers used by the update
// server to select a build compatible with this host. GOOS and GOARCH are
// sent verbatim so no OS/arch mapping code ships in the client.
func addOSContextToRequestHeader(req *http.Request) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"set" might be better than "add" because the header values are set rather than added. Also, ToRequestHeader could be removed from the function name. It is already indicated by the parameter type.

How about, for example, setPlatformHeaders or setOSHeaders?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's go with setOSHeaders

Comment thread updater/update/osinfo.go Outdated
req.Header.Set(headerOSTypeKey, runtime.GOOS)
req.Header.Set(headerOSArchKey, runtime.GOARCH)
// Best effort: an unknown OS version is better than a failed update check.
if version, err := osVersion(); err == nil && version != "" {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

osVersion shouldn't return an empty version if it returns a nil error.

Comment thread updater/update/osversion_darwin.go Outdated
// See the project's LICENSE file for more information.
//

//go:build darwin

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary

Comment thread updater/update/osversion_other.go Outdated
// osVersion is unknown on unsupported platforms; the X-OS-Version header is
// simply omitted.
func osVersion() (string, error) {
return "", nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning an error, such as "unsupported OS", would be better.

Comment thread updater/update/osversion_windows.go Outdated
// See the project's LICENSE file for more information.
//

//go:build windows

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary

Comment thread updater/update/osversion_windows.go Outdated
// SILVER - Service Wrapper
// Auto Updater
//
// Copyright (c) 2014-2021 PaperCut Software http://www.papercut.com/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2026

Comment thread updater/update/osversion_other.go Outdated
// SILVER - Service Wrapper
// Auto Updater
//
// Copyright (c) 2014-2021 PaperCut Software http://www.papercut.com/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2026

Comment thread updater/update/osinfo.go
Ranganath Gunawardane added 9 commits July 6, 2026 16:14
X-OS-Arch reports runtime.GOARCH, the architecture of the running
binary, which under emulation differs from the host: an x64 build under
Rosetta 2 or Windows-on-ARM reports amd64 on an arm64 machine.
X-OS-Native-Arch reports the host architecture so the update server can
distinguish hosts that could run a native build from hosts that cannot
(e.g. genuine Intel Macs vs M1 Macs still on the x64 build).

Detection is best effort with runtime.GOARCH as the fallback:
- macOS: sysctl.proc_translated (1 under Rosetta 2)
- Windows: IsWow64Process2 native machine (Windows 10 1511+; older
  hosts cannot be ARM64 machines, so the fallback is correct there)
- others: assumed equal to the binary architecture
The _darwin/_windows/_linux filename suffixes already constrain these
files; only the _other files need explicit tags.
An empty string with a nil error was a silent sentinel; the caller
already omits the X-OS-Version header on error, so behavior is
unchanged.
uname's release string carries distro suffixes on most distributions
(5.15.0-91-generic, 4.18.0-477.10.1.el8_8.x86_64) which would break the
update server's segment-by-segment numeric version comparison. Trim to
the leading dot-separated numeric part so X-OS-Version is always
comparable (5.15.0-91-generic → 5.15.0).

Windows and macOS are unaffected; their sources are numeric by
construction.
osVersion now guarantees a non-empty version on nil error: linux
returns an error for a kernel release with no numeric prefix, darwin
for an empty sysctl value, and windows is numeric by construction.
The caller drops its empty-string check accordingly.
Set describes Header.Set semantics; the parameter type already says it
operates on a request.
Black-box tests via an export_test.go bridge; header names are asserted
as literal wire strings rather than the package constants.
Matches the updater's existing best-effort convention (see
addIDProfileToRequestHeader): print the error and continue, so an
omitted X-OS-Version header is diagnosable in the field.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants