LoginSignup
1
0

More than 1 year has passed since last update.

Flutterでプロキシ設定を検出する

Posted at

はじめに

プロキシが設定されている場合、Webブラウザの場合は設定したプロキシ設定を使って、自動に通信してくれますが、Flutterでは自分でプロキシの設定を検出して、処理します。

パッケージで解決しているのがないかなと検索したら、大体iOS/Android対応で、デスクトップ環境に対応したものを見つけられなかったので、detect_proxy_settingを作成しました。

今回、どのような手順で作ったのかをまとめてみました。

プラグイン用のプロジェクトを作成

ネイティブのコードを書く場合は、プラグインの開発となります。コマンドで簡単にプラグイン開発の雛形が作成できます。以下の例は、android,ios,macos,Windows用のひな形を作り、iosはswift、androidはkotlinで開発するように指定しています。

flutter create --org '<ネームスペース>'
 --template=plugin --platforms=android,ios,macos,windows
 -i swift -a kotlin `<パッケージ名>`

コマンドを実行すると、各環境のバージョン情報を返すネイティブのコードが書かれたプラグインが作られるので、これをもとに書き換えています。

本来であれば、この流れに従って作るんですが、自分で作成したのは、url_launcherを参考にして作りました。url_launcherは環境ごとのパッケージを用意して、まとまったものを一つのパッケージとして公開しています。

melosを使ったみたかったのもあり、パッケージを分ける構成にしています。melosは、lernaのdart版で、複数のパッケージを単一のリポジトリを管理するためのツールで、パッケージの公開やテストをまとめて処理できます。

melos test # テスト
melos publish # 公開(dry-run)

プロキシ設定の取得

まずは、デフォルトのプロキシ設定を取得する方法を紹介します。MacOSとiOSはSwiftで同じコードが使えるので、まとめています。

iOS, MacOS
guard let setting = CFNetworkCopySystemProxySettings()?.takeRetainedValue() else {
  return
}
Android
val connectivityManager = flutterPluginBinding.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val setting = connectivityManager?.getDefaultProxy()
Windows
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig;
BOOL result = WinHttpGetDefaultProxyConfiguration(&proxyConfig);
if (result == FALSE) {
  return;
}
WINHTTP_PROXY_INFO proxyInfo; 
result = WinHttpGetIEProxyConfigForCurrentUser(&proxyInfo);
if (result == FALSE) {
    return;
}

次に、URLを元にプロキシを適用するかどうか調べる方法です。

iOS, MacOS
guard let proxySetting = CFNetworkCopySystemProxySettings()?.takeRetainedValue() else {
  return
}
guard let proxies = CFNetworkCopyProxiesForURL(url, proxySetting).takeRetainedValue()
 as? [[String: Any]], proxies.count == 0 {
  return
}

Android
val proxies = ProxySelector.getDefault().select(url)
if (proxies.size == 0) return
Windows
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig;
BOOL result = WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig);
if (result == FALSE) {
  return;
}

HINTERNET hSession = WinHttpOpen(nullptr,
 WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
 WINHTTP_NO_PROXY_NAME,
 WINHTTP_NO_PROXY_BYPASS,
 WINHTTP_FLAG_ASYNC);

WINHTTP_AUTOPROXY_OPTIONS proxyOptions;
ZeroMemory(&proxyOptions, sizeof(WINHTTP_AUTOPROXY_OPTIONS));
if (proxyConfig.fAutoDetect) {
  proxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
  proxyOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
} else if(proxyConfig.lpszAutoConfigUrl) {
  proxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
  proxyOptions.lpszAutoConfigUrl = proxyConfig.lpszAutoConfigUrl;
}

WINHTTP_PROXY_INFO proxyInfo; 
result = WinHttpGetProxyForUrl(hSession, url, proxyOptions, &proxyInfo);
if (result == FALSE) {
    return;
}

テスト

各パッケージに分かれているテストを動かして、動作確認すればいいんですが、今回作ったものがネットワークを利用するもので、テストを動かすことができず、サンプルプログラムで実際に呼び出して、動作を確認する手法をとりました。一つの環境が動けば、それに合わせて出力を合わせればいいので、OSの部分でどう返すのかを調べれば、なんとかなります。

公開

各環境で動くことが確認できたので、パッケージを公開します。しかし、僕はここで大きくミスを犯しました。パッケージ名は似たようなパッケージ名は使えません。公開すると消すこともできません。僕は、dry-runで特に問題ないので、そのまま公開作業をしたら、パッケージ名が近いということで、メインのパッケージだけ拒絶され、それ以外が公開されるという自体になりました。

パッケージ名を決めるときに、最初に類似の物がないか、必ず検索してください。

結局、メインだけdetect_proxy_settingと変えて、それ以外はそのまま公開しています。各プラットフォーム別のパッケージは検索が引っかからないようにしています。

最後に

4/27にFlutter × Kotlin Multiplatform by CyberAgent #6のイベントを開催します。

Flutter for Windowsの話をして、アプリ開発をどのように行うか話す予定です。オンラインでの開催なので、お気軽にご参加ください。よろしくお願いいたします。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0