From 39880a6214771eacd3f1ca2f276456a543e92e2a Mon Sep 17 00:00:00 2001 From: simon Date: Sun, 15 Sep 2024 12:49:47 +0200 Subject: [PATCH 1/7] possible a good fix --- .vscode/launch.json | 17 +++++++++++++++++ app/login.tsx | 21 +++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..aa033738 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}\\index" + } + ] +} \ No newline at end of file diff --git a/app/login.tsx b/app/login.tsx index 9cb37339..ec52bd1e 100644 --- a/app/login.tsx +++ b/app/login.tsx @@ -44,6 +44,27 @@ const Login: React.FC = () => { useEffect(() => { (async () => { + // add a test that checks if the url contains http/https + if (_apiUrl.startsWith("http") || _apiUrl.startsWith("https")) { + { + setServer({ + address: _apiUrl, + }); + } + } else { + //check if url requires ssl + // use curl to see if https is required + // if not use http + // if yes use https + const test = fetch("http://jellyfin.oakgrove.site/web/#/home.html") + .then((response) => console.log(response)) + .catch((error) => console.error(error)); + + const test2 = fetch("https://jellyfin.oakgrove.site/web/#/home.html") + .then((response) => console.log(response)) + .catch((error) => console.error(error)); + } + if (_apiUrl) { setServer({ address: _apiUrl, From 7f9c89356088c8fe97e42d8c210ade30a2322aeb Mon Sep 17 00:00:00 2001 From: simon Date: Sun, 15 Sep 2024 18:10:22 +0200 Subject: [PATCH 2/7] remove need for http/https --- app/login.tsx | 62 ++++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/app/login.tsx b/app/login.tsx index ec52bd1e..ad4c36d8 100644 --- a/app/login.tsx +++ b/app/login.tsx @@ -44,27 +44,8 @@ const Login: React.FC = () => { useEffect(() => { (async () => { - // add a test that checks if the url contains http/https - if (_apiUrl.startsWith("http") || _apiUrl.startsWith("https")) { - { - setServer({ - address: _apiUrl, - }); - } - } else { - //check if url requires ssl - // use curl to see if https is required - // if not use http - // if yes use https - const test = fetch("http://jellyfin.oakgrove.site/web/#/home.html") - .then((response) => console.log(response)) - .catch((error) => console.error(error)); - - const test2 = fetch("https://jellyfin.oakgrove.site/web/#/home.html") - .then((response) => console.log(response)) - .catch((error) => console.error(error)); - } - + // we might re-use the checkUrl function here to check the url as well + // however, I don't think it should be necessary for now if (_apiUrl) { setServer({ address: _apiUrl, @@ -99,12 +80,35 @@ const Login: React.FC = () => { setLoading(false); } }; - - const handleConnect = (url: string) => { - if (!url.startsWith("http")) { - Alert.alert("Error", "URL needs to start with http or https."); - return; + 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 + "/web/#/home.html", {mode: 'cors'}).then((response) => { + return response.status; + }).catch((error) => { + console.log(error) + }) === 200) { + return "https://"+url + } else if (await fetch("http://" + url + "/web/#/home.html", {redirect: "manual"}).then((response) => { + return response.status; + }).catch((error) => { + console.log(error) + }) === 200) { + return "http://"+url + } + return undefined; + } + + const handleConnect = async (url: string) => { + if (!url.startsWith("http")) { + const result = await checkUrl(url); + if (result === undefined) return; + url = result; + } + console.log(url); setServer({ address: url.trim() }); }; @@ -239,11 +243,8 @@ const Login: React.FC = () => { textContentType="URL" maxLength={500} /> - - Server URL requires http or https - - @@ -253,3 +254,4 @@ const Login: React.FC = () => { }; export default Login; + From 25656cb7f1fad8589517d46cf6c230801893b52f Mon Sep 17 00:00:00 2001 From: simon Date: Sun, 15 Sep 2024 18:13:38 +0200 Subject: [PATCH 3/7] remove need for http/https --- .vscode/launch.json | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index aa033738..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Launch Program", - "skipFiles": [ - "/**" - ], - "program": "${workspaceFolder}\\index" - } - ] -} \ No newline at end of file From 19a53da8a7050102aab195022a7e726fc7f2510e Mon Sep 17 00:00:00 2001 From: simon Date: Sun, 15 Sep 2024 18:45:26 +0200 Subject: [PATCH 4/7] remove need for http/https --- app/login.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/login.tsx b/app/login.tsx index ad4c36d8..c47b76a7 100644 --- a/app/login.tsx +++ b/app/login.tsx @@ -92,7 +92,7 @@ const Login: React.FC = () => { console.log(error) }) === 200) { return "https://"+url - } else if (await fetch("http://" + url + "/web/#/home.html", {redirect: "manual"}).then((response) => { + } else if (await fetch("http://" + url + "/web/#/home.html", {mode: 'cors'}).then((response) => { return response.status; }).catch((error) => { console.log(error) From b67a4f18435904c84e4a53d232d5165a4affda32 Mon Sep 17 00:00:00 2001 From: simon Date: Sun, 15 Sep 2024 18:48:16 +0200 Subject: [PATCH 5/7] fixed some inconsistencies --- app/login.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/login.tsx b/app/login.tsx index c47b76a7..780d8a13 100644 --- a/app/login.tsx +++ b/app/login.tsx @@ -103,13 +103,17 @@ const Login: React.FC = () => { } const handleConnect = async (url: string) => { + url = url.trim(); if (!url.startsWith("http")) { const result = await checkUrl(url); - if (result === undefined) return; + if (result === undefined){ + Alert.alert("Invalid URL", "Please enter a valid URL"); + return; + } url = result; } console.log(url); - setServer({ address: url.trim() }); + setServer({ address: url }); }; const handleQuickConnect = async () => { From 5e141f27c41172ee4a36d50ccc17855fbdede8cc Mon Sep 17 00:00:00 2001 From: simon Date: Mon, 16 Sep 2024 16:37:45 +0200 Subject: [PATCH 6/7] /System/Info/Public instead of /web --- app/login.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/login.tsx b/app/login.tsx index 780d8a13..38621e97 100644 --- a/app/login.tsx +++ b/app/login.tsx @@ -86,13 +86,13 @@ const Login: React.FC = () => { url = url.slice(0, -1); } - if( await fetch("https://" + url + "/web/#/home.html", {mode: 'cors'}).then((response) => { + 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 + "/web/#/home.html", {mode: 'cors'}).then((response) => { + } else if (await fetch("http://" + url + "/System/Info/Public", {mode: 'cors'}).then((response) => { return response.status; }).catch((error) => { console.log(error) From 3577aae7cc01f421eba819f5cc0419f5eb19151e Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Mon, 16 Sep 2024 21:15:57 +0200 Subject: [PATCH 7/7] 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; -