fix: improved server check

Included timeout, check url even if http is included, doc strings
This commit is contained in:
Fredrik Burmester
2024-09-16 21:15:57 +02:00
parent 5e141f27c4
commit 3577aae7cc

View File

@@ -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<boolean>(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<string | undefined>} 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<void>}
*
* 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}
/>
</View>
<Button onPress={ async () => await handleConnect(serverURL)} className="mb-2">
<Button
loading={loadingServerCheck}
disabled={loadingServerCheck}
onPress={async () => await handleConnect(serverURL)}
className="mb-2"
>
Connect
</Button>
</View>
@@ -258,4 +314,3 @@ const Login: React.FC = () => {
};
export default Login;