Skip to content

feat: add native HTTP and HTTPS proxy - #64

Open
marutichintan wants to merge 1 commit into
AceDroidX:masterfrom
marutichintan:codex/native-http-proxy
Open

feat: add native HTTP and HTTPS proxy#64
marutichintan wants to merge 1 commit into
AceDroidX:masterfrom
marutichintan:codex/native-http-proxy

Conversation

@marutichintan

Copy link
Copy Markdown

Add http and https proxy service in settings.

@AceDroidX

Copy link
Copy Markdown
Owner

Thank you for submitting this PR. However, I do not think this feature is suitable for inclusion in this project.

The primary purpose of this project is to manage and run frpc and frps. frp already provides a built-in http_proxy client plugin with support for:

  • HTTP forward proxying;
  • HTTPS CONNECT tunneling;
  • HTTP proxy username and password authentication;
  • Integration with TCP proxies, STCP visitors, and other frp features.

Therefore, the main use cases implemented by this PR can already be covered using native frp configurations, without maintaining another proxy server inside the application.

In addition, this PR would introduce an NDK/CMake/JNI/C++ implementation independent of frp. The project would need to maintain its own HTTP request parsing, DNS resolution, socket forwarding, concurrent connection handling, and HTTPS CONNECT implementation.

The current implementation also has several limitations:

  • The listening port is fixed to 8080.
  • It always listens on all network interfaces.
  • It does not support proxy authentication.
  • It does not support SOCKS5.
  • It may become an unauthenticated open proxy on an untrusted network.
  • It substantially duplicates functionality already maintained upstream by frp.

The following compares this PR with three native frp configurations. There are four deployment modes in total.

Mode 1 can replace the functionality of this PR, while Modes 2 and 3 cover two additional deployment scenarios.


This PR: native proxy on Android

Traffic flow

Client
  → Android:8080
  → Native C++ HTTP proxy on Android
  → Destination website

The connection to the destination website is created directly by the Android device:

Exit IP = Android device public IP

This implementation does not use frpc or frps, and it cannot move the network exit to a remote server.


Mode 1: frps and frpc + http_proxy both running on Android

This mode is suitable when:

  • Clients connect directly to a proxy port on the Android device.
  • The actual HTTP proxy runs on Android.
  • The Android network should be used as the exit.

Traffic flow

Client
  → Android:8080
  → frps
  → frpc/http_proxy on the same Android device
  → Destination website

Android: frps.toml

# Only accept the local frpc control connection.
bindAddr = "127.0.0.1"
bindPort = 7000

# Expose proxy ports to the LAN.
proxyBindAddr = "0.0.0.0"

auth.method = "token"
auth.token = "replace-with-a-strong-frp-token"

Android: frpc.toml

serverAddr = "127.0.0.1"
serverPort = 7000

auth.method = "token"
auth.token = "replace-with-a-strong-frp-token"

[[proxies]]
name = "android-http-proxy"
type = "tcp"
remotePort = 8080

[proxies.plugin]
type = "http_proxy"
httpUser = "proxyuser"
httpPassword = "replace-with-a-strong-proxy-password"

Client configuration

HTTP proxy host: Android LAN IP
Port: 8080
Username: proxyuser
Password: replace-with-a-strong-proxy-password

Because the http_proxy plugin runs on Android:

Exit IP = Android device public IP

This configuration provides the same essential functionality as this PR while reusing frp's existing proxy implementation and authentication support.


Mode 2: remote frps with frpc + http_proxy running on Android

This mode is suitable when:

  • Clients connect to a proxy port on a remote server.
  • The actual HTTP proxy runs on Android.
  • The Android network should be used as the exit.

Traffic flow

Client
  → Remote server:6000
  → frps
  → frp tunnel
  → http_proxy plugin in Android frpc
  → Destination website

Remote server: frps.toml

bindAddr = "0.0.0.0"
bindPort = 7000

auth.method = "token"
auth.token = "replace-with-a-strong-frp-token"

Android: frpc.toml

serverAddr = "VPS_PUBLIC_IP"
serverPort = 7000

auth.method = "token"
auth.token = "replace-with-a-strong-frp-token"

[[proxies]]
name = "android-http-proxy"
type = "tcp"
remotePort = 6000

[proxies.plugin]
type = "http_proxy"
httpUser = "proxyuser"
httpPassword = "replace-with-a-strong-proxy-password"

Client configuration

HTTP proxy host: VPS_PUBLIC_IP
Port: 6000
Username: proxyuser
Password: replace-with-a-strong-proxy-password

Although the client connects to the remote server, the http_proxy plugin actually runs on Android:

Exit IP = Android device public IP

Mode 3: local proxy endpoint on Android with the remote server as the exit

This mode is suitable when:

  • LAN clients connect to ANDROID_IP:8080.
  • Traffic is transported to a remote server through an frp tunnel.
  • The remote server connects to destination websites.
  • The final exit IP should be the remote server IP.

Traffic flow

LAN client
  → Android:8080
  → STCP visitor in Android frpc
  → Remote frps
  → http_proxy plugin in remote frpc
  → Destination website

Remote server: frps.toml

bindAddr = "0.0.0.0"
bindPort = 7000

auth.method = "token"
auth.token = "replace-with-a-strong-frp-token"

Remote server: exit-side frpc.toml

user = "proxy-exit"

serverAddr = "127.0.0.1"
serverPort = 7000

auth.method = "token"
auth.token = "replace-with-a-strong-frp-token"

[[proxies]]
name = "vps-http-exit"
type = "stcp"
secretKey = "replace-with-a-strong-stcp-secret"

[proxies.plugin]
type = "http_proxy"
httpUser = "proxyuser"
httpPassword = "replace-with-a-strong-proxy-password"

Android: visitor-side frpc.toml

user = "proxy-exit"

serverAddr = "VPS_PUBLIC_IP"
serverPort = 7000

auth.method = "token"
auth.token = "replace-with-a-strong-frp-token"

[[visitors]]
name = "vps-http-exit-visitor"
type = "stcp"
serverName = "vps-http-exit"
secretKey = "replace-with-a-strong-stcp-secret"

bindAddr = "0.0.0.0"
bindPort = 8080

LAN client configuration

HTTP proxy host: Android LAN IP
Port: 8080
Username: proxyuser
Password: replace-with-a-strong-proxy-password

Because the actual http_proxy plugin runs on the remote server:

Exit IP = Remote server public IP

If the proxy should only be available to the Android device itself, the visitor can instead use:

bindAddr = "127.0.0.1"

Comparison

Mode Client connects to HTTP proxy actually runs on Exit IP Proxy authentication
This PR Android:8080 Native C++ service on Android Android Not supported
Android frps + Android frpc plugin Android:8080 Android Android Supported
Remote frps + Android frpc plugin Remote server:6000 Android Android Supported
Android STCP visitor + remote frpc plugin Android:8080 Remote server Remote server Supported

The three native frp configurations already cover:

  1. Providing a local or LAN proxy endpoint on Android with Android as the exit;
  2. Exposing an Android-hosted proxy through a remote frps, while retaining Android as the exit;
  3. Providing a local or LAN proxy endpoint on Android while using a remote server as the exit.

The one-click experience provided by this PR is useful. However, I believe the more appropriate implementation would be to help users generate and manage the corresponding frp configurations through the application UI, rather than adding and maintaining another proxy server implementation.

For these reasons, I will not merge this PR. Thank you again for the contribution and for exploring this use case.


中文

感谢你提交这个 PR,但是我认为这个功能不适合合并到本项目中。

本项目的主要定位是管理和运行 frpcfrps,而 frp 本身已经提供了内置的 http_proxy 客户端插件,支持:

  • HTTP 正向代理;
  • HTTPS CONNECT 隧道;
  • HTTP 代理用户名和密码认证;
  • 与 TCP、STCP visitor 等 frp 功能组合使用。

因此,本 PR 实现的主要使用场景已经可以通过 frp 原生配置完成,不需要在应用中另外维护一套代理服务器。

此外,本 PR 会给项目增加一套独立于 frp 的 NDK/CMake/JNI/C++ 实现,需要自行维护 HTTP 请求解析、DNS、套接字转发、并发连接和 HTTPS CONNECT 等逻辑。

当前实现还存在以下限制:

  • 端口固定为 8080
  • 固定监听所有网络接口;
  • 没有代理认证;
  • 没有 SOCKS5 支持;
  • 在不可信网络中可能成为未认证的开放代理;
  • 与 frp 上游已经维护的功能存在较大重复。

下面对本 PR 与三种 frp 原生方案进行比较,共四种实现方式。

方式一即可替代本 PR 的功能,方式二和方式三则覆盖了另外两种使用场景。


本 PR:Android 原生代理

流量路径

客户端
  → Android:8080
  → Android 上的原生 C++ HTTP 代理
  → 目标网站

最终访问目标网站的连接由 Android 设备直接发起:

出口 IP = Android 设备的公网 IP

这个实现不经过 frpcfrps,也不能将流量出口切换到远程服务器。


方式一:Android 同时运行 frps 和 frpc + http_proxy

这个方案适合:

  • 客户端直接连接 Android 的代理端口;
  • 实际 HTTP 代理运行在 Android;
  • 使用 Android 的网络作为出口。

流量路径

客户端
  → Android:8080
  → frps
  → 同一 Android 设备上的 frpc/http_proxy
  → 目标网站

Android:frps.toml

# 仅接收本机 frpc 的控制连接。
bindAddr = "127.0.0.1"
bindPort = 7000

# 向局域网开放代理端口。
proxyBindAddr = "0.0.0.0"

auth.method = "token"
auth.token = "replace-with-a-strong-frp-token"

Android:frpc.toml

serverAddr = "127.0.0.1"
serverPort = 7000

auth.method = "token"
auth.token = "replace-with-a-strong-frp-token"

[[proxies]]
name = "android-http-proxy"
type = "tcp"
remotePort = 8080

[proxies.plugin]
type = "http_proxy"
httpUser = "proxyuser"
httpPassword = "replace-with-a-strong-proxy-password"

客户端配置

HTTP 代理地址:Android 设备的局域网 IP
端口:8080
用户名:proxyuser
密码:replace-with-a-strong-proxy-password

因为 http_proxy 插件运行在 Android 设备:

出口 IP = Android 设备的公网 IP

该配置可以提供与本 PR 相同的核心功能,同时复用 frp 已有的代理实现和认证能力。


方式二:远程运行 frps,Android 运行 frpc + http_proxy

这个方案适合:

  • 客户端连接远程服务器的代理端口;
  • 实际 HTTP 代理运行在 Android;
  • 使用 Android 的网络作为出口。

流量路径

客户端
  → 远程服务器:6000
  → frps
  → frp 隧道
  → Android frpc 的 http_proxy 插件
  → 目标网站

远程服务器:frps.toml

bindAddr = "0.0.0.0"
bindPort = 7000

auth.method = "token"
auth.token = "replace-with-a-strong-frp-token"

Android:frpc.toml

serverAddr = "VPS_PUBLIC_IP"
serverPort = 7000

auth.method = "token"
auth.token = "replace-with-a-strong-frp-token"

[[proxies]]
name = "android-http-proxy"
type = "tcp"
remotePort = 6000

[proxies.plugin]
type = "http_proxy"
httpUser = "proxyuser"
httpPassword = "replace-with-a-strong-proxy-password"

客户端配置

HTTP 代理地址:VPS_PUBLIC_IP
端口:6000
用户名:proxyuser
密码:replace-with-a-strong-proxy-password

虽然客户端连接的是远程服务器,但 http_proxy 插件实际运行在 Android:

出口 IP = Android 设备的公网 IP

方式三:Android 提供本地代理入口,远程服务器作为出口

这个方案适合:

  • 局域网客户端连接 ANDROID_IP:8080
  • 流量通过 frp 隧道发送到远程服务器;
  • 远程服务器负责访问目标网站;
  • 最终出口为远程服务器 IP。

流量路径

局域网客户端
  → Android:8080
  → Android frpc 的 STCP visitor
  → 远程 frps
  → 远程 frpc 的 http_proxy 插件
  → 目标网站

远程服务器:frps.toml

bindAddr = "0.0.0.0"
bindPort = 7000

auth.method = "token"
auth.token = "replace-with-a-strong-frp-token"

远程服务器:出口端 frpc.toml

user = "proxy-exit"

serverAddr = "127.0.0.1"
serverPort = 7000

auth.method = "token"
auth.token = "replace-with-a-strong-frp-token"

[[proxies]]
name = "vps-http-exit"
type = "stcp"
secretKey = "replace-with-a-strong-stcp-secret"

[proxies.plugin]
type = "http_proxy"
httpUser = "proxyuser"
httpPassword = "replace-with-a-strong-proxy-password"

Android:访问端 frpc.toml

user = "proxy-exit"

serverAddr = "VPS_PUBLIC_IP"
serverPort = 7000

auth.method = "token"
auth.token = "replace-with-a-strong-frp-token"

[[visitors]]
name = "vps-http-exit-visitor"
type = "stcp"
serverName = "vps-http-exit"
secretKey = "replace-with-a-strong-stcp-secret"

bindAddr = "0.0.0.0"
bindPort = 8080

局域网客户端配置

HTTP 代理地址:Android 设备的局域网 IP
端口:8080
用户名:proxyuser
密码:replace-with-a-strong-proxy-password

因为真正的 http_proxy 插件运行在远程服务器:

出口 IP = 远程服务器的公网 IP

如果代理只允许 Android 设备本机使用,可以将 visitor 改为:

bindAddr = "127.0.0.1"

四种方式对比

方式 客户端连接地址 HTTP 代理实际运行位置 出口 IP 代理认证
本 PR Android:8080 Android 原生 C++ 服务 Android 不支持
Android frps + Android frpc 插件 Android:8080 Android Android 支持
远程 frps + Android frpc 插件 远程服务器:6000 Android Android 支持
Android STCP visitor + 远程 frpc 插件 Android:8080 远程服务器 远程服务器 支持

由此可见,frp 原生的三种组合已经覆盖了:

  1. 在 Android 上提供本地或局域网代理入口,并使用 Android 作为出口;
  2. 通过远程 frps 暴露 Android 上的代理,并继续使用 Android 作为出口;
  3. 在 Android 上提供本地或局域网代理入口,同时使用远程服务器作为出口。

本 PR 的一键开关体验确实比较方便,但我认为更合适的实现方式是在应用界面中帮助用户生成和管理上述 frp 配置,而不是在项目中增加并长期维护另一套代理服务器。

基于以上原因,我不会合并这个 PR。再次感谢你的贡献和对这个使用场景的探索。

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.

3 participants