Sans-IO HTTP vocabulary for MoonBit SDKs: request/response types, a Transport trait, Retry-After and Link parsing, a multipart writer, an incremental SSE parser, an RFC 6265 cookie jar and a scripted fake transport. No dependencies; runs on every backend.
pub(open) trait BodyStream {
async fn read_some(Self) -> Bytes? raise HttpError
fn close(Self) -> Unit
}pub(open) trait Transport {
async fn send(Self, Request) -> Response raise HttpError
async fn send_stream(Self, Request) -> (ResponseHead, &BodyStream) raise HttpError
}test {
assert_true(@http.Connect("refused").is_unsent())
assert_true(@http.Timeout(phase=Dial, ms=1000).is_unsent())
assert_true(@http.Proxy(status=407, message="auth").is_unsent())
assert_false(@http.Timeout(phase=Head, ms=1000).is_unsent())
assert_false(@http.Network(phase=Write, message="reset").is_unsent())
}test {
let request = @http.Request::post("/upload").body_bytes(b"abc")
assert_eq(request.body, Bytes(b"abc"))
assert_eq(request.body.length(), Some(3L))
assert_true(request.body.is_replayable())
}test {
let headers = @http.Headers::from_array([("Accept", "text/plain")])
assert_eq(headers.get("ACCEPT"), Some("text/plain"))
}test {
let request = @http.Request::post("/items")
.header("Accept", "application/json")
.body_text("hello")
assert_eq(request.body, Bytes(b"hello"))
}test {
let response : @http.Response = {
status: 200,
headers: @http.Headers::new(),
body: b"hello",
}
assert_eq(response.text(), "hello")
assert_true(response.is_success())
}pub struct StreamBody {
// private fields
}impl Eq for StreamBodyimpl Debug for StreamBodytest {
let base = @http.Url::parse("HTTPS://Example.COM:443/a/b?x=1")
assert_eq(base.to_string(), "https://example.com/a/b?x=1")
let next = base.resolve(@http.Url::parse("../c d"))
assert_eq(next.to_string(), "https://example.com/c%20d")
}test {
let url = @http.Url::parse("https://h/p?a=1#top")
let next = url.append_query_params([("b", "x y"), ("a", "2")])
assert_eq(next.to_string(), "https://h/p?a=1&b=x%20y&a=2#top")
}test {
let url = @http.Url::parse("https://h/p?page=1&q=a+b&page=2")
let next = url.merge_query_params([("page", "3")])
assert_eq(next.to_string(), "https://h/p?q=a+b&page=3")
}test {
let base = @http.Url::parse("http://a/b/c/d;p?q")
assert_eq(base.resolve(@http.Url::parse("../g")).to_string(), "http://a/b/g")
assert_eq(base.resolve(@http.Url::parse("//g")).to_string(), "http://g")
}fn append_query(url : String, name : String, value : String) -> Stringfn encode_utf8(text : String) -> Bytestest {
let lone = String::from_array([(0xD800).unsafe_to_char()])
assert_eq(@http.encode_utf8("é" + lone), b"\xC3\xA9\xEF\xBF\xBD")
}async fn follow_redirects(request : Request, send : async (Request) -> Response raise HttpError, max_redirects? : Int, allow_downgrade? : Bool, credential_headers? : Array[String], on_redirect? : (RedirectHop) -> Unit) -> Response raise RedirectErrorasync fn follow_redirects_stream(request : Request, send_stream : async (Request) -> (ResponseHead, &BodyStream) raise HttpError, max_redirects? : Int, allow_downgrade? : Bool, credential_headers? : Array[String], on_redirect? : (StreamRedirectHop) -> Unit) -> (ResponseHead, &BodyStream) raise RedirectErrortest {
assert_eq(
@http.form_urlencode([("q", "a b&c"), ("lang", "日本")]),
"q=a+b%26c&lang=%E6%97%A5%E6%9C%AC",
)
}fn parse_retry_after(value : String, now_unix_ms? : Int64) -> Int?test {
assert_eq(@http.parse_retry_after("1.2349"), Some(1234))
}fn percent_encode(value : String) -> Stringfn percent_encode_path(value : String) -> Stringtest {
let _ : @http.Middleware = @http.redirects(max_redirects=5)
}Install
Download zipSans-IO HTTP vocabulary for MoonBit SDKs: request/response types, a Transport trait, Retry-After and Link parsing, a multipart writer, an incremental SSE parser, an RFC 6265 cookie jar and a scripted fake transport. No dependencies; runs on every backend.