Skip to content

Commit ee215fc

Browse files
committed
Fix connection regression: restore default HTTP port 80 behavior
- Fix port handling: only add port if explicitly provided via --port flag - Restore old behavior where addresses without ports use default HTTP port (80) - Keep status as 'connecting' while actively retrying instead of immediately showing 'disconnected' - Show error reason in footer when disconnected - Fix missing dependencies in connection effect (useTls, httpPort, insecure) - Restore initial connection test for immediate feedback Fixes issue where connections to devices on default HTTP port (80) were broken after commit 02865a4 which incorrectly defaulted to port 4403.
1 parent 897fad5 commit ee215fc

File tree

3 files changed

+71
-26
lines changed

3 files changed

+71
-26
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@meshtastic/cli-viewer",
3-
"version": "1.8.0",
3+
"version": "1.8.1",
44
"type": "module",
55
"scripts": {
66
"dev": "bun run src/index.ts",

src/transport/http.ts

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,15 @@ export class HttpTransport implements Transport {
7474
let useTlsFlag = tls;
7575

7676
if (!hasPort) {
77-
// If no port in address, use provided port or default to 4403
78-
const defaultPort = port || 4403;
79-
addressWithPort = `${address}:${defaultPort}`;
77+
// If no port in address and port is explicitly provided, use it
78+
// Otherwise, don't add a port (use default HTTP/HTTPS port 80/443)
79+
if (port !== undefined) {
80+
addressWithPort = `${address}:${port}`;
81+
}
82+
// If no port provided and no port in address, use default HTTP port (no :port in URL)
8083
}
8184
// If port is provided via flag but address also has a port, flag takes precedence
82-
else if (port) {
85+
else if (port !== undefined) {
8386
// Extract hostname/IP from address
8487
const hostnameMatch = address.match(/^(.+):\d+$/);
8588
if (hostnameMatch) {
@@ -99,11 +102,36 @@ export class HttpTransport implements Transport {
99102

100103
Logger.info("HttpTransport", "Attempting connection", { address, tls: useTlsFlag, url });
101104

102-
// Skip strict connection test - start polling immediately
103-
// The polling loop will handle connection errors gracefully
104-
// This is more resilient for cases where the server accepts connections
105-
// but endpoints may hang or require specific conditions
106-
Logger.info("HttpTransport", "Starting transport (connection will be verified during polling)", { url, insecure });
105+
// Perform initial connection test with short timeout for immediate feedback
106+
// This helps users know if the address/port is wrong before polling starts
107+
try {
108+
Logger.info("HttpTransport", "Testing initial connection", { url });
109+
const testResponse = await fetch(`${url}/api/v1/fromradio?all=false`, getFetchOptions(url, insecure, {
110+
method: "GET",
111+
headers: { Accept: "application/x-protobuf" },
112+
signal: AbortSignal.timeout(3000), // Short timeout for initial test
113+
}));
114+
if (!testResponse.ok) {
115+
Logger.warn("HttpTransport", "Initial connection test returned non-OK status", {
116+
status: testResponse.status,
117+
statusText: testResponse.statusText,
118+
url
119+
});
120+
// Don't throw - continue anyway, polling will handle it
121+
} else {
122+
Logger.info("HttpTransport", "Initial connection test successful", { url });
123+
}
124+
} catch (error) {
125+
// Log the error with URL for debugging
126+
const errorMsg = error instanceof Error ? error.message : String(error);
127+
Logger.warn("HttpTransport", "Initial connection test failed (will retry during polling)", error as Error, {
128+
url,
129+
error: errorMsg
130+
});
131+
// Don't throw - start polling anyway to allow retries
132+
}
133+
134+
Logger.info("HttpTransport", "Starting transport and polling", { url, insecure });
107135
const transport = new HttpTransport(url, insecure);
108136
transport.startPolling();
109137
return transport;
@@ -174,20 +202,23 @@ export class HttpTransport implements Transport {
174202
30000 // Max 30 seconds
175203
);
176204

177-
this.emit({
178-
type: "status",
179-
status: "disconnected",
180-
reason: e instanceof Error ? e.message : "unknown",
181-
});
182-
183205
// Stop polling if too many consecutive errors
184206
if (this.consecutiveErrors >= this.MAX_CONSECUTIVE_ERRORS) {
185207
Logger.error("HttpTransport", "Too many consecutive errors, stopping polling", undefined, {
186208
consecutiveErrors: this.consecutiveErrors
187209
});
210+
this.emit({
211+
type: "status",
212+
status: "disconnected",
213+
reason: e instanceof Error ? e.message : "unknown",
214+
});
188215
this.running = false;
189216
return;
190217
}
218+
219+
// While actively retrying, keep status as "connecting" to show we're still trying
220+
// Only emit disconnected when we've given up (handled above)
221+
// This provides better UX - user sees "connecting" while we're actively retrying
191222

192223
await new Promise((r) => setTimeout(r, backoffDelay));
193224
continue; // Skip normal poll interval after backoff

src/ui/App.tsx

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,8 @@ export function App({ address, packetStore, nodeStore, skipConfig = false, skipN
153153
// Connect to device
154154
useEffect(() => {
155155
let cancelled = false;
156-
Logger.info("App", "Initiating device connection", { address });
156+
setConnectError(null); // Clear any previous errors
157+
Logger.info("App", "Initiating device connection", { address, useTls, httpPort, insecure });
157158
(async () => {
158159
try {
159160
const t = await HttpTransport.create(address, useTls, httpPort, insecure);
@@ -165,8 +166,8 @@ export function App({ address, packetStore, nodeStore, skipConfig = false, skipN
165166
if (!cancelled) {
166167
const msg = e instanceof Error ? e.message : String(e);
167168
Logger.error("App", "Connection failed", e as Error, { address });
168-
console.error(`Connection failed: ${msg}`);
169-
process.exit(1);
169+
setConnectError(msg);
170+
// Don't exit immediately - let user see the error and quit manually
170171
}
171172
}
172173
})();
@@ -176,7 +177,7 @@ export function App({ address, packetStore, nodeStore, skipConfig = false, skipN
176177
}
177178
cancelled = true;
178179
};
179-
}, [address]);
180+
}, [address, useTls, httpPort, insecure]);
180181

181182
// Track terminal resize with 500ms debounce
182183
useEffect(() => {
@@ -961,13 +962,20 @@ export function App({ address, packetStore, nodeStore, skipConfig = false, skipN
961962
reason: (output as any).reason,
962963
});
963964
setStatus(output.status);
964-
if (output.status === "connected" && !configRequested) {
965-
Logger.info("App", "Connected - requesting config", { skipConfig, skipNodes });
966-
configRequested = true;
967-
if (!skipConfig) {
968-
requestConfig(skipNodes);
965+
// Clear error on successful connection
966+
if (output.status === "connected") {
967+
setConnectError(null);
968+
if (!configRequested) {
969+
Logger.info("App", "Connected - requesting config", { skipConfig, skipNodes });
970+
configRequested = true;
971+
if (!skipConfig) {
972+
requestConfig(skipNodes);
973+
}
974+
fetchOwnerFallback();
969975
}
970-
fetchOwnerFallback();
976+
} else if (output.status === "disconnected" && (output as any).reason) {
977+
// Show connection error reason to user
978+
setConnectError((output as any).reason);
971979
}
972980
} else if (output.type === "packet") {
973981
Logger.debug("App", "Processing packet from device");
@@ -3403,6 +3411,12 @@ export function App({ address, packetStore, nodeStore, skipConfig = false, skipN
34033411
{/* Status bar */}
34043412
<Box height={1} paddingX={1} flexWrap="nowrap" overflow="hidden">
34053413
<Text color={statusColor}>{status.toUpperCase()}</Text>
3414+
{status === "disconnected" && connectError && (
3415+
<>
3416+
<Text color={theme.fg.muted}> | </Text>
3417+
<Text color={theme.packet.encrypted}>{connectError}</Text>
3418+
</>
3419+
)}
34063420
<Text color={theme.fg.muted}> | </Text>
34073421
<Text color={theme.fg.secondary}>{packets.length} pkts</Text>
34083422
<Text color={theme.fg.muted}> | </Text>

0 commit comments

Comments
 (0)