
Nanofish
A lightweight, no_std HTTP client and server for embedded systems built on Embassy networking with zero-copy response handling.
Nanofish is designed for embedded systems with limited memory. It provides a simple HTTP client and server that works without heap allocation, making it suitable for microcontrollers and IoT devices. The library uses zero-copy response handling where response data is borrowed directly from user-provided buffers, keeping memory usage predictable and efficient.
Key Features
- Zero-Copy Response Handling - Response data is borrowed directly from user-provided buffers with no copying
- User-Controlled Memory - You provide the buffer and control exactly how much memory is used
- Configurable Buffer Sizes - Compile-time buffer size configuration using const generics for optimal memory usage
- No Standard Library - Full
no_stdcompatibility with no heap allocations - Embassy Integration - Built on Embassy's async networking
- Complete HTTP Support - All standard HTTP methods (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, TRACE, CONNECT)
- HTTP Server - Built-in async server with customizable timeouts and request handling
- Smart Response Parsing - Automatic text/binary detection based on Content-Type headers
- Easy Header Management - Pre-defined constants and helper methods for common headers
- Optional TLS Support - HTTPS client support with embedded-tls when enabled (server is HTTP-only)
- Timeout & Retry Support - Built-in handling for network issues
- DNS Resolution - Automatic hostname resolution
Installation & Feature Flags
Basic HTTP Support (Default)
[]
= "0.9.1"
With TLS/HTTPS Support
[]
= { = "0.9.1", = ["tls"] }
Available Features
tls- Enables HTTPS/TLS support viaembedded-tls- When disabled (default): Only HTTP requests are supported
- When enabled: Full HTTPS support with TLS 1.2/1.3
Zero-Copy Architecture
Unlike traditional HTTP clients that copy response data multiple times, Nanofish uses a zero-copy approach:
Traditional HTTP Clients:
Network → Internal Buffer (copy #1) → Response Struct (copy #2) → User Code (copy #3)
Nanofish Zero-Copy:
Network → YOUR Buffer (direct) → Zero-Copy References → User Code (no copies!)
Benefits:
- Better Performance - No memory copying overhead
- Memory Efficient - Uses only the memory you provide
- Predictable - No hidden allocations
- Embedded-Friendly - Works well in resource-limited environments
HTTP Client
Quick Start
Here's a simple example showing how to use Nanofish:
use ;
use Stack;
async
let client = new;
let mut response_buffer = ;
let headers = ;
let custom_headers = ;
let = client.get.await?;
println!;
Zero-Copy Benefits
// Traditional approach (copies data):
// 1. Read from network → internal buffer (copy #1)
// 2. Parse response → response struct (copy #2)
// 3. User gets → copied data (copy #3)
// Nanofish zero-copy approach:
// 1. Read from network → YOUR buffer (direct)
// 2. Parse response → references to YOUR buffer (zero-copy)
// 3. User gets → direct references to YOUR buffer (zero-copy)
let mut small_buffer = ; // For small responses
let mut large_buffer = ; // For large responses
// Same API, different memory usage - YOU decide!
let = client.get.await?;
let = client.get.await?;
Header Convenience Features
Nanofish provides helpful APIs for working with HTTP headers:
Pre-defined Header Constants
use headers;
// Common header names
let content_type = CONTENT_TYPE; // "Content-Type"
let authorization = AUTHORIZATION; // "Authorization"
let user_agent = USER_AGENT; // "User-Agent"
let accept = ACCEPT; // "Accept"
Pre-defined MIME Types
use mime_types;
// Common MIME types
let json = JSON; // "application/json"
let xml = XML; // "application/xml"
let text = TEXT; // "text/plain"
let html = HTML; // "text/html"
Convenience Methods
use ;
// Easy creation of common headers
let headers = ;
Response Handling
Nanofish automatically determines the appropriate response body type based on the Content-Type header:
use ResponseBody;
// The response body is automatically parsed based on content type
match &response.body
if response.is_success
if response.is_client_error
if response.is_server_error
// You can also check status directly on the status code:
if response.status_code.is_success
if let Some = response.content_length
HTTP Methods Support
Nanofish provides convenience methods for all standard HTTP verbs:
// All methods require a buffer and return (HttpResponse, bytes_read)
let mut buffer = ;
// GET request
let = client.get.await?;
// POST request with JSON body
let json_body = br#"{"name": "John", "email": "john@example.com"}"#;
let post_headers = ;
let = client.post.await?;
// PUT request
let = client.put.await?;
// DELETE request
let = client.delete.await?;
// Other HTTP methods
let = client.patch.await?;
let = client.head.await?;
let = client.options.await?;
let = client.trace.await?;
let = client.connect.await?;
All methods return a Result<(HttpResponse, usize), Error> where:
HttpResponsecontains zero-copy references to data in your bufferusizeis the number of bytes read into your buffer
Client Memory Configuration
Just like the server, you can choose different client sizes:
use ;
// Default client (4KB buffers) - good for most use cases
let client = new;
// Small client (1KB buffers) - for memory-constrained devices
let client = new;
// Custom client with your own buffer sizes
type CustomClient<'a> = ;
// TCP_RX ↑ ↑ TCP_TX ↑ ↑ TLS_WRITE ↑ REQUEST
// TLS_READ ↑
let client = new;
Buffer Size Parameters
TCP_RX: TCP receive buffer size (default: 4096 bytes)TCP_TX: TCP transmit buffer size (default: 4096 bytes)TLS_READ: TLS read record buffer size (default: 4096 bytes)TLS_WRITE: TLS write record buffer size (default: 4096 bytes)RQ: HTTP request buffer size for building requests (default: 1024 bytes)
Choose buffer sizes based on your memory constraints and expected payload sizes. The request buffer size determines the maximum size of HTTP requests that can be built, including headers and request line.
Memory Efficiency Examples
Choose your buffer size based on your needs:
// Scenario 1: Memory-constrained device (1KB buffer)
let mut tiny_buffer = ;
let = client.get.await?;
// Perfect for small API responses, status checks, etc.
// Scenario 2: Streaming large data (32KB buffer)
let mut large_buffer = ;
let = client.get.await?;
// Handle larger responses, file downloads, etc.
// Scenario 3: Reuse the same buffer for multiple requests
let mut shared_buffer = ;
for url in urls
HTTP Server
Nanofish includes a built-in HTTP server perfect for embedded systems and IoT devices. The server is async, lightweight, and has customizable timeouts.
Important Note: The server only supports plain HTTP connections, not HTTPS/TLS. While the Nanofish client supports both HTTP and HTTPS, the server implementation is HTTP-only. For secure connections in production, use a reverse proxy (like nginx) or load balancer that handles TLS termination.
Basic Server Usage
use ;
use Stack;
// Create a simple request handler
;
async
Server Memory Configuration
Just like the client, you can choose different server sizes:
use ;
// Default server (4KB buffers) - good for most use cases
let server = new;
// Small server (1KB buffers) - for memory-constrained devices
let server = new;
// Custom server with your own buffer sizes
type MyServer = ; // RX, TX, Request, Response buffer sizes
let server = new;
Server Timeouts
You can customize how long the server waits for different operations:
use ;
// Default timeouts: 10s accept, 30s read, 60s handler
let server = new;
// Custom timeouts
let timeouts = new;
let server = with_timeouts;
Request Information
Your handler receives detailed information about each request:
Simple Built-in Handler
For quick testing, you can use the built-in SimpleHandler:
use ;
async
The SimpleHandler provides:
GET /→ HTML welcome pageGET /health→ JSON status response- Everything else → 404 Not Found