1

To get my tiles I do:

style(styleUri = Style.MAPBOX_STREETS) {
    +vectorSource(id = "parcel-source") {
    url("http://example.com/{z}/{x}/{y}.png")
}

But I need to provide a token to a tile provider, with an HTTP header like Authorization: bearer [token].

The documentation shows a skeleton for an HTTP interceptor, but give little clue on how to implement the 5 functions of the interface to be implemented. I can't find the involved interface in the API reference neither. There is an open issue logged for a similar problem.

Also, a solution working for v9.5 seems not available anymore in v10 (HttpRequestUtil missing).

Jérôme Pietri
  • 187
  • 1
  • 11

4 Answers4

2

It was observed that this method only works fine during debug. When making the release build this gives an error because of duplicated modules (as we do not use exclude the local project and external libs have duplicated modules for mapbox http stack).

If you want to add an header please use an interceptor to the mapbox requests like its suggested here: https://github.com/mapbox/mapbox-maps-android/issues/610

1

Hi for anyone struggling with this, here is how I implemented it:

@MapboxModule(type = CommonHttpClient)
class CustomMapboxOkHttpService : HttpServiceInterface {
    val map = MapboxOkHttpService()

    override fun setInterceptor(interceptor: HttpServiceInterceptorInterface?) {
        map.setInterceptor(interceptor)
    }

    override fun setMaxRequestsPerHost(max: Byte) {
        map.setMaxRequestsPerHost(max)
    }

    override fun request(request: HttpRequest, callback: HttpResponseCallback): Long {
        token?.let {
            request.headers["Authorization"] = "Bearer $it"
        }
        return map.request(request, callback)
    }

    override fun cancelRequest(id: Long, callback: ResultCallback) {
        map.cancelRequest(id, callback)
    }

    override fun supportsKeepCompression(): Boolean {
        return map.supportsKeepCompression()
    }

    override fun download(options: DownloadOptions, callback: DownloadStatusCallback): Long {
        return map.download(options, callback)
    }

    companion object {
        var token: String? = null
    }
}

Do NOT exclude common library as stated in documentation here

peromed
  • 58
  • 4
1

I ran into this issue when trying to use the HttpServiceInterface approach also described in the documentation. In v10 there is another option to use HttpServiceFactory.getInstance().setInterceptor. This interface is much simpler to implement and each method is expected to return the same object.

This was my approach:

class CustomMapboxIntercepter() : HttpServiceInterceptorInterface {

    override fun onRequest(request: HttpRequest): HttpRequest {
        request.headers["Authorization"] = "Bearer $token" 
        return request
    }

    override fun onDownload(download: DownloadOptions): DownloadOptions {
        return download
    }

    override fun onResponse(response: HttpResponse): HttpResponse {
        return response
    }
}

Then just setting the intercepter based on the class:

HttpServiceFactory.getInstance().setIntercepter(CustomMapboxIntercepter())

Using HttpServiceIntercepterInterface also makes it easier to have arguments in the constructor which is where the above mentioned issue occurs for me. It seems it will also be possible in v11 but with a small change to the API:

Replace HttpServiceFactory.getInstance().setInterceptor with HttpServiceFactory.setHttpServiceInterceptor.

k4lk
  • 46
  • 3
0

I faced a similar problem while migrating our project from mapbox 9 to 10 . we used to create our own okHttp client with certificate pinning and interceptors and supply it to the mapbox , that was fairly easy in mapbox 9 but was not able to find an easy way to do without going the road of replacing the whole http part as suggested in there documentation .

to fix the issue temporary I used a simple concept in java were if you provide the same package structure you can replace specific classes

So I have done the following :

  • created a new package in my project (com.mapbox.common.module.okhttp)

  • searched for a class named LazyClient in the mapbox sdk

  • copied the class as is to my new directory .

  • added changes to the methods responsible for creating the okhttp client by adding certificate pinning and interceptors .

      /**
      copied from mapbox sdk with minor updates
      */
      class LazyClient {
      private final SocketFactory socketFactory;
      private final boolean disableHttp2;
    
      private byte maxRequestsPerHost = 0;
    
      private static final long DEFAULT_CONNECT_TIMEOUT_SEC = 30;
      private static final long DEFAULT_READ_TIMEOUT_SEC = 60;
    
      private volatile OkHttpClient client;
    
      private boolean shouldEnablePinning = isPinningEnabled();
    
      LazyClient(@Nullable SocketFactory socketFactory, boolean disableHttp2) {
          this.socketFactory = socketFactory;
          this.disableHttp2 = disableHttp2;
      }
    
      OkHttpClient get() {
          if (client == null) {
              synchronized (this) {
                  if (client == null) {
                      client = buildOkHttpClient(socketFactory, disableHttp2);
                      assert client != null;
                      if (maxRequestsPerHost != 0) {
                          client.dispatcher().setMaxRequestsPerHost(maxRequestsPerHost);
                      }
                  }
              }
          }
          return client;
      }
    
      synchronized void setMaxRequestsPerHost(byte max) {
          maxRequestsPerHost = max;
          if (maxRequestsPerHost != 0) {
              synchronized (this) {
                  OkHttpClient client = this.client;
                  if (client != null) {
                      client.dispatcher().setMaxRequestsPerHost(max);
                  }
              }
          }
      }
    
    
      private static OkHttpClient buildOkHttpClient(@Nullable SocketFactory socketFactory, boolean disableHttp2) {
    
    
          OkHttpClient.Builder builder =
                  CommonOkHttp.INSTANCE.builder()
                  //new OkHttpClient.Builder()
                  .eventListenerFactory(NetworkUsageListener.FACTORY)
                  .connectTimeout(DEFAULT_CONNECT_TIMEOUT_SEC, TimeUnit.SECONDS)
                  .readTimeout(DEFAULT_READ_TIMEOUT_SEC, TimeUnit.SECONDS)
                  .addNetworkInterceptor(new CustomInterceptor());
    
          if(isPinningEnabled()){
              builder.certificatePinner(certPinner);
          }
    
          if (socketFactory != null) {
              builder.socketFactory(socketFactory);
          }
          if (disableHttp2) {
              // TODO: We are forcing HTTP1.1 since we are getting the following exception for download task for big files with HTTP2:
              //       "okhttp3.internal.http2.StreamResetException: stream was reset: PROTOCOL_ERROR"
              builder.protocols(Arrays.asList(Protocol.HTTP_1_1));
          }
    
          return builder.build();
    

    }

Notes :

  • You can think of this as a temporary patch until we have a more proper way .
  • this might break in future releases of mapbox.
  • the main positive thing about this approach is that the amount of coding needed is very limited .
A.Alqadomi
  • 1,529
  • 3
  • 25
  • 33