Repository navigation
Discovery resends its search and takes only IPv4; the idle limit spares a connecting socket; descriptions compare as routers store them (2.0.4) - #16
Merged
MorningLightMountain713 merged 6 commits intoOct 19, 2026
Conversation
… takes only an IPv4 location, and waits 5 s by default After the first M-SEARCH the same search is sent again 250, 750, 1750 and 3750 ms later, until a gateway answers, the search ends or the instance is closed. UDP loses datagrams, and some routers drop searches for a second or more at a time; the gaps double, as DHCP's retransmissions do, so the later copies reach a router that was silent for the first. pupnp sends each search twice and GSSDP three times for the same reason. Discovery takes only an answer whose location is an IPv4 address. Some routers answer an IPv4 search twice, with an IPv6 location first, and both the mappings and the host's address (its route to the router) are IPv4. The discovery timeout defaults to 5000 ms, which the last resend fits in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r while a connection connects The agent's `timeout` option is set on every socket from creation, so the 3 s idle limit for kept connections ran while a new connection was still connecting, before the request's own 10 s timeout took over. A router slow to accept - miniupnpd under load drops SYNs once its backlog of five is full, and the kernel's retries land at 1, 3 and 7 s - had requests cut off at 3 s, reported by axios as its own 10 s timeout. The agent now has no socket timeout; a "free" listener, after the agent's own, gives a socket the idle limit only once it is in the pool. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ion) FRITZ!Box keeps every mapping description with its punctuation rewritten: "Flux_App_web" comes back as "Flux.App.web", "FluxOS Reserved" as "FluxOS.Reserved". canonicalDescription turns every run of characters other than letters and digits into one underscore, so each form compares equal. getMappings matches a string description filter that way, and a re-sent createMapping that gets 718 compares the held mapping's description that way. A RegExp filter is applied as given. canonicalDescription is exported for callers comparing stored descriptions themselves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Discovery sends its search again until a router answers and takes only an IPv4 router; a kept connection's idle limit no longer cuts off a connection that is still connecting; and descriptions compare as routers store them.
Discovery
The M-SEARCH goes out again 250, 750, 1750 and 3750 ms after the first, until a gateway answers or the search ends. UDP loses datagrams, and some routers drop searches for a second or more at a time: behind one OPNsense, a single search went unanswered 24 times in 100, three searches 500 ms apart (GSSDP's schedule) 4 times, and this schedule never. pupnp sends each search twice and GSSDP three times for the same reason. The discovery timeout defaults to 5000 ms, which the last resend fits in.
Discovery takes only an answer whose location is an IPv4 address. Some routers (a D-Link DIR-X3260 and a Cisco XSR150DX in the fleet) answer an IPv4 search twice, with an IPv6 location first; 2.0.3 took it, could not resolve the host's IPv4 address from it, and every mapping failed.
Kept connections
2.0.3 gave the agent its 3 s idle limit as the agent's
timeout, which Node applies to every socket from creation, connecting included. A router slow to accept — miniupnpd under load drops SYNs once its backlog of five is full, and the kernel's retries land at 1, 3 and 7 s — had requests cut off at 3 s, reported by axios as its own 10 s timeout. The agent now has no socket timeout; a socket gets the idle limit only once it is in the pool.Descriptions
FRITZ!Box rewrites a description's punctuation: "Flux_App_web" comes back "Flux.App.web", "FluxOS Reserved" as "FluxOS.Reserved" (every one of 1,521 Flux mappings on the fleet's FRITZ!Boxes).
canonicalDescription, exported, turns every run of characters other than letters and digits into one underscore. A stringdescriptionfilter ongetMappings, and the description check on a re-sentcreateMappingthat gets 718, compare that way. A RegExp filter is applied as given.Tests
npm test: 892 passing, on Node 24 and on Node 20.8. Each new branch was broken on its own and turns its test red; the connect-timeout test is red against 2.0.3's agent.npm run flux-test:igd2: green against IGD v2 and IGD v1.🤖 Generated with Claude Code