207 lines
5.8 KiB
Markdown
207 lines
5.8 KiB
Markdown
# fasthttp
|
|
|
|
The `fasthttp` module is a high-performance HTTP server library for V that provides low-level socket management and non-blocking I/O.
|
|
|
|
## Features
|
|
|
|
- **High Performance**: Uses platform-specific I/O multiplexing:
|
|
- `epoll` on Linux for efficient connection handling
|
|
- `kqueue` on macOS and BSD for high-performance event notification
|
|
- **Non-blocking I/O**: Handles multiple concurrent connections efficiently
|
|
- **Simple API**: Easy-to-use request handler pattern
|
|
- **Cross-platform**: Supports Linux, macOS, FreeBSD, OpenBSD, NetBSD and DragonFly
|
|
|
|
## Installation
|
|
|
|
The module is part of the standard V library. Import it in your V code:
|
|
|
|
```v
|
|
import fasthttp
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
Here's a minimal HTTP server example:
|
|
|
|
```v oksyntax
|
|
import fasthttp
|
|
|
|
fn handle_request(req fasthttp.HttpRequest) ![]u8 {
|
|
path := req.buffer[req.path.start..req.path.start + req.path.len].bytestr()
|
|
|
|
mut body := ''
|
|
mut status_line := ''
|
|
|
|
if path == '/' {
|
|
body = 'Hello, World!\n'
|
|
status_line = 'HTTP/1.1 200 OK'
|
|
} else {
|
|
body = '${path} not found\n'
|
|
status_line = 'HTTP/1.1 404 Not Found'
|
|
}
|
|
|
|
headers := [
|
|
status_line,
|
|
'Content-Type: text/plain',
|
|
'Content-Length: ${body.len}',
|
|
'Connection: close',
|
|
]
|
|
header_string := headers.join('\r\n')
|
|
|
|
return '${header_string}\r\n\r\n${body}'.bytes()
|
|
}
|
|
|
|
fn main() {
|
|
mut server := fasthttp.new_server(fasthttp.ServerConfig{
|
|
port: 3000
|
|
handler: handle_request
|
|
}) or {
|
|
eprintln('Failed to create server: ${err}')
|
|
return
|
|
}
|
|
|
|
println('Server listening on http://localhost:3000')
|
|
server.run() or { eprintln('error: ${err}') }
|
|
}
|
|
```
|
|
|
|
## API Reference
|
|
|
|
### `HttpRequest` Struct
|
|
|
|
Represents an incoming HTTP request.
|
|
|
|
**Fields:**
|
|
|
|
- `buffer: []u8` - The raw request buffer containing the complete HTTP request
|
|
- `method: Slice` - The HTTP method (GET, POST, etc.)
|
|
- `path: Slice` - The request path
|
|
- `version: Slice` - The HTTP version (e.g., "HTTP/1.1")
|
|
- `client_conn_fd: int` - Internal socket file descriptor
|
|
|
|
### `Slice` Struct
|
|
|
|
Represents a slice of the request buffer.
|
|
|
|
**Fields:**
|
|
|
|
- `start: int` - Starting index in the buffer
|
|
- `len: int` - Length of the slice
|
|
|
|
**Usage:**
|
|
|
|
```v ignore
|
|
method := req.buffer[req.method.start..req.method.start + req.method.len].bytestr()
|
|
path := req.buffer[req.path.start..req.path.start + req.path.len].bytestr()
|
|
```
|
|
|
|
## Request Handler Pattern
|
|
|
|
The handler function receives an `HttpRequest` and must return either:
|
|
|
|
- `[]u8` - A byte array containing the HTTP response body
|
|
- An error if processing failed
|
|
|
|
The handler should extract method and path information from the request and route accordingly.
|
|
|
|
**Example:**
|
|
|
|
```v ignore
|
|
fn my_handler(req fasthttp.HttpRequest) ![]u8 {
|
|
method := req.buffer[req.method.start..req.method.start + req.method.len].bytestr()
|
|
path := req.buffer[req.path.start..req.path.start + req.path.len].bytestr()
|
|
|
|
match method {
|
|
'GET' {
|
|
if path == '/' {
|
|
return 'Home page'.bytes()
|
|
}
|
|
}
|
|
'POST' {
|
|
if path == '/api/data' {
|
|
return 'Data received'.bytes()
|
|
}
|
|
}
|
|
else {}
|
|
}
|
|
|
|
return '404 Not Found'.bytes()
|
|
}
|
|
```
|
|
|
|
## Response Format
|
|
|
|
Responses should be returned as byte arrays.
|
|
The server will send them directly to the client as HTTP response bodies.
|
|
|
|
```v ignore
|
|
// Simple text response
|
|
return 'Hello, World!'.bytes()
|
|
|
|
// HTML response
|
|
return '<html><body>Hello</body></html>'.bytes()
|
|
|
|
// JSON response
|
|
return '{"message": "success"}'.bytes()
|
|
```
|
|
|
|
## Example
|
|
|
|
See the complete example in `examples/fasthttp/` for a more
|
|
detailed server implementation with multiple routes and controllers.
|
|
|
|
```sh
|
|
./v examples/fasthttp
|
|
./examples/fasthttp/fasthttp
|
|
```
|
|
|
|
## Platform Support
|
|
|
|
- **Linux**: Uses `epoll` for high-performance I/O multiplexing
|
|
- **macOS**: Uses `kqueue` for event notification
|
|
- **Windows**: Currently not supported
|
|
|
|
## Performance Considerations
|
|
|
|
- The `fasthttp` module is designed for high throughput and low latency
|
|
- Handler functions should be efficient; blocking operations will affect other connections
|
|
- Use goroutines within handlers if you need to perform long-running operations without
|
|
blocking the I/O loop
|
|
|
|
## Request-scoped allocation with `-prealloc`
|
|
|
|
When an application is compiled with `-prealloc`, `fasthttp` starts a scoped
|
|
prealloc arena for each request before decoding the HTTP request and before
|
|
calling the request handler. All V allocations made by the request parser, the
|
|
handler, and code called by the handler use that request arena while the handler
|
|
is running.
|
|
|
|
The arena is freed as a unit after the response no longer needs request-owned
|
|
data. On Linux the normal response path sends the response synchronously, then
|
|
ends the request arena. On macOS and BSD the response buffer can be kept by the
|
|
connection until `kqueue` finishes writing it; in that case `fasthttp` detaches
|
|
the scope from the request thread and frees it after the write completes.
|
|
|
|
This means request-local V allocations are cheap bump-pointer allocations, and
|
|
freeing them does not require walking individual objects. Startup state, server
|
|
state, and allocations made directly by C libraries are not part of a request
|
|
arena. If a handler starts V `spawn` work while the request scope is active, the
|
|
generated thread wrapper retains that scope until the spawned function returns;
|
|
void spawned functions also run inside their own scoped arena, which is freed at
|
|
thread exit. Manual takeover responses transfer ownership to user code and
|
|
currently abandon the request arena, so long-lived takeover handlers should
|
|
manage their own allocation lifetime explicitly.
|
|
|
|
To inspect request arena usage while developing, build with:
|
|
|
|
```sh
|
|
v -prealloc -d trace_prealloc run .
|
|
```
|
|
|
|
## Notes
|
|
|
|
- HTTP headers are currently not parsed; the entire request is available in the buffer
|
|
- Only the request method, path, and version are parsed automatically
|
|
- Response status codes and headers must be manually constructed if needed
|
|
- The module provides low-level access for maximum control and performance
|