mirror of https://git.sr.ht/~slondr/spart
0.1
This commit is contained in:
parent
c9ced1fc5a
commit
ccaf0cd537
|
@ -6,3 +6,7 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
# Some functionality of the normal url crate is only available for "special" schemes such as http.
|
||||||
|
# As we want to use this functionality for spartan urls, we use a patched version instead.
|
||||||
|
url = { git = "https://github.com/slondr/rust-url-additional-specials", branch = "master" }
|
||||||
|
urlencoding = "~2.1"
|
||||||
|
|
149
src/lib.rs
149
src/lib.rs
|
@ -1,8 +1,151 @@
|
||||||
|
pub mod spart {
|
||||||
|
use url::Url;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
|
||||||
|
pub struct Request {
|
||||||
|
host: String,
|
||||||
|
path: String,
|
||||||
|
content_length: usize,
|
||||||
|
data: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Request {
|
||||||
|
pub fn from_url(parsed_url: Url) -> Result<Request, &'static str> {
|
||||||
|
if parsed_url.scheme() != "spartan" {
|
||||||
|
return Err("Not a spartan URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
match parsed_url.host_str() {
|
||||||
|
None => Err("No hostname"),
|
||||||
|
Some(unparsed_host) => {
|
||||||
|
let (data, content_length) = match parsed_url.query() {
|
||||||
|
None => (None, 0),
|
||||||
|
Some(s) => {
|
||||||
|
let decoded_data = urlencoding::decode(s).unwrap().into_owned();
|
||||||
|
let decoded_len = decoded_data.chars().count();
|
||||||
|
(Some(decoded_data), decoded_len)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let host = unparsed_host.to_string();
|
||||||
|
let path = parsed_url.path().to_string();
|
||||||
|
|
||||||
|
Ok(Request { host, path, data, content_length })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl std::fmt::Display for Request {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||||
|
write!(f, "{} {} {}\r\n{}", self.host, self.path, self.content_length, match &self.data {
|
||||||
|
None => String::new(),
|
||||||
|
Some(d) => d.to_string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(r: String) -> Result<String, &'static str> {
|
||||||
|
match Url::parse(&r) {
|
||||||
|
Err(_) => Err("Cannot parse url"),
|
||||||
|
Ok(url) => {
|
||||||
|
let port = url.port_or_known_default().unwrap(); // patched version of `uri` DOES have a default
|
||||||
|
let request = Request::from_url(url)?;
|
||||||
|
let mut connection = std::net::TcpStream::connect(format!("{}:{}", request.host, port)).unwrap();
|
||||||
|
let request_string = request.to_string();
|
||||||
|
|
||||||
|
match connection.write_all(request_string.as_bytes()) {
|
||||||
|
Err(_) => Err("Error writing to socket"),
|
||||||
|
Ok(()) => {
|
||||||
|
let mut buffer = String::new();
|
||||||
|
match connection.read_to_string(&mut buffer) {
|
||||||
|
Err(_) => Err("Unable to read response"),
|
||||||
|
Ok(_) => Ok(buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::spart::Request;
|
||||||
|
|
||||||
|
/// taken from 5.1 of the spec
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_works() {
|
fn basic_url_mapping_without_slash() {
|
||||||
let result = 2 + 2;
|
let request = Request::from_url(url::Url::parse("spartan://example.com").unwrap()).unwrap();
|
||||||
assert_eq!(result, 4);
|
assert_eq!("example.com / 0\r\n", format!("{}", request))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_url_mapping_with_slash() {
|
||||||
|
let request = Request::from_url(url::Url::parse("spartan://example.com/").unwrap()).unwrap();
|
||||||
|
assert_eq!("example.com / 0\r\n", format!("{}", request))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_url_mapping_with_port() {
|
||||||
|
let request = Request::from_url(url::Url::parse("spartan://example.com:3000/").unwrap()).unwrap();
|
||||||
|
assert_eq!("example.com / 0\r\n", format!("{}", request))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_url_mapping_with_anchor() {
|
||||||
|
let request = Request::from_url(url::Url::parse("spartan://example.com/#about").unwrap()).unwrap();
|
||||||
|
assert_eq!("example.com / 0\r\n", format!("{}", request))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_url_mapping_with_user() {
|
||||||
|
let request = Request::from_url(url::Url::parse("spartan://anon@example.com/").unwrap()).unwrap();
|
||||||
|
assert_eq!("example.com / 0\r\n", format!("{}", request))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_mapping_from_ip_address() {
|
||||||
|
let request = Request::from_url(url::Url::parse("spartan://127.0.0.1/").unwrap()).unwrap();
|
||||||
|
assert_eq!("127.0.0.1 / 0\r\n", format!("{}", request))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_mapping_from_ip6_address() {
|
||||||
|
let request = Request::from_url(url::Url::parse("spartan://[::1]/").unwrap()).unwrap();
|
||||||
|
assert_eq!("[::1] / 0\r\n", format!("{}", request))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_mapping_with_punycode() {
|
||||||
|
let request = Request::from_url(url::Url::parse("spartan://examplé.com/").unwrap()).unwrap();
|
||||||
|
assert_eq!("xn--exampl-gva.com / 0\r\n", format!("{}", request))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_mapping_with_urlencoded_path() {
|
||||||
|
let request = Request::from_url(url::Url::parse("spartan://example.com/my%20file.txt").unwrap()).unwrap();
|
||||||
|
assert_eq!("example.com /my%20file.txt 0\r\n", format!("{}", request))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_mapping_with_unicode_path() {
|
||||||
|
let request = Request::from_url(url::Url::parse("spartan://example.com/café.txt").unwrap()).unwrap();
|
||||||
|
assert_eq!("example.com /caf%C3%A9.txt 0\r\n", format!("{}", request))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_mapping_with_data() {
|
||||||
|
let request = Request::from_url(url::Url::parse("spartan://example.com?a=1&b=2").unwrap()).unwrap();
|
||||||
|
assert_eq!("example.com / 7\r\na=1&b=2", format!("{}", request))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_mapping_with_urlencoded_data() {
|
||||||
|
let request = Request::from_url(url::Url::parse("spartan://example.com?hello%20world").unwrap()).unwrap();
|
||||||
|
assert_eq!("example.com / 11\r\nhello world", format!("{}", request))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue