Skip to content

Commit 7ccdc0b

Browse files
authored
Merge pull request #17 from khess13/main
Fix redirect handling in download utils + add connection timeouts
2 parents cacabe4 + abf5c19 commit 7ccdc0b

2 files changed

Lines changed: 78 additions & 18 deletions

File tree

common/src/main/java/dev/oxydien/simpleModSync/io/FileOperations.java

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import java.net.*;
1010
import java.nio.file.Files;
1111
import java.nio.file.Path;
12-
import java.nio.file.Paths;
1312

1413
public class FileOperations {
1514
private final SyncSchema syncSchema;
@@ -38,16 +37,49 @@ public void DownloadFromUri(String uri, Path output, int index) {
3837
}
3938

4039
public void downloadFileWithProgress(String uriString, Path outputPath, ProgressCallback callback) throws IOException, URISyntaxException {
41-
URL url = new URI(uriString).toURL();
42-
URLConnection connection = url.openConnection();
40+
// Manually follow redirects so cross-protocol redirects (e.g. http -> https
41+
// from URL shorteners) are handled correctly.
42+
int maxRedirects = 10;
43+
String currentUri = uriString;
44+
URLConnection connection = null;
45+
boolean resolved = false;
46+
47+
for (int i = 0; i < maxRedirects; i++) {
48+
URL url = new URI(currentUri).toURL();
49+
connection = url.openConnection();
50+
connection.setConnectTimeout(10_000);
51+
connection.setReadTimeout(30_000);
52+
53+
if (connection instanceof HttpURLConnection httpURLConnection) {
54+
httpURLConnection.setRequestMethod("GET");
55+
httpURLConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; SimpleModSync)");
56+
httpURLConnection.setInstanceFollowRedirects(false);
57+
58+
int responseCode = httpURLConnection.getResponseCode();
59+
if (responseCode >= 300 && responseCode < 400) {
60+
String location = httpURLConnection.getHeaderField("Location");
61+
httpURLConnection.disconnect();
62+
if (location == null) {
63+
throw new IOException("Redirect (HTTP " + responseCode + ") with no Location header");
64+
}
65+
if (!location.startsWith("http://") && !location.startsWith("https://")) {
66+
URL base = new URI(currentUri).toURL();
67+
location = new URL(base, location).toString();
68+
}
69+
currentUri = location;
70+
continue;
71+
}
72+
}
73+
resolved = true;
74+
break; // non-HTTP connection or non-redirect response — proceed to download
75+
}
4376

44-
if (connection instanceof HttpURLConnection httpURLConnection) {
45-
httpURLConnection.setRequestMethod("GET");
77+
if (!resolved) {
78+
throw new IOException("Too many redirects while fetching: " + uriString);
4679
}
4780

4881
long fileSize = connection.getContentLengthLong();
4982
InputStream inputStream = connection.getInputStream();
50-
5183
OutputStream outputStream = Files.newOutputStream(outputPath);
5284

5385
byte[] buffer = new byte[4096];
@@ -79,4 +111,4 @@ public void downloadFileWithProgress(String uriString, Path outputPath, Progress
79111
callback.onProgress(100);
80112
}
81113
}
82-
}
114+
}

common/src/main/java/dev/oxydien/simpleModSync/utils/DownloadUtils.java

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,46 @@
1212

1313
public class DownloadUtils {
1414
public static String downloadString(String uriString) throws IOException, URISyntaxException {
15-
URL url = new URI(uriString).toURL();
16-
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
17-
connection.setRequestMethod("GET");
15+
int maxRedirects = 10;
16+
String currentUri = uriString;
1817

19-
InputStream inputStream = connection.getInputStream();
20-
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
21-
String jsonString = reader.lines().collect(Collectors.joining("\n"));
18+
for (int i = 0; i < maxRedirects; i++) {
19+
URL url = new URI(currentUri).toURL();
20+
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
21+
connection.setConnectTimeout(10_000);
22+
connection.setReadTimeout(30_000);
23+
connection.setRequestMethod("GET");
24+
// setting the User-Agent header for CDNs that care about bots
25+
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; SimpleModSync)");
26+
connection.setInstanceFollowRedirects(false); // we handle redirects manually
2227

23-
reader.close();
24-
inputStream.close();
25-
connection.disconnect();
28+
int responseCode = connection.getResponseCode();
2629

27-
return jsonString;
30+
if (responseCode >= 300 && responseCode < 400) {
31+
String location = connection.getHeaderField("Location");
32+
connection.disconnect();
33+
if (location == null) {
34+
throw new IOException("Redirect (HTTP " + responseCode + ") with no Location header");
35+
}
36+
// Handle relative redirect URLs
37+
if (!location.startsWith("http://") && !location.startsWith("https://")) {
38+
location = new URI(currentUri).resolve(location).toString();
39+
}
40+
currentUri = location;
41+
continue;
42+
}
43+
44+
InputStream inputStream = connection.getInputStream();
45+
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
46+
String jsonString = reader.lines().collect(Collectors.joining("\n"));
47+
48+
reader.close();
49+
inputStream.close();
50+
connection.disconnect();
51+
52+
return jsonString;
53+
}
54+
55+
throw new IOException("Too many redirects while fetching: " + uriString);
2856
}
29-
}
57+
}

0 commit comments

Comments
 (0)