Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic proxy url #20

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

OliverGesch
Copy link

Hi Blaugold,
I am using your client library for my Flutter app and since Unsplash does not allow storing the secret and access key on the user devices I built a small cloud authentication proxy.
Your client library currently does not allow to change the URL and omit the credentials parameter. I did some minor changes to

  1. make the URL a parameter (proxyBaseUrl) but keep the default Unsplash URL in case no URL is provided,
  2. make the unsplashCredentials parameter of the Settings class optional including a null check when adding the credentials to the HTTP header,
  3. introduced a bearerToken parameter for situations when the proxy requires authentication as well,
  4. introduced a new test case to make sure that a missing credentials parameter is not breaking any of the functionality,
  5. updated the readme accordingly.

I am new to Dart programming so let me know if there are things I could improve in the code.
Best regards
Oliver

@blaugold
Copy link
Owner

@OliverGesch Thanks for taking the time to create a PR.

I think you can already achieve what you need without requiring any changes to this package:

import 'dart:io';

import 'package:http/http.dart';
import 'package:unsplash_client/unsplash_client.dart';

class ProxyHttpClient extends BaseClient {
  ProxyHttpClient({
    required this.baseUrl,
    required this.proxyBaseUrl,
    this.bearerToken,
    Client? httpClient,
  }) : _httpClient = httpClient ?? Client();

  final String baseUrl;
  final String proxyBaseUrl;
  final String? bearerToken;

  final Client _httpClient;

  @override
  Future<StreamedResponse> send(BaseRequest request) {
    // Replace the base URL with the proxy base URL.
    final proxyUrl = Uri.parse(
      request.url.toString().replaceFirst(baseUrl, proxyBaseUrl),
    );

    final proxyRequest = _copyRequest(request, url: proxyUrl, headers: {
      // Add the headers from the original request, except for the
      // authorization header.
      ...Map.from(request.headers)..remove(HttpHeaders.authorizationHeader),
      // If a bearer token was provided, add it to the request.
      if (bearerToken != null)
        HttpHeaders.authorizationHeader: 'Bearer $bearerToken'
    });

    return _httpClient.send(proxyRequest);
  }

  @override
  void close() => _httpClient.close();

  StreamedRequest _copyRequest(
    BaseRequest original, {
    required Uri? url,
    required Map<String, String>? headers,
  }) {
    final request = StreamedRequest(original.method, url ?? original.url)
      ..contentLength = original.contentLength
      ..followRedirects = original.followRedirects
      ..headers.addAll(headers ?? original.headers)
      ..maxRedirects = original.maxRedirects
      ..persistentConnection = original.persistentConnection;

    original.finalize().listen(
          request.sink.add,
          onError: request.sink.addError,
          onDone: request.sink.close,
          cancelOnError: true,
        );

    return request;
  }
}

final unsplashClient = UnsplashClient(
  settings: ClientSettings(
    // Not used, but required.
    credentials: AppCredentials(accessKey: ''),
  ),
  httpClient: ProxyHttpClient(
    baseUrl: 'https://api.unsplash.com/',
    proxyBaseUrl: 'https://unsplash-auth-proxy.example.com/',
    bearerToken: '*********',
  ),
);

@OliverGesch
Copy link
Author

@blaugold Thank you very much for the code! Really appreciate it. I just tried it and it works just as expected. Maybe asking you first on how to solve my challenge would have saved me some time ;)
But anyway, I learned a lot by going through your code and preparing this PR. No wasted time!

Do you think it might be helpful to keep your example code somewhere in this repo for other people facing the same challenge?

Anyway, feel free to close and reject my PR. It is not required anymore. Or let me know if I should close it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants