feat: serve gemini behind a tls terminator
This commit is contained in:
parent
dc2114a4d2
commit
c29364d8e0
8 changed files with 395 additions and 39 deletions
|
|
@ -66,6 +66,17 @@ bind = "127.0.0.1:0"
|
|||
formats = ["gemtext"]
|
||||
default_site = "one"
|
||||
|
||||
[listener.gemini]
|
||||
protocol = "gemini"
|
||||
bind = "127.0.0.1:0"
|
||||
formats = ["gemtext"]
|
||||
default_site = "one"
|
||||
|
||||
[listener.gemstrict]
|
||||
protocol = "gemini"
|
||||
bind = "127.0.0.1:0"
|
||||
formats = ["gemtext"]
|
||||
|
||||
[listener.nex]
|
||||
protocol = "nex"
|
||||
bind = "127.0.0.1:0"
|
||||
|
|
@ -367,6 +378,97 @@ fn spartan_refuses_a_malformed_request_line() {
|
|||
assert_eq!(server.send("spartan", b"one.test / notanumber\r\n"), "4 Bad request\r\n");
|
||||
}
|
||||
|
||||
// -- Gemini --------------------------------------------------------------
|
||||
//
|
||||
// TLS is terminated in front of this listener, so these tests speak plaintext
|
||||
// to it exactly as a terminator's back end would.
|
||||
|
||||
#[test]
|
||||
fn gemini_serves_gemtext() {
|
||||
let server = Server::start();
|
||||
let response = server.send("gemini", b"gemini://one.test/\r\n");
|
||||
assert!(response.starts_with("20 text/gemini; charset=utf-8\r\n"), "{response}");
|
||||
assert!(response.contains("# One"), "{response}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gemini_routes_by_the_url_authority() {
|
||||
let server = Server::start();
|
||||
assert!(server.send("gemini", b"gemini://one.test/\r\n").contains("# One"));
|
||||
assert!(server.send("gemini", b"gemini://two.test/\r\n").contains("# Two"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gemini_accepts_the_port_in_the_authority() {
|
||||
// A client that reached a non-default port puts it in the URL it sends.
|
||||
let server = Server::start();
|
||||
assert!(server.send("gemini", b"gemini://one.test:1965/\r\n").contains("# One"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gemini_redirects_the_md_form_to_a_relative_target() {
|
||||
// Relative rather than absolute: the port this socket is bound to is the
|
||||
// terminator's back end, not the one the client reached.
|
||||
let server = Server::start();
|
||||
assert_eq!(server.send("gemini", b"gemini://one.test/about.md\r\n"), "31 /about\r\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gemini_reports_a_missing_page() {
|
||||
let server = Server::start();
|
||||
assert_eq!(server.send("gemini", b"gemini://one.test/nope\r\n"), "51 Not found\r\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gemini_serves_a_raw_file_with_its_media_type() {
|
||||
let server = Server::start();
|
||||
let response = server.send("gemini", b"gemini://one.test/img.png\r\n");
|
||||
assert!(response.starts_with("20 image/png\r\n"), "{response}");
|
||||
assert!(response.ends_with("not really a png"), "{response}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gemini_falls_back_to_the_default_site_for_an_unknown_host() {
|
||||
let server = Server::start();
|
||||
assert!(server.send("gemini", b"gemini://elsewhere.test/\r\n").contains("# One"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gemini_refuses_an_unknown_host_without_a_default_site() {
|
||||
let server = Server::start();
|
||||
let response = server.send("gemstrict", b"gemini://elsewhere.test/\r\n");
|
||||
assert_eq!(response, "53 Proxy request refused\r\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gemini_refuses_another_scheme_as_a_proxy_request() {
|
||||
let server = Server::start();
|
||||
let response = server.send("gemini", b"https://one.test/\r\n");
|
||||
assert_eq!(response, "53 Proxy request refused\r\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gemini_rejects_a_request_that_is_not_an_absolute_url() {
|
||||
let server = Server::start();
|
||||
let response = server.send("gemini", b"/about\r\n");
|
||||
assert!(response.starts_with("59 Bad request"), "{response}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gemini_rejects_userinfo_and_fragments() {
|
||||
let server = Server::start();
|
||||
assert!(server.send("gemini", b"gemini://u@one.test/\r\n").starts_with("59 "));
|
||||
assert!(server.send("gemini", b"gemini://one.test/about#top\r\n").starts_with("59 "));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gemini_rejects_a_url_over_the_spec_limit() {
|
||||
// 1024 bytes of URL plus CRLF is the most the spec allows.
|
||||
let server = Server::start();
|
||||
let long = format!("gemini://one.test/{}\r\n", "a".repeat(1100));
|
||||
assert!(server.send("gemini", long.as_bytes()).starts_with("59 "), "oversized URL");
|
||||
}
|
||||
|
||||
// -- Nex -----------------------------------------------------------------
|
||||
|
||||
#[test]
|
||||
|
|
@ -542,12 +644,19 @@ protocol = "http"
|
|||
bind = "127.0.0.1:0"
|
||||
formats = ["wml", "html"]
|
||||
default_site = "one"
|
||||
|
||||
[listener.gemini]
|
||||
protocol = "gemini"
|
||||
bind = "127.0.0.1:0"
|
||||
formats = ["gemtext"]
|
||||
default_site = "one"
|
||||
"#,
|
||||
root = root.to_str().unwrap(),
|
||||
);
|
||||
let config: ServerConfig = toml::from_str(&text).unwrap();
|
||||
let registry = Arc::new(itsybitsy::registry::build());
|
||||
let formats: Vec<String> = ["wml", "html"].iter().map(|s| s.to_string()).collect();
|
||||
let formats: Vec<String> =
|
||||
["wml", "html", "gemtext"].iter().map(|s| s.to_string()).collect();
|
||||
let sites = Arc::new(SiteSet::build(&config, registry.clone(), formats).unwrap());
|
||||
|
||||
let bound = serve::bind(&config, registry, sites).unwrap();
|
||||
|
|
@ -599,6 +708,15 @@ default_site = "one"
|
|||
assert_eq!(split(&response).0, "HTTP/1.1 404 Not Found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_gemini_client_is_redirected_to_the_parent_page() {
|
||||
// Gemini never serves WML, so a card URL always resolves to the whole
|
||||
// document rather than to a card that does not exist in gemtext.
|
||||
let server = wml_server();
|
||||
let response = server.send("gemini", b"gemini://one.test/trail/weather\r\n");
|
||||
assert_eq!(response, "31 /trail\r\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_index_deck_links_out_to_each_card() {
|
||||
let server = wml_server();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue