tauri_plugin_http/lib.rs
1// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5//! Access the HTTP client written in Rust.
6//!
7//! ## Cargo features
8//!
9//! ### Reqwest feature forwards
10//!
11//! These features forwards [`reqwest`](https://docs.rs/reqwest/0.13/reqwest/index.html) features:
12//!
13//! - **http2** *(enabled by default)*: Enables HTTP/2 support.
14//! - **native-tls**: Enables TLS functionality provided by native-tls, including ALPN.
15//! - **native-tls-vendored**: Enables the vendored feature of native-tls.
16//! - **rustls-tls** *(enabled by default)*: Enables TLS functionality provided by rustls, verifying
17//! certificates with the platform verifier. Uses `ring` as the crypto provider, installed as the
18//! process default only if none is installed yet.
19//! - **blocking**: Provides the [blocking](https://docs.rs/reqwest/0.13/reqwest/blocking/index.html) client API.
20//! - **charset** *(enabled by default)*: Improved support for decoding text.
21//! - **cookies** *(enabled by default)*: Provides cookie session support.
22//! - **gzip**: Provides response body gzip decompression.
23//! - **brotli**: Provides response body brotli decompression.
24//! - **zstd**: Provides response body zstd decompression.
25//! - **deflate**: Provides response body deflate decompression.
26//! - **json**: Provides serialization and deserialization for JSON bodies.
27//! - **multipart**: Provides functionality for multipart forms.
28//! - **stream**: Adds support for futures::Stream.
29//! - **socks**: Provides SOCKS5 proxy support.
30//! - **hickory-dns**: Enables a Hickory DNS async resolver instead of the default threadpool using
31//! getaddrinfo.
32//! - **system-proxy** *(enabled by default)*: Use Windows and macOS system proxy settings automatically.
33//!
34//! ### tauri-plugin-http features
35//!
36//! - **tracing**: Adds request, response, and cookie-store diagnostics through `tracing`.
37//! - **unsafe-headers**: Allows webview requests to send any headers.
38//! - **dangerous-settings**: Allows dangerous client settings such as accepting invalid certificates or hostnames.
39//!
40//! ## Security
41//!
42//! The URL scope is checked on every hop of a redirect chain, not only on the URL requested by
43//! the frontend, so every redirect target must also be allowed by the scope. Otherwise a server on
44//! an allowed origin could redirect the request to any other origin - a `localhost` service, an
45//! internal host or a cloud metadata endpoint - and hand its response to the webview.
46
47use tauri::{
48 Manager, Runtime,
49 plugin::{Builder, TauriPlugin},
50};
51
52pub use error::{Error, Result};
53
54mod commands;
55mod error;
56#[cfg(feature = "cookies")]
57mod reqwest_cookie_store;
58mod scope;
59
60#[cfg(feature = "cookies")]
61const COOKIES_FILENAME: &str = ".cookies";
62
63pub(crate) struct Http {
64 #[cfg(feature = "cookies")]
65 cookies_jar: std::sync::Arc<crate::reqwest_cookie_store::CookieStoreMutex>,
66}
67
68pub fn init<R: Runtime>() -> TauriPlugin<R> {
69 Builder::new("http")
70 .setup(|app, _api| {
71 #[cfg(feature = "cookies")]
72 let cookies_jar = {
73 use crate::reqwest_cookie_store::*;
74 use std::fs::File;
75 use std::io::BufReader;
76
77 let cache_dir = app.path().app_cache_dir()?;
78 std::fs::create_dir_all(&cache_dir)?;
79
80 let path = cache_dir.join(COOKIES_FILENAME);
81 let file = File::options()
82 .create(true)
83 .append(true)
84 .read(true)
85 .open(&path)?;
86
87 let reader = BufReader::new(file);
88 CookieStoreMutex::load(path.clone(), reader).unwrap_or_else(|_e| {
89 #[cfg(feature = "tracing")]
90 tracing::warn!(
91 "failed to load cookie store: {_e}, falling back to empty store"
92 );
93 CookieStoreMutex::new(path, Default::default())
94 })
95 };
96
97 let state = Http {
98 #[cfg(feature = "cookies")]
99 cookies_jar: std::sync::Arc::new(cookies_jar),
100 };
101
102 app.manage(state);
103
104 Ok(())
105 })
106 .on_event(|app, event| {
107 #[cfg(feature = "cookies")]
108 if let tauri::RunEvent::Exit = event {
109 let state = app.state::<Http>();
110
111 match state.cookies_jar.request_save() {
112 Ok(rx) => {
113 let _ = rx.recv();
114 }
115 Err(_e) => {
116 #[cfg(feature = "tracing")]
117 tracing::error!("failed to save cookie jar: {_e}");
118 }
119 }
120 }
121
122 #[cfg(not(feature = "cookies"))]
123 let _ = (app, event);
124 })
125 .invoke_handler(tauri::generate_handler![
126 commands::fetch,
127 commands::fetch_cancel,
128 commands::fetch_send,
129 commands::fetch_read_body,
130 commands::fetch_cancel_body,
131 ])
132 .build()
133}