http
HTTP/1.1 request parser and response writer. Parses raw HTTP request bytes into structured data and writes formatted HTTP responses to TCP sockets. Designed for use with the tcp package.
Types
HttpRequest
Parsed HTTP request with zero-copy slices into the raw buffer:
struct HttpRequest {
method: []i8 // "GET", "POST", etc.
path: []i8 // "/index.html"
query: []i8 // "key=val&key2=val2" (empty if no query)
version: []i8 // "HTTP/1.1"
headers: []Header // parsed headers (up to MAX_HEADERS)
body: []i8 // request body (empty for GET/HEAD)
}Header
A single HTTP header as name-value pair:
struct Header {
name: []i8 // e.g. "Content-Type"
value: []i8 // e.g. "text/html"
}HttpResponse
HTTP response to send back to the client:
struct HttpResponse {
status: i32 // HTTP status code (200, 404, etc.)
headers: []i8 // raw header string (e.g. "Content-Type: text/html\r\n")
body: []i8 // response body
}Constants
| Constant | Value | Description |
|---|---|---|
MAX_HEADERS | 32 | Maximum number of headers parsed per request |
Usage
Simple HTTP Server
import tcp
import http
fn main() {
// Set up TCP listener on port 8080
let addr = SockAddrIn {
sin_family: AF_INET,
sin_port: htons(8080),
sin_addr: htonl(0),
sin_zero: [0, 0, 0, 0, 0, 0, 0, 0],
}
let fd = syscall(SYS_SOCKET, AF_INET, SOCK_STREAM, 0)
syscall(SYS_BIND, fd, &addr, 16)
syscall(SYS_LISTEN, fd, 128)
loop {
let client = syscall(SYS_ACCEPT4, fd, 0, 0, 0)
// Read request
let buf = [4096]i8
let n = syscall(SYS_RECVFROM, client, buf, 4096, 0, 0)
// Parse request
let req = parse_request(buf[0..n]).unwrap()
// Build response based on path
let body = "<h1>Hello from Jda!</h1>"
let resp = HttpResponse {
status: 200,
headers: "Content-Type: text/html\r\n",
body: body,
}
// Send response
send_response(client, resp)
tcp_close_fd(client)
}
}Function Reference
| Function | Signature | Description |
|---|---|---|
parse_request | (buf: []i8) -> Result<HttpRequest, []i8> | Parse raw HTTP request bytes |
send_response | (fd: i32, resp: HttpResponse) | Write HTTP response to socket |
status_to_text | (code: i32) -> []i8 | Convert status code to reason phrase |
str_eq_ci | (a: []i8, b: []i8) -> bool | Case-insensitive string comparison |
Detailed API
parse_request
fn parse_request(buf: []i8) -> Result<HttpRequest, []i8>Parse a raw HTTP request from a byte buffer. Extracts the method, path, query string, HTTP version, headers, and body. The query string is split from the path at the ? character.
let raw = "GET /api/users?page=1 HTTP/1.1\r\nHost: localhost\r\n\r\n"
let req = parse_request(raw).unwrap()
// req.method == "GET"
// req.path == "/api/users"
// req.query == "page=1"
// req.version == "HTTP/1.1"
// req.headers[0].name == "Host"
// req.headers[0].value == "localhost"Returns: Ok(HttpRequest) on success, Err(message) on parse failure.
send_response
fn send_response(fd: i32, resp: HttpResponse)Write a complete HTTP response to a TCP socket. Formats the status line, headers, and body according to HTTP/1.1 format with CRLF line endings.
let resp = HttpResponse {
status: 404,
headers: "Content-Type: text/plain\r\n",
body: "Not Found",
}
send_response(client_fd, resp)
// Writes:
// HTTP/1.1 404 Not Found\r\n
// Content-Type: text/plain\r\n
// \r\n
// Not Foundstatus_to_text
fn status_to_text(code: i32) -> []i8Convert an HTTP status code to its standard reason phrase.
let text = status_to_text(200) // "OK"
let text = status_to_text(404) // "Not Found"
let text = status_to_text(500) // "Internal Server Error"str_eq_ci
fn str_eq_ci(a: []i8, b: []i8) -> boolCase-insensitive string comparison. Useful for comparing HTTP header names.
let eq = str_eq_ci("Content-Type", "content-type")
// eq == trueread_token
fn read_token(buf: []i8, pos: &mut i64) -> []i8Internal: Read a whitespace-delimited token from the buffer, advancing pos past the token and any following whitespace.
read_until
fn read_until(buf: []i8, pos: &mut i64, delim: i8) -> []i8Internal: Read bytes from the buffer until the delimiter delim is encountered. Advances pos to the position after the delimiter.
read_until_crlf
fn read_until_crlf(buf: []i8, pos: &mut i64) -> []i8Internal: Read bytes until the sequence \r\n is found.
skip_sp
fn skip_sp(buf: []i8, pos: &mut i64)Internal: Skip one or more space characters ( ) at the current position.
skip_crlf
fn skip_crlf(buf: []i8, pos: &mut i64)Internal: Skip a \r\n sequence at the current position.
is_crlf
fn is_crlf(buf: []i8, pos: i64) -> boolInternal: Check if the buffer at pos contains the sequence \r\n.
find_char
fn find_char(s: []i8, c: i8) -> i64Internal: Find the first occurrence of character c in the slice s. Returns the index, or -1 if not found.
tcp_write_raw
fn tcp_write_raw(fd: i32, s: []i8)Internal: Write the raw bytes of slice s to the file descriptor fd.
Internal Functions
| Function | Signature | Description |
|---|---|---|
read_token | (buf: []i8, pos: &mut i64) -> []i8 | Read a whitespace-delimited token |
read_until | (buf: []i8, pos: &mut i64, delim: i8) -> []i8 | Read until a delimiter character |
read_until_crlf | (buf: []i8, pos: &mut i64) -> []i8 | Read until \r\n |
skip_sp | (buf: []i8, pos: &mut i64) | Skip whitespace characters |
skip_crlf | (buf: []i8, pos: &mut i64) | Skip a \r\n sequence |
is_crlf | (buf: []i8, pos: i64) -> bool | Check if position is at \r\n |
find_char | (s: []i8, c: i8) -> i64 | Find first occurrence of a character |
tcp_write_raw | (fd: i32, s: []i8) | Write raw bytes to a TCP socket |