Skip to content

feat(server): add responseCompression option to force name compression - #2278

Open
HoMS1987 wants to merge 1 commit into
0xERR0R:mainfrom
HoMS1987:feat/response-compression
Open

HoMS1987 wants to merge 1 commit into
0xERR0R:mainfrom
HoMS1987:feat/response-compression

Conversation

@HoMS1987

Copy link
Copy Markdown

Fixes #2272.

Since #2102, blocky lets Truncate decide whether a response is compressed, and #2213 re-enables compression for responses larger than 512 bytes. Small responses, e.g. a single A record, therefore go out without name compression. RFC 1035 §4.1.4 requires receivers to understand both forms, but some embedded stub resolvers don't: the Garmin Index scale from #2272 silently discards the uncompressed answer and retransmits the query until it gives up. Before v0.32.0 it worked, since every response was compressed.

This PR adds an opt-in responseCompression section. The default behaviour, including the #2102 optimization, is unchanged.

responseCompression:
  # compress every response, as blocky did before v0.32.0
  always: false
  # compress responses to these clients: IPs, CIDRs or client names (wildcards supported)
  clients:
    - 192.168.178.10
    - smart-scale*

clients uses the same identifier forms as clientGroupsBlock (IP, CIDR, client name with filepath.Match wildcards, case-insensitive), except FQDN identifiers. That keeps the cost of compression limited to the devices that need it. always is there for users who can't identify the affected client, or just want the old behaviour back.

Implementation

  • config.ResponseCompression: IPs/CIDRs are parsed once in validate (reusing parseCIDRorIP from the rate limiter); everything else is treated as a client name pattern. Empty entries are rejected.
  • clientQuery gets a forceCompress field, applied in normalizeResponse after Truncate and the existing > dns.MinMsgSize rule.
  • forceCompress is set in Server.resolve after the resolver chain, because request.ClientNames is only populated by the client names resolver inside the chain. newClientQuery runs before that, so it can't be set there.

The two options are independent (Always is a single || in ForceFor, Clients is its own field and matcher), so if you'd rather ship only one of them, the other can be dropped without touching the rest.

Tests

  • config/response_compression_test.go: IP, CIDR (v4/v6), exact and wildcard names, case-insensitivity, IP identifiers not matching as names, empty entries rejected.
  • server/server_test.go, under response compression: a single-A response is compressed with always, for a matching IP and for a matching client name (set by the mocked chain, like the client names resolver would), and stays uncompressed for a non-matching client. Each case also checks the packed bytes for the c0 0c pointer. The three positive cases fail without the change in server.go.
  • docs/config.schema.json regenerated via go generate ./config/...; docs and docs/config.yml updated.

I ran the tests locally on Windows. config and server are green except for tests that also fail on an unmodified main there (Linux-specific error messages and unix sockets); CI will cover Linux.

Since 0xERR0R#2102 responses that fit the client's buffer and 512 bytes
uncompressed are sent without name compression. Some embedded stub
resolvers (e.g. the Garmin Index scale) discard such responses.

responseCompression.always compresses every response (pre-v0.32.0
behaviour), responseCompression.clients does so only for the listed
IPs, CIDRs or client names. The default behaviour is unchanged.

Refs 0xERR0R#2272
@0xERR0R

0xERR0R commented Sep 28, 2026

Copy link
Copy Markdown
Owner

Thanks for the investigation and implementation proposal. Is there any reason not to always enable compression? Adding additional configuration is a kinda overkill imho. What do you mean?

@HoMS1987

Copy link
Copy Markdown
Author

For me just a global enable/disable also would be OK. I just wanted to provide both options.
The per client option can be removed from my proposal.

@HoMS1987

HoMS1987 commented Oct 4, 2026

Copy link
Copy Markdown
Author

If you want me to remove the per client option just tell me and I will do that 🙂

@0xERR0R

0xERR0R commented Oct 5, 2026

Copy link
Copy Markdown
Owner

In order to keep the config simple, I would just enable compression without possibility to disable it

@HoMS1987

HoMS1987 commented Oct 5, 2026

Copy link
Copy Markdown
Author

But wasn't there a reason to just use compression for responses larger than 512 bytes in #2102 ?
Of course I would be ok when compression is just enabled globally again.

@0xERR0R

0xERR0R commented Oct 5, 2026

Copy link
Copy Markdown
Owner

I think it was a review finding but if it causes other problems I dont see any objections to revert this change. I think it is pure technical stuff and we should not overload user with such configuration posibility.

@HoMS1987

HoMS1987 commented Oct 5, 2026

Copy link
Copy Markdown
Author

OK, I'm fine with that 🙂👍

This branch has not been deployed

No deployments
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.

Regression since v0.32.0 (#2102): small responses sent without name compression break embedded client (Garmin Index scale)

2 participants