diff options
Diffstat (limited to 'op_crates/web/lib.rs')
-rw-r--r-- | op_crates/web/lib.rs | 159 |
1 files changed, 132 insertions, 27 deletions
diff --git a/op_crates/web/lib.rs b/op_crates/web/lib.rs index 000884092..f67fd81a1 100644 --- a/op_crates/web/lib.rs +++ b/op_crates/web/lib.rs @@ -1,42 +1,22 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +use deno_core::error::generic_error; +use deno_core::error::type_error; use deno_core::error::uri_error; use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; +use deno_core::url::form_urlencoded; +use deno_core::url::quirks; +use deno_core::url::Url; use deno_core::JsRuntime; use deno_core::ZeroCopyBuf; -use idna::domain_to_ascii; -use idna::domain_to_ascii_strict; use serde::Deserialize; +use serde::Serialize; +use std::panic::catch_unwind; use std::path::PathBuf; -pub fn op_domain_to_ascii( - _state: &mut deno_core::OpState, - args: Value, - _zero_copy: &mut [ZeroCopyBuf], -) -> Result<Value, AnyError> { - #[derive(Deserialize)] - #[serde(rename_all = "camelCase")] - struct DomainToAscii { - domain: String, - be_strict: bool, - } - - let args: DomainToAscii = serde_json::from_value(args)?; - if args.be_strict { - domain_to_ascii_strict(args.domain.as_str()) - } else { - domain_to_ascii(args.domain.as_str()) - } - .map_err(|err| { - let message = format!("Invalid IDNA encoded domain name: {:?}", err); - uri_error(message) - }) - .map(|domain| json!(domain)) -} - /// Load and execute the javascript code. pub fn init(isolate: &mut JsRuntime) { let files = vec![ @@ -79,6 +59,131 @@ pub fn init(isolate: &mut JsRuntime) { } } +/// Parse `UrlParseArgs::href` with an optional `UrlParseArgs::base_href`, or an +/// optional part to "set" after parsing. Return `UrlParts`. +pub fn op_parse_url( + _state: &mut deno_core::OpState, + args: Value, + _zero_copy: &mut [ZeroCopyBuf], +) -> Result<Value, AnyError> { + #[derive(Deserialize)] + #[serde(rename_all = "camelCase")] + struct UrlParseArgs { + href: String, + base_href: Option<String>, + // If one of the following are present, this is a setter call. Apply the + // proper `Url::set_*()` method after (re)parsing `href`. + set_hash: Option<String>, + set_host: Option<String>, + set_hostname: Option<String>, + set_password: Option<String>, + set_pathname: Option<String>, + set_port: Option<String>, + set_protocol: Option<String>, + set_search: Option<String>, + set_username: Option<String>, + } + let args: UrlParseArgs = serde_json::from_value(args)?; + let base_url = args + .base_href + .as_ref() + .map(|b| Url::parse(b).map_err(|_| type_error("Invalid base URL"))) + .transpose()?; + let mut url = Url::options() + .base_url(base_url.as_ref()) + .parse(&args.href) + .map_err(|_| type_error("Invalid URL"))?; + + if let Some(hash) = args.set_hash.as_ref() { + quirks::set_hash(&mut url, hash); + } else if let Some(host) = args.set_host.as_ref() { + quirks::set_host(&mut url, host).map_err(|_| uri_error("Invalid host"))?; + } else if let Some(hostname) = args.set_hostname.as_ref() { + quirks::set_hostname(&mut url, hostname) + .map_err(|_| uri_error("Invalid hostname"))?; + } else if let Some(password) = args.set_password.as_ref() { + quirks::set_password(&mut url, password) + .map_err(|_| uri_error("Invalid password"))?; + } else if let Some(pathname) = args.set_pathname.as_ref() { + quirks::set_pathname(&mut url, pathname); + } else if let Some(port) = args.set_port.as_ref() { + quirks::set_port(&mut url, port).map_err(|_| uri_error("Invalid port"))?; + } else if let Some(protocol) = args.set_protocol.as_ref() { + quirks::set_protocol(&mut url, protocol) + .map_err(|_| uri_error("Invalid protocol"))?; + } else if let Some(search) = args.set_search.as_ref() { + quirks::set_search(&mut url, search); + } else if let Some(username) = args.set_username.as_ref() { + quirks::set_username(&mut url, username) + .map_err(|_| uri_error("Invalid username"))?; + } + + #[derive(Serialize)] + struct UrlParts<'a> { + href: &'a str, + hash: &'a str, + host: &'a str, + hostname: &'a str, + origin: &'a str, + password: &'a str, + pathname: &'a str, + port: &'a str, + protocol: &'a str, + search: &'a str, + username: &'a str, + } + // TODO(nayeemrmn): Panic that occurs in rust-url for the `non-spec:` + // url-constructor wpt tests: https://github.com/servo/rust-url/issues/670. + let username = catch_unwind(|| quirks::username(&url)).map_err(|_| { + generic_error(format!( + "Internal error while parsing \"{}\"{}, \ + see https://github.com/servo/rust-url/issues/670", + args.href, + args + .base_href + .map(|b| format!(" against \"{}\"", b)) + .unwrap_or_default() + )) + })?; + Ok(json!(UrlParts { + href: quirks::href(&url), + hash: quirks::hash(&url), + host: quirks::host(&url), + hostname: quirks::hostname(&url), + origin: &quirks::origin(&url), + password: quirks::password(&url), + pathname: quirks::pathname(&url), + port: quirks::port(&url), + protocol: quirks::protocol(&url), + search: quirks::search(&url), + username, + })) +} + +pub fn op_parse_url_search_params( + _state: &mut deno_core::OpState, + args: Value, + _zero_copy: &mut [ZeroCopyBuf], +) -> Result<Value, AnyError> { + let search: String = serde_json::from_value(args)?; + let search_params: Vec<_> = form_urlencoded::parse(search.as_bytes()) + .into_iter() + .collect(); + Ok(json!(search_params)) +} + +pub fn op_stringify_url_search_params( + _state: &mut deno_core::OpState, + args: Value, + _zero_copy: &mut [ZeroCopyBuf], +) -> Result<Value, AnyError> { + let search_params: Vec<(String, String)> = serde_json::from_value(args)?; + let search = form_urlencoded::Serializer::new(String::new()) + .extend_pairs(search_params) + .finish(); + Ok(json!(search)) +} + pub fn get_declaration() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_web.d.ts") } |