Bug 1151899 - Include rust-url-capi; r=valentin,ted
☠☠ backed out by b62b6568d65e ☠ ☠
authorManish Goregaokar <manishearth@gmail.com>
Wed, 19 Oct 2016 18:37:05 +0530
changeset 320080 7b5270bac41b49133cb44af4851f120c1de7cdd4
parent 320079 62f38c70d152bbd94d617ecf64b76453bc5f57c3
child 320081 b62b6568d65e63bd2f514f7a5099135065431123
push id20749
push userryanvm@gmail.com
push dateSat, 29 Oct 2016 13:21:21 +0000
treeherderfx-team@1b170b39ed6b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvalentin, ted
bugs1151899
milestone52.0a1
Bug 1151899 - Include rust-url-capi; r=valentin,ted MozReview-Commit-ID: 1ITlPLvwzF4
netwerk/base/rust-url-capi/.gitignore
netwerk/base/rust-url-capi/Cargo.toml
netwerk/base/rust-url-capi/src/error_mapping.rs
netwerk/base/rust-url-capi/src/lib.rs
netwerk/base/rust-url-capi/src/rust-url-capi.h
netwerk/base/rust-url-capi/src/string_utils.rs
netwerk/base/rust-url-capi/test/Makefile
netwerk/base/rust-url-capi/test/test.cpp
toolkit/library/gtest/rust/Cargo.lock
toolkit/library/rust/Cargo.lock
toolkit/library/rust/shared/Cargo.toml
toolkit/library/rust/shared/lib.rs
new file mode 100644
--- /dev/null
+++ b/netwerk/base/rust-url-capi/.gitignore
@@ -0,0 +1,2 @@
+/target
+/Cargo.lock
new file mode 100644
--- /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"
new file mode 100644
--- /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
+    }
+  }
+}
new file mode 100644
--- /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);
+}
+
new file mode 100644
--- /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
new file mode 100644
--- /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()
+  }
+}
new file mode 100644
--- /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
new file mode 100644
--- /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
--- a/toolkit/library/gtest/rust/Cargo.lock
+++ b/toolkit/library/gtest/rust/Cargo.lock
@@ -13,19 +13,40 @@ version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "gkrust-shared"
 version = "0.1.0"
 dependencies = [
  "mp4parse_capi 0.5.1",
  "nsstring 0.1.0",
+ "rust_url_capi 0.0.1",
 ]
 
 [[package]]
+name = "idna"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "matches"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "mp4parse"
 version = "0.5.1"
 dependencies = [
  "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "mp4parse-gtest"
@@ -44,10 +65,46 @@ version = "0.1.0"
 
 [[package]]
 name = "nsstring-gtest"
 version = "0.1.0"
 dependencies = [
  "nsstring 0.1.0",
 ]
 
+[[package]]
+name = "rust_url_capi"
+version = "0.0.1"
+dependencies = [
+ "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "url"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "matches 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [metadata]
 "checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
+"checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11"
+"checksum libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "408014cace30ee0f767b1c4517980646a573ec61a57957aeeabcac8ac0a02e8d"
+"checksum matches 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc3ad8109fa4b522f9b0cd81440422781f564aaf8c195de6b9d6642177ad0dd"
+"checksum unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c1f7ceb96afdfeedee42bade65a0d585a6a0106f681b6749c8ff4daa8df30b3f"
+"checksum unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "26643a2f83bac55f1976fb716c10234485f9202dcd65cfbdf9da49867b271172"
+"checksum url 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8527c62d9869a08325c38272b3f85668df22a65890c61a639d233dc0ed0b23a2"
--- a/toolkit/library/rust/Cargo.lock
+++ b/toolkit/library/rust/Cargo.lock
@@ -11,19 +11,40 @@ version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "gkrust-shared"
 version = "0.1.0"
 dependencies = [
  "mp4parse_capi 0.5.1",
  "nsstring 0.1.0",
+ "rust_url_capi 0.0.1",
 ]
 
 [[package]]
+name = "idna"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "matches"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "mp4parse"
 version = "0.5.1"
 dependencies = [
  "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "mp4parse_capi"
@@ -31,10 +52,46 @@ version = "0.5.1"
 dependencies = [
  "mp4parse 0.5.1",
 ]
 
 [[package]]
 name = "nsstring"
 version = "0.1.0"
 
+[[package]]
+name = "rust_url_capi"
+version = "0.0.1"
+dependencies = [
+ "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "url"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "matches 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [metadata]
 "checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
+"checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11"
+"checksum libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "408014cace30ee0f767b1c4517980646a573ec61a57957aeeabcac8ac0a02e8d"
+"checksum matches 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc3ad8109fa4b522f9b0cd81440422781f564aaf8c195de6b9d6642177ad0dd"
+"checksum unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c1f7ceb96afdfeedee42bade65a0d585a6a0106f681b6749c8ff4daa8df30b3f"
+"checksum unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "26643a2f83bac55f1976fb716c10234485f9202dcd65cfbdf9da49867b271172"
+"checksum url 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8527c62d9869a08325c38272b3f85668df22a65890c61a639d233dc0ed0b23a2"
--- a/toolkit/library/rust/shared/Cargo.toml
+++ b/toolkit/library/rust/shared/Cargo.toml
@@ -3,16 +3,17 @@ name = "gkrust-shared"
 version = "0.1.0"
 authors = ["nobody@mozilla.org"]
 license = "MPL-2.0"
 description = "Shared Rust code for libxul"
 
 [dependencies]
 mp4parse_capi = { path = "../../../../media/libstagefright/binding/mp4parse_capi" }
 nsstring = { path = "../../../../xpcom/rust/nsstring" }
+rust_url_capi = { path = "../../../../netwerk/base/rust-url-capi" }
 
 [lib]
 path = "lib.rs"
 test = false
 doctest = false
 bench = false
 doc = false
 plugin = false
--- a/toolkit/library/rust/shared/lib.rs
+++ b/toolkit/library/rust/shared/lib.rs
@@ -1,6 +1,7 @@
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 extern crate mp4parse_capi;
 extern crate nsstring;
+extern crate rust_url_capi;