guppy_protocol/lib.rs
1//! An implementation of the
2//! [Guppy protocol](https://github.com/dimkr/guppy-protocol) v0.4.4
3//! (`guppy://`, UDP port 6775): an async client and server with chunking,
4//! per-packet acknowledgement, and retransmission, plus a small CLI.
5//!
6//! Guppy is dimkr's smolweb-over-UDP protocol, inspired by TFTP, DNS, and
7//! Spartan. A request is a single datagram carrying a URL (user input rides
8//! the query component, percent-encoded). The server answers with either a
9//! special single-digit packet — `1 <prompt>` / `3 <url>` redirect /
10//! `4 <error>` — or a **success** packet `<seq> <mimetype>\r\n<data>` whose
11//! sequence number starts at a random value in `[6, 2^31-1]`, followed by
12//! continuation packets `<seq>\r\n<data>` (seq incrementing by one), ended by
13//! an empty continuation (the end-of-file packet). The client acknowledges
14//! every success/continuation/EOF packet by echoing its sequence number.
15//! Lost packets are handled by retransmission on both sides.
16//!
17//! This crate is independent and unaffiliated with the protocol's author.
18//! The crates.io name is qualified (`guppy-protocol`) because the bare
19//! `guppy` name is used by an unrelated project.
20//!
21//! ```no_run
22//! # async fn run() -> Result<(), guppy_protocol::ClientError> {
23//! use guppy_protocol::{GuppyResponse, fetch};
24//!
25//! match fetch("guppy://guppy.mozz.us/", &Default::default()).await? {
26//! GuppyResponse::Success { mime, body } => {
27//! println!("{} bytes of {mime}", body.len());
28//! }
29//! other => println!("{other:?}"),
30//! }
31//! # Ok(()) }
32//! ```
33
34/// Guppy's default port ('gu').
35pub const GUPPY_PORT: u16 = 6775;
36
37/// The spec's request-size ceiling: the URL plus the trailing CRLF must fit
38/// in 2048 bytes.
39pub const MAX_REQUEST_BYTES: usize = 2048;
40
41/// The largest sequence number (maximum value of a signed 32-bit integer).
42pub const MAX_SEQ: u32 = 2_147_483_647;
43
44/// The smallest first-packet sequence number.
45pub const MIN_SEQ: u32 = 6;
46
47mod client;
48mod packet;
49mod server;
50
51pub use client::{ClientError, FetchOptions, fetch};
52pub use packet::{Packet, parse_packet};
53pub use server::{FileHandler, Handler, Request, ServerConfig, serve};
54
55/// A complete guppy response, as returned by the client.
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub enum GuppyResponse {
58 /// A reassembled successful response.
59 Success { mime: String, body: Vec<u8> },
60 /// `1 <prompt>` — repeat the request with user input in the URL query.
61 Prompt { text: String },
62 /// `3 <url>` — re-request at this (possibly relative) URL.
63 Redirect { target: String },
64 /// `4 <error>` — a human-readable error for the user.
65 Error { message: String },
66}