package com.grigowashere.aismap.utils; import android.util.Log; import java.io.InputStream; import java.net.ConnectException; import java.net.HttpURLConnection; import java.net.NoRouteToHostException; import java.net.SocketTimeoutException; import java.net.URL; import java.net.UnknownHostException; import java.security.cert.CertificateException; import java.util.Locale; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLPeerUnverifiedException; /** * Lightweight reachability check for {@code https://aismap.local/} (feasibility / diagnostics). * Runs on a background thread; callers must not invoke from the UI thread. */ public final class AismapLocalHttpsProbe { private static final String TAG = "AismapLocalHttpsProbe"; /** Hub HTTPS root — user-requested feasibility target. */ public static final String PROBE_URL = "https://aismap.local/"; private static final int CONNECT_TIMEOUT_MS = 2500; private static final int READ_TIMEOUT_MS = 2500; private AismapLocalHttpsProbe() {} /** * Performs a GET with short timeouts. Returns a short UI-safe line (no stack traces). */ public static String probeOnce() { HttpURLConnection conn = null; try { URL url = new URL(PROBE_URL); conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(CONNECT_TIMEOUT_MS); conn.setReadTimeout(READ_TIMEOUT_MS); conn.setRequestMethod("GET"); conn.setInstanceFollowRedirects(true); conn.setUseCaches(false); int code = conn.getResponseCode(); InputStream body = code >= HttpURLConnection.HTTP_BAD_REQUEST ? conn.getErrorStream() : conn.getInputStream(); drain(body); if (code >= 200 && code < 400) { return "HTTPS aismap.local: OK HTTP " + code; } return "HTTPS aismap.local: HTTP " + code; } catch (Throwable t) { Log.w(TAG, "probe failed: " + t.getClass().getSimpleName() + ": " + t.getMessage(), t); return "HTTPS aismap.local: " + classify(t); } finally { if (conn != null) { try { conn.disconnect(); } catch (Throwable ignore) {} } } } private static void drain(InputStream in) { if (in == null) return; byte[] buf = new byte[512]; try (InputStream stream = in) { while (stream.read(buf) != -1) { /* discard */ } } catch (Throwable ignore) {} } static String classify(Throwable t) { if (t instanceof SSLHandshakeException || t instanceof SSLPeerUnverifiedException) { return "TLS/cert issue"; } if (t instanceof CertificateException) { return "TLS/cert issue"; } if (t instanceof SSLException) { String m = t.getMessage(); if (m != null && m.toLowerCase(Locale.US).contains("cert")) { return "TLS/cert issue"; } return "TLS: " + t.getClass().getSimpleName(); } if (t instanceof SocketTimeoutException) { return "timeout"; } if (t instanceof UnknownHostException) { return "no DNS (aismap.local)"; } if (t instanceof ConnectException || t instanceof NoRouteToHostException) { return "no route / refused"; } Throwable c = t.getCause(); if (c != null && c != t) { return classify(c); } String m = t.getMessage(); if (m != null && !m.isEmpty()) { String shortM = m.length() > 48 ? m.substring(0, 45) + "…" : m; return t.getClass().getSimpleName() + ": " + shortM; } return t.getClass().getSimpleName(); } }