Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ devtools = [ ]
transparent = [ ]
fullscreen = [ ]
linux-body = [ "webkit2gtk/v2_40" ]
mac-proxy = [ ]

[dependencies]
libc = "0.2"
Expand Down
47 changes: 47 additions & 0 deletions examples/proxy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2020-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use wry::webview::proxy::{ProxyConfig, ProxyConnection, ProxyEndpoint, ProxyType};

fn main() -> wry::Result<()> {
use wry::{
application::{
event::{Event, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
},
webview::WebViewBuilder,
};

let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_title("Proxy Test")
.build(&event_loop)?;

let http_proxy = ProxyConnection::Http(ProxyEndpoint {
host: "localhost".to_string(),
port: "3128".to_string(),
});

let _webview = WebViewBuilder::new(window)?
.with_proxy_config(ProxyConfig {
proxy_type: ProxyType::Http,
proxy_connection: http_proxy,
})
.with_url("https://www.myip.com/")?
.build()?;

event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;

match event {
Event::NewEvents(StartCause::Init) => println!("Wry has started!"),
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
_ => (),
}
});
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,6 @@ pub enum Error {
#[cfg(target_os = "android")]
#[error(transparent)]
JniError(#[from] tao::platform::android::ndk_glue::jni::errors::Error),
#[error("Failed to create proxy endpoint")]
ProxyEndpointCreationFailed,
}
20 changes: 20 additions & 0 deletions src/webview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

//! [`WebView`] struct and associated types.

pub mod proxy;
Comment thread
dklassic marked this conversation as resolved.
Outdated
mod web_context;

pub use web_context::WebContext;
Expand Down Expand Up @@ -50,6 +51,7 @@ use windows::{Win32::Foundation::HWND, Win32::UI::WindowsAndMessaging::DestroyWi

use std::{borrow::Cow, path::PathBuf, rc::Rc};

use proxy::ProxyConfig;
pub use url::Url;

#[cfg(target_os = "windows")]
Expand Down Expand Up @@ -235,6 +237,13 @@ pub struct WebViewAttributes {

/// Set a handler closure to process page load events.
pub on_page_load_handler: Option<Box<dyn Fn(PageLoadEvent, String)>>,

/// Set a proxy configuration for the webview.
///
/// - **macOS**: Requires macOS 14.0+ and the `mac-proxy` feature flag to be enabled. Supports HTTP CONNECT and SOCKSv5 proxies.
/// - **Windows / Linux**: Supports HTTP CONNECT and SOCKSv5 proxies.
Comment thread
dklassic marked this conversation as resolved.
Outdated
/// - **Android / iOS:** Not supported.
pub proxy_config: Option<ProxyConfig>,
}

impl Default for WebViewAttributes {
Expand Down Expand Up @@ -267,6 +276,7 @@ impl Default for WebViewAttributes {
incognito: false,
autoplay: true,
on_page_load_handler: None,
proxy_config: None,
}
}
}
Expand Down Expand Up @@ -655,6 +665,16 @@ impl<'a> WebViewBuilder<'a> {
self
}

/// Set a proxy configuration for the webview.
///
/// - **macOS**: Requires macOS 14.0+ and the `mac-proxy` feature flag to be enabled. Supports HTTP CONNECT and SOCKSv5 proxies.
/// - **Windows / Linux**: Supports HTTP CONNECT and SOCKSv5 proxies.
/// - **Android / iOS:** Not supported.
pub fn with_proxy_config(mut self, configuration: ProxyConfig) -> Self {
self.webview.proxy_config = Some(configuration);
self
}

/// Consume the builder and create the [`WebView`].
///
/// Platform-specific behavior:
Expand Down
26 changes: 26 additions & 0 deletions src/webview/proxy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#[derive(Debug, Clone)]
pub enum ProxyType {
/// HTTP CONNECT
Http,
/// SOCKSv5
Socks5,
}
#[derive(Debug, Clone)]
pub struct ProxyEndpoint {
/// Proxy server host (e.g. 192.168.0.100, localhost, example.com, etc.)
pub host: String,
/// Proxy server port (e.g. 1080, 3128, etc.)
pub port: String,
}
#[derive(Debug, Clone)]
pub enum ProxyConnection {
/// Connect to proxy server via HTTP CONNECT
Http(ProxyEndpoint),
/// Connect to proxy server via SOCKSv5
Socks5(ProxyEndpoint),
}
#[derive(Debug, Clone)]
pub struct ProxyConfig {
pub proxy_type: ProxyType,
pub proxy_connection: ProxyConnection,
}
Comment thread
dklassic marked this conversation as resolved.
Outdated
25 changes: 20 additions & 5 deletions src/webview/webkitgtk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ use std::{
};
use url::Url;
use webkit2gtk::{
traits::*, AutoplayPolicy, LoadEvent, NavigationPolicyDecision, PolicyDecisionType, SettingsExt,
URIRequest, UserContentInjectedFrames, UserScript, UserScriptInjectionTime, WebView,
WebViewBuilder, WebsitePoliciesBuilder,
traits::*, AutoplayPolicy, LoadEvent, NavigationPolicyDecision, NetworkProxyMode,
NetworkProxySettings, PolicyDecisionType, SettingsExt, URIRequest, UserContentInjectedFrames,
UserScript, UserScriptInjectionTime, WebView, WebViewBuilder, WebsitePoliciesBuilder,
};
use webkit2gtk_sys::{
webkit_get_major_version, webkit_get_micro_version, webkit_get_minor_version,
Expand All @@ -29,7 +29,9 @@ pub use web_context::WebContextImpl;

use crate::{
application::{platform::unix::*, window::Window},
webview::{web_context::WebContext, PageLoadEvent, WebViewAttributes, RGBA},
webview::{
proxy::ProxyConnection, web_context::WebContext, PageLoadEvent, WebViewAttributes, RGBA,
},
Error, Result,
};

Expand Down Expand Up @@ -71,7 +73,20 @@ impl InnerWebView {
}
}
};

if let Some(proxy_setting) = attributes.proxy_config {
let proxy_uri = match proxy_setting.proxy_connection {
ProxyConnection::Http(endpoint) => format!("http://{}:{}", endpoint.host, endpoint.port),
ProxyConnection::Socks5(endpoint) => {
format!("socks5://{}:{}", endpoint.host, endpoint.port)
}
};
use webkit2gtk::WebContextExt;
if let Some(website_data_manager) = web_context.context().website_data_manager() {
let mut settings = NetworkProxySettings::new(Some(&proxy_uri.as_str()), &[]);
website_data_manager
.set_network_proxy_settings(NetworkProxyMode::Custom, Some(&mut settings));
}
}
let webview = {
let mut webview = WebViewBuilder::new();
webview = webview.user_content_manager(web_context.manager());
Expand Down
36 changes: 31 additions & 5 deletions src/webview/webview2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,24 @@

mod file_drop;

use super::proxy::ProxyConfig;
use crate::{
webview::{PageLoadEvent, WebContext, WebViewAttributes, RGBA},
webview::{proxy::ProxyConnection, PageLoadEvent, WebContext, WebViewAttributes, RGBA},
Error, Result,
};

use file_drop::FileDropController;
use url::Url;

use std::{
collections::HashSet, fmt::Write, iter::once, mem::MaybeUninit, os::windows::prelude::OsStrExt,
path::PathBuf, rc::Rc, sync::mpsc, sync::Arc,
collections::HashSet,
fmt::Write,
iter::once,
mem::MaybeUninit,
os::windows::prelude::OsStrExt,
path::PathBuf,
rc::Rc,
sync::{mpsc, Arc},
};

use once_cell::unsync::OnceCell;
Expand Down Expand Up @@ -74,7 +81,12 @@ impl InnerWebView {
let file_drop_handler = attributes.file_drop_handler.take();
let file_drop_window = window.clone();

let env = Self::create_environment(&web_context, pl_attrs.clone(), attributes.autoplay)?;
let env = Self::create_environment(
&web_context,
pl_attrs.clone(),
attributes.autoplay,
attributes.proxy_config.clone(),
Comment thread
dklassic marked this conversation as resolved.
Outdated
)?;
let controller = Self::create_controller(hwnd, &env, attributes.incognito)?;
let webview = Self::init_webview(window, hwnd, attributes, &env, &controller, pl_attrs)?;

Expand All @@ -96,6 +108,7 @@ impl InnerWebView {
web_context: &Option<&mut WebContext>,
pl_attrs: super::PlatformSpecificWebViewAttributes,
autoplay: bool,
proxy_config: Option<ProxyConfig>,
) -> webview2_com::Result<ICoreWebView2Environment> {
let (tx, rx) = mpsc::channel();

Expand Down Expand Up @@ -131,11 +144,24 @@ impl InnerWebView {
// remove "mini menu" - See https://github.com/tauri-apps/wry/issues/535
// and "smart screen" - See https://github.com/tauri-apps/tauri/issues/1345
format!(
"--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection{}",
"--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection{}{}",
if autoplay {
" --autoplay-policy=no-user-gesture-required"
} else {
""
},
if let Some(proxy_setting) = proxy_config {
match proxy_setting.proxy_connection {
ProxyConnection::Http(endpoint) => {
format!(" --proxy-server=http://{}:{}", endpoint.host, endpoint.port)
}
ProxyConnection::Socks5(endpoint) => format!(
" --proxy-server=socks5://{}:{}",
Comment thread
dklassic marked this conversation as resolved.
Outdated
endpoint.host, endpoint.port
),
}
} else {
"".to_string()
}
)
}))
Expand Down
35 changes: 35 additions & 0 deletions src/webview/wkwebview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ mod download;
#[cfg(target_os = "macos")]
mod file_drop;
mod navigation;
#[cfg(feature = "mac-proxy")]
mod proxy;
#[cfg(target_os = "macos")]
mod synthetic_mouse_events;

Expand Down Expand Up @@ -43,6 +45,14 @@ use file_drop::{add_file_drop_methods, set_file_drop_handler};
#[cfg(target_os = "ios")]
use crate::application::platform::ios::WindowExtIOS;

#[cfg(feature = "mac-proxy")]
use crate::webview::{
proxy::ProxyConnection,
wkwebview::proxy::{
nw_endpoint_t, nw_proxy_config_create_http_connect, nw_proxy_config_create_socksv5,
},
};

use crate::{
application::{
dpi::{LogicalSize, PhysicalSize},
Expand Down Expand Up @@ -314,6 +324,23 @@ impl InnerWebView {
let _preference: id = msg_send![config, preferences];
let _yes: id = msg_send![class!(NSNumber), numberWithBool:1];

#[cfg(feature = "mac-proxy")]
if let Some(proxy_config) = attributes.proxy_config {
let proxy_config = match proxy_config.proxy_connection {
ProxyConnection::Http(endpoint) => {
let nw_endpoint = nw_endpoint_t::try_from(endpoint).unwrap();
nw_proxy_config_create_http_connect(nw_endpoint, nil)
}
ProxyConnection::Socks5(endpoint) => {
let nw_endpoint = nw_endpoint_t::try_from(endpoint).unwrap();
nw_proxy_config_create_socksv5(nw_endpoint)
}
};

let proxies: id = msg_send![class!(NSArray), arrayWithObject: proxy_config];
let () = msg_send![data_store, setProxyConfigurations: proxies];
}

#[cfg(target_os = "macos")]
(*webview).set_ivar(ACCEPT_FIRST_MOUSE, attributes.accept_first_mouse);

Expand Down Expand Up @@ -1123,6 +1150,14 @@ impl NSString {
}
}

#[allow(dead_code)] // only used when `mac-proxy` feature is enabled
fn to_cstr(&self) -> *const c_char {
unsafe {
let utf_8_string = msg_send![self.0, UTF8String];
utf_8_string
}
}

fn as_ptr(&self) -> id {
self.0
}
Expand Down
67 changes: 67 additions & 0 deletions src/webview/wkwebview/proxy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2020-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use cocoa::base::nil;
use libc::c_char;

use crate::{webview::proxy::ProxyEndpoint, Error};

use super::NSString;

#[allow(non_camel_case_types)]
pub type nw_endpoint_t = *mut objc::runtime::Object;
#[allow(non_camel_case_types)]
pub type nw_relay_hop_t = *mut objc::runtime::Object;
#[allow(non_camel_case_types)]
pub type nw_protocol_options_t = *mut objc::runtime::Object;
#[allow(non_camel_case_types)]
pub type nw_proxy_config_t = *mut objc::runtime::Object;

#[link(name = "Network", kind = "framework")]
extern "C" {
#[allow(dead_code)]
fn nw_endpoint_create_url(url: *const c_char) -> nw_endpoint_t;
#[allow(dead_code)]
fn nw_endpoint_get_url(endpoint: nw_endpoint_t) -> *const c_char;
fn nw_endpoint_create_host(host: *const c_char, port: *const c_char) -> nw_endpoint_t;
#[allow(dead_code)]
fn nw_proxy_config_set_username_and_password(
proxy_config: nw_proxy_config_t,
username: *const c_char,
password: *const c_char,
);
#[allow(dead_code)]
fn nw_relay_hop_create(
http3_relay_endpoint: nw_endpoint_t,
http2_relay_endpoint: nw_endpoint_t,
relay_tls_options: nw_protocol_options_t,
) -> nw_relay_hop_t;
#[allow(dead_code)]
fn nw_proxy_config_create_relay(
first_hop: nw_relay_hop_t,
second_hop: nw_relay_hop_t,
) -> nw_proxy_config_t;
pub fn nw_proxy_config_create_socksv5(proxy_endpoint: nw_endpoint_t) -> nw_proxy_config_t;
pub fn nw_proxy_config_create_http_connect(
proxy_endpoint: nw_endpoint_t,
proxy_tls_options: nw_protocol_options_t,
) -> nw_proxy_config_t;
}
Comment thread
pewsheen marked this conversation as resolved.

impl TryFrom<ProxyEndpoint> for nw_endpoint_t {
type Error = Error;
fn try_from(endpoint: ProxyEndpoint) -> Result<Self, Error> {
unsafe {
let endpoint_host = NSString::new(&endpoint.host).to_cstr();
let endpoint_port = NSString::new(&endpoint.port).to_cstr();
let endpoint = nw_endpoint_create_host(endpoint_host, endpoint_port);

match endpoint {
#[allow(non_upper_case_globals)]
nil => Err(Error::ProxyEndpointCreationFailed),
_ => Ok(endpoint),
}
}
}
}