LibURL: Expose rust host parsing over FFI
To be used instead of the C++ implementation.
This commit is contained in:
parent
b624276e72
commit
57cc7b04ee
6 changed files with 93 additions and 23 deletions
|
|
@ -534,6 +534,9 @@ static ErrorOr<String> domain_to_ascii(StringView domain, bool be_strict)
|
|||
// https://url.spec.whatwg.org/#concept-host-parser
|
||||
Optional<Host> Parser::parse_host(StringView input, bool is_opaque)
|
||||
{
|
||||
#ifdef ENABLE_RUST
|
||||
return RustIntegration::parse_host(input, is_opaque);
|
||||
#endif
|
||||
// 1. If input starts with U+005B ([), then:
|
||||
if (input.starts_with('[')) {
|
||||
// 1. If input does not end with U+005D (]), IPv6-unclosed validation error, return failure.
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use crate::url::Url;
|
|||
use crate::url::basic_parse;
|
||||
use crate::url::basic_parse_into;
|
||||
use crate::url::is_special_scheme;
|
||||
use crate::url::parse_host;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
|
|
@ -91,6 +92,7 @@ pub struct RustBasicParseOptions {
|
|||
}
|
||||
|
||||
pub type FfiUrlResultFn = unsafe extern "C" fn(*mut c_void, *const RustFfiUrl);
|
||||
pub type FfiHostResultFn = unsafe extern "C" fn(*mut c_void, *const FfiUrlHost);
|
||||
|
||||
fn abort_on_panic<F: FnOnce() -> R, R>(f: F) -> R {
|
||||
match catch_unwind(AssertUnwindSafe(f)) {
|
||||
|
|
@ -287,3 +289,33 @@ pub unsafe extern "C" fn rust_url_basic_parse(
|
|||
did_succeed
|
||||
})
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// `input` must be valid for `input_length` bytes.
|
||||
/// `on_complete` is called exactly once with either a host result or null.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn rust_url_parse_host(
|
||||
input: *const u8,
|
||||
input_length: usize,
|
||||
is_opaque: bool,
|
||||
ctx: *mut c_void,
|
||||
on_complete: FfiHostResultFn,
|
||||
) -> bool {
|
||||
abort_on_panic(|| {
|
||||
// SAFETY: caller guarantees input is valid.
|
||||
let input_bytes = unsafe { std::slice::from_raw_parts(input, input_length) };
|
||||
let input_str = String::from_utf8_lossy(input_bytes);
|
||||
|
||||
let Some(host) = parse_host(&input_str, is_opaque) else {
|
||||
// SAFETY: on_complete is a valid function pointer; ctx is caller-provided.
|
||||
unsafe { on_complete(ctx, std::ptr::null()) };
|
||||
return false;
|
||||
};
|
||||
|
||||
let ffi_result = host_to_ffi(Some(&host));
|
||||
|
||||
// SAFETY: ffi_result borrows from host, which lives until the callback returns.
|
||||
unsafe { on_complete(ctx, &raw const ffi_result) };
|
||||
true
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -521,7 +521,7 @@ fn ends_in_a_number_checker(input: &str) -> bool {
|
|||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#concept-host-parser
|
||||
pub(super) fn parse_host(input: &str, is_opaque: bool) -> Option<Host> {
|
||||
pub(crate) fn parse_host(input: &str, is_opaque: bool) -> Option<Host> {
|
||||
// 1. If input starts with U+005B ([), then:
|
||||
if input.starts_with('[') {
|
||||
// 1. If input does not end with U+005D (]), IPv6-unclosed validation error, return failure.
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ mod scheme;
|
|||
mod serialize;
|
||||
mod types;
|
||||
|
||||
pub(crate) use self::host::parse_host;
|
||||
pub(crate) use self::scheme::default_port_for_scheme;
|
||||
pub(crate) use self::scheme::is_special_scheme;
|
||||
pub(crate) use self::scheme::special_schemes;
|
||||
|
|
|
|||
|
|
@ -174,6 +174,32 @@ static FFI::FfiUrlHost host_to_ffi(Optional<Host> const& host)
|
|||
return result;
|
||||
}
|
||||
|
||||
static Optional<Host> host_from_ffi(FFI::FfiUrlHost const& ffi)
|
||||
{
|
||||
if (!ffi.has_host)
|
||||
return {};
|
||||
|
||||
switch (ffi.kind) {
|
||||
case FFI::RustUrlHostKind::String:
|
||||
return Host(string_from_ffi({ ffi.string_data, ffi.string_length }));
|
||||
case FFI::RustUrlHostKind::Ipv4: {
|
||||
u32 const n = (static_cast<u32>(ffi.ipv4[0]) << 24)
|
||||
| (static_cast<u32>(ffi.ipv4[1]) << 16)
|
||||
| (static_cast<u32>(ffi.ipv4[2]) << 8)
|
||||
| static_cast<u32>(ffi.ipv4[3]);
|
||||
return Host(IPv4Address(NetworkOrdered<u32>(n)));
|
||||
}
|
||||
case FFI::RustUrlHostKind::Ipv6: {
|
||||
Array<u16, 8> pieces;
|
||||
for (size_t i = 0; i < 8; i++)
|
||||
pieces[i] = (static_cast<u16>(ffi.ipv6[i * 2]) << 8) | ffi.ipv6[(i * 2) + 1];
|
||||
return Host(IPv6Address(pieces));
|
||||
}
|
||||
}
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
static Optional<URL> url_from_ffi(FFI::RustFfiUrl const& ffi)
|
||||
{
|
||||
URL url;
|
||||
|
|
@ -181,28 +207,8 @@ static Optional<URL> url_from_ffi(FFI::RustFfiUrl const& ffi)
|
|||
url.set_username(string_from_ffi(ffi.username));
|
||||
url.set_password(string_from_ffi(ffi.password));
|
||||
|
||||
if (ffi.host.has_host) {
|
||||
switch (ffi.host.kind) {
|
||||
case FFI::RustUrlHostKind::String:
|
||||
url.set_host(Host(string_from_ffi({ ffi.host.string_data, ffi.host.string_length })));
|
||||
break;
|
||||
case FFI::RustUrlHostKind::Ipv4: {
|
||||
u32 const n = (static_cast<u32>(ffi.host.ipv4[0]) << 24)
|
||||
| (static_cast<u32>(ffi.host.ipv4[1]) << 16)
|
||||
| (static_cast<u32>(ffi.host.ipv4[2]) << 8)
|
||||
| static_cast<u32>(ffi.host.ipv4[3]);
|
||||
url.set_host(Host(IPv4Address(NetworkOrdered<u32>(n))));
|
||||
break;
|
||||
}
|
||||
case FFI::RustUrlHostKind::Ipv6: {
|
||||
Array<u16, 8> pieces;
|
||||
for (size_t i = 0; i < 8; i++)
|
||||
pieces[i] = (static_cast<u16>(ffi.host.ipv6[i * 2]) << 8) | ffi.host.ipv6[(i * 2) + 1];
|
||||
url.set_host(Host(IPv6Address(pieces)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (auto host = host_from_ffi(ffi.host); host.has_value())
|
||||
url.set_host(host.release_value());
|
||||
|
||||
if (ffi.has_port)
|
||||
url.set_port(ffi.port);
|
||||
|
|
@ -388,6 +394,10 @@ struct ParseCallbackCtx {
|
|||
URL* url_inout;
|
||||
};
|
||||
|
||||
struct HostParseCallbackCtx {
|
||||
Optional<Host>* result;
|
||||
};
|
||||
|
||||
static void on_basic_parse_complete(void* ctx_ptr, FFI::RustFfiUrl const* ffi_result)
|
||||
{
|
||||
auto* ctx = static_cast<ParseCallbackCtx*>(ctx_ptr);
|
||||
|
|
@ -398,6 +408,29 @@ static void on_basic_parse_complete(void* ctx_ptr, FFI::RustFfiUrl const* ffi_re
|
|||
*ctx->url_inout = **ctx->result;
|
||||
}
|
||||
|
||||
static void on_parse_host_complete(void* ctx_ptr, FFI::FfiUrlHost const* ffi_result)
|
||||
{
|
||||
auto* ctx = static_cast<HostParseCallbackCtx*>(ctx_ptr);
|
||||
if (!ffi_result)
|
||||
return;
|
||||
*ctx->result = host_from_ffi(*ffi_result);
|
||||
}
|
||||
|
||||
Optional<Host> parse_host(StringView input, bool is_opaque)
|
||||
{
|
||||
Optional<Host> result;
|
||||
HostParseCallbackCtx ctx { .result = &result };
|
||||
bool const did_succeed = FFI::rust_url_parse_host(
|
||||
reinterpret_cast<uint8_t const*>(input.characters_without_null_termination()),
|
||||
input.length(),
|
||||
is_opaque,
|
||||
&ctx,
|
||||
on_parse_host_complete);
|
||||
if (!did_succeed)
|
||||
return {};
|
||||
return result;
|
||||
}
|
||||
|
||||
Optional<URL> parse_basic_url(StringView input, Optional<URL const&> base_url, URL* url, Optional<Parser::State> state_override, Optional<StringView> encoding)
|
||||
{
|
||||
auto const state_override_from_cpp = [](Parser::State state) {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
namespace URL::RustIntegration {
|
||||
|
||||
Optional<URL> parse_basic_url(StringView input, Optional<URL const&> base_url = {}, URL* url = nullptr, Optional<Parser::State> state_override = {}, Optional<StringView> encoding = {});
|
||||
Optional<Host> parse_host(StringView input, bool is_opaque = false);
|
||||
|
||||
class URLPattern {
|
||||
public:
|
||||
|
|
|
|||
Loading…
Reference in a new issue