mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-08 13:00:18 +00:00
An integration test builds the library without cfg(test), so it checks the release configuration: the remote agents refuse plain HTTP (an upper-case scheme too) without connecting, loopback links still work, and with local-mirror the remote agents take plain HTTP. A refused hop's message names only the host. ureq's message holds the whole link, and a redirect to the file host carries a signed query. host() also drops a query and any user:password@.
53 lines
2.6 KiB
Rust
53 lines
2.6 KiB
Rust
//! `Http::new` as an app gets it: an integration test builds the library without `cfg(test)`, so
|
|
//! this is the release configuration (or the `local-mirror` one, with that feature on).
|
|
|
|
use std::io::{BufRead, BufReader, Write};
|
|
use std::net::TcpListener;
|
|
use std::sync::Arc;
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
|
|
use craft_update::http::{Fetched, Http};
|
|
|
|
/// A plain-HTTP server on loopback that answers every request with `{}`; counts the requests.
|
|
fn serve() -> (String, Arc<AtomicUsize>) {
|
|
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
|
let base = format!("http://{}", listener.local_addr().unwrap());
|
|
let seen = Arc::new(AtomicUsize::new(0));
|
|
let count = seen.clone();
|
|
std::thread::spawn(move || {
|
|
for stream in listener.incoming() {
|
|
let Ok(mut stream) = stream else { return };
|
|
let mut reader = BufReader::new(stream.try_clone().unwrap());
|
|
let mut line = String::new();
|
|
while reader.read_line(&mut line).unwrap_or(0) > 0 && line != "\r\n" {
|
|
line.clear();
|
|
}
|
|
count.fetch_add(1, Ordering::SeqCst);
|
|
let _ = write!(stream, "HTTP/1.1 200 OK\r\nContent-Length: 2\r\nConnection: close\r\n\r\n{{}}");
|
|
}
|
|
});
|
|
(base, seen)
|
|
}
|
|
|
|
#[test]
|
|
fn the_remote_agents_take_plain_http_only_with_a_local_mirror() {
|
|
let (base, seen) = serve();
|
|
let http = Http::new("craft-update-tests/0").unwrap();
|
|
// `get_api` and `redirect_target` always use the agents for remote hosts.
|
|
let api = http.get_api(&format!("{base}/repos/storytold/x/releases"), None);
|
|
let redirect = http.redirect_target(&format!("{base}/releases/latest"));
|
|
if cfg!(feature = "local-mirror") {
|
|
assert!(matches!(api, Ok(Fetched::Body { .. })), "{api:?}");
|
|
assert!(matches!(redirect, Err(ref e) if e.contains("HTTP 200")), "{redirect:?}");
|
|
assert_eq!(seen.load(Ordering::SeqCst), 2);
|
|
} else {
|
|
assert!(api.as_ref().is_err_and(|e| e.contains("refused plain HTTP")), "{api:?}");
|
|
assert!(redirect.as_ref().is_err_and(|e| e.contains("refused plain HTTP")), "{redirect:?}");
|
|
// An upper-case scheme isn't a loopback link, so it goes to a remote agent too.
|
|
assert!(http.get_text(&format!("HTTP{}", &base[4..])).is_err_and(|e| e.contains("refused plain HTTP")));
|
|
assert_eq!(seen.load(Ordering::SeqCst), 0, "a refused request never connects");
|
|
}
|
|
// A loopback link goes to the direct agents, which serve plain HTTP in every build.
|
|
assert_eq!(http.get_text(&format!("{base}/SHA256SUMS.txt")).unwrap().as_deref(), Some("{}"));
|
|
}
|