summaryrefslogtreecommitdiffstats
path: root/netwerk/base/rust-url-capi
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /netwerk/base/rust-url-capi
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'netwerk/base/rust-url-capi')
-rw-r--r--netwerk/base/rust-url-capi/.gitignore2
-rw-r--r--netwerk/base/rust-url-capi/Cargo.toml19
-rw-r--r--netwerk/base/rust-url-capi/src/error_mapping.rs68
-rw-r--r--netwerk/base/rust-url-capi/src/lib.rs477
-rw-r--r--netwerk/base/rust-url-capi/src/rust-url-capi.h45
-rw-r--r--netwerk/base/rust-url-capi/src/string_utils.rs57
-rw-r--r--netwerk/base/rust-url-capi/test/Makefile4
-rw-r--r--netwerk/base/rust-url-capi/test/test.cpp141
8 files changed, 813 insertions, 0 deletions
diff --git a/netwerk/base/rust-url-capi/.gitignore b/netwerk/base/rust-url-capi/.gitignore
new file mode 100644
index 000000000..4fffb2f89
--- /dev/null
+++ b/netwerk/base/rust-url-capi/.gitignore
@@ -0,0 +1,2 @@
+/target
+/Cargo.lock
diff --git a/netwerk/base/rust-url-capi/Cargo.toml b/netwerk/base/rust-url-capi/Cargo.toml
new file mode 100644
index 000000000..ecdb53058
--- /dev/null
+++ b/netwerk/base/rust-url-capi/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+
+name = "rust_url_capi"
+version = "0.0.1"
+authors = ["Valentin Gosu <valentin.gosu@gmail.com>"]
+
+[profile.dev]
+opt-level = 3
+debug = true
+rpath = true
+lto = true
+
+[lib]
+name = "rust_url_capi"
+
+
+[dependencies]
+libc = "0.2.0"
+url = "1.2.1"
diff --git a/netwerk/base/rust-url-capi/src/error_mapping.rs b/netwerk/base/rust-url-capi/src/error_mapping.rs
new file mode 100644
index 000000000..f20afb263
--- /dev/null
+++ b/netwerk/base/rust-url-capi/src/error_mapping.rs
@@ -0,0 +1,68 @@
+use url::ParseError;
+
+pub trait ErrorCode {
+ fn error_code(&self) -> i32;
+}
+
+impl<T: ErrorCode> ErrorCode for Result<(), T> {
+ fn error_code(&self) -> i32 {
+ match *self {
+ Ok(_) => 0,
+ Err(ref error) => error.error_code(),
+ }
+ }
+}
+
+impl ErrorCode for () {
+ fn error_code(&self) -> i32 {
+ return -1;
+ }
+}
+impl ErrorCode for ParseError {
+ fn error_code(&self) -> i32 {
+ return -1;
+// match *self {
+// ParseError::EmptyHost => -1,
+// ParseError::InvalidScheme => -2,
+// ParseError::InvalidPort => -3,
+// ParseError::InvalidIpv6Address => -4,
+// ParseError::InvalidDomainCharacter => -5,
+// ParseError::InvalidCharacter => -6,
+// ParseError::InvalidBackslash => -7,
+// ParseError::InvalidPercentEncoded => -8,
+// ParseError::InvalidAtSymbolInUser => -9,
+// ParseError::ExpectedTwoSlashes => -10,
+// ParseError::ExpectedInitialSlash => -11,
+// ParseError::NonUrlCodePoint => -12,
+// ParseError::RelativeUrlWithScheme => -13,
+// ParseError::RelativeUrlWithoutBase => -14,
+// ParseError::RelativeUrlWithNonRelativeBase => -15,
+// ParseError::NonAsciiDomainsNotSupportedYet => -16,
+// ParseError::CannotSetJavascriptFragment => -17,
+// ParseError::CannotSetPortWithFileLikeScheme => -18,
+// ParseError::CannotSetUsernameWithNonRelativeScheme => -19,
+// ParseError::CannotSetPasswordWithNonRelativeScheme => -20,
+// ParseError::CannotSetHostPortWithNonRelativeScheme => -21,
+// ParseError::CannotSetHostWithNonRelativeScheme => -22,
+// ParseError::CannotSetPortWithNonRelativeScheme => -23,
+// ParseError::CannotSetPathWithNonRelativeScheme => -24,
+// }
+ }
+}
+
+pub enum NSError {
+ OK,
+ InvalidArg,
+ Failure,
+}
+
+impl ErrorCode for NSError {
+ #[allow(overflowing_literals)]
+ fn error_code(&self) -> i32 {
+ match *self {
+ NSError::OK => 0,
+ NSError::InvalidArg => 0x80070057,
+ NSError::Failure => 0x80004005
+ }
+ }
+}
diff --git a/netwerk/base/rust-url-capi/src/lib.rs b/netwerk/base/rust-url-capi/src/lib.rs
new file mode 100644
index 000000000..e2997ce46
--- /dev/null
+++ b/netwerk/base/rust-url-capi/src/lib.rs
@@ -0,0 +1,477 @@
+extern crate url;
+use url::{Url, ParseError, ParseOptions};
+use url::quirks;
+extern crate libc;
+use libc::size_t;
+
+
+use std::mem;
+use std::str;
+
+#[allow(non_camel_case_types)]
+pub type rusturl_ptr = *const libc::c_void;
+
+mod string_utils;
+pub use string_utils::*;
+
+mod error_mapping;
+use error_mapping::*;
+
+fn parser<'a>() -> ParseOptions<'a> {
+ Url::options()
+}
+
+fn default_port(scheme: &str) -> Option<u32> {
+ match scheme {
+ "ftp" => Some(21),
+ "gopher" => Some(70),
+ "http" => Some(80),
+ "https" => Some(443),
+ "ws" => Some(80),
+ "wss" => Some(443),
+ "rtsp" => Some(443),
+ "moz-anno" => Some(443),
+ "android" => Some(443),
+ _ => None,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_new(spec: *mut libc::c_char, len: size_t) -> rusturl_ptr {
+ let slice = std::slice::from_raw_parts(spec as *const libc::c_uchar, len as usize);
+ let url_spec = match str::from_utf8(slice) {
+ Ok(spec) => spec,
+ Err(_) => return 0 as rusturl_ptr
+ };
+
+ let url = match parser().parse(url_spec) {
+ Ok(url) => url,
+ Err(_) => return 0 as rusturl_ptr
+ };
+
+ let url = Box::new(url);
+ Box::into_raw(url) as rusturl_ptr
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_free(urlptr: rusturl_ptr) {
+ if urlptr.is_null() {
+ return ();
+ }
+ let url: Box<Url> = Box::from_raw(urlptr as *mut url::Url);
+ drop(url);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_spec(urlptr: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let url: &Url = mem::transmute(urlptr);
+ cont.assign(&url.to_string())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_scheme(urlptr: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let url: &Url = mem::transmute(urlptr);
+ cont.assign(&url.scheme())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_username(urlptr: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let url: &Url = mem::transmute(urlptr);
+ if url.cannot_be_a_base() {
+ cont.set_size(0)
+ } else {
+ cont.assign(url.username())
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_password(urlptr: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let url: &Url = mem::transmute(urlptr);
+ match url.password() {
+ Some(p) => cont.assign(&p.to_string()),
+ None => cont.set_size(0)
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_host(urlptr: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let url: &Url = mem::transmute(urlptr);
+
+ match url.host() {
+ Some(h) => cont.assign(&h.to_string()),
+ None => cont.set_size(0)
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_port(urlptr: rusturl_ptr) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let url: &Url = mem::transmute(urlptr);
+
+ match url.port() {
+ Some(port) => port as i32,
+ None => -1
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_path(urlptr: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let url: &Url = mem::transmute(urlptr);
+ if url.cannot_be_a_base() {
+ cont.set_size(0)
+ } else {
+ cont.assign(url.path())
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_query(urlptr: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let url: &Url = mem::transmute(urlptr);
+ match url.query() {
+ Some(ref s) => cont.assign(s),
+ None => cont.set_size(0)
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_fragment(urlptr: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let url: &Url = mem::transmute(urlptr);
+
+ match url.fragment() {
+ Some(ref fragment) => cont.assign(fragment),
+ None => cont.set_size(0)
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_has_fragment(urlptr: rusturl_ptr) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let url: &Url = mem::transmute(urlptr);
+
+ match url.fragment() {
+ Some(_) => return 1,
+ None => return 0
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_scheme(urlptr: rusturl_ptr, scheme: *mut libc::c_char, len: size_t) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let mut url: &mut Url = mem::transmute(urlptr);
+ let slice = std::slice::from_raw_parts(scheme as *const libc::c_uchar, len as usize);
+
+ let scheme_ = match str::from_utf8(slice).ok() {
+ Some(p) => p,
+ None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+ };
+
+ quirks::set_protocol(url, scheme_).error_code()
+}
+
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_username(urlptr: rusturl_ptr, username: *mut libc::c_char, len: size_t) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let mut url: &mut Url = mem::transmute(urlptr);
+ let slice = std::slice::from_raw_parts(username as *const libc::c_uchar, len as usize);
+
+ let username_ = match str::from_utf8(slice).ok() {
+ Some(p) => p,
+ None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+ };
+
+ quirks::set_username(url, username_).error_code()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_password(urlptr: rusturl_ptr, password: *mut libc::c_char, len: size_t) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let mut url: &mut Url = mem::transmute(urlptr);
+ let slice = std::slice::from_raw_parts(password as *const libc::c_uchar, len as usize);
+
+ let password_ = match str::from_utf8(slice).ok() {
+ Some(p) => p,
+ None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+ };
+
+ quirks::set_password(url, password_).error_code()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_host_and_port(urlptr: rusturl_ptr, host_and_port: *mut libc::c_char, len: size_t) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let mut url: &mut Url = mem::transmute(urlptr);
+ let slice = std::slice::from_raw_parts(host_and_port as *const libc::c_uchar, len as usize);
+
+ let host_and_port_ = match str::from_utf8(slice).ok() {
+ Some(p) => p,
+ None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+ };
+
+ quirks::set_host(url, host_and_port_).error_code()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_host(urlptr: rusturl_ptr, host: *mut libc::c_char, len: size_t) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let mut url: &mut Url = mem::transmute(urlptr);
+ let slice = std::slice::from_raw_parts(host as *const libc::c_uchar, len as usize);
+
+ let hostname = match str::from_utf8(slice).ok() {
+ Some(h) => h,
+ None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+ };
+
+ quirks::set_hostname(url, hostname).error_code()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_port(urlptr: rusturl_ptr, port: *mut libc::c_char, len: size_t) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let mut url: &mut Url = mem::transmute(urlptr);
+ let slice = std::slice::from_raw_parts(port as *const libc::c_uchar, len as usize);
+
+ let port_ = match str::from_utf8(slice).ok() {
+ Some(p) => p,
+ None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+ };
+
+ quirks::set_port(url, port_).error_code()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_port_no(urlptr: rusturl_ptr, new_port: i32) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let mut url: &mut Url = mem::transmute(urlptr);
+ if url.cannot_be_a_base() {
+ -100
+ } else {
+ if url.scheme() == "file" {
+ return -100;
+ }
+ match default_port(url.scheme()) {
+ Some(def_port) => if new_port == def_port as i32 {
+ let _ = url.set_port(None);
+ return NSError::OK.error_code();
+ },
+ None => {}
+ };
+ if new_port > std::u16::MAX as i32 || new_port < 0 {
+ let _ = url.set_port(None);
+ } else {
+ let _ = url.set_port(Some(new_port as u16));
+ }
+ NSError::OK.error_code()
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_path(urlptr: rusturl_ptr, path: *mut libc::c_char, len: size_t) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let mut url: &mut Url = mem::transmute(urlptr);
+ let slice = std::slice::from_raw_parts(path as *const libc::c_uchar, len as usize);
+
+ let path_ = match str::from_utf8(slice).ok() {
+ Some(p) => p,
+ None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+ };
+
+ quirks::set_pathname(url, path_).error_code()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_query(urlptr: rusturl_ptr, query: *mut libc::c_char, len: size_t) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let mut url: &mut Url = mem::transmute(urlptr);
+ let slice = std::slice::from_raw_parts(query as *const libc::c_uchar, len as usize);
+
+ let query_ = match str::from_utf8(slice).ok() {
+ Some(p) => p,
+ None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+ };
+
+ quirks::set_search(url, query_).error_code()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_fragment(urlptr: rusturl_ptr, fragment: *mut libc::c_char, len: size_t) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let mut url: &mut Url = mem::transmute(urlptr);
+ let slice = std::slice::from_raw_parts(fragment as *const libc::c_uchar, len as usize);
+
+ let fragment_ = match str::from_utf8(slice).ok() {
+ Some(p) => p,
+ None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+ };
+
+ quirks::set_hash(url, fragment_).error_code()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_resolve(urlptr: rusturl_ptr, resolve: *mut libc::c_char, len: size_t, cont: *mut libc::c_void) -> i32 {
+ if urlptr.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let url: &mut Url = mem::transmute(urlptr);
+
+ let slice = std::slice::from_raw_parts(resolve as *const libc::c_uchar, len as usize);
+
+ let resolve_ = match str::from_utf8(slice).ok() {
+ Some(p) => p,
+ None => return NSError::Failure.error_code()
+ };
+
+ match parser().base_url(Some(&url)).parse(resolve_).ok() {
+ Some(u) => cont.assign(&u.to_string()),
+ None => cont.set_size(0)
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_common_base_spec(urlptr1: rusturl_ptr, urlptr2: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+ if urlptr1.is_null() || urlptr2.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let url1: &Url = mem::transmute(urlptr1);
+ let url2: &Url = mem::transmute(urlptr2);
+
+ if url1 == url2 {
+ return cont.assign(&url1.to_string());
+ }
+
+ if url1.scheme() != url2.scheme() ||
+ url1.host() != url2.host() ||
+ url1.username() != url2.username() ||
+ url1.password() != url2.password() ||
+ url1.port() != url2.port() {
+ return cont.set_size(0);
+ }
+
+ let path1 = match url1.path_segments() {
+ Some(path) => path,
+ None => return cont.set_size(0)
+ };
+ let path2 = match url2.path_segments() {
+ Some(path) => path,
+ None => return cont.set_size(0)
+ };
+
+ let mut url = url1.clone();
+ url.set_query(None);
+ let _ = url.set_host(None);
+ {
+ let mut new_segments = if let Ok(segments) = url.path_segments_mut() {
+ segments
+ } else {
+ return cont.set_size(0)
+ };
+
+ for (p1, p2) in path1.zip(path2) {
+ if p1 != p2 {
+ break;
+ } else {
+ new_segments.push(p1);
+ }
+ }
+ }
+
+ cont.assign(&url.to_string())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_relative_spec(urlptr1: rusturl_ptr, urlptr2: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+ if urlptr1.is_null() || urlptr2.is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ let url1: &Url = mem::transmute(urlptr1);
+ let url2: &Url = mem::transmute(urlptr2);
+
+ if url1 == url2 {
+ return cont.set_size(0);
+ }
+
+ if url1.scheme() != url2.scheme() ||
+ url1.host() != url2.host() ||
+ url1.username() != url2.username() ||
+ url1.password() != url2.password() ||
+ url1.port() != url2.port() {
+ return cont.assign(&url2.to_string());
+ }
+
+ let mut path1 = match url1.path_segments() {
+ Some(path) => path,
+ None => return cont.assign(&url2.to_string())
+ };
+ let mut path2 = match url2.path_segments() {
+ Some(path) => path,
+ None => return cont.assign(&url2.to_string())
+ };
+
+ // TODO: file:// on WIN?
+
+ // Exhaust the part of the iterators that match
+ while let (Some(ref p1), Some(ref p2)) = (path1.next(), path2.next()) {
+ if p1 != p2 {
+ break;
+ }
+ }
+
+ let mut buffer: String = "".to_string();
+ for _ in path1 {
+ buffer = buffer + "../";
+ }
+ for p2 in path2 {
+ buffer = buffer + p2 + "/";
+ }
+
+ return cont.assign(&buffer);
+}
+
diff --git a/netwerk/base/rust-url-capi/src/rust-url-capi.h b/netwerk/base/rust-url-capi/src/rust-url-capi.h
new file mode 100644
index 000000000..8d7a05aed
--- /dev/null
+++ b/netwerk/base/rust-url-capi/src/rust-url-capi.h
@@ -0,0 +1,45 @@
+#ifndef __RUST_URL_CAPI
+#define __RUST_URL_CAPI
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rusturl;
+typedef struct rusturl* rusturl_ptr;
+
+rusturl_ptr rusturl_new(const char *spec, size_t src_len);
+void rusturl_free(rusturl_ptr url);
+
+int32_t rusturl_get_spec(rusturl_ptr url, void*);
+int32_t rusturl_get_scheme(rusturl_ptr url, void*);
+int32_t rusturl_get_username(rusturl_ptr url, void*);
+int32_t rusturl_get_password(rusturl_ptr url, void*);
+int32_t rusturl_get_host(rusturl_ptr url, void*);
+int32_t rusturl_get_port(rusturl_ptr url); // returns port or -1
+int32_t rusturl_get_path(rusturl_ptr url, void*);
+int32_t rusturl_get_query(rusturl_ptr url, void*);
+int32_t rusturl_get_fragment(rusturl_ptr url, void*);
+int32_t rusturl_has_fragment(rusturl_ptr url); // 1 true, 0 false, < 0 error
+
+int32_t rusturl_set_scheme(rusturl_ptr url, const char *scheme, size_t len);
+int32_t rusturl_set_username(rusturl_ptr url, const char *user, size_t len);
+int32_t rusturl_set_password(rusturl_ptr url, const char *pass, size_t len);
+int32_t rusturl_set_host_and_port(rusturl_ptr url, const char *hostport, size_t len);
+int32_t rusturl_set_host(rusturl_ptr url, const char *host, size_t len);
+int32_t rusturl_set_port(rusturl_ptr url, const char *port, size_t len);
+int32_t rusturl_set_port_no(rusturl_ptr url, const int32_t port);
+int32_t rusturl_set_path(rusturl_ptr url, const char *path, size_t len);
+int32_t rusturl_set_query(rusturl_ptr url, const char *path, size_t len);
+int32_t rusturl_set_fragment(rusturl_ptr url, const char *path, size_t len);
+
+int32_t rusturl_resolve(rusturl_ptr url, const char *relative, size_t len, void*);
+int32_t rusturl_common_base_spec(rusturl_ptr url1, rusturl_ptr url2, void*);
+int32_t rusturl_relative_spec(rusturl_ptr url1, rusturl_ptr url2, void*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __RUST_URL_CAPI \ No newline at end of file
diff --git a/netwerk/base/rust-url-capi/src/string_utils.rs b/netwerk/base/rust-url-capi/src/string_utils.rs
new file mode 100644
index 000000000..ae68a60dc
--- /dev/null
+++ b/netwerk/base/rust-url-capi/src/string_utils.rs
@@ -0,0 +1,57 @@
+extern crate libc;
+use libc::size_t;
+
+extern crate std;
+use std::ptr;
+
+use error_mapping::*;
+
+extern "C" {
+ fn c_fn_set_size(user: *mut libc::c_void, size: size_t) -> i32;
+ fn c_fn_get_buffer(user: *mut libc::c_void) -> *mut libc::c_char;
+}
+
+pub trait StringContainer {
+ fn set_size(&self, size_t) -> i32;
+ fn get_buffer(&self) -> *mut libc::c_char;
+ fn assign(&self, content: &str) -> i32;
+}
+
+impl StringContainer for *mut libc::c_void {
+ fn set_size(&self, size: size_t) -> i32 {
+ if (*self).is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+ unsafe {
+ c_fn_set_size(*self, size);
+ }
+
+ return NSError::OK.error_code();
+ }
+ fn get_buffer(&self) -> *mut libc::c_char {
+ if (*self).is_null() {
+ return 0 as *mut libc::c_char;
+ }
+ unsafe {
+ c_fn_get_buffer(*self)
+ }
+ }
+ fn assign(&self, content: &str) -> i32 {
+ if (*self).is_null() {
+ return NSError::InvalidArg.error_code();
+ }
+
+ unsafe {
+ let slice = content.as_bytes();
+ c_fn_set_size(*self, slice.len());
+ let buf = c_fn_get_buffer(*self);
+ if buf.is_null() {
+ return NSError::Failure.error_code();
+ }
+
+ ptr::copy(slice.as_ptr(), buf as *mut u8, slice.len());
+ }
+
+ NSError::OK.error_code()
+ }
+}
diff --git a/netwerk/base/rust-url-capi/test/Makefile b/netwerk/base/rust-url-capi/test/Makefile
new file mode 100644
index 000000000..a4e2fd0cf
--- /dev/null
+++ b/netwerk/base/rust-url-capi/test/Makefile
@@ -0,0 +1,4 @@
+all:
+ cd .. && cargo build
+ g++ -Wall -o test test.cpp ../target/debug/librust*.a -ldl -lpthread -lrt -lgcc_s -lpthread -lc -lm -std=c++0x
+ ./test
diff --git a/netwerk/base/rust-url-capi/test/test.cpp b/netwerk/base/rust-url-capi/test/test.cpp
new file mode 100644
index 000000000..6e90ea43b
--- /dev/null
+++ b/netwerk/base/rust-url-capi/test/test.cpp
@@ -0,0 +1,141 @@
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "../src/rust-url-capi.h"
+
+class StringContainer
+{
+public:
+ StringContainer()
+ {
+ mBuffer = nullptr;
+ mLength = 0;
+ }
+
+ ~StringContainer()
+ {
+ free(mBuffer);
+ mBuffer = nullptr;
+ }
+
+ void SetSize(size_t size)
+ {
+ mLength = size;
+ if (mBuffer) {
+ mBuffer = (char *)realloc(mBuffer, size);
+ return;
+ }
+ mBuffer = (char *)malloc(size);
+ }
+
+ char * GetBuffer()
+ {
+ return mBuffer;
+ }
+
+ void CheckEquals(const char * ref) {
+ int32_t refLen = strlen(ref);
+ printf("CheckEquals: %s (len:%d)\n", ref, refLen);
+ if (refLen != mLength || strncmp(mBuffer, ref, mLength)) {
+ printf("\t--- ERROR ---\n");
+ printf("Got : ");
+ fwrite(mBuffer, mLength, 1, stdout);
+ printf(" (len:%d)\n", mLength);
+ exit(-1);
+ }
+ printf("-> OK\n");
+ }
+private:
+ int32_t mLength;
+ char * mBuffer;
+};
+
+extern "C" int32_t c_fn_set_size(void * container, size_t size)
+{
+ ((StringContainer *) container)->SetSize(size);
+ return 0;
+}
+
+extern "C" char * c_fn_get_buffer(void * container)
+{
+ return ((StringContainer *) container)->GetBuffer();
+}
+
+#define TEST_CALL(func, expected) \
+{ \
+ int32_t code = func; \
+ printf("%s -> code %d\n", #func, code); \
+ assert(code == expected); \
+ printf("-> OK\n"); \
+} \
+
+
+int main() {
+ // Create URL
+ rusturl_ptr url = rusturl_new("http://example.com/path/some/file.txt",
+ strlen("http://example.com/path/some/file.txt"));
+ assert(url); // Check we have a URL
+
+ StringContainer container;
+
+ TEST_CALL(rusturl_get_spec(url, &container), 0);
+ container.CheckEquals("http://example.com/path/some/file.txt");
+ TEST_CALL(rusturl_set_host(url, "test.com", strlen("test.com")), 0);
+ TEST_CALL(rusturl_get_host(url, &container), 0);
+ container.CheckEquals("test.com");
+ TEST_CALL(rusturl_get_path(url, &container), 0);
+ container.CheckEquals("/path/some/file.txt");
+ TEST_CALL(rusturl_set_path(url, "hello/../else.txt", strlen("hello/../else.txt")), 0);
+ TEST_CALL(rusturl_get_path(url, &container), 0);
+ container.CheckEquals("/else.txt");
+ TEST_CALL(rusturl_resolve(url, "./bla/file.txt", strlen("./bla/file.txt"), &container), 0);
+ container.CheckEquals("http://test.com/bla/file.txt");
+ TEST_CALL(rusturl_get_scheme(url, &container), 0);
+ container.CheckEquals("http");
+ TEST_CALL(rusturl_set_username(url, "user", strlen("user")), 0);
+ TEST_CALL(rusturl_get_username(url, &container), 0);
+ container.CheckEquals("user");
+ TEST_CALL(rusturl_get_spec(url, &container), 0);
+ container.CheckEquals("http://user@test.com/else.txt");
+ TEST_CALL(rusturl_set_password(url, "pass", strlen("pass")), 0);
+ TEST_CALL(rusturl_get_password(url, &container), 0);
+ container.CheckEquals("pass");
+ TEST_CALL(rusturl_get_spec(url, &container), 0);
+ container.CheckEquals("http://user:pass@test.com/else.txt");
+ TEST_CALL(rusturl_set_username(url, "", strlen("")), 0);
+ TEST_CALL(rusturl_set_password(url, "", strlen("")), 0);
+ TEST_CALL(rusturl_get_spec(url, &container), 0);
+ container.CheckEquals("http://test.com/else.txt");
+ TEST_CALL(rusturl_set_host_and_port(url, "example.org:1234", strlen("example.org:1234")), 0);
+ TEST_CALL(rusturl_get_host(url, &container), 0);
+ container.CheckEquals("example.org");
+ assert(rusturl_get_port(url) == 1234);
+ TEST_CALL(rusturl_set_port(url, "9090", strlen("9090")), 0);
+ assert(rusturl_get_port(url) == 9090);
+ TEST_CALL(rusturl_set_query(url, "x=1", strlen("x=1")), 0);
+ TEST_CALL(rusturl_get_query(url, &container), 0);
+ container.CheckEquals("x=1");
+ TEST_CALL(rusturl_set_fragment(url, "fragment", strlen("fragment")), 0);
+ TEST_CALL(rusturl_get_fragment(url, &container), 0);
+ container.CheckEquals("fragment");
+ TEST_CALL(rusturl_get_spec(url, &container), 0);
+ container.CheckEquals("http://example.org:9090/else.txt?x=1#fragment");
+
+ // Free the URL
+ rusturl_free(url);
+
+ url = rusturl_new("http://example.com/#",
+ strlen("http://example.com/#"));
+ assert(url); // Check we have a URL
+
+ assert(rusturl_has_fragment(url) == 1);
+ TEST_CALL(rusturl_set_fragment(url, "", 0), 0);
+ assert(rusturl_has_fragment(url) == 0);
+ TEST_CALL(rusturl_get_spec(url, &container), 0);
+ container.CheckEquals("http://example.com/");
+
+ rusturl_free(url);
+
+ printf("SUCCESS\n");
+ return 0;
+} \ No newline at end of file