From 3577aae7cc01f421eba819f5cc0419f5eb19151e Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Mon, 16 Sep 2024 21:15:57 +0200 Subject: [PATCH] fix: improved server check Included timeout, check url even if http is included, doc strings --- app/login.tsx | 117 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 86 insertions(+), 31 deletions(-) diff --git a/app/login.tsx b/app/login.tsx index 38621e97..0a4ac90d 100644 --- a/app/login.tsx +++ b/app/login.tsx @@ -80,40 +80,92 @@ const Login: React.FC = () => { setLoading(false); } }; - async function checkUrl(url: string) { - // remove trailing / so we don't double add double / in my checker - if (url.endsWith("/")) { - url = url.slice(0, -1); - } - if( await fetch("https://" + url + "/System/Info/Public", {mode: 'cors'}).then((response) => { - return response.status; - }).catch((error) => { - console.log(error) - }) === 200) { - return "https://"+url - } else if (await fetch("http://" + url + "/System/Info/Public", {mode: 'cors'}).then((response) => { - return response.status; - }).catch((error) => { - console.log(error) - }) === 200) { - return "http://"+url + const [loadingServerCheck, setLoadingServerCheck] = useState(false); + + /** + * Checks the availability and validity of a Jellyfin server URL. + * + * This function attempts to connect to a Jellyfin server using the provided URL. + * It tries both HTTPS and HTTP protocols, with a timeout to handle long 404 responses. + * + * @param {string} url - The base URL of the Jellyfin server to check. + * @returns {Promise} A Promise that resolves to: + * - The full URL (including protocol) if a valid Jellyfin server is found. + * - undefined if no valid server is found at the given URL. + * + * Side effects: + * - Sets loadingServerCheck state to true at the beginning and false at the end. + * - Logs errors and timeout information to the console. + */ + async function checkUrl(url: string) { + url = url.endsWith("/") ? url.slice(0, -1) : url; + setLoadingServerCheck(true); + + const protocols = ["https://", "http://"]; + const timeout = 2000; // 2 seconds timeout for long 404 responses + + try { + for (const protocol of protocols) { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeout); + + try { + const response = await fetch(`${protocol}${url}/System/Info/Public`, { + mode: "cors", + signal: controller.signal, + }); + clearTimeout(timeoutId); + if (response.ok) { + return `${protocol}${url}`; + } + } catch (e) { + const error = e as Error; + if (error.name === "AbortError") { + console.log(`Request to ${protocol}${url} timed out`); + } else { + console.error(`Error checking ${protocol}${url}:`, error); + } + } + } + return undefined; + } finally { + setLoadingServerCheck(false); } - return undefined; } - + + /** + * Handles the connection attempt to a Jellyfin server. + * + * This function trims the input URL, checks its validity using the `checkUrl` function, + * and sets the server address if a valid connection is established. + * + * @param {string} url - The URL of the Jellyfin server to connect to. + * + * @returns {Promise} + * + * Side effects: + * - Calls `checkUrl` to validate the server URL. + * - Shows an alert if the connection fails. + * - Sets the server address using `setServer` if the connection is successful. + * + */ const handleConnect = async (url: string) => { url = url.trim(); - if (!url.startsWith("http")) { - const result = await checkUrl(url); - if (result === undefined){ - Alert.alert("Invalid URL", "Please enter a valid URL"); - return; - } - url = result; + + const result = await checkUrl( + url.startsWith("http") ? new URL(url).host : url + ); + + if (result === undefined) { + Alert.alert( + "Connection failed", + "Could not connect to the server. Please check the URL and your network connection." + ); + return; } - console.log(url); - setServer({ address: url }); + + setServer({ address: result }); }; const handleQuickConnect = async () => { @@ -150,7 +202,6 @@ const Login: React.FC = () => { color="black" onPress={() => { removeServer(); - setServerURL(""); }} justify="between" iconLeft={ @@ -248,7 +299,12 @@ const Login: React.FC = () => { maxLength={500} /> - @@ -258,4 +314,3 @@ const Login: React.FC = () => { }; export default Login; -