From 7bc0cae10f1e8b1928988a584a8c5360ac906ff9 Mon Sep 17 00:00:00 2001 From: Vinicius Fortuna Date: Tue, 17 Oct 2023 18:53:04 -0400 Subject: [PATCH] feat: expose host and port on mobileproxy (#107) * Expose host and port on mobileproxy * Update README to use new functions --- x/mobileproxy/README.md | 52 +++++++++++++++++++++++------------- x/mobileproxy/mobileproxy.go | 30 +++++++++++++++++---- 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/x/mobileproxy/README.md b/x/mobileproxy/README.md index a9115a7b..a838df8a 100644 --- a/x/mobileproxy/README.md +++ b/x/mobileproxy/README.md @@ -56,9 +56,17 @@ The header file below is an example of the Objective-C interface that Go Mobile - (nonnull instancetype)initWithRef:(_Nonnull id)ref; - (nonnull instancetype)init; /** - * Address returns the actual IP and port the server is bound to. + * Address returns the IP and port the server is bound to. */ - (NSString* _Nonnull)address; +/** + * Host returns the IP the server is bound to. + */ +- (NSString* _Nonnull)host; +/** + * Port returns the port the server is bound to. + */ +- (long)port; /** * Stop gracefully stops the proxy service, waiting for at most timeout seconds before forcefully closing it. The function takes a timeoutSeconds number instead of a [time.Duration] so it's compatible with Go Mobile. @@ -89,20 +97,20 @@ The files below are examples of the Java interface that Go Mobile generates. ```java // Code generated by gobind. DO NOT EDIT. -// Java class mobileproxy.mobileproxy is a proxy for talking to a Go program. +// Java class mobileproxy.Mobileproxy is a proxy for talking to a Go program. // // autogenerated by gobind -lang=java github.com/Jigsaw-Code/outline-sdk/x/mobileproxy package mobileproxy; import go.Seq; -public abstract class mobileproxy { +public abstract class Mobileproxy { static { Seq.touch(); // for loading the native library _init(); } - private mobileproxy() {} // uninstantiable + private Mobileproxy() {} // uninstantiable // touch is called from other bound packages to initialize this package public static void touch() {} @@ -113,11 +121,10 @@ public abstract class mobileproxy { /** * RunProxy runs a local web proxy that listens on localAddress, and uses the transportConfig to - create the [transport.StreamDialer] to use to connect to the destination from the proxy requests. + create a [transport.StreamDialer] that is used to connect to the requested destination. */ public static native Proxy runProxy(String localAddress, String transportConfig) throws Exception; } - ``` `Proxy.java`: @@ -136,7 +143,7 @@ import go.Seq; * Proxy enables you to get the actual address bound by the server and stop the service when no longer needed. */ public final class Proxy implements Seq.Proxy { - static { mobileproxy.touch(); } + static { Mobileproxy.touch(); } private final int refnum; @@ -152,11 +159,20 @@ public final class Proxy implements Seq.Proxy { private static native int __New(); /** - * Address returns the actual IP and port the server is bound to. + * Address returns the IP and port the server is bound to. */ public native String address(); /** - * Stops gracefully stops the proxy service, waiting for at most timeout seconds before forcefully closing it. + * Host returns the IP the server is bound to. + */ + public native String host(); + /** + * Port returns the port the server is bound to. + */ + public native long port(); + /** + * Stop gracefully stops the proxy service, waiting for at most timeout seconds before forcefully closing it. + The function takes a timeoutSeconds number instead of a [time.Duration] so it's compatible with Go Mobile. */ public native void stop(long timeoutSeconds); @Override public boolean equals(Object o) { @@ -193,9 +209,7 @@ On Android, you can have the following Kotlin code: ```kotlin // Use port zero to let the system pick an open port for you. val proxy = mobileproxy.runProxy("localhost:0", "split:3") -// Find the address the local proxy is bound to. -val proxyAddress = proxy.address() -// Configure your networking library with proxyAddress. +// Configure your networking library using proxy.host() and proxy.port() or proxy.address(). // ... // Stop running the proxy. proxy.stop() @@ -215,7 +229,7 @@ Dart example: ```dart HttpClient client = HttpClient(); client.findProxy = (Uri uri) { - return "PROXY localhost:1234"; + return "PROXY " + proxy.address(); }; ``` @@ -227,18 +241,18 @@ Set the proxy with [`OkHttpClient.Builder.proxy`](https://square.github.io/okhtt Kotlin example: ```kotlin -val proxy = Proxy(Proxy.Type.HTTP, InetSocketAddress("localhost", 1234)) -val client = OkHttpClient.Builder().proxy(proxy).build() +val proxyConfig = Proxy(Proxy.Type.HTTP, InetSocketAddress(proxy.host(), proxy.port())) +val client = OkHttpClient.Builder().proxy(proxyConfig).build() ``` ### JVM (Java, Kotlin) In the JVM, you can configure the proxy to use with [system properties](https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html): ```kotlin -System.setProperty("http.proxyHost", "localhost") -System.setProperty("http.proxyPort", "1234") -System.setProperty("https.proxyHost", "localhost") -System.setProperty("https.proxyPort", "1234") +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())) ``` Note that this may not fully work on Android, since it will only affect the JVM, not native code. You should also make sure you set this early in your code. diff --git a/x/mobileproxy/mobileproxy.go b/x/mobileproxy/mobileproxy.go index f413dd15..51d976a6 100644 --- a/x/mobileproxy/mobileproxy.go +++ b/x/mobileproxy/mobileproxy.go @@ -24,6 +24,7 @@ import ( "log" "net" "net/http" + "strconv" "time" "github.com/Jigsaw-Code/outline-sdk/x/config" @@ -32,13 +33,24 @@ import ( // Proxy enables you to get the actual address bound by the server and stop the service when no longer needed. type Proxy struct { - address string - server *http.Server + host string + port int + server *http.Server } -// Address returns the actual IP and port the server is bound to. +// Address returns the IP and port the server is bound to. func (p *Proxy) Address() string { - return p.address + return net.JoinHostPort(p.host, strconv.Itoa(p.port)) +} + +// Host returns the IP the server is bound to. +func (p *Proxy) Host() string { + return p.host +} + +// Port returns the port the server is bound to. +func (p *Proxy) Port() int { + return p.port } // Stop gracefully stops the proxy service, waiting for at most timeout seconds before forcefully closing it. @@ -68,5 +80,13 @@ func RunProxy(localAddress string, transportConfig string) (*Proxy, error) { server := &http.Server{Handler: httpproxy.NewConnectHandler(dialer)} go server.Serve(listener) - return &Proxy{address: listener.Addr().String(), server: server}, nil + host, portStr, err := net.SplitHostPort(listener.Addr().String()) + if err != nil { + return nil, fmt.Errorf("could not parse proxy address '%v': %v", listener.Addr().String(), err) + } + port, err := strconv.Atoi(portStr) + if err != nil { + return nil, fmt.Errorf("could not parse proxy port '%v': %v", portStr, err) + } + return &Proxy{host: host, port: port, server: server}, nil }