package com.mopub.nativeads; import android.os.AsyncTask; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.mopub.common.logging.MoPubLog; import com.mopub.common.util.AsyncTasks; import com.mopub.common.util.Intents; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; class UrlResolutionTask extends AsyncTask<String, Void, String> { private static final int REDIRECT_LIMIT = 10; interface UrlResolutionListener { void onSuccess(@NonNull String resolvedUrl); void onFailure(); } @NonNull private final UrlResolutionListener mListener; public static void getResolvedUrl(@NonNull final String urlString, @NonNull final UrlResolutionListener listener) { final UrlResolutionTask urlResolutionTask = new UrlResolutionTask(listener); try { AsyncTasks.safeExecuteOnExecutor(urlResolutionTask, urlString); } catch (Exception e) { MoPubLog.d("Failed to resolve url", e); listener.onFailure(); } } UrlResolutionTask(@NonNull UrlResolutionListener listener) { mListener = listener; } @Nullable @Override protected String doInBackground(@Nullable String... urls) { if (urls == null || urls.length == 0) { return null; } String previousUrl = null; try { String locationUrl = urls[0]; int redirectCount = 0; while (locationUrl != null && redirectCount < REDIRECT_LIMIT) { // if location url is not http(s), assume it's an Android deep link // this scheme will fail URL validation so we have to check early if (!Intents.isHttpUrl(locationUrl)) { return locationUrl; } previousUrl = locationUrl; locationUrl = getRedirectLocation(locationUrl); redirectCount++; } } catch (IOException e) { return null; } return previousUrl; } @Nullable private String getRedirectLocation(@NonNull final String urlString) throws IOException { final URL url = new URL(urlString); HttpURLConnection httpUrlConnection = null; try { httpUrlConnection = (HttpURLConnection) url.openConnection(); httpUrlConnection.setInstanceFollowRedirects(false); int responseCode = httpUrlConnection.getResponseCode(); if (responseCode >= 300 && responseCode < 400) { return httpUrlConnection.getHeaderField("Location"); } else { return null; } } finally { if (httpUrlConnection != null) { httpUrlConnection.disconnect(); } } } @Override protected void onPostExecute(@Nullable final String resolvedUrl) { super.onPostExecute(resolvedUrl); if (isCancelled() || resolvedUrl == null) { onCancelled(); } else { mListener.onSuccess(resolvedUrl); } } @Override protected void onCancelled() { super.onCancelled(); mListener.onFailure(); } }