Skip to main content

cove_runtime/
http.rs

1//! `http`: fetching over the network, and listening on a port.
2//!
3//! The Language Card lists the network among the operations that are typed
4//! Host APIs rather than ambient authority, and `examples/server/main.cove`
5//! shows the shape it expects:
6//!
7//! ```cove
8//! let server = http.listen(8080)?
9//! while server.handle(routes)? {
10//! }
11//! server.close()?
12//! ```
13//!
14//! Two things there are not ordinary host calls. `server` is a resource
15//! handle: `listen` hands back a name for a socket the host keeps, and later
16//! calls are made on that name rather than on the module. And `handle` is
17//! given a routing table whose entries hold Cove closures, which it has to
18//! *run*. ADR 0013 is what makes both possible —
19//! [`crate::host::ResourceHandle`] for the first and [`crate::host::Reentry`]
20//! for the second.
21//!
22//! The loop belongs to the program rather than to the host. A `serve` that
23//! never returned would be a host call outside the reach of the run's fuel,
24//! its deadline, and its cancellation; `handle` answers one request and
25//! returns, so the loop around it is ordinary Cove code with ordinary
26//! safepoints.
27//!
28//! That is only half of it, because `handle` itself waits — for a connection,
29//! and then for the request on it — and a host call is a hole in the
30//! safepoint chain for as long as it lasts. So the waiting is bounded the way
31//! [`crate::host::HostApi`] says a blocking operation must bound it. A real
32//! listener accepts by polling a nonblocking socket, looks at the run's
33//! cancellation and at what is left of its deadline between polls, and
34//! answers "nothing more to serve" when either says to stop, which ends the
35//! program's own loop and lets the run stop at its next safepoint with the
36//! diagnostic the budget owns. One request gets one deadline covering its
37//! line, its headers, and its body together, no longer than what the run has
38//! left. Stopping the run stops the server, and now that is true while it is
39//! idle as well as while it is busy.
40//!
41//! Three implementations ship. [`Http::real`] speaks HTTP/1.1 over TCP, and
42//! is deliberately small: one request per connection, `Connection: close`, no
43//! keep-alive, no chunked transfer, and a listener that binds loopback only,
44//! because granting `http` should not publish a port to the network the
45//! machine is on.
46//!
47//! It is small in what it will hold, too, and a reader should know where the
48//! lines are before finding one. A request line longer than eight kibibytes
49//! is answered `414`; a header line longer than eight kibibytes, more than a
50//! hundred headers, or more than thirty-two kibibytes of them together are
51//! answered `431`; a body over one mebibyte is answered `413`. Each bound is
52//! applied while the request is being read rather than after, so what a peer
53//! claims never decides what this host allocates. A `Content-Length` that is
54//! not a plain count of bytes is answered `400`, as are two that disagree,
55//! and a `Transfer-Encoding` of any kind is answered `501`, because a body
56//! whose end this host cannot find is one it will not start. The bounds are
57//! constants, not configuration: this host answers JSON on loopback, and
58//! nothing about that job is served by letting a peer choose how much of this
59//! process it occupies.
60//!
61//! The client is bounded on the same argument and by the same number. A
62//! response body over one mebibyte is an error rather than an allocation, and
63//! `MAX_RESPONSE_BYTES` is where that is said — a server this host reaches
64//! is no more this process's to trust than a peer that connects to it, and
65//! [ADR 0018](../../../docs/adr/0018-streaming-file-io.md) already settled
66//! that a host reads what it decided to read rather than what the input asked
67//! it to.
68//!
69//! It is bounded in time the same way as well, and by the same mechanism: a
70//! client waiting for a response polls a socket with a short timeout and
71//! looks at the run's cancellation and deadline between reads, exactly as the
72//! listener polls for a connection. That is what makes a `clock.timeout`
73//! around a `fetch` cut the fetch short rather than be reported once the
74//! server has answered — [ADR 0024](../../../docs/adr/0024-a-stop-is-a-bound-not-a-point.md)
75//! says a stop is a bound, and a bound the operation under it never observes
76//! is not one. The connect is the step that is bounded without being polled;
77//! `connect_within` says what that leaves open.
78//!
79//! What the client answers is an `http.Response`: the status the server sent
80//! and the body it carried. A status outside 200-299 is an answer and not a
81//! failure, so a program can tell a `404` it received from a connection it
82//! could not make, which is what a `Result<String, Error>` could only say in
83//! prose. [`Http::recorded`] is the fake: `fetch` answers from a table of
84//! canned responses and a listener replays a scripted queue of requests, so a
85//! program that serves is testable without a socket. [`Http::denied`] refuses
86//! everything and says why.
87
88use std::collections::BTreeMap;
89use std::io::{BufRead, BufReader, ErrorKind, Read, Write};
90use std::net::{TcpListener, TcpStream, ToSocketAddrs};
91use std::rc::Rc;
92use std::sync::atomic::{AtomicU64, Ordering};
93use std::sync::{Arc, Mutex};
94use std::time::{Duration, Instant};
95
96use cove_schema::builtins::RESULT;
97
98use crate::error::RuntimeError;
99use crate::host::{HostApi, NoReentry, Reentry, ResourceHandle};
100use crate::schema::ModuleSchema;
101use crate::value::{EnumValue, Repr, StructValue, Value};
102
103/// How long the real host is willing to spend reading one whole request.
104///
105/// This is the allowance for the request line, the headers, and the body
106/// together, not for each read that makes them up: a connection that opens
107/// and dribbles a byte at a time would otherwise hold `handle` open for as
108/// long as the peer chose to keep it. A run with a deadline shortens it
109/// further, since waiting past the run's own end serves nobody.
110const READ_TIMEOUT: Duration = Duration::from_secs(30);
111
112/// How long either of this host's waits goes without looking at the run.
113///
114/// The same reasoning as `clock`'s own `WATCH_INTERVAL`, and the same value:
115/// this is a granularity, so it decides only how long past a cancellation or
116/// a deadline a wait may run before it notices. It bounds both waits this
117/// module has — the sleep between looks at a listener nobody has connected to
118/// yet, and the socket timeout a client reads a response under — because they
119/// are the same trade in two places, and two numbers would be two answers to
120/// one question. Waiting is what keeps a poll a poll rather than a spin: a
121/// few hundred wakeups a second cost nothing measurable, and a loop with no
122/// wait in it would burn a core to learn the same thing.
123const POLL_INTERVAL: Duration = Duration::from_millis(2);
124
125/// The most of a request line the real host will read.
126///
127/// Eight kibibytes is the figure the common servers settled on, and it is
128/// generous for what it has to hold: a method, a target, and a version. A
129/// peer that needs more of a URL than this has a problem this host cannot
130/// help with, and a peer sending an endless first line is the case the bound
131/// exists for — the read stops here rather than at the end of what the peer
132/// felt like sending.
133const MAX_REQUEST_LINE: usize = 8 * 1024;
134
135/// The most of one header line the real host will read.
136///
137/// The same eight kibibytes, for the same reason. One header is a name and a
138/// value, and a value that does not fit in eight kibibytes is not a value
139/// this host has any use for.
140const MAX_HEADER_BYTES: usize = 8 * 1024;
141
142/// The most the real host will read of all the header lines together.
143///
144/// A bound on each line alone bounds nothing: a peer can send a great many
145/// short ones. So the lines are counted as they arrive and the total is what
146/// stops them, at four full-size headers' worth, which is more than any
147/// ordinary client sends and far less than a peer with time on its hands
148/// would like to send.
149const MAX_HEADERS_BYTES: usize = 32 * 1024;
150
151/// How many header lines the real host will read.
152///
153/// The byte total already bounds the memory; this bounds the work, since a
154/// hundred thousand empty headers cost almost no bytes and still have to be
155/// looked at one at a time. A hundred is several times what a browser sends.
156const MAX_HEADER_COUNT: usize = 100;
157
158/// The most body the real host will hold.
159///
160/// One mebibyte, because this host answers JSON on loopback and is not a
161/// place to upload a file to. The number is the whole of the promise: a peer
162/// cannot make this process hold more than this per request no matter what
163/// its `Content-Length` says, since the claim is checked against this bound
164/// before a byte of body is read and the bytes are collected as they arrive
165/// rather than reserved against the claim.
166const MAX_BODY_BYTES: usize = 1024 * 1024;
167
168/// The most of a response the real client will hold.
169///
170/// The same mebibyte as [`MAX_BODY_BYTES`], because it is the same promise
171/// read from the other side: this host will not let something at the other
172/// end of a socket decide how much of this process it occupies, and which end
173/// opened the connection does not change that. A server a program chose to
174/// fetch from is not more trustworthy than a peer that connected to the
175/// listener — a URL in a manifest reaches whatever is answering on that port
176/// today.
177///
178/// The bound is the host's and not the program's, for the reason ADR 0018
179/// gives about `files.Reader.readLine`: a caller has no way to know how large
180/// an answer it has not seen yet is, so a per-request bound would make every
181/// caller answer a question about a response that has not arrived. It is
182/// counted over the whole response as it arrives — status line, headers, and
183/// body together, which is what the client actually holds — rather than
184/// checked against a `Content-Length`, since what a peer claims never decides
185/// what this host allocates.
186const MAX_RESPONSE_BYTES: usize = 1024 * 1024;
187
188/// What `http` declares about itself.
189///
190/// The table is [`cove_schema::hosts::HTTP`], so the description the compiler
191/// checks a call against, the one the boundary dispatches through, and the
192/// one `cove trace` reads out of a recorded file are the same bytes.
193const SCHEMA: ModuleSchema = cove_schema::hosts::HTTP;
194
195/// `http`: reaching a server, and being one.
196pub struct Http {
197    source: HttpSource,
198    /// Every listener this host still has open, by the identity it issued.
199    ///
200    /// This is the whole of what a handle addresses. A handle whose entry is
201    /// gone — closed, or issued by some other run — finds nothing here, which
202    /// is what makes a stale handle a reported error rather than a call on a
203    /// socket that means something else now.
204    open: Mutex<BTreeMap<u64, Listener>>,
205    /// The identity the next listener gets. Zero is never issued, so a
206    /// handle's number reads as the order the run opened them in.
207    next_id: AtomicU64,
208}
209
210enum HttpSource {
211    /// Sockets, for real.
212    Real,
213    /// Canned responses for `fetch`, and a scripted request queue for a
214    /// listener to replay.
215    Recorded {
216        answers: BTreeMap<String, RecordedResponse>,
217        requests: Vec<ScriptedRequest>,
218        /// What every handled request answered, in order, so a test can read
219        /// back what the program served.
220        served: Arc<Mutex<Vec<String>>>,
221    },
222    /// A host with no network at all.
223    Denied,
224}
225
226/// One answer a fake client gives, as a test wrote it.
227///
228/// It is a status and a body because that is what an `http.Response` is, so a
229/// test writes down exactly what the program will see. A fake that recorded
230/// only bodies could not produce the one thing a client most often has to
231/// handle — a server that answered, and answered badly.
232#[derive(Clone, Debug)]
233pub struct RecordedResponse {
234    /// The status the fake server sent, such as `200` or `404`.
235    pub status: i64,
236    /// The body it carried.
237    pub body: String,
238}
239
240impl RecordedResponse {
241    /// A `200` carrying `body`, which is what most recorded answers are.
242    pub fn ok(body: &str) -> RecordedResponse {
243        RecordedResponse::new(200, body)
244    }
245
246    /// A `status` carrying `body`.
247    pub fn new(status: i64, body: &str) -> RecordedResponse {
248        RecordedResponse {
249            status,
250            body: body.to_string(),
251        }
252    }
253}
254
255/// One request a fake listener hands to the program, as a test wrote it.
256#[derive(Clone, Debug)]
257pub struct ScriptedRequest {
258    /// `Get` or `Post`, the case name of `http.Method`.
259    pub method: String,
260    /// The path, such as `/health`.
261    pub path: String,
262    /// The request body, which is empty for a `Get`.
263    pub body: String,
264}
265
266impl ScriptedRequest {
267    /// A `Get` of `path` with no body.
268    pub fn get(path: &str) -> ScriptedRequest {
269        ScriptedRequest {
270            method: "Get".to_string(),
271            path: path.to_string(),
272            body: String::new(),
273        }
274    }
275
276    /// A `Post` of `body` to `path`.
277    pub fn post(path: &str, body: &str) -> ScriptedRequest {
278        ScriptedRequest {
279            method: "Post".to_string(),
280            path: path.to_string(),
281            body: body.to_string(),
282        }
283    }
284}
285
286/// One open listener, on whichever side of the boundary it really lives.
287enum Listener {
288    /// A bound socket.
289    Real(TcpListener),
290    /// A queue of requests, and the port the program asked for.
291    Scripted {
292        port: i64,
293        requests: Vec<ScriptedRequest>,
294    },
295}
296
297impl Http {
298    /// A host that speaks HTTP/1.1 over TCP.
299    ///
300    /// `listen` binds loopback only. Granting `http` is authority to talk to
301    /// the machine's own network stack, not permission to publish a service
302    /// on every interface the machine has.
303    pub fn real() -> Self {
304        Http::with_source(HttpSource::Real)
305    }
306
307    /// A fake that answers `fetch` from `answers` and lets a listener replay
308    /// `requests`, for tests.
309    ///
310    /// The key is the URL exactly as the program writes it. This is a
311    /// recorded answer, not a client: a fake that resolved a host name would
312    /// be reaching the network the grant was supposed to describe.
313    pub fn recorded(
314        answers: BTreeMap<String, RecordedResponse>,
315        requests: Vec<ScriptedRequest>,
316    ) -> Self {
317        Http::with_source(HttpSource::Recorded {
318            answers,
319            requests,
320            served: Arc::new(Mutex::new(Vec::new())),
321        })
322    }
323
324    /// What this host has served so far, for a test to read back.
325    ///
326    /// A real host serves to a socket and keeps nothing, so it answers with
327    /// an empty log: what went out is on the other end of the connection.
328    pub fn served(&self) -> Served {
329        match &self.source {
330            HttpSource::Recorded { served, .. } => Served(Arc::clone(served)),
331            _ => Served(Arc::new(Mutex::new(Vec::new()))),
332        }
333    }
334
335    /// A host with no network, which refuses every call and says so.
336    pub fn denied() -> Self {
337        Http::with_source(HttpSource::Denied)
338    }
339
340    fn with_source(source: HttpSource) -> Self {
341        Http {
342            source,
343            open: Mutex::new(BTreeMap::new()),
344            next_id: AtomicU64::new(1),
345        }
346    }
347
348    /// Opens a listener and issues the handle that names it.
349    fn listen(&self, port: i64) -> Result<Value, RuntimeError> {
350        if !(0..=65535).contains(&port) {
351            return Ok(Value::err(Value::error(format!(
352                "http: {port} is not a port number"
353            ))));
354        }
355        let listener = match &self.source {
356            HttpSource::Real => match TcpListener::bind(("127.0.0.1", port as u16)) {
357                Ok(listener) => Listener::Real(listener),
358                Err(e) => {
359                    return Ok(Value::err(Value::error(format!(
360                        "http: cannot listen on 127.0.0.1:{port}: {e}"
361                    ))))
362                }
363            },
364            HttpSource::Recorded { requests, .. } => Listener::Scripted {
365                port,
366                requests: requests.clone(),
367            },
368            HttpSource::Denied => {
369                return Ok(Value::err(Value::error(
370                    "http: this host has no network, so nothing can listen",
371                )))
372            }
373        };
374        let id = self.next_id.fetch_add(1, Ordering::Relaxed);
375        self.locked().insert(id, listener);
376        Ok(Value::ok(Value(Repr::Resource(ResourceHandle::new(
377            "http",
378            &SCHEMA.resources[0],
379            id,
380        )))))
381    }
382
383    fn locked(&self) -> std::sync::MutexGuard<'_, BTreeMap<u64, Listener>> {
384        self.open
385            .lock()
386            .unwrap_or_else(|poisoned| poisoned.into_inner())
387    }
388
389    /// `GET url`, answering the response the server sent.
390    ///
391    /// Whatever status came back is an `Ok`, because a server that answered
392    /// `404` answered: the status is a fact about the endpoint and the client
393    /// is the wrong place to decide which facts are failures. An `Err` is
394    /// reserved for a run that learned nothing — a URL this host will not
395    /// send, a connection it could not make, a read that ran out of time, or
396    /// a response larger than it will hold — so those two are told apart by
397    /// their shape rather than by the wording of a message.
398    ///
399    /// `back` is the way back to the run, and the real client needs it for
400    /// both of the bounds it is under: what the run has left before its
401    /// deadline, which clamps how long it will wait for an answer, and
402    /// whether the run has been asked to stop, which a `clock.timeout` around
403    /// this call raises while the client is already waiting. A run with
404    /// neither is bounded by [`READ_TIMEOUT`] alone.
405    fn fetch(&self, url: &str, back: &dyn Reentry) -> Value {
406        match &self.source {
407            HttpSource::Real => match fetch_over_tcp(url, back) {
408                Ok((status, body)) => Value::ok(response(status, &body)),
409                Err(message) => Value::err(Value::error(message)),
410            },
411            HttpSource::Recorded { answers, .. } => match answers.get(url) {
412                Some(answer) => Value::ok(response(answer.status, &answer.body)),
413                None => Value::err(Value::error(format!(
414                    "http: no recorded answer for `{url}`"
415                ))),
416            },
417            HttpSource::Denied => Value::err(Value::error(
418                "http: this host has no network, so no request can be sent",
419            )),
420        }
421    }
422
423    /// Serves one request, and answers whether one arrived.
424    ///
425    /// `false` means there is nothing more to serve, which is what ends the
426    /// loop the program wrote around this call. A fake listener says it when
427    /// its scripted queue is empty. A real one says it when the run was
428    /// cancelled, or ran out of time, while it was waiting for a connection:
429    /// the wait is a poll rather than a blocking `accept`, so the run's
430    /// controls reach it.
431    ///
432    /// Answering `false` rather than an error of its own is deliberate. The
433    /// program's loop is what should end, and the reason the run stopped
434    /// belongs to the budget that holds the limit — the next safepoint raises
435    /// `Cancelled` or `Deadline` naming the value that was configured. A host
436    /// that invented a failure here would put a second, worse account of the
437    /// same event in front of the reader, and would hand a program a Cove
438    /// `Err` it could catch and ignore.
439    fn serve_one(
440        &self,
441        handle: &ResourceHandle,
442        routes: &Value,
443        back: &mut dyn Reentry,
444    ) -> Result<Value, RuntimeError> {
445        let Value(Repr::Array(routes)) = routes else {
446            return Err(RuntimeError::new(format!(
447                "`http.Server.handle` takes an `Array<http.Route>`, but found `{}`",
448                routes.type_name()
449            )));
450        };
451        // Whatever the next request needs is taken while the lock is held,
452        // and the lock is released before the handler runs: a handler is
453        // Cove code, and Cove code may call this host again.
454        let next = {
455            let mut open = self.locked();
456            match open.get_mut(&handle.id) {
457                None => return Err(stale(handle, "handle")),
458                Some(Listener::Scripted { requests, .. }) => {
459                    if requests.is_empty() {
460                        return Ok(Value::ok(Value(Repr::Bool(false))));
461                    }
462                    Next::Scripted(requests.remove(0))
463                }
464                Some(Listener::Real(listener)) => match listener.try_clone() {
465                    Ok(listener) => Next::Real(listener),
466                    Err(e) => {
467                        return Ok(Value::err(Value::error(format!(
468                            "http: cannot accept on {handle}: {e}"
469                        ))))
470                    }
471                },
472            }
473        };
474
475        let (asked, connection) = match next {
476            Next::Scripted(scripted) => (scripted, None),
477            Next::Real(listener) => match accept_when_ready(&listener, back) {
478                Waited::Connected(stream) => {
479                    // One deadline for the whole request, and never more of it
480                    // than the run itself has left.
481                    let until = Instant::now() + bounded(READ_TIMEOUT, back.time_left());
482                    match read_request(&stream, until) {
483                        Ok((method, path, body)) => (
484                            ScriptedRequest {
485                                method: method_case(&method),
486                                path,
487                                body,
488                            },
489                            Some(stream),
490                        ),
491                        // A request that could not be read is still a request
492                        // that arrived, so the peer is told why and the
493                        // program's loop goes round again.
494                        Err(unread) => {
495                            let _ = write_response(
496                                &stream,
497                                unread.status,
498                                &json_string(&unread.message),
499                            );
500                            return Ok(Value::ok(Value(Repr::Bool(true))));
501                        }
502                    }
503                }
504                Waited::Stopped => return Ok(Value::ok(Value(Repr::Bool(false)))),
505                Waited::Failed(e) => {
506                    return Ok(Value::err(Value::error(format!(
507                        "http: cannot accept on {handle}: {e}"
508                    ))))
509                }
510            },
511        };
512
513        let (status, body) = match route_for(routes, &asked) {
514            Some(handler) => {
515                let answered = back.call(
516                    &handler,
517                    vec![request(&asked.method, &asked.path, &asked.body)],
518                )?;
519                response_of(&answered)?
520            }
521            None => (
522                404,
523                json_string(&format!("no route for {} {}", asked.method, asked.path)),
524            ),
525        };
526
527        match connection {
528            Some(stream) => {
529                if let Err(message) = write_response(&stream, status, &body) {
530                    return Ok(Value::err(Value::error(message)));
531                }
532            }
533            None => self.record_served(status, &body),
534        }
535        Ok(Value::ok(Value(Repr::Bool(true))))
536    }
537
538    /// Remembers what a fake listener answered, so a test can read it back.
539    fn record_served(&self, status: i64, body: &str) {
540        if let HttpSource::Recorded { served, .. } = &self.source {
541            served
542                .lock()
543                .unwrap_or_else(|poisoned| poisoned.into_inner())
544                .push(format!("{status} {body}"));
545        }
546    }
547}
548
549/// Where the next request is coming from, decided while the lock is held.
550enum Next {
551    Scripted(ScriptedRequest),
552    Real(TcpListener),
553}
554
555/// What waiting for a connection came to.
556enum Waited {
557    /// A client connected, and its stream is ready to be read.
558    Connected(TcpStream),
559    /// The run was cancelled or ran out of time while nothing was arriving.
560    Stopped,
561    /// The listener itself failed, which is the host's problem rather than
562    /// the run's.
563    Failed(std::io::Error),
564}
565
566/// Waits for one connection, in steps short enough for the run's controls to
567/// reach the wait.
568///
569/// A blocking `accept` is the whole of the problem this exists to solve. The
570/// runtime checks cancellation and the deadline before it dispatches a host
571/// call and cannot check either again until the call returns, so a `handle`
572/// sitting in `accept` with nobody connecting is unreachable by both: the
573/// comment that used to sit on [`Http::serve_one`], promising that such a
574/// program "runs until the run itself is stopped", described something the
575/// implementation could not do. Polling a nonblocking listener turns the wait
576/// into a loop with a place to look in it, and the looking is what makes the
577/// promise true.
578///
579/// The listener is a clone of the one the host owns and no lock is held here,
580/// which matters more than it looks: this wait is the longest thing this
581/// module ever does, and holding the host's mutex across it would queue every
582/// other task behind an idle server.
583fn accept_when_ready(listener: &TcpListener, back: &dyn Reentry) -> Waited {
584    if let Err(e) = listener.set_nonblocking(true) {
585        return Waited::Failed(e);
586    }
587    loop {
588        match listener.accept() {
589            Ok((stream, _)) => {
590                // A nonblocking listener hands back a nonblocking stream on
591                // some platforms, and reading one of those in a loop is
592                // exactly the spin this function exists to avoid. The read
593                // path wants an ordinary blocking socket with a timeout on
594                // it, so the mode goes back before anything reads.
595                return match stream.set_nonblocking(false) {
596                    Ok(()) => Waited::Connected(stream),
597                    Err(e) => Waited::Failed(e),
598                };
599            }
600            Err(e) if e.kind() == ErrorKind::WouldBlock => {
601                // A connection that is already waiting is served even by a
602                // run that is stopping, because it cost nothing to take and
603                // refusing it would leave a client with no answer at all.
604                // This is where there is nothing to lose by giving up.
605                if stopped(back) {
606                    return Waited::Stopped;
607                }
608                std::thread::sleep(POLL_INTERVAL);
609            }
610            // A signal delivered to this thread interrupts the call without
611            // saying anything about the socket, so the socket is asked again.
612            Err(e) if e.kind() == ErrorKind::Interrupted => {}
613            Err(e) => return Waited::Failed(e),
614        }
615    }
616}
617
618/// Whether the run behind a host call has asked a wait to end.
619///
620/// A cancellation and a deadline with nothing left are one question to a host
621/// that is waiting, and the answer to both is to stop waiting. They are asked
622/// together, here, so that the two waits this module has — the listener's for
623/// a connection and the client's for a response — cannot come to disagree
624/// about what a bound is. They did: the listener asked both between polls and
625/// the client asked neither, so a `clock.timeout` around a `fetch` was
626/// reported when the read returned rather than cutting it short.
627///
628/// A cancellation is the flag [`Reentry::is_cancelled`] describes, which is
629/// everything a safepoint in Cove code would stop on — the run's own stop,
630/// the task's, and the flag of any bounded call this one is nested inside. A
631/// `clock.timeout` around a host call is that last one.
632fn stopped(back: &dyn Reentry) -> bool {
633    back.is_cancelled() || back.time_left().is_some_and(|left| left.is_zero())
634}
635
636/// The shorter of what this host allows itself and what the run has left.
637///
638/// A host willing to wait thirty seconds for a peer should not wait thirty
639/// seconds on behalf of a run that had two hundred milliseconds to live. A
640/// run with no deadline is bounded by the host's own allowance alone, which
641/// is the only bound there is to apply.
642fn bounded(allowance: Duration, time_left: Option<Duration>) -> Duration {
643    match time_left {
644        Some(left) => allowance.min(left),
645        None => allowance,
646    }
647}
648
649/// The handler of the first route that matches `asked`.
650fn route_for(routes: &[Value], asked: &ScriptedRequest) -> Option<Value> {
651    routes.iter().find_map(|route| {
652        let Value(Repr::Struct(route)) = route else {
653            return None;
654        };
655        let method = match route.get("method") {
656            Some(Value(Repr::Enum(method))) => method.case.to_string(),
657            _ => return None,
658        };
659        let path = match route.get("path") {
660            Some(Value(Repr::Str(path))) => path.to_string(),
661            _ => return None,
662        };
663        (method == asked.method && path == asked.path).then(|| route.get("handler").cloned())?
664    })
665}
666
667/// The status and body a handler answered with.
668///
669/// A handler may answer with a response, or with a `Result` carrying one:
670/// both are what Cove source writes, and an `Err` is an ordinary failure the
671/// server reports as a `500` rather than a reason to stop serving.
672fn response_of(value: &Value) -> Result<(i64, String), RuntimeError> {
673    match value {
674        Value(Repr::Struct(structure)) if &*structure.type_name == "http.Response" => {
675            let status = match structure.get("status") {
676                Some(Value(Repr::Int(status))) => *status,
677                _ => 200,
678            };
679            let body = match structure.get("body") {
680                Some(Value(Repr::Str(body))) => body.to_string(),
681                Some(other) => json_of(other),
682                None => String::new(),
683            };
684            Ok((status, body))
685        }
686        Value(Repr::Enum(result)) if &*result.type_name == RESULT.name => {
687            match value.ok_payload() {
688                Some(payload) => response_of(payload.first().unwrap_or(&Value(Repr::Unit))),
689                None => Ok((
690                    500,
691                    json_string(
692                        &result
693                            .payload
694                            .first()
695                            .map(ToString::to_string)
696                            .unwrap_or_default(),
697                    ),
698                )),
699            }
700        }
701        other => Err(RuntimeError::new(format!(
702            "a route handler must answer with an `http.Response`, but this one answered `{}`",
703            other.type_name()
704        ))
705        .with_help("build one with `http.json(status, value)`")),
706    }
707}
708
709/// The `http.Method` case a wire method name is.
710fn method_case(method: &str) -> String {
711    match method.to_ascii_uppercase().as_str() {
712        "POST" => "Post".to_string(),
713        _ => "Get".to_string(),
714    }
715}
716
717/// What a fake host served, for a test to read back.
718///
719/// A test drives the program and then asks this what went out, rather than
720/// asking the host: the host is the program's boundary, and a test that
721/// reached into it would be testing the boundary rather than the program.
722#[derive(Clone)]
723pub struct Served(Arc<Mutex<Vec<String>>>);
724
725impl Served {
726    /// Every response the program served, in order, as `<status> <body>`.
727    pub fn responses(&self) -> Vec<String> {
728        self.0
729            .lock()
730            .unwrap_or_else(|poisoned| poisoned.into_inner())
731            .clone()
732    }
733}
734
735/// A call on a handle whose resource this host no longer has.
736fn stale(handle: &ResourceHandle, op: &str) -> RuntimeError {
737    RuntimeError::new(format!(
738        "`{handle}` is closed, so `{op}` has nothing to act on"
739    ))
740    .with_rule(
741        "A host resource handle names a resource the host owns. Closing the resource ends the handle; the name outlives it and addresses nothing.",
742    )
743    .with_help("open a new one, or move the `close` after the last use")
744}
745
746impl HostApi for Http {
747    fn module_schema(&self) -> ModuleSchema {
748        SCHEMA
749    }
750
751    /// `fetch` is the one module-level operation that waits, so it is the one
752    /// that needs the way back: not to run a callback, but to ask how long
753    /// the run it belongs to still has and whether it has been told to stop.
754    /// Everything else here touches only what it was given.
755    fn call_with(
756        &self,
757        op: &str,
758        args: Vec<Value>,
759        back: &mut dyn Reentry,
760    ) -> Result<Value, RuntimeError> {
761        match op {
762            "fetch" => {
763                let [Value(Repr::Str(url))] = args.as_slice() else {
764                    unreachable!("checked by HostRegistry::call")
765                };
766                Ok(self.fetch(url, back))
767            }
768            _ => self.call(op, args),
769        }
770    }
771
772    fn call(&self, op: &str, args: Vec<Value>) -> Result<Value, RuntimeError> {
773        match op {
774            "fetch" => {
775                let [Value(Repr::Str(url))] = args.as_slice() else {
776                    unreachable!("checked by HostRegistry::call")
777                };
778                // Reached only by a caller holding the host directly, which
779                // has no run behind it: no deadline to be clamped by, and
780                // nothing that could stop it partway.
781                Ok(self.fetch(url, &NoReentry))
782            }
783            "json" => {
784                let [Value(Repr::Int(status)), body] = args.as_slice() else {
785                    unreachable!("checked by HostRegistry::call")
786                };
787                Ok(response(*status, &json_of(body)))
788            }
789            "listen" => {
790                let [Value(Repr::Int(port))] = args.as_slice() else {
791                    unreachable!("checked by HostRegistry::call")
792                };
793                self.listen(*port)
794            }
795            _ => unreachable!("checked by HostRegistry::call"),
796        }
797    }
798
799    fn call_resource(
800        &self,
801        handle: &ResourceHandle,
802        op: &str,
803        args: Vec<Value>,
804        back: &mut dyn Reentry,
805    ) -> Result<Value, RuntimeError> {
806        match op {
807            "port" => match self.locked().get(&handle.id) {
808                Some(Listener::Real(listener)) => Ok(Value(Repr::Int(
809                    listener
810                        .local_addr()
811                        .map(|a| i64::from(a.port()))
812                        .unwrap_or(0),
813                ))),
814                Some(Listener::Scripted { port, .. }) => Ok(Value(Repr::Int(*port))),
815                None => Err(stale(handle, "port")),
816            },
817            "handle" => {
818                let [routes] = args.as_slice() else {
819                    unreachable!("checked by HostRegistry::call")
820                };
821                self.serve_one(handle, routes, back)
822            }
823            "close" => match self.locked().remove(&handle.id) {
824                Some(_) => Ok(Value::ok(Value(Repr::Unit))),
825                None => Err(stale(handle, "close")),
826            },
827            _ => unreachable!("checked by HostRegistry::call_resource"),
828        }
829    }
830}
831
832/// `http.Response(status: ..., body: ...)`.
833fn response(status: i64, body: &str) -> Value {
834    Value(Repr::Struct(Rc::new(StructValue {
835        type_name: "http.Response".into(),
836        fields: vec![
837            ("status".into(), Value(Repr::Int(status))),
838            ("body".into(), Value(Repr::Str(body.into()))),
839        ],
840        opaque: false,
841    })))
842}
843
844/// `http.Request(method: ..., path: ..., body: ...)`.
845fn request(method: &str, path: &str, body: &str) -> Value {
846    Value(Repr::Struct(Rc::new(StructValue {
847        type_name: "http.Request".into(),
848        fields: vec![
849            ("method".into(), method_value(method)),
850            ("path".into(), Value(Repr::Str(path.into()))),
851            ("body".into(), Value(Repr::Str(body.into()))),
852        ],
853        opaque: false,
854    })))
855}
856
857/// `http.Method.Get` and `http.Method.Post`.
858fn method_value(case: &str) -> Value {
859    Value(Repr::Enum(Box::new(EnumValue {
860        type_name: "http.Method".into(),
861        case: case.into(),
862        payload: crate::value::Payload::Empty,
863    })))
864}
865
866/// Renders a Cove value as JSON.
867///
868/// This is the encoding `http.json` names, so it is the host's and not the
869/// trace's: a response body is what a client will read, with no room for the
870/// tags a trace needs in order to be read back as a value.
871fn json_of(value: &Value) -> String {
872    match value {
873        Value(Repr::Unit) => "null".to_string(),
874        Value(Repr::Bool(b)) => b.to_string(),
875        Value(Repr::Int(n)) => n.to_string(),
876        Value(Repr::Float(x)) if x.is_finite() => format!("{x:?}"),
877        Value(Repr::Str(s)) => json_string(s),
878        Value(Repr::Array(items)) => {
879            let items = items.iter().map(json_of).collect::<Vec<_>>().join(",");
880            format!("[{items}]")
881        }
882        Value(Repr::Vector(storage)) => {
883            let items = storage
884                .elements
885                .borrow()
886                .iter()
887                .map(json_of)
888                .collect::<Vec<_>>()
889                .join(",");
890            format!("[{items}]")
891        }
892        Value(Repr::Map(entries)) => {
893            let entries = entries
894                .iter()
895                .map(|(key, value)| format!("{}:{}", json_string(&key.to_string()), json_of(value)))
896                .collect::<Vec<_>>()
897                .join(",");
898            format!("{{{entries}}}")
899        }
900        Value(Repr::Struct(structure)) => {
901            let fields = structure
902                .fields
903                .iter()
904                .map(|(name, field)| format!("{}:{}", json_string(name), json_of(field)))
905                .collect::<Vec<_>>()
906                .join(",");
907            format!("{{{fields}}}")
908        }
909        // A case with no payload is its name, which is how an enum reads as
910        // JSON; one with a payload carries it alongside.
911        Value(Repr::Enum(enumeration)) if enumeration.payload.is_empty() => {
912            json_string(&enumeration.case)
913        }
914        Value(Repr::Enum(enumeration)) => {
915            let payload = enumeration
916                .payload
917                .iter()
918                .map(json_of)
919                .collect::<Vec<_>>()
920                .join(",");
921            format!("{{{}:[{payload}]}}", json_string(&enumeration.case))
922        }
923        // Anything else has no JSON of its own, so it goes out as what it
924        // printed rather than as a shape a reader would misread.
925        other => json_string(&other.to_string()),
926    }
927}
928
929/// One JSON string literal, with the escapes JSON requires.
930fn json_string(s: &str) -> String {
931    let mut out = String::with_capacity(s.len() + 2);
932    out.push('"');
933    for c in s.chars() {
934        match c {
935            '"' => out.push_str("\\\""),
936            '\\' => out.push_str("\\\\"),
937            '\n' => out.push_str("\\n"),
938            '\r' => out.push_str("\\r"),
939            '\t' => out.push_str("\\t"),
940            c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
941            c => out.push(c),
942        }
943    }
944    out.push('"');
945    out
946}
947
948/// Sends one `GET` and reads the status and body that came back.
949///
950/// Every status the peer sent is answered, including the ones a program will
951/// call failures. Deciding that here is what the old signature did, and it
952/// cost the caller the difference between a server that refused and a server
953/// that was never reached: both arrived as `Err` and only the wording told
954/// them apart. A status is data, so it travels as data.
955///
956/// One allowance covers the whole exchange — [`READ_TIMEOUT`], clamped by
957/// whatever the run has left — and it is taken before the connect, so a slow
958/// handshake spends it rather than being handed a fresh one for the read. The
959/// response is bounded in size by [`MAX_RESPONSE_BYTES`] as well, checked as
960/// the bytes arrive: a peer that keeps sending is stopped at the bound rather
961/// than after it, which is the only order in which a bound on an allocation
962/// means anything.
963///
964/// `back` is here rather than a pre-computed `Duration` because a deadline is
965/// not the only bound a `fetch` is under. A run's deadline can be folded into
966/// a timeout before a read begins; a `clock.timeout` raised *while* the read
967/// is in flight is a flag, and a flag has to be looked at. So the read is a
968/// poll — see [`read_response_within`] — and this is what it polls.
969fn fetch_over_tcp(url: &str, back: &dyn Reentry) -> Result<(i64, String), String> {
970    let (authority, path) = split_url(url)?;
971    let allowance = bounded(READ_TIMEOUT, back.time_left());
972    if allowance.is_zero() {
973        return Err(format!(
974            "http: the run ran out of time before {authority} could be asked"
975        ));
976    }
977    if back.is_cancelled() {
978        return Err(format!(
979            "http: the run was stopped before {authority} could be asked"
980        ));
981    }
982    // The clock starts before the connect, because a connect that spends the
983    // allowance has spent it.
984    let until = Instant::now() + allowance;
985    let mut stream = connect_within(&authority, allowance)?;
986    let request = format!(
987        "GET {path} HTTP/1.1\r\nHost: {authority}\r\nConnection: close\r\nAccept: */*\r\n\r\n"
988    );
989    stream
990        .write_all(request.as_bytes())
991        .map_err(|e| format!("http: cannot send to {authority}: {e}"))?;
992    let answer = read_response_within(&stream, &authority, until, back)?;
993    let answer = String::from_utf8_lossy(&answer).into_owned();
994    let (head, body) = answer
995        .split_once("\r\n\r\n")
996        .ok_or_else(|| format!("http: {authority} sent no complete response"))?;
997    let status = head
998        .lines()
999        .next()
1000        .and_then(|line| line.split_whitespace().nth(1))
1001        .and_then(|code| code.parse::<i64>().ok())
1002        .ok_or_else(|| format!("http: {authority} sent no status line"))?;
1003    Ok((status, body.to_string()))
1004}
1005
1006/// Opens a connection to `authority`, waiting no longer than `allowance`.
1007///
1008/// `TcpStream::connect` has no timeout of its own, so a host that is routable
1009/// and silent holds a run for as long as the platform's own handshake retries
1010/// take — minutes on some of them, and longer than [`READ_TIMEOUT`] on all of
1011/// them. `connect_timeout` bounds that, and it wants an address rather than a
1012/// name, which is why the name is resolved here first and why every address
1013/// it resolves to is tried in turn: a name with one unreachable address and
1014/// one reachable address is a name this host can reach.
1015///
1016/// Two things this does not do, and a reader should have them straight.
1017/// Resolution is still unbounded, because `std` offers no way to bound it
1018/// short of a thread that outlives the call. And the handshake is a bound
1019/// rather than a poll: nothing looks at the run's cancellation while it is in
1020/// flight, so a `clock.timeout` raised during a connect is still reported
1021/// when the connect returns. What is closed is a connect that outlived every
1022/// bound there was; what is left is one that outlives a cancellation by at
1023/// most this allowance. Closing that needs a nonblocking connect polled for
1024/// writability, which `std` does not portably offer either.
1025fn connect_within(authority: &str, allowance: Duration) -> Result<TcpStream, String> {
1026    let addresses = authority
1027        .to_socket_addrs()
1028        .map_err(|e| format!("http: cannot connect to {authority}: {e}"))?;
1029    let mut refused = None;
1030    for address in addresses {
1031        match TcpStream::connect_timeout(&address, allowance) {
1032            Ok(stream) => return Ok(stream),
1033            Err(e) => refused = Some(e),
1034        }
1035    }
1036    Err(match refused {
1037        Some(e) => format!("http: cannot connect to {authority}: {e}"),
1038        // `to_socket_addrs` answered without failing and without an address,
1039        // so there was nothing to try and no error to report but that.
1040        None => format!("http: cannot connect to {authority}: it names no address"),
1041    })
1042}
1043
1044/// Reads a whole response, in steps short enough for the run's controls to
1045/// reach the wait.
1046///
1047/// This is [`accept_when_ready`]'s shape, for [`accept_when_ready`]'s reason:
1048/// a host call is a hole in the safepoint chain for as long as it lasts, so a
1049/// wait that means to be bounded needs somewhere in it to look. One blocking
1050/// read under the whole allowance had nowhere. The run's deadline was folded
1051/// into the socket's timeout before the read began, which did bound it — but
1052/// a `clock.timeout` raises a flag partway through, and nothing was reading
1053/// the flag, so such a bound was reported when the peer finally answered or
1054/// when thirty seconds were up. Giving the socket [`POLL_INTERVAL`] instead
1055/// of the whole allowance turns the wait into a loop with a place to look in
1056/// it, and [`stopped`] is what it looks at.
1057///
1058/// `until` is one allowance for the whole response and does not start again
1059/// on a read that succeeded: a server dribbling a byte at a time must not be
1060/// able to renew it. The timeout, by contrast, is set once rather than
1061/// re-armed each time round, because unlike the listener's it does not shrink
1062/// — the cost is that the last wait may overrun `until` by a poll interval,
1063/// and the saving is a syscall per chunk of a large response.
1064///
1065/// The [`MAX_RESPONSE_BYTES`] bound is checked after each read rather than by
1066/// asking the peer how much it intends to send, so a response with no
1067/// `Content-Length`, a dishonest one, or none at all is held to the same
1068/// number. The buffer grows with what arrived and never with what was
1069/// claimed.
1070fn read_response_within(
1071    stream: &TcpStream,
1072    authority: &str,
1073    until: Instant,
1074    back: &dyn Reentry,
1075) -> Result<Vec<u8>, String> {
1076    stream
1077        .set_read_timeout(Some(POLL_INTERVAL))
1078        .map_err(|e| format!("http: cannot bound the read from {authority}: {e}"))?;
1079    let mut reader = stream;
1080    let mut answer = Vec::new();
1081    let mut chunk = [0u8; 8 * 1024];
1082    loop {
1083        // Both bounds are asked before every read and not only after one that
1084        // came back empty, so a peer that keeps sending cannot outlast them
1085        // by never letting the socket go quiet.
1086        if stopped(back) {
1087            return Err(format!(
1088                "http: the run was stopped before {authority} answered"
1089            ));
1090        }
1091        if Instant::now() >= until {
1092            return Err(format!(
1093                "http: {authority} did not answer within the time allowed for it"
1094            ));
1095        }
1096        match reader.read(&mut chunk) {
1097            Ok(0) => return Ok(answer),
1098            Ok(read) => {
1099                answer.extend_from_slice(&chunk[..read]);
1100                if answer.len() > MAX_RESPONSE_BYTES {
1101                    return Err(format!(
1102                        "http: {authority} sent more than the {MAX_RESPONSE_BYTES} bytes this host reads"
1103                    ));
1104                }
1105            }
1106            // Nothing arrived within one interval. That is neither a failure
1107            // nor the end of the response; it is the pause this loop exists
1108            // to have, and what happens in it is the two checks above.
1109            Err(e) if matches!(e.kind(), ErrorKind::WouldBlock | ErrorKind::TimedOut) => {}
1110            // A signal delivered to this thread interrupts the call without
1111            // saying anything about the socket, so the socket is asked again.
1112            Err(e) if e.kind() == ErrorKind::Interrupted => {}
1113            Err(e) => return Err(format!("http: cannot read from {authority}: {e}")),
1114        }
1115    }
1116}
1117
1118/// Splits `http://host:port/path` into what to connect to and what to ask
1119/// for.
1120///
1121/// Only `http` is understood. A `https` URL is refused rather than fetched
1122/// over a plaintext socket, because silently downgrading a URL a program
1123/// wrote as encrypted would be the worst possible answer.
1124fn split_url(url: &str) -> Result<(String, String), String> {
1125    let rest = match url.split_once("://") {
1126        Some(("http", rest)) => rest,
1127        Some(("https", _)) => {
1128            return Err(format!(
1129                "http: `{url}` is https, which this host does not speak"
1130            ))
1131        }
1132        Some((scheme, _)) => {
1133            return Err(format!("http: `{url}` uses the unknown scheme `{scheme}`"))
1134        }
1135        None => return Err(format!("http: `{url}` is not an absolute URL")),
1136    };
1137    let (authority, path) = match rest.find('/') {
1138        Some(at) => (&rest[..at], &rest[at..]),
1139        None => (rest, "/"),
1140    };
1141    if authority.is_empty() {
1142        return Err(format!("http: `{url}` names no host"));
1143    }
1144    let authority = if authority.contains(':') {
1145        authority.to_string()
1146    } else {
1147        format!("{authority}:80")
1148    };
1149    Ok((authority, path.to_string()))
1150}
1151
1152/// The reason phrase for a status, for the response line.
1153fn reason(status: i64) -> &'static str {
1154    match status {
1155        200 => "OK",
1156        201 => "Created",
1157        204 => "No Content",
1158        400 => "Bad Request",
1159        404 => "Not Found",
1160        408 => "Request Timeout",
1161        413 => "Payload Too Large",
1162        414 => "URI Too Long",
1163        431 => "Request Header Fields Too Large",
1164        500 => "Internal Server Error",
1165        501 => "Not Implemented",
1166        _ => "Status",
1167    }
1168}
1169
1170/// One request that could not be read, and what the peer is told about it.
1171struct Unread {
1172    /// What this becomes on the wire: `400` for a request this host could not
1173    /// make sense of, `408` for one whose time ran out, `413`, `414`, or
1174    /// `431` for one bigger than a bound, and `501` for one that asks for
1175    /// something this host does not do.
1176    status: i64,
1177    /// What went wrong, which is also the response body.
1178    message: String,
1179}
1180
1181impl Unread {
1182    /// A request this host could not read at all.
1183    fn malformed(message: String) -> Unread {
1184        Unread {
1185            status: 400,
1186            message,
1187        }
1188    }
1189
1190    /// A request that passed one of the bounds this host holds itself to.
1191    ///
1192    /// The status is what says which bound, and saying so is the whole reason
1193    /// these are not all `400`: a client told `413` knows to send less body,
1194    /// and a client told `431` knows to send fewer headers, where a client
1195    /// told "bad request" knows only that this host was unhappy.
1196    fn too_large(status: i64, message: String) -> Unread {
1197        Unread { status, message }
1198    }
1199
1200    /// A request that is well formed and asks for something this host does
1201    /// not do, which is a different admission from refusing it.
1202    fn unsupported(message: String) -> Unread {
1203        Unread {
1204            status: 501,
1205            message,
1206        }
1207    }
1208
1209    /// A request whose deadline passed before it was whole.
1210    ///
1211    /// A peer that stopped halfway through a request and one that is being
1212    /// slow on purpose look identical from here, and neither needs a
1213    /// different answer: the time allowed for this request is up.
1214    fn timed_out() -> Unread {
1215        Unread {
1216            status: 408,
1217            message: "http: this request did not arrive within the time allowed for it".to_string(),
1218        }
1219    }
1220
1221    /// What one failed read means. A timeout is the request's own deadline
1222    /// running out, since that is the only timeout the socket was ever given;
1223    /// anything else is a connection this host cannot read.
1224    fn from_read(what: &str, e: std::io::Error) -> Unread {
1225        match e.kind() {
1226            ErrorKind::WouldBlock | ErrorKind::TimedOut => Unread::timed_out(),
1227            _ => Unread::malformed(format!("http: cannot read {what}: {e}")),
1228        }
1229    }
1230}
1231
1232/// Gives `stream` whatever is left of the deadline the whole request shares.
1233///
1234/// `set_read_timeout(Some(Duration::ZERO))` is refused by the platform, and
1235/// rightly: a zero timeout is how the socket API spells "no timeout", which
1236/// is the opposite of what an exhausted allowance is asking for. So a
1237/// deadline that has already passed stops here rather than being handed to
1238/// the socket as permission to wait forever.
1239fn allow_until(stream: &TcpStream, until: Instant) -> Result<(), Unread> {
1240    let left = until.saturating_duration_since(Instant::now());
1241    if left.is_zero() {
1242        return Err(Unread::timed_out());
1243    }
1244    stream
1245        .set_read_timeout(Some(left))
1246        .map_err(|e| Unread::malformed(format!("http: cannot bound the read: {e}")))
1247}
1248
1249/// Reads one HTTP/1.1 request from `stream`, giving the request line, the
1250/// headers, and the body together until `until` and no longer.
1251///
1252/// One deadline, not a timeout per read. A timeout that started again on
1253/// every successful read would bound each read and the request not at all: a
1254/// peer sending one byte every twenty-nine seconds keeps the call alive for
1255/// as long as it likes, and a run that meant to stop in two hundred
1256/// milliseconds waits for all of it. So the socket is re-armed before each
1257/// read with what remains of the one allowance, and the first read that finds
1258/// nothing left gives up.
1259fn read_request(stream: &TcpStream, until: Instant) -> Result<(String, String, String), Unread> {
1260    let mut reader = BufReader::new(stream);
1261    let line = match line_within(&mut reader, stream, until, MAX_REQUEST_LINE, "the request line")? {
1262        Line::Read(line) => line,
1263        // Nothing at all arrived, which is a connection that opened and
1264        // closed rather than a request to complain about.
1265        Line::Ended => String::new(),
1266        Line::TooLong => {
1267            return Err(Unread::too_large(
1268                414,
1269                format!(
1270                    "http: this request line is longer than the {MAX_REQUEST_LINE} bytes this host reads"
1271                ),
1272            ))
1273        }
1274    };
1275    let mut parts = line.split_whitespace();
1276    let method = parts.next().unwrap_or_default().to_string();
1277    let target = parts.next().unwrap_or_default().to_string();
1278
1279    let mut headers = 0usize;
1280    let mut header_bytes = 0usize;
1281    // The text of `Content-Length` as it was sent, kept rather than parsed so
1282    // that a second one can be compared with it before either is trusted.
1283    let mut claimed: Option<String> = None;
1284    let mut coding: Option<String> = None;
1285    loop {
1286        let header = match line_within(&mut reader, stream, until, MAX_HEADER_BYTES, "a header")? {
1287            Line::Read(header) => header,
1288            Line::Ended => break,
1289            Line::TooLong => {
1290                return Err(Unread::too_large(
1291                    431,
1292                    format!(
1293                        "http: this request has a header longer than the {MAX_HEADER_BYTES} bytes this host reads"
1294                    ),
1295                ))
1296            }
1297        };
1298        if header.trim().is_empty() {
1299            break;
1300        }
1301        headers += 1;
1302        header_bytes += header.len();
1303        if headers > MAX_HEADER_COUNT {
1304            return Err(Unread::too_large(
1305                431,
1306                format!("http: this request has more than the {MAX_HEADER_COUNT} headers this host reads"),
1307            ));
1308        }
1309        if header_bytes > MAX_HEADERS_BYTES {
1310            return Err(Unread::too_large(
1311                431,
1312                format!(
1313                    "http: this request's headers are longer than the {MAX_HEADERS_BYTES} bytes this host reads"
1314                ),
1315            ));
1316        }
1317        let Some((name, value)) = header.split_once(':') else {
1318            continue;
1319        };
1320        let (name, value) = (name.trim(), value.trim());
1321        if name.eq_ignore_ascii_case("content-length") {
1322            // RFC 9110 lets a repeated `Content-Length` stand only when every
1323            // one of them says the same thing. Two that disagree are two
1324            // requests as far as anything downstream is concerned, and
1325            // picking one of them is how a proxy and a server come to
1326            // disagree about where a body ended.
1327            match &claimed {
1328                Some(first) if first != value => {
1329                    return Err(Unread::malformed(format!(
1330                        "http: this request gives `Content-Length` as both `{first}` and `{value}`"
1331                    )))
1332                }
1333                _ => claimed = Some(value.to_string()),
1334            }
1335        } else if name.eq_ignore_ascii_case("transfer-encoding") {
1336            coding = Some(value.to_string());
1337        }
1338    }
1339    // A transfer coding is where the body ends, and this host knows only the
1340    // one that `Content-Length` describes. Reading a chunked body as if it
1341    // were empty would leave its chunks on the socket and answer as though
1342    // there had been no body, which is a worse answer than saying no.
1343    if let Some(coding) = coding {
1344        return Err(Unread::unsupported(format!(
1345            "http: this host does not speak `Transfer-Encoding: {coding}`, so it cannot tell where this body ends"
1346        )));
1347    }
1348
1349    let length = match &claimed {
1350        Some(value) => content_length(value)?,
1351        None => 0,
1352    };
1353    let body = body_within(&mut reader, stream, until, length)?;
1354    let path = target.split('?').next().unwrap_or("/").to_string();
1355    Ok((method, path, String::from_utf8_lossy(&body).into_owned()))
1356}
1357
1358/// How reading one bounded line ended.
1359enum Line {
1360    /// A whole line, as it was sent, with whatever ended it still on it.
1361    Read(String),
1362    /// The peer said nothing more, which ends the headers as surely as a
1363    /// blank line does.
1364    Ended,
1365    /// The bound was reached with no end of line in sight. What is past it
1366    /// was never read, which is the point: a line is refused for its length
1367    /// without this host first finding out how long it really was.
1368    TooLong,
1369}
1370
1371/// Reads one line, no longer than `limit` bytes and no later than `until`.
1372///
1373/// The limit is a [`Read::take`] around the reader rather than a length
1374/// checked afterwards, so the bound governs what is read and not merely what
1375/// is kept. A peer that opens a connection and sends one enormous line gets
1376/// `limit` bytes of this host's attention and no more.
1377fn line_within(
1378    reader: &mut BufReader<&TcpStream>,
1379    stream: &TcpStream,
1380    until: Instant,
1381    limit: usize,
1382    what: &str,
1383) -> Result<Line, Unread> {
1384    allow_until(stream, until)?;
1385    let mut bytes = Vec::new();
1386    reader
1387        .by_ref()
1388        .take(limit as u64)
1389        .read_until(b'\n', &mut bytes)
1390        .map_err(|e| Unread::from_read(what, e))?;
1391    match bytes.last() {
1392        None => Ok(Line::Ended),
1393        Some(b'\n') => Ok(Line::Read(String::from_utf8_lossy(&bytes).into_owned())),
1394        Some(_) if bytes.len() >= limit => Ok(Line::TooLong),
1395        // Short of the bound and short of a newline is a peer that stopped
1396        // talking in the middle of a line, which is a request that will never
1397        // be whole rather than one that is too big.
1398        Some(_) => Err(Unread::malformed(format!(
1399            "http: this connection ended in the middle of {what}"
1400        ))),
1401    }
1402}
1403
1404/// The body length a `Content-Length` claims, if this host will read it.
1405///
1406/// The old reading of this header was `parse().unwrap_or(0)`, which turned
1407/// every unreadable length into "there is no body" — so a malformed request
1408/// was served as though it were a whole one, with its body still sitting on
1409/// the socket. A length is either a number this host will read or a reason to
1410/// refuse the request.
1411fn content_length(value: &str) -> Result<usize, Unread> {
1412    if value.is_empty() || !value.bytes().all(|b| b.is_ascii_digit()) {
1413        return Err(Unread::malformed(format!(
1414            "http: `Content-Length: {value}` is not a count of bytes"
1415        )));
1416    }
1417    match value.parse::<usize>() {
1418        Ok(length) if length <= MAX_BODY_BYTES => Ok(length),
1419        // A length past `MAX_BODY_BYTES` and one past what a `usize` can
1420        // count are the same refusal. Both are digits this host will not read
1421        // that many of, and neither is allocated for in order to find out.
1422        _ => Err(Unread::too_large(
1423            413,
1424            format!(
1425                "http: this request claims {value} bytes of body, and this host reads at most {MAX_BODY_BYTES}"
1426            ),
1427        )),
1428    }
1429}
1430
1431/// Reads the `length` bytes of body the headers claimed, until `until`.
1432///
1433/// `length` has already been checked against [`MAX_BODY_BYTES`], and the
1434/// bytes are gathered as they arrive rather than into a buffer sized from the
1435/// claim, so a peer that says a mebibyte and sends nothing costs this host
1436/// nothing. The deadline is re-armed each time round for the reason
1437/// [`read_request`] gives: one request has one allowance, and a peer that
1438/// dribbles its body must not renew it a chunk at a time.
1439fn body_within(
1440    reader: &mut BufReader<&TcpStream>,
1441    stream: &TcpStream,
1442    until: Instant,
1443    length: usize,
1444) -> Result<Vec<u8>, Unread> {
1445    let mut body = Vec::new();
1446    let mut chunk = [0u8; 8 * 1024];
1447    while body.len() < length {
1448        allow_until(stream, until)?;
1449        let want = chunk.len().min(length - body.len());
1450        match reader.read(&mut chunk[..want]) {
1451            Ok(0) => break,
1452            Ok(read) => body.extend_from_slice(&chunk[..read]),
1453            // A signal says nothing about the socket, so the socket is asked
1454            // again with what is left of the same allowance.
1455            Err(e) if e.kind() == ErrorKind::Interrupted => {}
1456            Err(e) => return Err(Unread::from_read("the body", e)),
1457        }
1458    }
1459    if body.len() < length {
1460        return Err(Unread::malformed(format!(
1461            "http: this request claims {length} bytes of body and sent {}",
1462            body.len()
1463        )));
1464    }
1465    Ok(body)
1466}
1467
1468/// Writes one response and closes the connection.
1469fn write_response(mut stream: &TcpStream, status: i64, body: &str) -> Result<(), String> {
1470    let head = format!(
1471        "HTTP/1.1 {status} {}\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n",
1472        reason(status),
1473        body.len()
1474    );
1475    stream
1476        .write_all(head.as_bytes())
1477        .and_then(|_| stream.write_all(body.as_bytes()))
1478        .and_then(|_| stream.flush())
1479        .map_err(|e| format!("http: cannot send the response: {e}"))
1480}
1481
1482#[cfg(test)]
1483mod tests {
1484    use super::*;
1485    use crate::budget::Cancellation;
1486    use crate::value::MapKey;
1487    use std::cell::RefCell;
1488    use std::rc::Rc;
1489    use std::sync::atomic::{AtomicBool, AtomicUsize};
1490
1491    fn err_message(value: Value) -> String {
1492        match value.err_payload() {
1493            Some(payload) => payload.first().map(ToString::to_string).unwrap_or_default(),
1494            None => panic!("expected `Err(...)`, found {value}"),
1495        }
1496    }
1497
1498    fn is_ok(value: &Value) -> bool {
1499        value.is_ok()
1500    }
1501
1502    /// The status and body of the `http.Response` an `Ok` carries.
1503    ///
1504    /// Both facts together, because both together are what `fetch` now
1505    /// answers and a helper that read one of them would let a test pin a body
1506    /// while saying nothing about the status it came with.
1507    fn ok_response(value: Value) -> (i64, String) {
1508        let Some(payload) = value.ok_payload() else {
1509            panic!("expected `Ok(...)`, found {value}");
1510        };
1511        match payload.first() {
1512            Some(Value(Repr::Struct(fields))) if &*fields.type_name == "http.Response" => {
1513                let status = match fields.get("status") {
1514                    Some(Value(Repr::Int(status))) => *status,
1515                    other => panic!("expected an `Int` status, found {other:?}"),
1516                };
1517                let body = match fields.get("body") {
1518                    Some(Value(Repr::Str(body))) => body.to_string(),
1519                    other => panic!("expected a `String` body, found {other:?}"),
1520                };
1521                (status, body)
1522            }
1523            other => panic!("expected `Ok(http.Response)`, found {other:?}"),
1524        }
1525    }
1526
1527    fn bool_ok(value: Value) -> bool {
1528        match value.ok_payload() {
1529            Some(payload) => match payload.first() {
1530                Some(Value(Repr::Bool(b))) => *b,
1531                other => panic!("expected `Ok(Bool)`, found {other:?}"),
1532            },
1533            None => panic!("expected `Ok(...)`, found {value}"),
1534        }
1535    }
1536
1537    /// The body a `http.json`-built `http.Response` carries, read back for a
1538    /// test to check the encoding rather than the module's own plumbing.
1539    fn response_body(value: Value) -> String {
1540        match value {
1541            Value(Repr::Struct(structure)) if &*structure.type_name == "http.Response" => {
1542                match structure.get("body") {
1543                    Some(Value(Repr::Str(body))) => body.to_string(),
1544                    other => panic!("expected a `String` body, found {other:?}"),
1545                }
1546            }
1547            other => panic!("expected an `http.Response`, found {other}"),
1548        }
1549    }
1550
1551    /// Opens a listener on `port` and answers the handle it issued.
1552    fn listen(http: &Http, port: i64) -> Arc<ResourceHandle> {
1553        let answered = http.call("listen", vec![Value(Repr::Int(port))]).unwrap();
1554        match answered.ok_payload() {
1555            Some(payload) => match payload.first() {
1556                Some(Value(Repr::Resource(handle))) => handle.clone(),
1557                other => panic!("expected `Ok(Resource)`, found {other:?}"),
1558            },
1559            None => panic!("expected `Ok(...)`, found {answered}"),
1560        }
1561    }
1562
1563    /// One `http.Route`, as Cove source would build it: a method, a path,
1564    /// and a handler the host never looks inside.
1565    fn route(method: &str, path: &str) -> Value {
1566        Value(Repr::Struct(Rc::new(StructValue {
1567            type_name: "http.Route".into(),
1568            fields: vec![
1569                (
1570                    "method".into(),
1571                    Value(Repr::Enum(Box::new(EnumValue {
1572                        type_name: "http.Method".into(),
1573                        case: method.into(),
1574                        payload: crate::value::Payload::Empty,
1575                    }))),
1576                ),
1577                ("path".into(), Value(Repr::Str(path.into()))),
1578                ("handler".into(), Value(Repr::Unit)),
1579            ],
1580            opaque: false,
1581        })))
1582    }
1583
1584    /// Reads and discards one HTTP/1.1 request's headers, so a test server
1585    /// can answer only after the client has actually sent its request —
1586    /// closing a socket with unread bytes still sitting in it can reset the
1587    /// connection before the response goes out.
1588    fn read_request_head(stream: &TcpStream) {
1589        let mut reader = BufReader::new(stream);
1590        loop {
1591            let mut line = String::new();
1592            let read = reader
1593                .read_line(&mut line)
1594                .expect("reading a header line should succeed");
1595            if read == 0 || line == "\r\n" || line == "\n" {
1596                break;
1597            }
1598        }
1599    }
1600
1601    /// A stub [`Reentry`] for tests, standing in for the interpreter: it runs
1602    /// the boxed closure it was built with instead of dispatching a route's
1603    /// handler into Cove code, and answers for a run that a test controls —
1604    /// one it can cancel, and one it can give a deadline to.
1605    struct StubReentry {
1606        calls: usize,
1607        respond: Box<dyn FnMut() -> Result<Value, RuntimeError>>,
1608        /// Every request the host handed a handler, so a test can ask what
1609        /// arrived rather than only what went back.
1610        seen: Rc<RefCell<Vec<Value>>>,
1611        /// The flag standing in for everything a safepoint would stop on.
1612        stop: Cancellation,
1613        /// When the run this stub stands for runs out of time, if a test gave
1614        /// it a deadline. An instant rather than a duration, so the answer
1615        /// shrinks while the host waits exactly as the real one does.
1616        expires_at: Option<Instant>,
1617        /// How many times the host asked whether the run had been stopped,
1618        /// which is how a test tells a poll from a spin.
1619        looks: Arc<AtomicUsize>,
1620    }
1621
1622    impl StubReentry {
1623        fn new(respond: impl FnMut() -> Result<Value, RuntimeError> + 'static) -> Self {
1624            StubReentry {
1625                calls: 0,
1626                respond: Box::new(respond),
1627                seen: Rc::new(RefCell::new(Vec::new())),
1628                stop: Cancellation::new(),
1629                expires_at: None,
1630                looks: Arc::new(AtomicUsize::new(0)),
1631            }
1632        }
1633
1634        /// The flag this stub reports, for a test to raise from a thread of
1635        /// its own while the host is waiting.
1636        fn stop(&self) -> Cancellation {
1637            self.stop.clone()
1638        }
1639
1640        /// Reports a run with `left` to live from now.
1641        fn expiring_in(mut self, left: Duration) -> Self {
1642            self.expires_at = Some(Instant::now() + left);
1643            self
1644        }
1645
1646        /// How many times the host looked at the run's state.
1647        fn looks(&self) -> Arc<AtomicUsize> {
1648            Arc::clone(&self.looks)
1649        }
1650
1651        /// The requests the host handed a handler, in order.
1652        fn seen(&self) -> Rc<RefCell<Vec<Value>>> {
1653            Rc::clone(&self.seen)
1654        }
1655    }
1656
1657    impl Reentry for StubReentry {
1658        fn call(&mut self, _callee: &Value, args: Vec<Value>) -> Result<Value, RuntimeError> {
1659            self.calls += 1;
1660            self.seen.borrow_mut().extend(args);
1661            (self.respond)()
1662        }
1663
1664        fn call_until(
1665            &mut self,
1666            callee: &Value,
1667            args: Vec<Value>,
1668            _stop: &Cancellation,
1669        ) -> Result<Value, RuntimeError> {
1670            self.call(callee, args)
1671        }
1672
1673        fn is_cancelled(&self) -> bool {
1674            self.looks.fetch_add(1, Ordering::Relaxed);
1675            self.stop.is_cancelled()
1676        }
1677
1678        fn time_left(&self) -> Option<Duration> {
1679            self.expires_at
1680                .map(|at| at.saturating_duration_since(Instant::now()))
1681        }
1682
1683        /// A stub stands in for the entry's own way back, which is the task a
1684        /// call made outside any spawned task belongs to.
1685        fn task(&self) -> u64 {
1686            crate::runtime::ENTRY_TASK
1687        }
1688    }
1689
1690    #[test]
1691    fn a_denied_host_refuses_to_fetch() {
1692        let http = Http::denied();
1693        let answer = http
1694            .call(
1695                "fetch",
1696                vec![Value(Repr::Str("http://example.com/".into()))],
1697            )
1698            .unwrap();
1699        assert_eq!(
1700            err_message(answer),
1701            "http: this host has no network, so no request can be sent"
1702        );
1703    }
1704
1705    #[test]
1706    fn a_denied_host_refuses_to_listen() {
1707        let http = Http::denied();
1708        let answer = http.call("listen", vec![Value(Repr::Int(8080))]).unwrap();
1709        assert_eq!(
1710            err_message(answer),
1711            "http: this host has no network, so nothing can listen"
1712        );
1713    }
1714
1715    #[test]
1716    fn a_recorded_fetch_answers_its_response() {
1717        let http = Http::recorded(
1718            BTreeMap::from([(
1719                "http://example.com/".to_string(),
1720                RecordedResponse::ok("hello"),
1721            )]),
1722            Vec::new(),
1723        );
1724        let answer = http
1725            .call(
1726                "fetch",
1727                vec![Value(Repr::Str("http://example.com/".into()))],
1728            )
1729            .unwrap();
1730        assert_eq!(ok_response(answer), (200, "hello".to_string()));
1731    }
1732
1733    /// A fake can record a status a program will call a failure, and the
1734    /// program is handed it as an answer.
1735    ///
1736    /// This is what a table of bodies could not express. A test that wanted
1737    /// to drive a program's `404` handling had no way to say `404`, so the
1738    /// only failure a fake could produce was "no recorded answer", which is
1739    /// the shape of a connection that was never made.
1740    #[test]
1741    fn a_recorded_fetch_answers_a_status_outside_the_2xx_range() {
1742        let http = Http::recorded(
1743            BTreeMap::from([(
1744                "http://example.com/missing".to_string(),
1745                RecordedResponse::new(404, "gone"),
1746            )]),
1747            Vec::new(),
1748        );
1749        let answer = http
1750            .call(
1751                "fetch",
1752                vec![Value(Repr::Str("http://example.com/missing".into()))],
1753            )
1754            .unwrap();
1755        assert_eq!(ok_response(answer), (404, "gone".to_string()));
1756    }
1757
1758    #[test]
1759    fn a_fetch_the_fake_has_no_answer_for_says_so() {
1760        let http = Http::recorded(BTreeMap::new(), Vec::new());
1761        let answer = http
1762            .call(
1763                "fetch",
1764                vec![Value(Repr::Str("http://example.com/missing".into()))],
1765            )
1766            .unwrap();
1767        assert_eq!(
1768            err_message(answer),
1769            "http: no recorded answer for `http://example.com/missing`"
1770        );
1771    }
1772
1773    #[test]
1774    fn listen_issues_a_task_safe_server_handle() {
1775        let http = Http::recorded(BTreeMap::new(), Vec::new());
1776        let handle = listen(&http, 0);
1777        assert_eq!(handle.qualified_type(), "http.Server");
1778        assert!(handle.task_safe);
1779    }
1780
1781    #[test]
1782    fn port_answers_the_port_the_program_asked_for() {
1783        let http = Http::recorded(BTreeMap::new(), Vec::new());
1784        let handle = listen(&http, 4242);
1785        match http
1786            .call_resource(&handle, "port", Vec::new(), &mut NoReentry)
1787            .unwrap()
1788        {
1789            Value(Repr::Int(port)) => assert_eq!(port, 4242),
1790            other => panic!("expected an `Int`, found {other}"),
1791        }
1792    }
1793
1794    #[test]
1795    fn handle_routes_a_matching_request_to_its_handler() {
1796        let http = Http::recorded(BTreeMap::new(), vec![ScriptedRequest::get("/health")]);
1797        let handle = listen(&http, 0);
1798
1799        let routes = Value(Repr::Array(vec![route("Get", "/health")].into()));
1800        let mut back = StubReentry::new(|| Ok(response(200, "healthy")));
1801        let answer = http
1802            .call_resource(&handle, "handle", vec![routes], &mut back)
1803            .unwrap();
1804
1805        assert!(
1806            bool_ok(answer),
1807            "a scripted request should have been served"
1808        );
1809        assert_eq!(back.calls, 1, "the handler runs exactly once per request");
1810        assert_eq!(http.served().responses(), vec!["200 healthy".to_string()]);
1811    }
1812
1813    #[test]
1814    fn handle_answers_404_for_an_unrouted_request() {
1815        let http = Http::recorded(BTreeMap::new(), vec![ScriptedRequest::get("/missing")]);
1816        let handle = listen(&http, 0);
1817
1818        let routes = Value(Repr::Array(vec![route("Get", "/health")].into()));
1819        let answer = http
1820            .call_resource(&handle, "handle", vec![routes], &mut NoReentry)
1821            .unwrap();
1822
1823        assert!(
1824            bool_ok(answer),
1825            "an unrouted request is still served, just with a 404"
1826        );
1827        assert_eq!(
1828            http.served().responses(),
1829            vec!["404 \"no route for Get /missing\"".to_string()]
1830        );
1831    }
1832
1833    #[test]
1834    fn handle_drains_its_scripted_queue_then_answers_false() {
1835        let http = Http::recorded(BTreeMap::new(), vec![ScriptedRequest::get("/health")]);
1836        let handle = listen(&http, 0);
1837        let routes = Value(Repr::Array(vec![route("Get", "/health")].into()));
1838        let mut back = StubReentry::new(|| Ok(response(200, "healthy")));
1839
1840        let first = http
1841            .call_resource(&handle, "handle", vec![routes.clone()], &mut back)
1842            .unwrap();
1843        assert!(bool_ok(first), "the one scripted request should be served");
1844
1845        let second = http
1846            .call_resource(&handle, "handle", vec![routes], &mut back)
1847            .unwrap();
1848        assert!(
1849            !bool_ok(second),
1850            "an empty queue answers false rather than waiting for more"
1851        );
1852    }
1853
1854    /// Runs `body` on a thread of its own and fails if it has not finished
1855    /// within `limit`.
1856    ///
1857    /// The rule below is one a host breaks by deadlocking, which is a way of
1858    /// failing that a test asserting on a result never reaches. This makes a
1859    /// regression a failure with a message rather than a suite that never
1860    /// ends.
1861    fn within<T: Send + 'static>(limit: Duration, body: impl FnOnce() -> T + Send + 'static) -> T {
1862        let (finished, done) = std::sync::mpsc::channel();
1863        std::thread::spawn(move || {
1864            let _ = finished.send(body());
1865        });
1866        done.recv_timeout(limit)
1867            .unwrap_or_else(|_| panic!("this did not finish within {limit:?}"))
1868    }
1869
1870    /// A host may not hold a lock of its own while it runs a Cove callback,
1871    /// because the callback is Cove code and Cove code may call the same host
1872    /// again. `serve_one` keeps the rule by taking what the next request
1873    /// needs out from under the table of open listeners and dropping the
1874    /// guard before the handler runs.
1875    ///
1876    /// So a handler may ask the very handle it is being served by for its
1877    /// port, and may open a second listener, and both answer. Held across the
1878    /// callback, either would deadlock this task on a lock three frames up
1879    /// its own stack — which is why the whole thing runs under a bound.
1880    #[test]
1881    fn a_handler_may_call_back_into_the_same_host_that_is_serving_it() {
1882        let (served, answered) = within(Duration::from_secs(10), || {
1883            let http = Arc::new(Http::recorded(
1884                BTreeMap::new(),
1885                vec![ScriptedRequest::get("/health")],
1886            ));
1887            let handle = listen(&http, 4242);
1888            let inside = Arc::clone(&http);
1889            let serving = Arc::clone(&handle);
1890            let mut back = StubReentry::new(move || {
1891                let port = inside.call_resource(&serving, "port", Vec::new(), &mut NoReentry)?;
1892                let second = inside.call("listen", vec![Value(Repr::Int(8080))])?;
1893                assert!(is_ok(&second), "a second listener opened: {second}");
1894                Ok(response(200, &format!("{port}")))
1895            });
1896
1897            let routes = Value(Repr::Array(vec![route("Get", "/health")].into()));
1898            let answer = http
1899                .call_resource(&handle, "handle", vec![routes], &mut back)
1900                .unwrap();
1901            (http.served().responses(), bool_ok(answer))
1902        });
1903        assert!(answered, "the scripted request was served");
1904        assert_eq!(served, vec!["200 4242".to_string()]);
1905    }
1906
1907    /// A handle is a name; closing the resource ends what it named, and a
1908    /// later call on the same name is a reported error rather than a call on
1909    /// whatever occupies the slot now.
1910    #[test]
1911    fn close_ends_the_handle_and_a_later_call_reports_it() {
1912        let http = Http::recorded(BTreeMap::new(), Vec::new());
1913        let handle = listen(&http, 0);
1914
1915        let closed = http
1916            .call_resource(&handle, "close", Vec::new(), &mut NoReentry)
1917            .unwrap();
1918        assert!(is_ok(&closed), "{closed}");
1919
1920        let error = http
1921            .call_resource(&handle, "port", Vec::new(), &mut NoReentry)
1922            .expect_err("a closed handle's port cannot be read");
1923        assert_eq!(
1924            error.message,
1925            format!("`{handle}` is closed, so `port` has nothing to act on")
1926        );
1927    }
1928
1929    #[test]
1930    fn json_encodes_a_struct_as_an_object() {
1931        let http = Http::denied();
1932        let payload = Value(Repr::Struct(Rc::new(StructValue {
1933            type_name: "demo.Point".into(),
1934            fields: vec![
1935                ("x".into(), Value(Repr::Int(1))),
1936                ("y".into(), Value(Repr::Int(2))),
1937            ],
1938            opaque: false,
1939        })));
1940        let answer = http
1941            .call("json", vec![Value(Repr::Int(200)), payload])
1942            .unwrap();
1943        assert_eq!(response_body(answer), "{\"x\":1,\"y\":2}");
1944    }
1945
1946    #[test]
1947    fn json_encodes_a_map_as_an_object() {
1948        let http = Http::denied();
1949        let mut map = BTreeMap::new();
1950        map.insert(MapKey::Str("a".to_string()), Value(Repr::Int(1)));
1951        let answer = http
1952            .call(
1953                "json",
1954                vec![Value(Repr::Int(200)), Value(Repr::Map(Rc::new(map)))],
1955            )
1956            .unwrap();
1957        assert_eq!(response_body(answer), "{\"a\":1}");
1958    }
1959
1960    #[test]
1961    fn json_encodes_a_string_with_its_quotes() {
1962        let http = Http::denied();
1963        let answer = http
1964            .call(
1965                "json",
1966                vec![Value(Repr::Int(200)), Value(Repr::Str("hi".into()))],
1967            )
1968            .unwrap();
1969        assert_eq!(response_body(answer), "\"hi\"");
1970    }
1971
1972    #[test]
1973    fn json_encodes_an_array() {
1974        let http = Http::denied();
1975        let payload = Value(Repr::Array(
1976            vec![Value(Repr::Int(1)), Value(Repr::Int(2))].into(),
1977        ));
1978        let answer = http
1979            .call("json", vec![Value(Repr::Int(200)), payload])
1980            .unwrap();
1981        assert_eq!(response_body(answer), "[1,2]");
1982    }
1983
1984    #[test]
1985    fn json_encodes_a_payload_free_enum_case_as_its_name() {
1986        let http = Http::denied();
1987        let payload = Value(Repr::Enum(Box::new(EnumValue {
1988            type_name: "demo.Color".into(),
1989            case: "Red".into(),
1990            payload: crate::value::Payload::Empty,
1991        })));
1992        let answer = http
1993            .call("json", vec![Value(Repr::Int(200)), payload])
1994            .unwrap();
1995        assert_eq!(response_body(answer), "\"Red\"");
1996    }
1997
1998    #[test]
1999    fn json_escapes_a_quote_and_a_newline() {
2000        let http = Http::denied();
2001        let payload = Value(Repr::Str("a\"b\nc".into()));
2002        let answer = http
2003            .call("json", vec![Value(Repr::Int(200)), payload])
2004            .unwrap();
2005        assert_eq!(response_body(answer), r#""a\"b\nc""#);
2006    }
2007
2008    #[test]
2009    fn split_url_refuses_https() {
2010        assert_eq!(
2011            split_url("https://example.com/").unwrap_err(),
2012            "http: `https://example.com/` is https, which this host does not speak"
2013        );
2014    }
2015
2016    #[test]
2017    fn split_url_refuses_an_unknown_scheme() {
2018        assert_eq!(
2019            split_url("ftp://example.com/").unwrap_err(),
2020            "http: `ftp://example.com/` uses the unknown scheme `ftp`"
2021        );
2022    }
2023
2024    #[test]
2025    fn split_url_refuses_a_non_absolute_url() {
2026        assert_eq!(
2027            split_url("example.com/path").unwrap_err(),
2028            "http: `example.com/path` is not an absolute URL"
2029        );
2030    }
2031
2032    #[test]
2033    fn a_real_fetch_reads_the_body_a_2xx_response_carries() {
2034        let listener =
2035            TcpListener::bind("127.0.0.1:0").expect("binding to loopback should succeed");
2036        let port = listener
2037            .local_addr()
2038            .expect("the bound address should be known")
2039            .port();
2040
2041        let server = std::thread::spawn(move || {
2042            let (mut stream, _) = listener
2043                .accept()
2044                .expect("accepting the one connection should succeed");
2045            read_request_head(&stream);
2046            let body = "hello from loopback";
2047            let response = format!(
2048                "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
2049                body.len(),
2050                body
2051            );
2052            stream
2053                .write_all(response.as_bytes())
2054                .expect("writing the canned response should succeed");
2055        });
2056
2057        let url = format!("http://127.0.0.1:{port}/");
2058        let answer = Http::real()
2059            .call("fetch", vec![Value(Repr::Str(url.into()))])
2060            .unwrap();
2061        server.join().expect("the server thread should not panic");
2062
2063        assert_eq!(
2064            ok_response(answer),
2065            (200, "hello from loopback".to_string())
2066        );
2067    }
2068
2069    /// The real client reads a status outside 200-299 off the wire and hands
2070    /// it on, with the body that came with it.
2071    ///
2072    /// This is the same server and the same client as the test above, and the
2073    /// only thing that differs is the status line, which is the point: a
2074    /// `404` is answered rather than refused, so the two tests differ by the
2075    /// number they assert and not by the shape they assert it in.
2076    #[test]
2077    fn a_real_fetch_answers_a_non_2xx_status_and_its_body() {
2078        let listener =
2079            TcpListener::bind("127.0.0.1:0").expect("binding to loopback should succeed");
2080        let port = listener
2081            .local_addr()
2082            .expect("the bound address should be known")
2083            .port();
2084
2085        let server = std::thread::spawn(move || {
2086            let (mut stream, _) = listener
2087                .accept()
2088                .expect("accepting the one connection should succeed");
2089            read_request_head(&stream);
2090            let body = "not found here";
2091            let response = format!(
2092                "HTTP/1.1 404 Not Found\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
2093                body.len(),
2094                body
2095            );
2096            stream
2097                .write_all(response.as_bytes())
2098                .expect("writing the canned response should succeed");
2099        });
2100
2101        let url = format!("http://127.0.0.1:{port}/");
2102        let answer = Http::real()
2103            .call("fetch", vec![Value(Repr::Str(url.clone().into()))])
2104            .unwrap();
2105        server.join().expect("the server thread should not panic");
2106
2107        assert_eq!(ok_response(answer), (404, "not found here".to_string()));
2108    }
2109
2110    /// A response past [`MAX_RESPONSE_BYTES`] is an error and not an
2111    /// allocation.
2112    ///
2113    /// The server here promises a `Content-Length` it then makes good on, so
2114    /// what stops the read is the bound rather than a peer caught lying: the
2115    /// client counts what arrives, and a server that means to send two
2116    /// mebibytes is stopped at one whether or not it said so first. The
2117    /// message names the bound, because a client told only that something was
2118    /// too large learns nothing it can act on.
2119    #[test]
2120    fn a_real_fetch_refuses_a_response_past_the_bound() {
2121        let listener =
2122            TcpListener::bind("127.0.0.1:0").expect("binding to loopback should succeed");
2123        let port = listener
2124            .local_addr()
2125            .expect("the bound address should be known")
2126            .port();
2127
2128        let server = std::thread::spawn(move || {
2129            let (mut stream, _) = listener
2130                .accept()
2131                .expect("accepting the one connection should succeed");
2132            read_request_head(&stream);
2133            let body = "a".repeat(MAX_RESPONSE_BYTES + 1);
2134            let response = format!(
2135                "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
2136                body.len(),
2137                body
2138            );
2139            // A client that stops reading at the bound closes the connection,
2140            // so the rest of this write may be refused. That is the bound
2141            // working rather than the server failing, so the result is
2142            // dropped.
2143            let _ = stream.write_all(response.as_bytes());
2144        });
2145
2146        let url = format!("http://127.0.0.1:{port}/");
2147        let answer = Http::real()
2148            .call("fetch", vec![Value(Repr::Str(url.into()))])
2149            .unwrap();
2150        let _ = server.join();
2151
2152        assert!(
2153            err_message(answer).ends_with(&format!(
2154                "sent more than the {MAX_RESPONSE_BYTES} bytes this host reads"
2155            )),
2156            "the bound is named"
2157        );
2158    }
2159
2160    /// Exercises the real host both ways at once: `listen` binds an ephemeral
2161    /// port, a client thread reaches it as an ordinary HTTP client would, and
2162    /// `handle` (driven by a stub reentry standing in for the interpreter)
2163    /// answers the request the way a program's own route handler would.
2164    #[test]
2165    fn the_real_host_serves_a_request_end_to_end_over_loopback() {
2166        let http = Http::real();
2167        let opened = http.call("listen", vec![Value(Repr::Int(0))]).unwrap();
2168        let Value(Repr::Enum(result)) = opened else {
2169            panic!("expected `Ok(...)`");
2170        };
2171        let Some(Value(Repr::Resource(handle))) = result.payload.into_vec().into_iter().next()
2172        else {
2173            panic!("`listen` should answer a handle");
2174        };
2175        let port = match http
2176            .call_resource(&handle, "port", Vec::new(), &mut NoReentry)
2177            .unwrap()
2178        {
2179            Value(Repr::Int(port)) => port,
2180            other => panic!("expected an `Int` port, found {other}"),
2181        };
2182
2183        let client = std::thread::spawn(move || {
2184            let mut stream = TcpStream::connect(("127.0.0.1", port as u16))
2185                .expect("connecting to the loopback listener should succeed");
2186            stream
2187                .write_all(b"GET /health HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n")
2188                .expect("writing the request should succeed");
2189            let mut answer = Vec::new();
2190            stream
2191                .read_to_end(&mut answer)
2192                .expect("reading the response should succeed");
2193            String::from_utf8_lossy(&answer).into_owned()
2194        });
2195
2196        let routes = Value(Repr::Array(vec![route("Get", "/health")].into()));
2197        let mut back = StubReentry::new(|| Ok(response(200, "healthy")));
2198        let served = bool_ok(
2199            http.call_resource(&handle, "handle", vec![routes], &mut back)
2200                .unwrap(),
2201        );
2202        assert!(served, "the listener should have answered one request");
2203
2204        let received = client.join().expect("the client thread should not panic");
2205        assert!(received.starts_with("HTTP/1.1 200"), "{received}");
2206        assert!(received.ends_with("healthy"), "{received}");
2207
2208        http.call_resource(&handle, "close", Vec::new(), &mut NoReentry)
2209            .unwrap();
2210    }
2211
2212    /// How long a test is willing to let a `handle` that should have stopped
2213    /// go on waiting.
2214    ///
2215    /// Generous, because the assertion is about the difference between
2216    /// stopping and not stopping rather than about latency: a call that is
2217    /// genuinely blocked in `accept` with nobody connecting never returns at
2218    /// all, and one that polls returns within a couple of poll intervals even
2219    /// on a machine with nothing to spare.
2220    const PROMPTLY: Duration = Duration::from_secs(2);
2221
2222    /// How long a test lets a wait run before stopping it.
2223    ///
2224    /// Long enough that the wait is unmistakably under way — the connection
2225    /// is open, the request is sent, and the client is inside its read loop
2226    /// — and short enough that [`PROMPTLY`] is an order of magnitude above
2227    /// it, which is what leaves the timing assertions room on a loaded
2228    /// machine without letting them pass against a client that waits out
2229    /// [`READ_TIMEOUT`].
2230    const CUT_SHORT_AFTER: Duration = Duration::from_millis(150);
2231
2232    /// A real listener with nobody connecting to it, and the routing table a
2233    /// program would hand `handle`.
2234    ///
2235    /// Nothing is ever routed in these tests. The point of each of them is
2236    /// what happens before a request exists.
2237    fn quiet_listener() -> (Http, Arc<ResourceHandle>, Value) {
2238        let http = Http::real();
2239        let handle = listen(&http, 0);
2240        (
2241            http,
2242            handle,
2243            Value(Repr::Array(vec![route("Get", "/health")].into())),
2244        )
2245    }
2246
2247    /// Cancelling a run that is waiting for a connection stops the wait.
2248    ///
2249    /// This is the acceptance test for the whole change, and it uses the real
2250    /// host: a real socket, bound to loopback, with nothing on the other end
2251    /// of it. A blocking `accept` would hang here forever and the test would
2252    /// have to be killed.
2253    #[test]
2254    fn cancelling_a_run_waiting_for_a_connection_stops_the_wait() {
2255        let (http, handle, routes) = quiet_listener();
2256        let mut back = StubReentry::new(|| panic!("nothing connects, so no handler runs"));
2257        let stop = back.stop();
2258        let raised_after = Duration::from_millis(50);
2259        // The clock is read before the thread that raises the flag can exist,
2260        // so the flag cannot be raised earlier than `started` plus the sleep
2261        // and the lower bound below is arithmetic rather than a race.
2262        let started = Instant::now();
2263        std::thread::spawn(move || {
2264            std::thread::sleep(raised_after);
2265            stop.cancel();
2266        });
2267
2268        let answer = http
2269            .call_resource(&handle, "handle", vec![routes], &mut back)
2270            .unwrap();
2271        let waited = started.elapsed();
2272
2273        assert!(
2274            !bool_ok(answer),
2275            "a listener that was stopped has nothing more to serve"
2276        );
2277        assert!(
2278            waited >= raised_after,
2279            "the wait ended before there was anything to end it, after {waited:?}"
2280        );
2281        assert!(
2282            waited < PROMPTLY,
2283            "the wait outlived the cancellation by {waited:?}"
2284        );
2285        assert_eq!(back.calls, 0, "no request arrived, so no handler ran");
2286    }
2287
2288    /// The same shape, with the run's deadline running out instead of a flag
2289    /// being raised: a host that only watched cancellation would still sit
2290    /// here until a client arrived.
2291    #[test]
2292    fn a_run_deadline_that_expires_while_waiting_for_a_connection_ends_the_wait() {
2293        let (http, handle, routes) = quiet_listener();
2294        let left = Duration::from_millis(50);
2295        // The stub's deadline runs from the moment it is built, so the clock
2296        // is read first: `started` is then no later than the deadline's own
2297        // origin, and the lower bound below cannot be lost to a rounding.
2298        let started = Instant::now();
2299        let mut back =
2300            StubReentry::new(|| panic!("nothing connects, so no handler runs")).expiring_in(left);
2301
2302        let answer = http
2303            .call_resource(&handle, "handle", vec![routes], &mut back)
2304            .unwrap();
2305        let waited = started.elapsed();
2306
2307        assert!(
2308            !bool_ok(answer),
2309            "a listener whose run has run out of time has nothing more to serve"
2310        );
2311        assert!(
2312            waited >= left,
2313            "the wait ended before the deadline it was waiting for, after {waited:?}"
2314        );
2315        assert!(
2316            waited < PROMPTLY,
2317            "the wait outlived the deadline by {waited:?}"
2318        );
2319    }
2320
2321    /// The bound is a poll and not a spin.
2322    ///
2323    /// There is no portable way to ask this process how much CPU it burned,
2324    /// so the test asks the thing the host itself does: how many times it
2325    /// looked at the run while it waited. A loop sleeping
2326    /// [`POLL_INTERVAL`] looks a few hundred times a second; a loop
2327    /// with no sleep in it would look millions of times over the same
2328    /// interval, so the two are never in danger of being confused.
2329    #[test]
2330    fn waiting_for_a_connection_polls_rather_than_spinning() {
2331        let (http, handle, routes) = quiet_listener();
2332        let waiting = Duration::from_millis(200);
2333        let mut back = StubReentry::new(|| panic!("nothing connects, so no handler runs"))
2334            .expiring_in(waiting);
2335        let looks = back.looks();
2336
2337        http.call_resource(&handle, "handle", vec![routes], &mut back)
2338            .unwrap();
2339
2340        let looks = looks.load(Ordering::Relaxed);
2341        let sleeping = (waiting.as_millis() / POLL_INTERVAL.as_millis()) as usize;
2342        assert!(looks >= 1, "the host never looked at the run at all");
2343        assert!(
2344            looks < sleeping * 20,
2345            "{looks} looks in {waiting:?} is a spin, not a poll at one every {POLL_INTERVAL:?}"
2346        );
2347    }
2348
2349    /// A peer that connects, says half of a request line, and then says
2350    /// nothing is answered `408` on the run's own deadline rather than on
2351    /// `READ_TIMEOUT`, which is thirty seconds away.
2352    ///
2353    /// This is the other half of the same problem. The socket had a timeout
2354    /// before this change too, but it was the host's alone, and it started
2355    /// again on every successful read.
2356    #[test]
2357    fn a_run_deadline_that_expires_while_reading_answers_408() {
2358        let http = Http::real();
2359        let handle = listen(&http, 0);
2360        let port = match http
2361            .call_resource(&handle, "port", Vec::new(), &mut NoReentry)
2362            .unwrap()
2363        {
2364            Value(Repr::Int(port)) => port,
2365            other => panic!("expected an `Int` port, found {other}"),
2366        };
2367
2368        // Half a request line, and then the peer holds the connection open
2369        // and says nothing more until it is answered.
2370        let client = std::thread::spawn(move || {
2371            let mut stream = TcpStream::connect(("127.0.0.1", port as u16))
2372                .expect("connecting to the loopback listener should succeed");
2373            stream
2374                .write_all(b"GET /health HTT")
2375                .expect("writing half a request line should succeed");
2376            let mut answer = Vec::new();
2377            stream
2378                .read_to_end(&mut answer)
2379                .expect("reading the response should succeed");
2380            String::from_utf8_lossy(&answer).into_owned()
2381        });
2382
2383        let routes = Value(Repr::Array(vec![route("Get", "/health")].into()));
2384        let mut back = StubReentry::new(|| panic!("no whole request arrives, so no handler runs"))
2385            .expiring_in(Duration::from_millis(150));
2386        let started = Instant::now();
2387        let answer = http
2388            .call_resource(&handle, "handle", vec![routes], &mut back)
2389            .unwrap();
2390        let took = started.elapsed();
2391
2392        assert!(
2393            bool_ok(answer),
2394            "a request that arrived and could not be read is still one that arrived"
2395        );
2396        assert!(
2397            took < PROMPTLY,
2398            "the read waited on `READ_TIMEOUT` rather than on the run, for {took:?}"
2399        );
2400        let received = client.join().expect("the client thread should not panic");
2401        assert!(
2402            received.starts_with("HTTP/1.1 408 Request Timeout"),
2403            "{received}"
2404        );
2405
2406        http.call_resource(&handle, "close", Vec::new(), &mut NoReentry)
2407            .unwrap();
2408    }
2409
2410    /// The clamp itself, without a socket: a run with less time left than the
2411    /// host's own allowance decides the allowance, and a run with no deadline
2412    /// leaves it alone.
2413    #[test]
2414    fn an_allowance_is_the_shorter_of_the_hosts_and_the_runs() {
2415        assert_eq!(bounded(READ_TIMEOUT, None), READ_TIMEOUT);
2416        assert_eq!(
2417            bounded(READ_TIMEOUT, Some(Duration::from_millis(200))),
2418            Duration::from_millis(200)
2419        );
2420        assert_eq!(
2421            bounded(READ_TIMEOUT, Some(Duration::from_secs(600))),
2422            READ_TIMEOUT
2423        );
2424        assert_eq!(bounded(READ_TIMEOUT, Some(Duration::ZERO)), Duration::ZERO);
2425    }
2426
2427    /// What one raw request at a real listener came to.
2428    struct Exchange {
2429        /// What `handle` answered: whether a request arrived at all.
2430        served: bool,
2431        /// What the peer read back, status line first.
2432        received: String,
2433        /// How long `handle` took, so a refusal that had to read the whole
2434        /// oversized thing first can be told from one that did not.
2435        took: Duration,
2436        /// The requests that reached a handler, which for a refused one is
2437        /// none.
2438        seen: Vec<Value>,
2439    }
2440
2441    /// Sends `request` to a real listener byte for byte and reports what came
2442    /// of it.
2443    ///
2444    /// The bytes go out exactly as written, which is the point: these tests
2445    /// are about requests no client library would let anyone send. The write
2446    /// is allowed to fail, because a host that refuses a request it has not
2447    /// finished reading closes a socket with bytes still in it, and the peer
2448    /// learns about that as an error on whichever call is in flight.
2449    fn serve_raw(request: Vec<u8>) -> Exchange {
2450        let http = Http::real();
2451        let handle = listen(&http, 0);
2452        let port = match http
2453            .call_resource(&handle, "port", Vec::new(), &mut NoReentry)
2454            .unwrap()
2455        {
2456            Value(Repr::Int(port)) => port,
2457            other => panic!("expected an `Int` port, found {other}"),
2458        };
2459
2460        let client = std::thread::spawn(move || {
2461            let mut stream = TcpStream::connect(("127.0.0.1", port as u16))
2462                .expect("connecting to the loopback listener should succeed");
2463            // A peer that is never answered must not hang this test; the
2464            // answer, or the lack of one, is what is asserted on.
2465            stream
2466                .set_read_timeout(Some(PROMPTLY))
2467                .expect("bounding the client's own read should succeed");
2468            let _ = stream.write_all(&request);
2469            let mut answer = Vec::new();
2470            let _ = stream.read_to_end(&mut answer);
2471            String::from_utf8_lossy(&answer).into_owned()
2472        });
2473
2474        let routes = Value(Repr::Array(
2475            vec![route("Get", "/health"), route("Post", "/echo")].into(),
2476        ));
2477        let mut back = StubReentry::new(|| Ok(response(200, "healthy")));
2478        let seen = back.seen();
2479        let started = Instant::now();
2480        let served = bool_ok(
2481            http.call_resource(&handle, "handle", vec![routes], &mut back)
2482                .unwrap(),
2483        );
2484        let took = started.elapsed();
2485        let received = client.join().expect("the client thread should not panic");
2486        http.call_resource(&handle, "close", Vec::new(), &mut NoReentry)
2487            .unwrap();
2488        let seen = seen.borrow().clone();
2489        Exchange {
2490            served,
2491            received,
2492            took,
2493            seen,
2494        }
2495    }
2496
2497    /// The status line of what the peer read back.
2498    fn status_line(exchange: &Exchange) -> &str {
2499        exchange
2500            .received
2501            .lines()
2502            .next()
2503            .unwrap_or_else(|| panic!("the peer was told nothing at all"))
2504    }
2505
2506    /// Asserts that a request was refused with `status`, told nobody about
2507    /// it, and did not have to be read to its end first.
2508    fn refused(exchange: &Exchange, status: &str) {
2509        assert_eq!(
2510            status_line(exchange),
2511            status,
2512            "the peer was told: {}",
2513            exchange.received
2514        );
2515        assert!(
2516            exchange.served,
2517            "a request that arrived and was refused is still one that arrived"
2518        );
2519        assert!(
2520            exchange.seen.is_empty(),
2521            "a refused request must not reach a handler"
2522        );
2523        assert!(
2524            exchange.took < PROMPTLY,
2525            "the refusal took {:?}, which is long enough to have read the whole thing",
2526            exchange.took
2527        );
2528    }
2529
2530    #[test]
2531    fn a_request_line_past_the_bound_is_refused_with_414() {
2532        let target = "/".to_string() + &"a".repeat(MAX_REQUEST_LINE);
2533        let request = format!("GET {target} HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n");
2534        refused(
2535            &serve_raw(request.into_bytes()),
2536            "HTTP/1.1 414 URI Too Long",
2537        );
2538    }
2539
2540    #[test]
2541    fn a_header_past_the_bound_is_refused_with_431() {
2542        let request = format!(
2543            "GET /health HTTP/1.1\r\nHost: 127.0.0.1\r\nX-Long: {}\r\n\r\n",
2544            "a".repeat(MAX_HEADER_BYTES)
2545        );
2546        refused(
2547            &serve_raw(request.into_bytes()),
2548            "HTTP/1.1 431 Request Header Fields Too Large",
2549        );
2550    }
2551
2552    #[test]
2553    fn more_headers_than_the_bound_are_refused_with_431() {
2554        let mut request = "GET /health HTTP/1.1\r\n".to_string();
2555        // Short enough that no number of them reaches the byte total first,
2556        // so this test is about the count and nothing else.
2557        for n in 0..=MAX_HEADER_COUNT {
2558            request.push_str(&format!("X-{n}: 1\r\n"));
2559        }
2560        request.push_str("\r\n");
2561        assert!(
2562            request.len() < MAX_HEADERS_BYTES,
2563            "this request should pass the count bound, not the byte one"
2564        );
2565        refused(
2566            &serve_raw(request.into_bytes()),
2567            "HTTP/1.1 431 Request Header Fields Too Large",
2568        );
2569    }
2570
2571    #[test]
2572    fn more_header_bytes_than_the_bound_are_refused_with_431() {
2573        let mut request = "GET /health HTTP/1.1\r\n".to_string();
2574        // Each one is well inside the single-header bound, and there are
2575        // fewer than the count allows, so only their total can refuse this.
2576        let padding = "a".repeat(4 * 1024);
2577        for n in 0..16 {
2578            request.push_str(&format!("X-{n}: {padding}\r\n"));
2579        }
2580        request.push_str("\r\n");
2581        refused(
2582            &serve_raw(request.into_bytes()),
2583            "HTTP/1.1 431 Request Header Fields Too Large",
2584        );
2585    }
2586
2587    #[test]
2588    fn a_body_past_the_bound_is_refused_with_413() {
2589        let request = format!(
2590            "POST /echo HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Length: {}\r\n\r\n",
2591            MAX_BODY_BYTES + 1
2592        );
2593        refused(
2594            &serve_raw(request.into_bytes()),
2595            "HTTP/1.1 413 Payload Too Large",
2596        );
2597    }
2598
2599    /// A claim nobody could honour is refused on the claim.
2600    ///
2601    /// The peer sends no body at all — only a header saying it is about to
2602    /// send nine hundred and ninety-nine terabytes of one. A host that sized
2603    /// a buffer from the claim would fail here in a way this process would
2604    /// not survive; a host that checked the claim first answers `413` in the
2605    /// time it takes to read one header, which is what the timing asserts.
2606    #[test]
2607    fn a_preposterous_content_length_is_refused_before_any_of_it_is_read() {
2608        let request =
2609            "POST /echo HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Length: 999999999999999\r\n\r\n";
2610        refused(
2611            &serve_raw(request.as_bytes().to_vec()),
2612            "HTTP/1.1 413 Payload Too Large",
2613        );
2614    }
2615
2616    #[test]
2617    fn a_content_length_that_is_not_a_number_is_refused_with_400() {
2618        let request = "POST /echo HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Length: some\r\n\r\n";
2619        refused(
2620            &serve_raw(request.as_bytes().to_vec()),
2621            "HTTP/1.1 400 Bad Request",
2622        );
2623    }
2624
2625    #[test]
2626    fn two_content_lengths_that_disagree_are_refused_with_400() {
2627        let request =
2628            "POST /echo HTTP/1.1\r\nContent-Length: 3\r\nContent-Length: 4\r\n\r\nabc\r\n\r\n";
2629        refused(
2630            &serve_raw(request.as_bytes().to_vec()),
2631            "HTTP/1.1 400 Bad Request",
2632        );
2633    }
2634
2635    /// RFC 9110 lets a repeated `Content-Length` stand when they agree, so
2636    /// this one is served rather than refused.
2637    #[test]
2638    fn two_content_lengths_that_agree_are_served() {
2639        let request = "POST /echo HTTP/1.1\r\nContent-Length: 3\r\nContent-Length: 3\r\n\r\nabc";
2640        let exchange = serve_raw(request.as_bytes().to_vec());
2641        assert_eq!(status_line(&exchange), "HTTP/1.1 200 OK");
2642        assert_eq!(body_of(&exchange.seen), "abc");
2643    }
2644
2645    #[test]
2646    fn a_transfer_encoding_is_refused_with_501() {
2647        let request =
2648            "POST /echo HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n3\r\nabc\r\n0\r\n\r\n";
2649        refused(
2650            &serve_raw(request.as_bytes().to_vec()),
2651            "HTTP/1.1 501 Not Implemented",
2652        );
2653    }
2654
2655    /// The bounds are for requests that pass them, and this one does not.
2656    #[test]
2657    fn a_request_with_an_ordinary_body_is_still_served() {
2658        let body = "{\"name\":\"cove\"}";
2659        let request = format!(
2660            "POST /echo HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Length: {}\r\n\r\n{body}",
2661            body.len()
2662        );
2663        let exchange = serve_raw(request.into_bytes());
2664        assert_eq!(status_line(&exchange), "HTTP/1.1 200 OK");
2665        assert!(
2666            exchange.received.ends_with("healthy"),
2667            "{}",
2668            exchange.received
2669        );
2670        assert_eq!(body_of(&exchange.seen), body);
2671    }
2672
2673    /// A body of exactly [`MAX_BODY_BYTES`] is inside the bound, not past it.
2674    #[test]
2675    fn a_body_of_exactly_the_bound_is_served() {
2676        let body = "a".repeat(MAX_BODY_BYTES);
2677        let request = format!(
2678            "POST /echo HTTP/1.1\r\nContent-Length: {}\r\n\r\n{body}",
2679            body.len()
2680        );
2681        let exchange = serve_raw(request.into_bytes());
2682        assert_eq!(status_line(&exchange), "HTTP/1.1 200 OK");
2683        assert_eq!(body_of(&exchange.seen).len(), MAX_BODY_BYTES);
2684    }
2685
2686    /// The body of the one request a handler was given.
2687    fn body_of(seen: &[Value]) -> String {
2688        match seen {
2689            [Value(Repr::Struct(request))] => match request.get("body") {
2690                Some(Value(Repr::Str(body))) => body.to_string(),
2691                other => panic!("expected a `String` body, found {other:?}"),
2692            },
2693            other => panic!("expected exactly one request, found {other:?}"),
2694        }
2695    }
2696
2697    /// The reading of `Content-Length` on its own, where a value too big for
2698    /// a `usize` can be written down without a socket having to carry it.
2699    #[test]
2700    fn a_content_length_is_a_count_of_bytes_or_a_refusal() {
2701        assert_eq!(content_length("0").ok(), Some(0));
2702        assert_eq!(content_length("12").ok(), Some(12));
2703        assert_eq!(
2704            content_length(&MAX_BODY_BYTES.to_string()).ok(),
2705            Some(MAX_BODY_BYTES)
2706        );
2707
2708        for value in ["", "-1", "1 2", "0x10", "12kb", "+3", "one"] {
2709            let refused = content_length(value).expect_err("this is not a count");
2710            assert_eq!(refused.status, 400, "`{value}` is malformed, not too big");
2711        }
2712
2713        // Past the bound, past a `usize`, and past anything at all: one
2714        // refusal, and none of them reserve what they claim.
2715        for value in [
2716            (MAX_BODY_BYTES + 1).to_string(),
2717            format!("{}", u64::MAX),
2718            "9".repeat(200),
2719        ] {
2720            let refused = content_length(&value).expect_err("this is too big");
2721            assert_eq!(refused.status, 413, "`{value}` is too big, not malformed");
2722        }
2723    }
2724
2725    /// A `fetch` made by a run with nothing left does not open a connection
2726    /// at all, let alone wait thirty seconds on one.
2727    #[test]
2728    fn a_fetch_with_no_time_left_is_refused_before_it_connects() {
2729        let back =
2730            StubReentry::new(|| panic!("a fetch runs no handler")).expiring_in(Duration::ZERO);
2731        let answer = fetch_over_tcp("http://127.0.0.1:1/", &back)
2732            .expect_err("a run with no time left cannot fetch");
2733        assert_eq!(
2734            answer,
2735            "http: the run ran out of time before 127.0.0.1:1 could be asked"
2736        );
2737    }
2738
2739    /// A `fetch` made by a run that has already been stopped is refused the
2740    /// same way, and says which of the two it was.
2741    ///
2742    /// The pair matters because the messages are the only thing that tells
2743    /// them apart, and a cancellation is not a deadline: a `clock.timeout`
2744    /// that expired, a cancelled task and a stopped run all arrive here as
2745    /// one flag, and none of them is "the run ran out of time".
2746    #[test]
2747    fn a_fetch_made_by_a_stopped_run_is_refused_before_it_connects() {
2748        let back = StubReentry::new(|| panic!("a fetch runs no handler"));
2749        back.stop().cancel();
2750        let answer =
2751            fetch_over_tcp("http://127.0.0.1:1/", &back).expect_err("a stopped run cannot fetch");
2752        assert_eq!(
2753            answer,
2754            "http: the run was stopped before 127.0.0.1:1 could be asked"
2755        );
2756    }
2757
2758    /// Binds loopback, accepts one connection, reads the request, and then
2759    /// says nothing at all until it is told to hang up.
2760    ///
2761    /// This is the server the two tests below need and no ordinary one will
2762    /// do: the bug they are about is a client that has connected, sent its
2763    /// request, and is waiting for bytes that are not coming. The thread
2764    /// holds the connection open rather than dropping it, because a dropped
2765    /// socket is an `Ok(0)` and would end the client's read for the wrong
2766    /// reason.
2767    fn stalling_server(hung_up: Arc<AtomicBool>) -> (u16, std::thread::JoinHandle<()>) {
2768        let listener =
2769            TcpListener::bind("127.0.0.1:0").expect("binding to loopback should succeed");
2770        let port = listener
2771            .local_addr()
2772            .expect("the bound address should be known")
2773            .port();
2774        let thread = std::thread::spawn(move || {
2775            let (stream, _) = listener
2776                .accept()
2777                .expect("accepting the one connection should succeed");
2778            read_request_head(&stream);
2779            while !hung_up.load(Ordering::Relaxed) {
2780                std::thread::sleep(Duration::from_millis(5));
2781            }
2782            drop(stream);
2783        });
2784        (port, thread)
2785    }
2786
2787    /// A cancellation raised while a `fetch` is waiting for a response ends
2788    /// the read, rather than being noticed once the read is over.
2789    ///
2790    /// This is issue #170. The old client folded the run's deadline into one
2791    /// `set_read_timeout` and then blocked, so a flag raised afterwards had
2792    /// nothing looking at it: against that code this test waits out
2793    /// `READ_TIMEOUT`, thirty seconds, and only then reports the bound. So
2794    /// the assertion is on how long it took and not only on what came back —
2795    /// the error is the same either way, and the whole of the bug is *when*.
2796    ///
2797    /// The server never answers and never hangs up, which is what makes the
2798    /// timing mean something: nothing but the cancellation can end this read.
2799    #[test]
2800    fn a_cancellation_raised_while_a_fetch_reads_cuts_the_read_short() {
2801        let hung_up = Arc::new(AtomicBool::new(false));
2802        let (port, server) = stalling_server(Arc::clone(&hung_up));
2803
2804        let back = StubReentry::new(|| panic!("a fetch runs no handler"));
2805        let stop = back.stop();
2806        let raising = std::thread::spawn(move || {
2807            std::thread::sleep(CUT_SHORT_AFTER);
2808            stop.cancel();
2809        });
2810
2811        let url = format!("http://127.0.0.1:{port}/");
2812        let started = Instant::now();
2813        let answer = fetch_over_tcp(&url, &back).expect_err("a cancelled fetch has no response");
2814        let took = started.elapsed();
2815
2816        hung_up.store(true, Ordering::Relaxed);
2817        raising.join().expect("the raising thread should not panic");
2818        server.join().expect("the server thread should not panic");
2819
2820        assert_eq!(
2821            answer,
2822            format!("http: the run was stopped before 127.0.0.1:{port} answered")
2823        );
2824        assert!(
2825            took >= CUT_SHORT_AFTER,
2826            "the read ended before the cancellation that was supposed to end it, after {took:?}"
2827        );
2828        assert!(
2829            took < PROMPTLY,
2830            "the read waited on `READ_TIMEOUT` rather than on the cancellation, for {took:?}"
2831        );
2832    }
2833
2834    /// The same read, ended by a run deadline instead of a cancellation.
2835    ///
2836    /// This half worked before, because a deadline is knowable before the
2837    /// read starts and was folded into the socket's timeout. It is here so
2838    /// that the two bounds are pinned in the same place and in the same
2839    /// shape, and so that the wording that tells them apart from a peer that
2840    /// simply took too long is not lost.
2841    #[test]
2842    fn a_run_deadline_that_expires_while_a_fetch_reads_cuts_the_read_short() {
2843        let hung_up = Arc::new(AtomicBool::new(false));
2844        let (port, server) = stalling_server(Arc::clone(&hung_up));
2845
2846        let back =
2847            StubReentry::new(|| panic!("a fetch runs no handler")).expiring_in(CUT_SHORT_AFTER);
2848        let url = format!("http://127.0.0.1:{port}/");
2849        let started = Instant::now();
2850        let answer = fetch_over_tcp(&url, &back).expect_err("an expired fetch has no response");
2851        let took = started.elapsed();
2852
2853        hung_up.store(true, Ordering::Relaxed);
2854        server.join().expect("the server thread should not panic");
2855
2856        assert_eq!(
2857            answer,
2858            format!("http: the run was stopped before 127.0.0.1:{port} answered")
2859        );
2860        assert!(
2861            took < PROMPTLY,
2862            "the read waited on `READ_TIMEOUT` rather than on the run, for {took:?}"
2863        );
2864    }
2865
2866    /// The wait for a response is a poll and not a spin.
2867    ///
2868    /// The same argument as `waiting_for_a_connection_polls_rather_than_spinning`
2869    /// and the same measurement: a loop that waits [`POLL_INTERVAL`] on the
2870    /// socket looks a few hundred times a second, and one that did not wait
2871    /// at all would look millions of times over the same interval. The two
2872    /// are never in danger of being confused.
2873    #[test]
2874    fn waiting_for_a_response_polls_rather_than_spinning() {
2875        let hung_up = Arc::new(AtomicBool::new(false));
2876        let (port, server) = stalling_server(Arc::clone(&hung_up));
2877
2878        let back =
2879            StubReentry::new(|| panic!("a fetch runs no handler")).expiring_in(CUT_SHORT_AFTER);
2880        let looks = back.looks();
2881        let url = format!("http://127.0.0.1:{port}/");
2882        let _ = fetch_over_tcp(&url, &back);
2883
2884        hung_up.store(true, Ordering::Relaxed);
2885        server.join().expect("the server thread should not panic");
2886
2887        let looks = looks.load(Ordering::Relaxed);
2888        let polling = (CUT_SHORT_AFTER.as_millis() / POLL_INTERVAL.as_millis()) as usize;
2889        assert!(looks >= 1, "the client never looked at the run at all");
2890        assert!(
2891            looks < polling * 20,
2892            "{looks} looks in {CUT_SHORT_AFTER:?} is a spin, not a poll at one every {POLL_INTERVAL:?}"
2893        );
2894    }
2895}