hyperlocal

repository·main·Indexed 42 days ago

https://github.com/softprops/hyperlocal

Hyper bindings for Unix domain sockets (UDS) version 0.9.1, enabling secure, host-local interprocess communication via HTTP without exposing TCP ports. It provides the UnixConnector for hyper::Client, the UnixListenerExt trait for serving HTTP over Unix sockets, and a specialized Uri type for socket addressing.

Tokens
2.2K
Snippets
8
Records
13
Agent score
53%

What's inside hyperlocal

  1. What is hyperlocal and when should I use it?

    main

    hyperlocal provides Hyper client and server bindings for Unix domain sockets (UDS).

    Unix domain sockets provide a mechanism for host-local interprocess communication. You should use hyperlocal when you want to expose simple HTTP interfaces for Unix daemons (like Docker) and want to limit access strictly to the current host without opening and exposing TCP ports.

  2. Build a Unix domain socket HTTP server

    main

    To build a server, you can use the UnixListenerExt extension trait provided by hyperlocal. This trait simplifies the process of accepting connections from a tokio::net::UnixListener and serving them using hyper::server::conn::http1::Builder.

    A typical workflow involves:

    1. Creating a tokio::net::UnixListener.
    2. Using UnixListenerExt to handle connections.
    3. Providing a request/response processing function via hyper::service::service_fn.

    You can test your running server using curl by specifying the --unix-socket flag:

    curl --unix-socket /tmp/hyperlocal.sock localhost
    $ curl --unix-socket /tmp/hyperlocal.sock localhost
  3. Build a Unix domain socket HTTP client

    main

    To write an HTTP client that communicates over Unix domain sockets, use the Client interface from the hyper-utils crate via hyperlocal bindings.

    Because Unix domain sockets do not use hostnames or IP addresses, you cannot use standard URL strings. Instead, you must use hyperlocal::Uri. This type represents both the file path to the domain socket and the resource URI path/query string.

    Hyper's client interface allows you to use factory methods like get, post, and delete to send standard HTTP methods.

  4. URI format for Unix domain sockets

    main

    When using UnixConnector, your URIs must follow a specific format:

    • Scheme: Must be unix.
    • Host: Must be a hex-encoded string representing the file path to the Unix domain socket.

    Example logic: If your socket is at /tmp/socket, the host part of the URI must be the hex representation of /tmp/socket.

  5. Construct a Unix HTTP client with UnixClientExt

    main

    For a more ergonomic way to create a client, use the UnixClientExt trait. This provides a .unix() method on hyper_util::client::legacy::Client (when the connector is UnixConnector) to quickly construct a client configured for Unix domain sockets.

    This is recommended if you do not need to access the low-level hyper::Client builder interface.

    use http_body_util::Full;
    use hyper::body::Bytes;
    use hyper_util::client::legacy::Client;
    use hyperlocal::{UnixClientExt, UnixConnector};
    
    // The .unix() method is provided by the UnixClientExt trait
    let client: Client<UnixConnector, Full<Bytes>> = Client::unix();
  6. Construct Unix Domain Socket URIs with `Uri::new`

    main

    The hyperlocal::Uri type is a convenience wrapper used to construct hyper::Uri objects specifically for Unix Domain Socket (UDS) communication. It converts a filesystem socket path into a unix:// scheme URI that hyper can understand.

    To create a new URI, use Uri::new(socket, path).

    Important Notes:

    • The socket argument must be a path to a Unix domain socket (implements AsRef<Path>).
    • The path argument must be an absolute path string (e.g., /).
    • Panics: This method will panic if the provided path is not absolute or if the resulting path string is malformed.
    • The resulting Uri can be converted into a standard hyper::Uri using .into() or From::from() to be used with hyper clients or servers.
    use hyper::Uri as HyperUri;
    use hyperlocal::Uri;
    
    // Create a URI for a socket at /tmp/hyperlocal.sock with path /
    let uri: HyperUri = Uri::new("/tmp/hyperlocal.sock", "/").into();
  7. Use Unix client features with UnixClientExt and UnixConnector

    main

    When the client feature is enabled, hyperlocal provides tools to use Unix domain sockets with hyper.

    • UnixConnector: A connector used to establish connections over Unix domain sockets.
    • UnixStream: The underlying stream type for Unix domain socket connections.
    • UnixClientExt: An extension trait that provides client-side functionality for hyper to work with Unix sockets.
    // Note: Specific usage depends on the implementation in the client module.
    // Ensure the 'client' feature is enabled in your Cargo.toml
    // [dependencies]
    // hyperlocal = { version = "0.9.1", features = ["client"] }
  8. Use Unix server features with UnixListenerExt

    main

    When the server feature is enabled, hyperlocal provides UnixListenerExt, an extension trait that allows you to set up a hyper server listening on a Unix domain socket.

    // Ensure the 'server' feature is enabled in your Cargo.toml
    // [dependencies]
    // hyperlocal = { version = "0.9.1", features = ["server"] }
  9. Use UnixConnector to build a hyper::Client

    main

    The UnixConnector is a hyper::client::legacy::connect::Connection implementation that allows a hyper::Client to communicate over Unix domain sockets. To use it, pass UnixConnector to the hyper_util::client::legacy::Client::builder method.

    Note that when using UnixConnector, the URI scheme must be unix and the host must be a hex-encoded representation of the file path to the socket.

    use http_body_util::Full;
    use hyper::body::Bytes;
    use hyper_util::{client::legacy::Client, rt::TokioExecutor};
    use hyperlocal::UnixConnector;
    
    let connector = UnixConnector;
    let client: Client<UnixConnector, Full<Bytes>> =
        Client::builder(TokioExecutor::new()).build(connector);
  10. Use the `UnixListenerExt` trait to serve HTTP over Unix domain sockets

    main

    The UnixListenerExt trait extends tokio::net::UnixListener to allow provisioning a hyper HTTP server over a Unix domain socket.

    By calling .serve() on a UnixListener, you can indefinitely accept and respond to incoming connections. The method requires a factory function that generates a response handler for each connection.

    Note on OS Compatibility: The implementation internally disables keep_alive on the HTTP/1 builder to prevent blocking and E_NOTCONN errors on macOS (OSX).

    use hyper::Response;
    use hyperlocal::UnixListenerExt;
    use tokio::net::UnixListener;
    
    let future = async move {
        let listener = UnixListener::bind("/tmp/hyperlocal.sock").expect("parsed unix path");
    
        listener
            .serve(|| {
                |_request| async {
                    Ok::<_, hyper::Error>(Response::new("Hello, world.".to_string()))
                }
            })
            .await
            .expect("failed to serve a connection")
    };
    
    future.await;
  11. The `UnixListenerExt::serve` method

    main

    The serve method is an extension of tokio::net::UnixListener that runs an infinite loop to accept connections and serve them using hyper.

    Signature

    fn serve<MakeResponseFn, ResponseFn, ResponseFuture, B, E>(
        self,
        f: MakeResponseFn,
    ) -> impl Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>>

    Generic Parameters

    • MakeResponseFn: A factory function Fn() -> ResponseFn used to create a service for each connection.
    • ResponseFn: A function Fn(Request<Incoming>) -> ResponseFuture that handles individual requests.
    • ResponseFuture: The future returned by the response handler, which must resolve to Result<Response<B>, E>.
    • B: The body type, which must implement hyper::body::Body + 'static.
    • E: The error type for the response future, which must implement std::error::Error + Send + Sync + 'static.