Sign in

    mio

    A powerful HTTP networking package for MoonBit.

    network
    http
    request
    response
    Download zip
    Author
    Version
    0.4.2
    License
    Apache-2.0
    Last updated
    last year
    Downloads
    874

    #oboard/mio

    A powerful and modern HTTP networking library for MoonBit with multi-backend support.

    Version License

    #Features

    • 🚀 Async/Await Support: Built-in async operations with @mio.run
    • 🌐 Complete HTTP Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD, CONNECT, TRACE
    • 📄 JSON Handling: Seamless JSON parsing and response handling
    • 📁 File Downloads: Built-in file download capabilities with custom save paths
    • 🌊 Stream Processing: Real-time data streaming with callback support
    • 📦 Binary Data Support: Native handling of binary data with unified Bytes interface across all backends
    • 🎯 Multi-Backend: Support for Native (libcurl), JavaScript (Fetch API), and WASM
    • ⚡ Type Safety: Full MoonBit type system support with error handling
    • 🔧 Flexible Options: Headers, credentials, modes, and request customization

    #Backend Support

    #Native Backend (native, llvm)

    • HTTP Engine: libcurl for robust HTTP requests
    • Features: All HTTP methods, file downloads, full header support
    • Dependencies: Requires libcurl system library
    • Performance: Optimized for server-side and native applications

    #JavaScript Backend (js)

    • HTTP Engine: Fetch API
    • Features: Browser and Node.js compatibility
    • Dependencies: No external dependencies
    • Performance: Optimized for web applications and frontend development

    #WebAssembly Backend (wasm, wasm-gc)

    • HTTP Engine: WASM-compatible implementation
    • Features: Cross-platform WebAssembly support
    • Dependencies: WASM runtime environment
    • Performance: Optimized for WASM applications

    #Installation

    Add to your moon.mod.json:

    { "deps": { "oboard/mio": "0.3.0" } }

    #Quick Start

    #Basic GET Request

    @mio.run(fn() {
    match (try? @mio.get("https://api.github.com")) {
    Ok(response) => {
    println("Status: " + response.statusCode.to_string())
    println("Response: " + response.text())
    }
    Err(e) => println("Error: " + e.to_string())
    }
    })

    #POST Request with JSON Data

    @mio.run(fn() {
    match (try? @mio.post("https://httpbin.org/post",
    data={
    "name": "MoonBit",
    "version": "0.3.0",
    "features": ["async", "http", "json"]
    })) {
    Ok(response) => {
    println("Posted successfully!")
    println(response.unwrap_json())
    }
    Err(e) => println("Failed: " + e.to_string())
    }
    })

    #Custom Headers and Options

    @mio.run(fn() {
    let headers = {
    "Authorization": "Bearer your-token",
    "User-Agent": "MoonBit-App/1.0",
    "Accept": "application/json"
    }

    match (try? @mio.get("https://api.example.com/data",
    headers=headers,
    credentials=SameOrigin,
    mode=CORS)) {
    Ok(response) => {
    if response.statusCode == 200 {
    let data = response.json()
    // Process your data
    }
    }
    Err(e) => println("Request failed: " + e.to_string())
    }
    })

    #File Download

    @mio.run(fn() {
    // Download with custom filename
    match (try? @mio.download("https://api.github.com/repos/moonbitlang/core",
    save_path="github_repo.json")) {
    Ok(_) => println("File downloaded successfully!")
    Err(e) => println("Download failed: " + e.to_string())
    }

    // Download with dynamic filename based on headers
    match (try? @mio.download("https://example.com/file.zip",
    save_path_fn=fn(headers) {
    match headers.get("content-disposition") {
    Some(disposition) => extract_filename(disposition)
    None => "downloaded_file.zip"
    }
    })) {
    Ok(_) => println("File downloaded with dynamic name!")
    Err(e) => println("Download failed: " + e.to_string())
    }
    })

    #Stream Requests

    @mio.run(fn() {
    // Stream processing with real-time data handling
    let chunks = []

    match (try? @mio.get_stream("https://api.example.com/stream",
    fn(chunk) {
    println("Received chunk: " + chunk)
    chunks.push(chunk)
    // Process each chunk as it arrives
    })) {
    Ok(response) => {
    println("Stream completed with status: " + response.statusCode.to_string())
    println("Total chunks received: " + chunks.length().to_string())
    }
    Err(e) => println("Stream failed: " + e.to_string())
    }
    })

    Note: Stream functionality is currently under development for native backend due to callback compatibility issues between MoonBit and C function pointers. It works properly on JavaScript and WASM backends.

    #API Reference

    #HTTP Methods

    All HTTP methods support the same optional parameters:

    • headers?: Map[String, String] - Custom HTTP headers
    • credentials?: FetchCredentials - Request credentials (Omit, SameOrigin, Include)
    • mode?: FetchMode - Request mode (CORS, NoCORS, SameOrigin, Navigate)

    // GET request
    (try? @mio.get(url, headers?, credentials?, mode?))

    // POST request with body or JSON data
    (try? @mio.post(url, body?, data?, headers?, credentials?, mode?))

    // Other HTTP methods
    (try? @mio.put(url, body?, headers?, credentials?, mode?))
    (try? @mio.delete(url, body?, headers?, credentials?, mode?))
    (try? @mio.patch(url, body?, headers?, credentials?, mode?))
    (try? @mio.options(url, body?, headers?, credentials?, mode?))
    (try? @mio.head(url, body?, headers?, credentials?, mode?))
    (try? @mio.connect(url, body?, headers?, credentials?, mode?))
    (try? @mio.trace(url, body?, headers?, credentials?, mode?))

    // Stream request with callback for real-time data processing
    (try? @mio.get_stream(url, callback, headers?, credentials?, mode?))

    #Response Handling

    // HttpResponse methods
    response.text() // Get response as string
    response.json() // Parse JSON (may raise ParseError)
    response.unwrap_json() // Safe JSON parsing (returns Json::null() on error)
    response.statusCode // HTTP status code
    response.headers // Response headers as Map[String, String]
    response.data // Raw response data as Bytes

    #Error Handling

    The library defines three main error types:

    • IOError - File system and I/O related errors
    • NetworkError - Network and HTTP request errors
    • ExecError - Execution and runtime errors

    #Advanced Usage

    #Custom Request with Full Control

    @mio.run(fn() {
    match (try? @mio.request("https://api.example.com/upload",
    http_method=POST,
    body="custom request body",
    headers={
    "Content-Type": "text/plain",
    "X-Custom-Header": "value"
    },
    credentials=Include,
    mode=CORS)) {
    Ok(response) => {
    // Handle response
    }
    Err(NetworkError) => {
    // Handle network errors
    }
    }
    })

    #Binary Data Handling

    Mio provides unified binary data support across all backends through the Bytes interface. All HTTP responses contain raw binary data in response.data, which can be processed as text or kept as binary.

    @mio.run(fn() {
    // All requests return binary data in response.data
    match (try? @mio.get("https://example.com/image.png")) {
    Ok(response) => {
    // response.data contains raw bytes (unified across all backends)
    @fs.write_bytes_to_file("image.png", response.data)

    // For text content, use response.text()
    let text_content = response.text()
    println("Content: " + text_content)
    }
    Err(e) => println("Failed to download: " + e.to_string())
    }
    })

    #Examples

    Check out these real-world projects using mio:

    • weatherquery - Weather query tool for Chinese locations
      • Fetches real-time weather data from APIs
      • Supports provinces, cities, and districts
      • Cross-platform (native and web)

    #Contributing

    1. Fork the repository
    2. Create your feature branch (git checkout -b feature/amazing-feature)
    3. Commit your changes (git commit -m 'Add amazing feature')
    4. Push to the branch (git push origin feature/amazing-feature)
    5. Open a Pull Request

    #License

    This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

    ExecError

    pub suberror ExecError

    impl Show for ExecError

    IOError

    pub suberror IOError

    impl Show for IOError

    NetworkError

    pub suberror NetworkError

    StreamError

    pub suberror StreamError

    impl Show for StreamError

    UnimplementedError

    pub suberror UnimplementedError

    FetchCredentials

    pub(all) enum FetchCredentials {
    Omit
    SameOrigin
    Include
    }

    FetchMode

    pub(all) enum FetchMode {
    CORS
    NoCORS
    SameOrigin
    Navigate
    }

    impl Eq for FetchMode
    impl Show for FetchMode
    impl ToJson for FetchMode

    HttpMethod

    pub(all) enum HttpMethod {
    GET
    POST
    PUT
    DELETE
    PATCH
    OPTIONS
    HEAD
    CONNECT
    TRACE
    }

    impl Eq for HttpMethod
    impl Show for HttpMethod

    HttpRequest

    pub(all) struct HttpRequest {
    request_method : HttpMethod
    url : String?
    path : String
    query : String?
    body : Json?
    }

    impl Eq for HttpRequest
    impl Show for HttpRequest

    HttpResponse

    pub struct HttpResponse {
    statusCode : Int
    headers : Map[String, String]
    data : Bytes
    }

    impl Eq for HttpResponse

    HttpResponse::text

    fn HttpResponse::text(self : HttpResponse) -> String

    HttpResponse::unwrap_json

    fn HttpResponse::unwrap_json(self : HttpResponse) -> Json

    HttpResponse::write_head

    fn HttpResponse::write_head(self : HttpResponse, statusCode : Int, headers : Map[String, String]) -> Unit

    SSEEvent

    pub struct SSEEvent {
    event_type : String?
    data : String
    id : String?
    retry : Int?
    }

    impl Eq for SSEEvent
    impl Show for SSEEvent

    SSEEvent::parse

    fn SSEEvent::parse(data : String) -> Array[SSEEvent]

    StreamReader

    pub struct StreamReader {
    closed : Bool
    buffer :
    T

    }

    StreamReader::close

    fn StreamReader::close(self : StreamReader) -> Unit

    StreamReader::new

    connect

    async fn connect(url : String, body? : String, headers? : Map[String, String], credentials? : FetchCredentials, mode? : FetchMode) -> HttpResponse raise Error

    delete

    async fn delete(url : String, body? : String, headers? : Map[String, String], credentials? : FetchCredentials, mode? : FetchMode) -> HttpResponse raise Error

    download

    async fn download(url : String, save_path? : String, save_path_fn? : (Map[String, String]) -> String, headers? : Map[String, String], credentials? : FetchCredentials, mode? : FetchMode) -> HttpResponse raise Error

    get

    async fn get(url : String, headers? : Map[String, String], credentials? : FetchCredentials, mode? : FetchMode) -> HttpResponse raise Error

    get_stream

    async fn get_stream(url : String, headers? : Map[String, String], credentials? : FetchCredentials, mode? : FetchMode, on_chunk : (String) -> Unit) -> HttpResponse raise NetworkError

    handle

    fn handle(_reqMethod : String, _mapping : String) -> Unit

    async fn head(url : String, body? : String, headers? : Map[String, String], credentials? : FetchCredentials, mode? : FetchMode) -> HttpResponse raise Error

    listen

    fn listen(_port : Int) -> Unit raise Error

    options

    async fn options(url : String, body? : String, headers? : Map[String, String], credentials? : FetchCredentials, mode? : FetchMode) -> HttpResponse raise Error

    patch

    async fn patch(url : String, body? : String, headers? : Map[String, String], credentials? : FetchCredentials, mode? : FetchMode) -> HttpResponse raise Error

    post

    async fn post(url : String, body? : String, data? : Json, headers? : Map[String, String], credentials? : FetchCredentials, mode? : FetchMode) -> HttpResponse raise Error

    post_stream

    async fn post_stream(url : String, data~ : Json, headers? : Map[String, String], credentials? : FetchCredentials, mode? : FetchMode, on_chunk : (String) -> Unit) -> HttpResponse raise NetworkError

    put

    async fn put(url : String, body? : String, headers? : Map[String, String], credentials? : FetchCredentials, mode? : FetchMode) -> HttpResponse raise Error

    request

    async fn request(url : String, http_method? : HttpMethod, body? : String, headers? : Map[String, String], credentials? : FetchCredentials, mode? : FetchMode, stream? : Bool) -> HttpResponse raise Error

    request_internal_ffi

    async fn request_internal_ffi(args : String) -> (String, String, Bytes) raise Error

    request_stream

    async fn request_stream(url : String, http_method? : HttpMethod, body? : Json, headers? : Map[String, String], credentials? : FetchCredentials, mode? : FetchMode, on_chunk~ : (String) -> Unit) -> HttpResponse raise NetworkError

    request_stream_internal_ffi

    async fn request_stream_internal_ffi(args : String, on_chunk : (String) -> Unit) -> String raise Error

    run

    fn run(f : async () -> Unit) -> Unit

    sse_connect

    async fn sse_connect(url : String, headers? : Map[String, String], credentials? : FetchCredentials, mode? : FetchMode, on_event : (SSEEvent) -> Unit) -> HttpResponse raise Error

    trace

    async fn trace(url : String, body? : String, headers? : Map[String, String], credentials? : FetchCredentials, mode? : FetchMode) -> HttpResponse raise Error