Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: custom protocol #2503

Merged
merged 16 commits into from
Aug 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/refactor-register-uri-scheme-protocol.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"tauri": patch
"tauri-runtime": patch
---

**Breaking change:** Removed `register_uri_scheme_protocol` from the `WebviewAttibutes` struct and renamed `register_global_uri_scheme_protocol` to `register_uri_scheme_protocol` on the `Builder` struct, which now takes a `Fn(&AppHandle, &http::Request) -> http::Response` closure.
7 changes: 7 additions & 0 deletions .changes/tauri-protocol.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"tauri": minor
"tauri-runtime": minor
"tauri-runtime-wry": minor
---

Migrate to latest custom protocol allowing `Partial content` streaming and Header parsing.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,4 @@ __handlers__/

# benches
gh-pages
test_video.mp4
lemarier marked this conversation as resolved.
Show resolved Hide resolved
91 changes: 80 additions & 11 deletions core/tauri-runtime-wry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
//! The [`wry`] Tauri [`Runtime`].

use tauri_runtime::{
http::{
Request as HttpRequest, RequestParts as HttpRequestParts, Response as HttpResponse,
ResponseParts as HttpResponseParts,
},
menu::{CustomMenuItem, Menu, MenuEntry, MenuHash, MenuItem, MenuUpdate, Submenu},
monitor::Monitor,
webview::{
Expand Down Expand Up @@ -54,6 +58,10 @@ use wry::{
monitor::MonitorHandle,
window::{Fullscreen, Icon as WindowIcon, UserAttentionType as WryUserAttentionType},
},
http::{
Request as WryHttpRequest, RequestParts as WryRequestParts, Response as WryHttpResponse,
ResponseParts as WryResponseParts,
},
webview::{
FileDropEvent as WryFileDropEvent, RpcRequest as WryRpcRequest, RpcResponse, WebContext,
WebView, WebViewBuilder,
Expand Down Expand Up @@ -95,9 +103,6 @@ mod system_tray;
#[cfg(feature = "system-tray")]
use system_tray::*;

mod mime_type;
use mime_type::MimeType;

type WebContextStore = Mutex<HashMap<Option<PathBuf>, WebContext>>;
// window
type WindowEventHandler = Box<dyn Fn(&WindowEvent) + Send>;
Expand Down Expand Up @@ -152,6 +157,72 @@ struct EventLoopContext {
proxy: EventLoopProxy<Message>,
}

struct HttpRequestPartsWrapper(HttpRequestParts);

impl From<HttpRequestPartsWrapper> for HttpRequestParts {
fn from(parts: HttpRequestPartsWrapper) -> Self {
Self {
method: parts.0.method,
uri: parts.0.uri,
headers: parts.0.headers,
}
}
}

impl From<HttpRequestParts> for HttpRequestPartsWrapper {
fn from(request: HttpRequestParts) -> Self {
Self(HttpRequestParts {
method: request.method,
uri: request.uri,
headers: request.headers,
})
}
}

impl From<WryRequestParts> for HttpRequestPartsWrapper {
fn from(request: WryRequestParts) -> Self {
Self(HttpRequestParts {
method: request.method,
uri: request.uri,
headers: request.headers,
})
}
}

struct HttpRequestWrapper(HttpRequest);

impl From<&WryHttpRequest> for HttpRequestWrapper {
fn from(req: &WryHttpRequest) -> Self {
Self(HttpRequest {
body: req.body.clone(),
head: HttpRequestPartsWrapper::from(req.head.clone()).0,
})
}
}

// response
struct HttpResponsePartsWrapper(WryResponseParts);
impl From<HttpResponseParts> for HttpResponsePartsWrapper {
fn from(response: HttpResponseParts) -> Self {
Self(WryResponseParts {
mimetype: response.mimetype,
status: response.status,
version: response.version,
headers: response.headers,
})
}
}

struct HttpResponseWrapper(WryHttpResponse);
impl From<HttpResponse> for HttpResponseWrapper {
fn from(response: HttpResponse) -> Self {
Self(WryHttpResponse {
body: response.body,
head: HttpResponsePartsWrapper::from(response.head).0,
})
}
}

pub struct MenuItemAttributesWrapper<'a>(pub WryMenuItemAttributes<'a>);

impl<'a> From<&'a CustomMenuItem> for MenuItemAttributesWrapper<'a> {
Expand Down Expand Up @@ -2327,6 +2398,7 @@ fn create_webview(
#[allow(unused_mut)]
let PendingWindow {
webview_attributes,
uri_scheme_protocols,
mut window_builder,
rpc_handler,
file_drop_handler,
Expand Down Expand Up @@ -2375,13 +2447,10 @@ fn create_webview(
handler,
));
}
for (scheme, protocol) in webview_attributes.uri_scheme_protocols {
webview_builder = webview_builder.with_custom_protocol(scheme, move |url| {
protocol(url)
.map(|data| {
let mime_type = MimeType::parse(&data, url);
(data, mime_type)
})
for (scheme, protocol) in uri_scheme_protocols {
webview_builder = webview_builder.with_custom_protocol(scheme, move |wry_request| {
protocol(&HttpRequestWrapper::from(wry_request).0)
.map(|tauri_response| HttpResponseWrapper::from(tauri_response).0)
.map_err(|_| wry::Error::InitScriptError)
});
}
Expand Down Expand Up @@ -2409,7 +2478,7 @@ fn create_webview(
.build()
.map_err(|e| Error::CreateWebview(Box::new(e)))?
} else {
let mut context = WebContext::new(webview_attributes.data_directory.clone());
let mut context = WebContext::new(webview_attributes.data_directory);
webview_builder
.with_web_context(&mut context)
.build()
Expand Down
3 changes: 3 additions & 0 deletions core/tauri-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ serde_json = "1.0"
thiserror = "1.0"
tauri-utils = { version = "1.0.0-beta.3", path = "../tauri-utils" }
uuid = { version = "0.8.2", features = [ "v4" ] }
http = "0.2.4"
http-range = "0.1.4"
infer = "0.4"

[target."cfg(windows)".dependencies]
winapi = "0.3"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::fmt;
const MIMETYPE_PLAIN: &str = "text/plain";

/// [Web Compatible MimeTypes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#important_mime_types_for_web_developers)
pub(crate) enum MimeType {
pub enum MimeType {
Css,
Csv,
Html,
Expand All @@ -18,6 +18,7 @@ pub(crate) enum MimeType {
OctetStream,
Rtf,
Svg,
Mp4,
}

impl std::fmt::Display for MimeType {
Expand All @@ -33,6 +34,7 @@ impl std::fmt::Display for MimeType {
MimeType::OctetStream => "application/octet-stream",
MimeType::Rtf => "application/rtf",
MimeType::Svg => "image/svg+xml",
MimeType::Mp4 => "video/mp4",
};
write!(f, "{}", mime)
}
Expand All @@ -53,6 +55,7 @@ impl MimeType {
Some("jsonld") => Self::Jsonld,
Some("rtf") => Self::Rtf,
Some("svg") => Self::Svg,
Some("mp4") => Self::Mp4,
// Assume HTML when a TLD is found for eg. `wry:://tauri.studio` | `wry://hello.com`
Some(_) => Self::Html,
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
Expand Down Expand Up @@ -118,6 +121,9 @@ mod tests {
let svg: String = MimeType::parse_from_uri("https://example.com/picture.svg").to_string();
assert_eq!(svg, String::from("image/svg+xml"));

let mp4: String = MimeType::parse_from_uri("https://example.com/video.mp4").to_string();
assert_eq!(mp4, String::from("video/mp4"));

let custom_scheme = MimeType::parse_from_uri("wry://tauri.studio").to_string();
assert_eq!(custom_scheme, String::from("text/html"));
}
Expand Down
20 changes: 20 additions & 0 deletions core/tauri-runtime/src/http/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

// custom wry types
mod mime_type;
mod request;
mod response;

pub use self::{
mime_type::MimeType,
request::{Request, RequestParts},
response::{Builder as ResponseBuilder, Response, ResponseParts},
};

// re-expose default http types
pub use http::{header, method, status, uri::InvalidUri, version, Uri};

// re-export httprange helper as it can be useful and we need it locally
pub use http_range::HttpRange;
117 changes: 117 additions & 0 deletions core/tauri-runtime/src/http/request.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use std::fmt;

use super::{
header::{HeaderMap, HeaderValue},
method::Method,
};

/// Represents an HTTP request from the WebView.
///
/// An HTTP request consists of a head and a potentially optional body.
///
/// ## Platform-specific
///
/// - **Linux:** Headers are not exposed.
pub struct Request {
pub head: RequestParts,
pub body: Vec<u8>,
}

/// Component parts of an HTTP `Request`
///
/// The HTTP request head consists of a method, uri, and a set of
/// header fields.
#[derive(Clone)]
pub struct RequestParts {
/// The request's method
pub method: Method,

/// The request's URI
pub uri: String,

/// The request's headers
pub headers: HeaderMap<HeaderValue>,
}

impl Request {
/// Creates a new blank `Request` with the body
#[inline]
pub fn new(body: Vec<u8>) -> Request {
Request {
head: RequestParts::new(),
body,
}
}

/// Returns a reference to the associated HTTP method.
#[inline]
pub fn method(&self) -> &Method {
&self.head.method
}

/// Returns a reference to the associated URI.
#[inline]
pub fn uri(&self) -> &str {
&self.head.uri
}

/// Returns a reference to the associated header field map.
#[inline]
pub fn headers(&self) -> &HeaderMap<HeaderValue> {
&self.head.headers
}

/// Returns a reference to the associated HTTP body.
#[inline]
pub fn body(&self) -> &Vec<u8> {
&self.body
}

/// Consumes the request returning the head and body RequestParts.
#[inline]
pub fn into_parts(self) -> (RequestParts, Vec<u8>) {
(self.head, self.body)
}
}

impl Default for Request {
fn default() -> Request {
Request::new(Vec::new())
}
}

impl fmt::Debug for Request {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Request")
.field("method", self.method())
.field("uri", &self.uri())
.field("headers", self.headers())
.field("body", self.body())
.finish()
}
}

impl RequestParts {
/// Creates a new default instance of `RequestParts`
fn new() -> RequestParts {
RequestParts {
method: Method::default(),
uri: "".into(),
headers: HeaderMap::default(),
}
}
}

impl fmt::Debug for RequestParts {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Parts")
.field("method", &self.method)
.field("uri", &self.uri)
.field("headers", &self.headers)
.finish()
}
}