mapbox / mapbox-gl-native

Interactive, thoroughly customizable maps in native Android, iOS, macOS, Node.js, and Qt applications, powered by vector tiles and OpenGL
https://mapbox.com/mobile
Other
4.35k stars 1.33k forks source link

[ios] Provide a custom NSURLSessionDelegate? #11888

Closed halset closed 2 years ago

halset commented 6 years ago

I want to use Mapbox GL with a tile server that uses HTTP Basic Authentication. For that, it would be nice to have a way to provide a NSURLSessionDelegate to the NSURLSession used to download tiles.

sebastianludwig commented 6 years ago

Related to #12026

datwelk commented 6 years ago

Regarding HTTP Basic Auth, Mapbox GL JS has a transformRequest option, see:

https://github.com/mapbox/mapbox-gl-js/pull/5021 https://github.com/mapbox/mapbox-gl-js/issues/5344

As a possible alternative to a custom NSURLSessionDelegate and for feature parity, it would be nice if the iOS implementation also provides the same functionality. We could then use it to set a HTTP Basic Auth header on the tile requests.

sebastianludwig commented 6 years ago

If I'm not mistaken, the iOS SDK offers that functionality as part of the MGLOfflineStorageDelegate.

halset commented 6 years ago

@sebastianludwig it return a NSURL. As far as I know, you can not use that to take a HTTP Basic Authentication challenge, ask the user for a username/password and then encode that in the new request.

sebastianludwig commented 6 years ago

Aaah, true - never mind đź‘Ť

1ec5 commented 6 years ago

We discussed doing something similar to transformRequest based on NSURLProtocol at one point in #7485, but we wound up implementing only a way to transform the URL. I think the primary consideration at the time was cross-platform compatibility, but mapbox/mapbox-gl-js#5021 and #10948 undermine that argument.

A way to provide either an NSURLSessionDelegate or an NSURLProtocol would be nice indeed, especially for folks who need to customize caching behavior.

/cc @kkaefer @julianrex

kkaefer commented 6 years ago

A way to provide either an NSURLSessionDelegate or an NSURLProtocol would be nice indeed, especially for folks who need to customize caching behavior.

Mapbox GL does its own caching and doesn't rely on Cocoa's built-in caching. We do this because we need to tightly control resource expiration and ensure that they don't get evicted for offline packs.

datwelk commented 5 years ago

A way to provide either an NSURLSessionDelegate or an NSURLProtocol would be nice indeed, especially for folks who need to customize caching behavior.

How could we move this functionality forward? It's crucial for us to adopt Mapbox GL. I'd be happy to help out if needed. @1ec5 @kkaefer

lilykaiser commented 5 years ago

Hi @datwelk , we do not have plans to implement this in the foreseeable future because of competing priorities that we think bring value to more Mapbox developers. We consider https://mapbox.github.io/top-issues/#!mapbox/mapbox-gl-native when setting our priorities. This page takes user reactions into account, so if you’d like to raise this issue on our priority list, please vote for it using the 👍 reaction. A next step for implementing this issue would be to lay out your design ideas here so we can make sure they’re compatible with the rest of our SDK design.

datwelk commented 5 years ago

Hi @lilykaiser, thanks for your reply. The top issue on that page is from October 31 2016, with most recent activity on February 12th 2018. Hence I can only conclude that there seems to be quite some time passing by without the top issue resolved - could you clarify how indicative of internal priorities this top issues list is?

lilykaiser commented 5 years ago

Hi @datwelk , the top issues list is a big part of how we gauge user interest on GitHub, it’s just that user interest is not the only factor that guides our choices. We also take into account the size of the project, feedback from internal teams, and strategic goals. We don’t get to work on as many top issues as we like, but we do consider how much users need them and try to work them into our roadmap.

sebastianludwig commented 5 years ago

I'd be happy to implement the feature, all I need is a guiding strategy how to go about it. I know, that's still a lot to ask, especially taking the time for the PR feedback into account too. But still, please let me know if that changes anything for you.

datwelk commented 5 years ago

FWIW, for now we have worked around the limitation of not being able to set custom HTTP headers by implementing a local proxy server running on the iOS device. Tile requests specify headers encoded as a URL param to localhost, localhost transforms this param into HTTP headers and relays the request + response.

halset commented 5 years ago

Take a look at #13491 for one possible solution to this issue.

JesseCrocker commented 5 years ago

I've accomplished adding headers by swizzling NSURLSessionConfiguration defaultSessionConfiguration and returning a config that is not the default if it was requested by mapbox-gl.

halset commented 5 years ago

@JesseCrocker How did you do that? And it is cleaner than #13491?

JesseCrocker commented 5 years ago

I dont know if swizzling is cleaner, but it will let you stay on a current release rather than having to use a custom build.

#import "NSURLSessionConfiguration+Referer.h"
#import <objc/runtime.h>
#import "NSObject+DTRuntime.h"

@implementation NSURLSessionConfiguration (Referer)

+ (void)load {
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
      [[self class] swizzleClassMethod:@selector(defaultSessionConfiguration) withMethod:@selector(__swizzle_defaultSessionConfiguration)];

  });
}

+(NSURLSessionConfiguration*) __swizzle_defaultSessionConfiguration {
  NSURLSessionConfiguration * sessionConfig = [NSURLSessionConfiguration __swizzle_defaultSessionConfiguration];

  NSArray<NSString*>* stack = [NSThread callStackSymbols];
  if ([stack count] > 1 && [stack[1] containsString:@"Mapbox"]) {
    NSMutableDictionary * headerDict = [[NSMutableDictionary alloc] initWithDictionary:sessionConfig.HTTPAdditionalHeaders];
    headerDict[@"referer"] = @"XXXXX";
    sessionConfig.HTTPAdditionalHeaders = headerDict;
  }

  return sessionConfig;
}

@end

NSObject+DTRuntime is from https://github.com/Cocoanetics/DTFoundation/blob/develop/Core/Source/Runtime/NSObject%2BDTRuntime.m

halset commented 5 years ago

Thanks! I will continue to use #13491 for authentication.

stale[bot] commented 5 years ago

This issue has been automatically detected as stale because it has not had recent activity and will be archived. Thank you for your contributions.

patrickkempff commented 4 years ago

Any news on this?

JanDriesen commented 3 years ago

Any news on this?

I have solved the issue with @JesseCrocker's attempt. Create an Extension for NSURLSessionConfiguration and add DTFoundation as Pod.

//
//  NSURLSessionConfiguration+Authorization.m
//  CartoDemo
//
//  Created by Jan Driesen on 03.08.20.
//  Copyright © 2020 Driesengard. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "NSURLSessionConfiguration+Authorization.h"
#import <objc/runtime.h>
#import "NSObject+DTRuntime.h"

@implementation NSURLSessionConfiguration (Authorization)

// "register the swizzle" at load
+ (void) load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [[self class] swizzleClassMethod:@selector(defaultSessionConfiguration) withMethod:@selector(__swizzle_defaultSessionConfiguration)];
    });
}

// Overrides the [NSURLSessionConfiguratuion defaultSessionConfiguration] method at runtime!
// this method gets used and adds an Authorization HTTP header field to the Requests made by Mapbox
+ (NSURLSessionConfiguration*) __swizzle_defaultSessionConfiguration
{
    NSURLSessionConfiguration * sessionConfig = [NSURLSessionConfiguration __swizzle_defaultSessionConfiguration];

    NSArray<NSString*>* stack = [NSThread callStackSymbols];
    if ([stack count] > 1 && [stack[1] containsString:@"Mapbox"]) {
        // here we add our authentification to get the correct responses from the server
        // TODO: replace the hardcoded values -> get the values from the bridging headers
        NSMutableDictionary * headerDict = [[NSMutableDictionary alloc] initWithDictionary:sessionConfig.HTTPAdditionalHeaders];
        headerDict[@"Authorization"] = [NSURLSessionConfiguration makeBase64AuthorizationForUser: @"username" password:@"somepassword"];
        sessionConfig.HTTPAdditionalHeaders = headerDict;
    }

    return sessionConfig;
}

// returns an HTTP Basic Authentification String for the user and password
+ (NSString *) makeBase64AuthorizationForUser:(NSString *)user password:(NSString *)pass
{
    NSString *authString = [NSString stringWithFormat:@"%@:%@", user, pass];
    NSString *auth64String = [[authString dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0];
    return [NSString stringWithFormat:@"Basic %@", auth64String];
}

@end