summaryrefslogtreecommitdiff
path: root/platform/android/MapboxGLAndroidModuleHttp
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android/MapboxGLAndroidModuleHttp')
-rw-r--r--platform/android/MapboxGLAndroidModuleHttp/.gitignore1
-rw-r--r--platform/android/MapboxGLAndroidModuleHttp/build.gradle29
-rw-r--r--platform/android/MapboxGLAndroidModuleHttp/src/main/AndroidManifest.xml1
-rw-r--r--platform/android/MapboxGLAndroidModuleHttp/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestImpl.java186
-rw-r--r--platform/android/MapboxGLAndroidModuleHttp/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUtil.java64
5 files changed, 281 insertions, 0 deletions
diff --git a/platform/android/MapboxGLAndroidModuleHttp/.gitignore b/platform/android/MapboxGLAndroidModuleHttp/.gitignore
new file mode 100644
index 0000000000..796b96d1c4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidModuleHttp/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/platform/android/MapboxGLAndroidModuleHttp/build.gradle b/platform/android/MapboxGLAndroidModuleHttp/build.gradle
new file mode 100644
index 0000000000..c31f28b1e1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidModuleHttp/build.gradle
@@ -0,0 +1,29 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion androidVersions.compileSdkVersion
+
+ defaultConfig {
+ minSdkVersion androidVersions.minSdkVersion
+ targetSdkVersion androidVersions.targetSdkVersion
+ versionCode 1
+ versionName "1.0"
+ buildConfigField "String", "GIT_REVISION_SHORT", String.format("\"%s\"", getGitRevision())
+ buildConfigField "String", "MAPBOX_VERSION_STRING", String.format("\"Mapbox/%s\"", project.VERSION_NAME)
+ }
+}
+
+dependencies {
+ api(project(':MapboxGLAndroidModuleBase'))
+ implementation dependenciesList.okhttp3
+ implementation dependenciesList.supportAnnotations
+ implementation dependenciesList.timber
+}
+
+def static getGitRevision() {
+ def cmd = "git rev-parse --short HEAD"
+ def proc = cmd.execute()
+ def ref = proc.text.trim()
+ return ref
+}
+
diff --git a/platform/android/MapboxGLAndroidModuleHttp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidModuleHttp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..bb66132347
--- /dev/null
+++ b/platform/android/MapboxGLAndroidModuleHttp/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+<manifest package="com.mapbox.mapboxsdk.http"/>
diff --git a/platform/android/MapboxGLAndroidModuleHttp/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestImpl.java b/platform/android/MapboxGLAndroidModuleHttp/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestImpl.java
new file mode 100644
index 0000000000..c562f1b147
--- /dev/null
+++ b/platform/android/MapboxGLAndroidModuleHttp/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestImpl.java
@@ -0,0 +1,186 @@
+package com.mapbox.mapboxsdk.http;
+
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
+import android.util.Log;
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import okhttp3.*;
+import timber.log.Timber;
+
+import javax.net.ssl.SSLException;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.NoRouteToHostException;
+import java.net.ProtocolException;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
+import static com.mapbox.mapboxsdk.http.HttpRequestUtil.toHumanReadableAscii;
+
+public class HttpRequestImpl extends HttpRequest {
+
+ private static final String userAgentString = toHumanReadableAscii(
+ String.format("%s %s (%s) Android/%s (%s)",
+ getApplicationIdentifier(),
+ BuildConfig.MAPBOX_VERSION_STRING,
+ BuildConfig.GIT_REVISION_SHORT,
+ Build.VERSION.SDK_INT,
+ Build.CPU_ABI)
+ );
+
+ private static OkHttpClient client = new OkHttpClient.Builder().dispatcher(getDispatcher()).build();
+ private static boolean logEnabled = true;
+ private static boolean logRequestUrl = false;
+
+ private Call call;
+
+ @Override
+ public void executeRequest(HttpRequestResponder httpRequest, long nativePtr, String resourceUrl,
+ String etag, String modified) {
+ OkHttpCallback callback = new OkHttpCallback(httpRequest);
+ try {
+ HttpUrl httpUrl = HttpUrl.parse(resourceUrl);
+ if (httpUrl == null) {
+ log(Log.ERROR, String.format("[HTTP] Unable to parse resourceUrl %s", resourceUrl));
+ return;
+ }
+
+ final String host = httpUrl.host().toLowerCase(MapboxConstants.MAPBOX_LOCALE);
+ resourceUrl = buildResourceUrl(host, resourceUrl, httpUrl.querySize());
+
+ final Request.Builder builder = new Request.Builder()
+ .url(resourceUrl)
+ .tag(resourceUrl.toLowerCase(MapboxConstants.MAPBOX_LOCALE))
+ .addHeader("User-Agent", userAgentString);
+ if (etag.length() > 0) {
+ builder.addHeader("If-None-Match", etag);
+ } else if (modified.length() > 0) {
+ builder.addHeader("If-Modified-Since", modified);
+ }
+
+ final Request request = builder.build();
+ call = client.newCall(request);
+ call.enqueue(callback);
+ } catch (Exception exception) {
+ callback.handleFailure(call, exception);
+ }
+ }
+
+ @Override
+ public void cancelRequest() {
+ // call can be null if the constructor gets aborted (e.g, under a NoRouteToHostException).
+ if (call != null) {
+ call.cancel();
+ }
+ }
+
+ public static void enablePrintRequestUrlOnFailure(boolean enabled) {
+ logRequestUrl = enabled;
+ }
+
+ public static void enableLog(boolean enabled) {
+ logEnabled = enabled;
+ }
+
+ public static void setOkHttpClient(OkHttpClient okHttpClient) {
+ HttpRequestImpl.client = okHttpClient;
+ }
+
+ private static class OkHttpCallback implements Callback {
+
+ private HttpRequestResponder httpRequest;
+
+ OkHttpCallback(HttpRequestResponder httpRequest) {
+ this.httpRequest = httpRequest;
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull IOException e) {
+ handleFailure(call, e);
+ }
+
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful()) {
+ log(Log.VERBOSE, String.format("[HTTP] Request was successful (code = %s).", response.code()));
+ } else {
+ // We don't want to call this unsuccessful because a 304 isn't really an error
+ String message = !TextUtils.isEmpty(response.message()) ? response.message() : "No additional information";
+ log(Log.DEBUG, String.format("[HTTP] Request with response code = %s: %s", response.code(), message));
+ }
+
+ ResponseBody responseBody = response.body();
+ if (responseBody == null) {
+ log(Log.ERROR, "[HTTP] Received empty response body");
+ return;
+ }
+
+ byte[] body;
+ try {
+ body = responseBody.bytes();
+ } catch (IOException ioException) {
+ onFailure(call, ioException);
+ // throw ioException;
+ return;
+ } finally {
+ response.close();
+ }
+
+ httpRequest.onResponse(response.code(),
+ response.header("ETag"),
+ response.header("Last-Modified"),
+ response.header("Cache-Control"),
+ response.header("Expires"),
+ response.header("Retry-After"),
+ response.header("x-rate-limit-reset"),
+ body);
+ }
+
+ private void handleFailure(Call call, Exception e) {
+ String errorMessage = e.getMessage() != null ? e.getMessage() : "Error processing the request";
+ int type = getFailureType(e);
+
+ if (logEnabled && call != null && call.request() != null) {
+ String requestUrl = call.request().url().toString();
+ logFailure(type, errorMessage, requestUrl);
+ }
+ httpRequest.handleFailure(type, errorMessage);
+ }
+
+ private void logFailure(int type, String errorMessage, String requestUrl) {
+ log(type == TEMPORARY_ERROR ? Log.DEBUG : type == CONNECTION_ERROR ? Log.INFO : Log.WARN,
+ String.format(
+ "Request failed due to a %s error: %s %s",
+ type == TEMPORARY_ERROR ? "temporary" : type == CONNECTION_ERROR ? "connection" : "permanent",
+ errorMessage,
+ logRequestUrl ? requestUrl : ""
+ )
+ );
+ }
+
+ private int getFailureType(Exception e) {
+ if ((e instanceof NoRouteToHostException) || (e instanceof UnknownHostException) || (e instanceof SocketException)
+ || (e instanceof ProtocolException) || (e instanceof SSLException)) {
+ return CONNECTION_ERROR;
+ } else if ((e instanceof InterruptedIOException)) {
+ return TEMPORARY_ERROR;
+ }
+ return PERMANENT_ERROR;
+ }
+ }
+
+ private static Dispatcher getDispatcher() {
+ Dispatcher dispatcher = new Dispatcher();
+ // Matches core limit set on
+ // https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/src/http_file_source.cpp#L192
+ dispatcher.setMaxRequestsPerHost(20);
+ return dispatcher;
+ }
+
+ static void log(int type, String errorMessage) {
+ if (logEnabled) {
+ Timber.log(type, errorMessage);
+ }
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidModuleHttp/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUtil.java b/platform/android/MapboxGLAndroidModuleHttp/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUtil.java
new file mode 100644
index 0000000000..d8b9591a63
--- /dev/null
+++ b/platform/android/MapboxGLAndroidModuleHttp/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUtil.java
@@ -0,0 +1,64 @@
+package com.mapbox.mapboxsdk.http;
+
+import okhttp3.OkHttpClient;
+import okio.Buffer;
+
+/**
+ * Utility class for setting OkHttpRequest configurations
+ */
+public class HttpRequestUtil {
+
+ /**
+ * Set the log state of OkHttpRequest. Default value is true.
+ * <p>
+ * This configuration will outlast the lifecycle of the Map.
+ * </p>
+ *
+ * @param enabled True will enable logging, false will disable
+ */
+ public static void setLogEnabled(boolean enabled) {
+ HttpRequestImpl.enableLog(enabled);
+ }
+
+ /**
+ * Enable printing of the request url when an error occurred. Default value is false.
+ * <p>
+ * Requires {@link #setLogEnabled(boolean)} to be activated.
+ * </p>
+ * <p>
+ * This configuration will outlast the lifecycle of the Map.
+ * </p>
+ *
+ * @param enabled True will print urls, false will disable
+ */
+ public static void setPrintRequestUrlOnFailure(boolean enabled) {
+ HttpRequestImpl.enablePrintRequestUrlOnFailure(enabled);
+ }
+
+ /**
+ * Set the OkHttpClient used for requesting map resources.
+ *
+ * @param client the OkHttpClient
+ */
+ public static void setOkHttpClient(OkHttpClient client) {
+ HttpRequestImpl.setOkHttpClient(client);
+ }
+
+ static String toHumanReadableAscii(String s) {
+ for (int i = 0, length = s.length(), c; i < length; i += Character.charCount(c)) {
+ c = s.codePointAt(i);
+ if (c > '\u001f' && c < '\u007f') {
+ continue;
+ }
+
+ Buffer buffer = new Buffer();
+ buffer.writeUtf8(s, 0, i);
+ for (int j = i; j < length; j += Character.charCount(c)) {
+ c = s.codePointAt(j);
+ buffer.writeUtf8CodePoint(c > '\u001f' && c < '\u007f' ? c : '?');
+ }
+ return buffer.readUtf8();
+ }
+ return s;
+ }
+} \ No newline at end of file