summaryrefslogtreecommitdiff
path: root/ext/url/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ext/url/lib.rs')
-rw-r--r--ext/url/lib.rs236
1 files changed, 160 insertions, 76 deletions
diff --git a/ext/url/lib.rs b/ext/url/lib.rs
index be229cec5..c6e91e155 100644
--- a/ext/url/lib.rs
+++ b/ext/url/lib.rs
@@ -3,7 +3,6 @@
mod urlpattern;
use deno_core::error::type_error;
-use deno_core::error::uri_error;
use deno_core::error::AnyError;
use deno_core::include_js_files;
use deno_core::op;
@@ -11,6 +10,7 @@ use deno_core::url::form_urlencoded;
use deno_core::url::quirks;
use deno_core::url::Url;
use deno_core::Extension;
+use deno_core::OpState;
use deno_core::ZeroCopyBuf;
use std::path::PathBuf;
@@ -25,8 +25,10 @@ pub fn init() -> Extension {
"01_urlpattern.js",
))
.ops(vec![
- op_url_parse::decl(),
op_url_reparse::decl(),
+ op_url_parse::decl(),
+ op_url_get_serialization::decl(),
+ op_url_parse_with_base::decl(),
op_url_parse_search_params::decl(),
op_url_stringify_search_params::decl(),
op_urlpattern_parse::decl(),
@@ -35,41 +37,95 @@ pub fn init() -> Extension {
.build()
}
-// UrlParts is a \n joined string of the following parts:
-// #[derive(Serialize)]
-// pub struct UrlParts {
-// href: String,
-// hash: String,
-// host: String,
-// hostname: String,
-// origin: String,
-// password: String,
-// pathname: String,
-// port: String,
-// protocol: String,
-// search: String,
-// username: String,
-// }
-// TODO: implement cleaner & faster serialization
-type UrlParts = String;
-
-/// Parse `UrlParseArgs::href` with an optional `UrlParseArgs::base_href`, or an
-/// optional part to "set" after parsing. Return `UrlParts`.
+/// Parse `href` with a `base_href`. Fills the out `buf` with URL components.
#[op]
-pub fn op_url_parse(
+pub fn op_url_parse_with_base(
+ state: &mut OpState,
+ href: String,
+ base_href: String,
+ buf: &mut [u8],
+) -> u32 {
+ let base_url = match Url::parse(&base_href) {
+ Ok(url) => url,
+ Err(_) => return ParseStatus::Err as u32,
+ };
+ parse_url(state, href, Some(&base_url), buf)
+}
+
+#[repr(u32)]
+pub enum ParseStatus {
+ Ok = 0,
+ OkSerialization = 1,
+ Err,
+}
+
+struct UrlSerialization(String);
+
+#[op]
+pub fn op_url_get_serialization(state: &mut OpState) -> String {
+ state.take::<UrlSerialization>().0
+}
+
+/// Parse `href` without a `base_url`. Fills the out `buf` with URL components.
+#[op]
+pub fn op_url_parse(state: &mut OpState, href: String, buf: &mut [u8]) -> u32 {
+ parse_url(state, href, None, buf)
+}
+
+/// `op_url_parse` and `op_url_parse_with_base` share the same implementation.
+///
+/// This function is used to parse the URL and fill the `buf` with internal
+/// offset values of the URL components.
+///
+/// If the serialized URL is the same as the input URL, then `UrlSerialization` is
+/// not set and returns `ParseStatus::Ok`.
+///
+/// If the serialized URL is different from the input URL, then `UrlSerialization` is
+/// set and returns `ParseStatus::OkSerialization`. JS side should check status and
+/// use `op_url_get_serialization` to get the serialized URL.
+///
+/// If the URL is invalid, then `UrlSerialization` is not set and returns `ParseStatus::Err`.
+///
+/// ```js
+/// const buf = new Uint32Array(8);
+/// const status = op_url_parse("http://example.com", buf.buffer);
+/// let serializedUrl = "";
+/// if (status === ParseStatus.Ok) {
+/// serializedUrl = "http://example.com";
+/// } else if (status === ParseStatus.OkSerialization) {
+/// serializedUrl = op_url_get_serialization();
+/// }
+/// ```
+#[inline]
+fn parse_url(
+ state: &mut OpState,
href: String,
- base_href: Option<String>,
-) -> Result<UrlParts, AnyError> {
- let base_url = base_href
- .as_ref()
- .map(|b| Url::parse(b).map_err(|_| type_error("Invalid base URL")))
- .transpose()?;
- let url = Url::options()
- .base_url(base_url.as_ref())
- .parse(&href)
- .map_err(|_| type_error("Invalid URL"))?;
-
- Ok(url_parts(url))
+ base_href: Option<&Url>,
+ buf: &mut [u8],
+) -> u32 {
+ match Url::options().base_url(base_href).parse(&href) {
+ Ok(url) => {
+ let inner_url = quirks::internal_components(&url);
+
+ let buf: &mut [u32] = as_u32_slice(buf);
+ buf[0] = inner_url.scheme_end;
+ buf[1] = inner_url.username_end;
+ buf[2] = inner_url.host_start;
+ buf[3] = inner_url.host_end;
+ buf[4] = inner_url.port.unwrap_or(0) as u32;
+ buf[5] = inner_url.path_start;
+ buf[6] = inner_url.query_start.unwrap_or(0);
+ buf[7] = inner_url.fragment_start.unwrap_or(0);
+ let serialization: String = url.into();
+ if serialization != href {
+ state.put(UrlSerialization(serialization));
+ ParseStatus::OkSerialization as u32
+ } else {
+ ParseStatus::Ok as u32
+ }
+ }
+ Err(_) => ParseStatus::Err as u32,
+ }
}
#[derive(PartialEq, Debug)]
@@ -86,58 +142,86 @@ pub enum UrlSetter {
Username = 8,
}
+const NO_PORT: u32 = 65536;
+
+fn as_u32_slice(slice: &mut [u8]) -> &mut [u32] {
+ assert_eq!(slice.len() % std::mem::size_of::<u32>(), 0);
+ // SAFETY: size is multiple of 4
+ unsafe {
+ std::slice::from_raw_parts_mut(
+ slice.as_mut_ptr() as *mut u32,
+ slice.len() / std::mem::size_of::<u32>(),
+ )
+ }
+}
+
#[op]
pub fn op_url_reparse(
+ state: &mut OpState,
href: String,
- setter_opts: (u8, String),
-) -> Result<UrlParts, AnyError> {
- let mut url = Url::options()
- .parse(&href)
- .map_err(|_| type_error("Invalid URL"))?;
+ setter: u8,
+ setter_value: String,
+ buf: &mut [u8],
+) -> u32 {
+ let mut url = match Url::options().parse(&href) {
+ Ok(url) => url,
+ Err(_) => return ParseStatus::Err as u32,
+ };
- let (setter, setter_value) = setter_opts;
if setter > 8 {
- return Err(type_error("Invalid URL setter"));
+ return ParseStatus::Err as u32;
}
// SAFETY: checked to be less than 9.
let setter = unsafe { std::mem::transmute::<u8, UrlSetter>(setter) };
let value = setter_value.as_ref();
- match setter {
- UrlSetter::Hash => quirks::set_hash(&mut url, value),
- UrlSetter::Host => quirks::set_host(&mut url, value)
- .map_err(|_| uri_error("Invalid host"))?,
- UrlSetter::Hostname => quirks::set_hostname(&mut url, value)
- .map_err(|_| uri_error("Invalid hostname"))?,
- UrlSetter::Password => quirks::set_password(&mut url, value)
- .map_err(|_| uri_error("Invalid password"))?,
- UrlSetter::Pathname => quirks::set_pathname(&mut url, value),
- UrlSetter::Port => quirks::set_port(&mut url, value)
- .map_err(|_| uri_error("Invalid port"))?,
- UrlSetter::Protocol => quirks::set_protocol(&mut url, value)
- .map_err(|_| uri_error("Invalid protocol"))?,
- UrlSetter::Search => quirks::set_search(&mut url, value),
- UrlSetter::Username => quirks::set_username(&mut url, value)
- .map_err(|_| uri_error("Invalid username"))?,
- }
+ let e = match setter {
+ UrlSetter::Hash => {
+ quirks::set_hash(&mut url, value);
+ Ok(())
+ }
+ UrlSetter::Host => quirks::set_host(&mut url, value),
- Ok(url_parts(url))
-}
+ UrlSetter::Hostname => quirks::set_hostname(&mut url, value),
+
+ UrlSetter::Password => quirks::set_password(&mut url, value),
-fn url_parts(url: Url) -> UrlParts {
- [
- quirks::href(&url),
- quirks::hash(&url),
- quirks::host(&url),
- quirks::hostname(&url),
- &quirks::origin(&url),
- quirks::password(&url),
- quirks::pathname(&url),
- quirks::port(&url),
- quirks::protocol(&url),
- quirks::search(&url),
- quirks::username(&url),
- ]
- .join("\n")
+ UrlSetter::Pathname => {
+ quirks::set_pathname(&mut url, value);
+ Ok(())
+ }
+ UrlSetter::Port => quirks::set_port(&mut url, value),
+
+ UrlSetter::Protocol => quirks::set_protocol(&mut url, value),
+ UrlSetter::Search => {
+ quirks::set_search(&mut url, value);
+ Ok(())
+ }
+ UrlSetter::Username => quirks::set_username(&mut url, value),
+ };
+
+ match e {
+ Ok(_) => {
+ let inner_url = quirks::internal_components(&url);
+
+ let buf: &mut [u32] = as_u32_slice(buf);
+ buf[0] = inner_url.scheme_end;
+ buf[1] = inner_url.username_end;
+ buf[2] = inner_url.host_start;
+ buf[3] = inner_url.host_end;
+ buf[4] = inner_url.port.map(|p| p as u32).unwrap_or(NO_PORT);
+ buf[5] = inner_url.path_start;
+ buf[6] = inner_url.query_start.unwrap_or(0);
+ buf[7] = inner_url.fragment_start.unwrap_or(0);
+ let serialization: String = url.into();
+ if serialization != href {
+ state.put(UrlSerialization(serialization));
+ ParseStatus::OkSerialization as u32
+ } else {
+ ParseStatus::Ok as u32
+ }
+ }
+ Err(_) => ParseStatus::Err as u32,
+ }
}
#[op]