Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ function App() {
const join = async (appId: string) => {
if (!appId) return;
const res = await startSession({ appId }).unwrap();
console.log("Joining debug session at " + res.url);
dispatch({ type: WS_CONNECT, payload: { url: res.url } });
const primaryUrl = res.fallbackUrl || res.url;
const fallbackUrl = res.fallbackUrl ? res.url : undefined;
console.log("Joining debug session at " + primaryUrl + (fallbackUrl ? " (fallback: " + fallbackUrl + ")" : ""));
dispatch({ type: WS_CONNECT, payload: { url: primaryUrl, fallbackUrl } });
};

const stop = (appId: string) => {
Expand Down
19 changes: 12 additions & 7 deletions src/features/DebugConsole/DebugConsole.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ const DebugConsole = ({isConnected, join, stop, clear}: DebugConsoleProps) => {
const { appId } = useAppParams();
const dispatch = useAppDispatch();
const messages = useAppSelector((state: RootState) => state.websocket.messages);
const failedUrl = useAppSelector((state: RootState) => state.websocket.failedUrl);
const failedUrls = useAppSelector((state: RootState) => state.websocket.failedUrls);
const searchText = useAppSelector(selectSearchText);
const certUrl = failedUrl
? new URL(failedUrl).origin.replace(/^wss:/, 'https:').replace(/^ws:/, 'http:')
const certUrls = failedUrls
? failedUrls.map((u: string) => new URL(u).origin.replace(/^wss:/, 'https:').replace(/^ws:/, 'http:'))
: null;
Comment on lines +30 to 32

const { data: doNotLoadConfigOnNextBoot } =
Expand Down Expand Up @@ -140,12 +140,17 @@ const DebugConsole = ({isConnected, join, stop, clear}: DebugConsoleProps) => {
</Button>
<span className="ps-2">Message Count: {messages.length}</span>
</div>
{certUrl && (
{certUrls && certUrls.length > 0 && (
<Alert variant="warning" className="py-2 px-3 mb-2" style={{ fontSize: '0.82rem' }}>
<strong>Connection failed.</strong> The debug server may have an untrusted certificate.{' '}
<Alert.Link href={certUrl} target="_blank" rel="noreferrer">
Open {certUrl} in a new tab
</Alert.Link>
{certUrls.map((certUrl: string, i: number) => (
<span key={certUrl}>
{i > 0 && ' or '}
<Alert.Link href={certUrl} target="_blank" rel="noreferrer">
Open {certUrl} in a new tab
</Alert.Link>
</span>
))}
{', accept the certificate, then try "Start Debug Session" again.'}
</Alert>
)}
Expand Down
1 change: 1 addition & 0 deletions src/store/apiSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ interface MethodParam {

interface DebugSession {
url: string;
fallbackUrl?: string;
}

export interface MobileControlClient {
Expand Down
47 changes: 30 additions & 17 deletions src/store/websocketMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const websocketMiddleware: Middleware = (store) => {
const { type } = action as WsConnectAction | WsDisconnectAction;

if (type === WS_CONNECT) {
const { url } = (action as WsConnectAction).payload;
const { url, fallbackUrl } = (action as WsConnectAction).payload;

console.log("[ws] Connecting to", url);

Expand All @@ -29,24 +29,37 @@ export const websocketMiddleware: Middleware = (store) => {
socket.close();
}

socket = new WebSocket(url);
socket.onopen = () => store.dispatch(connected());
socket.onclose = () => {
store.dispatch(disconnected());
socket = null;
};
socket.onerror = (err) => {
console.error("WebSocket error", err);
store.dispatch(connectionFailed(url));
};
socket.onmessage = (event: MessageEvent<string>) => {
try {
store.dispatch(messageReceived(JSON.parse(event.data)));
} catch (e) {
console.error("Failed to parse WebSocket message", e);
}
const connectToUrl = (targetUrl: string, fallback?: string) => {
socket = new WebSocket(targetUrl);
socket.onopen = () => store.dispatch(connected());
socket.onclose = () => {
store.dispatch(disconnected());
socket = null;
};
socket.onerror = (err) => {
console.error("WebSocket error", err);
if (fallback) {
console.log("[ws] Primary connection failed, falling back to", fallback);
connectToUrl(fallback);
} else {
// Report all attempted URLs (primary + fallback that was tried)
const attemptedUrls = fallbackUrl && targetUrl === fallbackUrl
? [url, fallbackUrl]
: [targetUrl];
store.dispatch(connectionFailed(attemptedUrls));
}
};
Comment on lines +33 to +51
socket.onmessage = (event: MessageEvent<string>) => {
try {
store.dispatch(messageReceived(JSON.parse(event.data)));
} catch (e) {
console.error("Failed to parse WebSocket message", e);
}
};
};

connectToUrl(url, fallbackUrl);

return;
}

Expand Down
12 changes: 6 additions & 6 deletions src/store/websocketSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { LogMessage } from "../shared/types/LogMessage";
interface WebsocketState {
messages: LogMessage[];
isConnected: boolean;
failedUrl: string | null;
failedUrls: string[] | null;
}

const initialState: WebsocketState = {
messages: [],
isConnected: false,
failedUrl: null,
failedUrls: null,
};

const websocketSlice = createSlice({
Expand All @@ -34,12 +34,12 @@ const websocketSlice = createSlice({
state.messages = [];
},
/** Dispatched by the middleware when a connection attempt fails */
connectionFailed(state, action: PayloadAction<string>) {
state.failedUrl = action.payload;
connectionFailed(state, action: PayloadAction<string[]>) {
state.failedUrls = action.payload;
},
/** Dispatched by the middleware when a new connection attempt starts */
connectionAttemptStarted(state) {
state.failedUrl = null;
state.failedUrls = null;
},
},
});
Expand All @@ -57,7 +57,7 @@ export const WS_DISCONNECT = "websocket/disconnect";

export interface WsConnectAction {
type: typeof WS_CONNECT;
payload: { url: string };
payload: { url: string; fallbackUrl?: string };
}

export interface WsDisconnectAction {
Expand Down