Adicionar o SDK do Outline ao seu app para dispositivos móveis

Este documento descreve como integrar o SDK do Outline aos seus aplicativos para dispositivos móveis, com foco na biblioteca MobileProxy para gerenciamento de proxy local simplificado.

MobileProxy é uma biblioteca baseada em Go projetada para simplificar a integração de funcionalidades de proxy em apps para dispositivos móveis. Ele usa o Go Mobile para gerar bibliotecas móveis, permitindo que você configure as bibliotecas de rede do app para rotear o tráfego por um proxy local.

App sem MobileProxy

App de conteúdo sem MobileProxy

App com MobileProxy

App de conteúdo com o MobileProxy

Etapa 1: criar bibliotecas móveis do MobileProxy

Use o gomobile para compilar o código Go em bibliotecas para Android e iOS.

  1. Clone o repositório do SDK Outline:

    git clone https://github.com/Jigsaw-Code/outline-sdk.git
    cd outline-sdk/x
    
  2. Crie os binários do Go Mobile com o go build:

    go build -o "$(pwd)/out/" golang.org/x/mobile/cmd/gomobile golang.org/x/mobile/cmd/gobind
    

    Como adicionar suporte ao Psiphon

    Para adicionar suporte ao uso da rede Psiphon, siga estas etapas extras:

    • Entre em contato com a equipe do Psiphon para receber uma configuração que dê acesso à rede. Isso pode exigir um contrato.
    • Adicione a configuração do Psiphon recebida à seção fallback da configuração SmartDialer.
    • Crie o proxy para dispositivos móveis usando a flag -tags psiphon:

      go build -tags psiphon -o "$(pwd)/out/" golang.org/x/mobile/cmd/gomobile golang.org/x/mobile/cmd/gobind
      

    A flag -tags psiphon é necessária porque a base de código do Psiphon é licenciada pela GPL, que pode impor restrições de licença ao seu próprio código. Talvez você queira receber uma licença especial deles.

  3. Gere bibliotecas para dispositivos móveis e adicione-as ao projeto:

    Android

    PATH="$(pwd)/out:$PATH" gomobile bind -ldflags='-s -w' -target=android -androidapi=21 -o "$(pwd)/out/mobileproxy.aar" github.com/Jigsaw-Code/outline-sdk/x/mobileproxy
    

    No Android Studio, selecione File > Import Project… para importar o pacote out/mobileproxy.aar gerado. Para mais ajuda, consulte Como criar e implantar para Android no Go Mobile.

    iOS

    PATH="$(pwd)/out:$PATH" gomobile bind -ldflags='-s -w' -target=ios -iosversion=11.0 -o "out/mobileproxy.xcframework" github.com/Jigsaw-Code/outline-sdk/x/mobileproxy
    

    Arraste o pacote out/mobileproxy.xcframework para o projeto do Xcode. Para mais ajuda, consulte Como criar e implantar para iOS no Go Mobile.

Etapa 2: executar o MobileProxy

Inicialize e inicie o proxy local MobileProxy no ambiente de execução do app. É possível usar uma configuração de transporte estático ou o Proxy inteligente para selecionar uma estratégia dinâmica.

  • Configuração de transporte estático: use a função RunProxy com um endereço local e uma configuração de transporte.

    Android

    import mobileproxy.*
    
    val dialer = StreamDialer("split:3")
    
    // Use port zero to let the system pick an open port for you.
    val proxy = Mobileproxy.runProxy("localhost:0", dialer)
    // Configure your networking library using proxy.host() and proxy.port() or proxy.address().
    // ...
    // Stop running the proxy.
    proxy.stop()
    

    iOS

    import Mobileproxy
    
    let dialer = MobileproxyStreamDialer("split:3")
    
    // Use port zero to let the system pick an open port for you.
    let proxy = MobileproxyRunProxy("localhost:0", dialer)
    // Configure your networking library using proxy.host() and proxy.port() or proxy.address().
    // ...
    // Stop running the proxy.
    proxy.stop()
    
  • Proxy inteligente: o proxy inteligente seleciona dinamicamente estratégias de DNS e TLS com base em domínios de teste especificados. É necessário especificar a estratégia de configuração no formato YAML (exemplo).

    Android

    val testDomains = Mobileproxy.newListFromLines("www.youtube.com\ni.ytimg.com")
    val strategiesConfig = "..."  // Config YAML.
    val dialer = Mobileproxy.newSmartStreamDialer(testDomains, strategiesConfig, Mobileproxy.newStderrLogWriter())
    
    // Use port zero to let the system pick an open port for you.
    val proxy = Mobileproxy.runProxy("localhost:0", dialer)
    // Configure your networking library using proxy.host() and proxy.port() or proxy.address().
    // ...
    // Stop running the proxy.
    proxy.stop()
    

    iOS

    import Mobileproxy
    
    var dialerError: NSError?
    let testDomains = MobileproxyNewListFromLines("www.youtube.com\ni.ytimg.com")
    let strategiesConfig = "..."  // Config YAML.
    let dialer = MobileproxyNewSmartStreamDialer(
        testDomains,
        strategiesConfig,
        MobileproxyNewStderrLogWriter(),
        &dialerError
    )
    
    var proxyError: NSError?
    // Use port zero to let the system pick an open port for you.
    MobileproxyRunProxy("localhost:0", dialer, &proxyError)
    // Configure your networking library using proxy.host() and proxy.port() or proxy.address().
    // ...
    // Stop running the proxy.
    proxy.stop()
    

Etapa 3: configurar clientes HTTP e bibliotecas de rede

Configure as bibliotecas de rede para usar o endereço e a porta do proxy local.

HttpClient do Dart/Flutter

Defina o proxy com HttpClient.findProxy.

HttpClient client = HttpClient();
client.findProxy = (Uri uri) {
  return "PROXY " + proxy.address();
};

OkHttp (Android)

Defina o proxy com OkHttpClient.Builder.proxy.

val proxyConfig = Proxy(Proxy.Type.HTTP, InetSocketAddress(proxy.host(), proxy.port()))
val client = OkHttpClient.Builder().proxy(proxyConfig).build()

JVM (Java, Kotlin)

Configure o proxy para uso com as propriedades do sistema:

System.setProperty("http.proxyHost", proxy.host())
System.setProperty("http.proxyPort", String.valueOf(proxy.port()))
System.setProperty("https.proxyHost", proxy.host())
System.setProperty("https.proxyPort", String.valueOf(proxy.port()))

Android Web View

Aplique uma configuração de proxy a todas as visualizações da Web no seu aplicativo com a biblioteca androidx.webview:

ProxyController.getInstance()
    .setProxyOverride(
        ProxyConfig.Builder()
            .addProxyRule(this.proxy!!.address())
            .build(),
        {}, // execution context for the following callback - do anything needed here once the proxy is applied, like refreshing web views
        {} // callback to be called once the ProxyConfig is applied
    )

Visualização da Web no iOS

A partir do iOS 17, é possível adicionar uma configuração de proxy a um WKWebView usando a propriedade WKWebsiteDataStore:

let configuration = WKWebViewConfiguration()
let endpoint = NWEndpoint.hostPort(host: NWEndpoint.Host(proxyHost), port: NWEndpoint.Port(proxyPort)!)
let proxyConfig = ProxyConfiguration.init(httpCONNECTProxy: endpoint)
let websiteDataStore = WKWebsiteDataStore.default()
websiteDataStore.proxyConfigurations = [proxyConfig]
let webview = WKWebView(configuration: configuration)

Avançado: gerar uma biblioteca para dispositivos móveis personalizada

Para casos de uso avançados, você pode gerar suas próprias bibliotecas para dispositivos móveis:

  1. Criar uma biblioteca Go: desenvolva um pacote Go que agrupe as funcionalidades necessárias do SDK.
  2. Gerar bibliotecas para dispositivos móveis: use gomobile bind para produzir arquivos do Android (AARs) e frameworks da Apple. Exemplos:
  3. Integrar no app: adicione a biblioteca gerada ao seu aplicativo para dispositivos móveis.