Fix: Crash on HTML response in EventRepository and others

- Fixed: Value <! DOCTYPE cannot be converted to JSONArray in EventRepository
- Fixed: Added HTML validation in UpdateManager and DeviceRegistry
- Fixed: Improved HTTP error handling in StreamUrlResolver
- Improved: Error messages in PlayerActivity
- Bumped version to 9.4.3
This commit is contained in:
renato97
2026-01-26 21:44:07 +01:00
parent 05625ffe50
commit bac564eb4f
8 changed files with 343 additions and 26 deletions

View File

@@ -8,8 +8,8 @@ android {
applicationId "com.streamplayer"
minSdk 21
targetSdk 33
versionCode 100000
versionName "10.0"
versionCode 100100
versionName "10.0.1"
buildConfigField "String", "DEVICE_REGISTRY_URL", '"http://194.163.191.200:4000"'
}

View File

@@ -81,6 +81,15 @@ public class DeviceRegistry {
throw new IOException("HTTP " + response.code());
}
String responseText = response.body().string();
// Validar que no sea HTML antes de parsear
if (responseText != null) {
String trimmed = responseText.trim();
if (trimmed.startsWith("<!") || trimmed.startsWith("<html")) {
throw new IOException("El servidor devolvió HTML en lugar de JSON");
}
}
JSONObject json = new JSONObject(responseText);
JSONObject deviceJson = json.optJSONObject("device");
JSONObject verificationJson = json.optJSONObject("verification");

View File

@@ -79,19 +79,51 @@ public class EventRepository {
connection.setConnectTimeout(15000);
connection.setReadTimeout(15000);
connection.setRequestMethod("GET");
try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder builder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
connection.setRequestProperty("Accept", "application/json");
connection.setRequestProperty("User-Agent", "StreamPlayer/1.0");
try {
int responseCode = connection.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
throw new IOException("Error HTTP " + responseCode + ": " + connection.getResponseMessage());
}
String contentType = connection.getContentType();
if (contentType != null && !contentType.contains("json")) {
throw new IOException("El servidor devolvió " + contentType + " en lugar de JSON. Verifica que la URL sea correcta.");
}
try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder builder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
}
String response = builder.toString();
// Validar que no sea HTML
if (response.trim().startsWith("<!") || response.trim().startsWith("<html")) {
throw new IOException("El servidor devolvió HTML en lugar de JSON. La URL del endpoint puede estar incorrecta o el servidor tiene problemas.");
}
return response;
}
return builder.toString();
} finally {
connection.disconnect();
}
}
private List<EventItem> parseEvents(String json) throws JSONException {
if (json == null || json.trim().isEmpty()) {
throw new JSONException("La respuesta está vacía");
}
// Validar que no sea HTML antes de parsear
String trimmed = json.trim();
if (trimmed.startsWith("<!") || trimmed.startsWith("<html")) {
throw new JSONException("Se recibió HTML en lugar de JSON");
}
JSONArray array = new JSONArray(json);
List<EventItem> events = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {

View File

@@ -22,6 +22,7 @@ import com.google.android.exoplayer2.source.hls.HlsMediaSource;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
@@ -100,8 +101,10 @@ public class PlayerActivity extends AppCompatActivity {
try {
String resolvedUrl = StreamUrlResolver.resolve(channelUrl);
runOnUiThread(() -> startPlayback(resolvedUrl));
} catch (IOException e) {
runOnUiThread(() -> showError("No se pudo conectar con el canal: " + e.getMessage()));
} catch (Exception e) {
runOnUiThread(() -> showError("Error al obtener stream: " + e.getMessage()));
runOnUiThread(() -> showError("Error inesperado: " + e.getMessage()));
}
}).start();
}

View File

@@ -61,15 +61,32 @@ public final class StreamUrlResolver {
connection.setReadTimeout(15000);
connection.setRequestProperty("User-Agent", USER_AGENT);
connection.setRequestProperty("Accept", "text/html,application/xhtml+xml");
connection.connect();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder builder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
try {
int responseCode = connection.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
throw new IOException("Error HTTP " + responseCode + " al cargar la página del stream");
}
String contentType = connection.getContentType();
// Validar que sea contenido web (HTML)
if (contentType != null && !contentType.contains("html") && !contentType.contains("text")) {
// A veces puede venir sin content type o application/octet-stream,
// pero si es explícitamente una imagen o algo así, abortamos.
if (contentType.startsWith("image/") || contentType.startsWith("video/") || contentType.startsWith("audio/")) {
throw new IOException("El servidor devolvió " + contentType + " en lugar de HTML");
}
}
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder builder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
}
return builder.toString();
}
return builder.toString();
} finally {
connection.disconnect();
}

View File

@@ -172,6 +172,16 @@ public class UpdateManager {
}
private UpdateInfo parseRelease(String responseBody) throws JSONException, IOException {
if (responseBody == null || responseBody.trim().isEmpty()) {
throw new JSONException("La respuesta está vacía");
}
// Validar que no sea HTML antes de parsear
String trimmed = responseBody.trim();
if (trimmed.startsWith("<!") || trimmed.startsWith("<html")) {
throw new JSONException("Se recibió HTML en lugar de JSON");
}
JSONObject releaseJson = new JSONObject(responseBody);
String tagName = releaseJson.optString("tag_name", "");
String versionName = deriveVersionName(tagName, releaseJson.optString("name"));
@@ -244,6 +254,11 @@ public class UpdateManager {
}
String json = response.body().string();
if (!TextUtils.isEmpty(json)) {
// Validar que no sea HTML antes de parsear
String trimmed = json.trim();
if (trimmed.startsWith("<!") || trimmed.startsWith("<html")) {
continue;
}
return new JSONObject(json);
}
}