summaryrefslogtreecommitdiff
path: root/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java
blob: 10ab7a5bbb57cc1707b4ff2af3d4f347b3109f51 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package com.mapbox.mapboxsdk.http;

import android.text.TextUtils;
import android.util.Log;

import com.mapbox.mapboxsdk.constants.MapboxConstants;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.ProtocolException;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.concurrent.locks.ReentrantLock;

import javax.net.ssl.SSLException;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

class HTTPRequest implements Callback {
    
    private static OkHttpClient mClient = new OkHttpClient();
    private final String LOG_TAG = HTTPRequest.class.getName();

    private static final int CONNECTION_ERROR = 0;
    private static final int TEMPORARY_ERROR = 1;
    private static final int PERMANENT_ERROR = 2;

    // Reentrancy is not needed, but "Lock" is an
    // abstract class.
    private ReentrantLock mLock = new ReentrantLock();

    private long mNativePtr = 0;

    private Call mCall;
    private Request mRequest;

    private native void nativeOnFailure(int type, String message);

    private native void nativeOnResponse(int code, String etag, String modified, String cacheControl, String expires, byte[] body);

    private HTTPRequest(long nativePtr, String resourceUrl, String userAgent, String etag, String modified) {
        mNativePtr = nativePtr;

        try {
            Request.Builder builder = new Request.Builder().url(resourceUrl).tag(resourceUrl.toLowerCase(MapboxConstants.MAPBOX_LOCALE)).addHeader("User-Agent", userAgent);
            if (etag.length() > 0) {
                builder = builder.addHeader("If-None-Match", etag);
            } else if (modified.length() > 0) {
                builder = builder.addHeader("If-Modified-Since", modified);
            }
            mRequest = builder.build();
            mCall = mClient.newCall(mRequest);
            mCall.enqueue(this);
        } catch (Exception e) {
            onFailure(e);
        }
    }

    public void cancel() {
        mCall.cancel();

        // TODO: We need a lock here because we can try
        // to cancel at the same time the request is getting
        // answered on the OkHTTP thread. We could get rid of
        // this lock by using Runnable when we move Android
        // implementation of mbgl::RunLoop to Looper.
        mLock.lock();
        mNativePtr = 0;
        mLock.unlock();
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        if (response.isSuccessful()) {
            Log.v(LOG_TAG, String.format("[HTTP] Request was successful (code = %d).", 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.d(LOG_TAG, String.format(
                    "[HTTP] Request with response code = %d: %s",
                    response.code(), message));
        }

        byte[] body;
        try {
            body = response.body().bytes();
        } catch (IOException e) {
            onFailure(e);
            //throw e;
            return;
        } finally {
            response.body().close();
        }

        mLock.lock();
        if (mNativePtr != 0) {
            nativeOnResponse(response.code(), response.header("ETag"), response.header("Last-Modified"), response.header("Cache-Control"), response.header("Expires"), body);
        }
        mLock.unlock();
    }

    @Override
    public void onFailure(Call call, IOException e) {
        onFailure(e);
    }

    private void onFailure(Exception e) {
        Log.w(LOG_TAG, String.format("[HTTP] Request could not be executed: %s", e.getMessage()));

        int type = PERMANENT_ERROR;
        if ((e instanceof UnknownHostException) || (e instanceof SocketException) || (e instanceof ProtocolException) || (e instanceof SSLException)) {
            type = CONNECTION_ERROR;
        } else if ((e instanceof InterruptedIOException)) {
            type = TEMPORARY_ERROR;
        }

        String errorMessage = e.getMessage() != null ? e.getMessage() : "Error processing the request";

        mLock.lock();
        if (mNativePtr != 0) {
            nativeOnFailure(type, errorMessage);
        }
        mLock.unlock();
    }
}