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)
}

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

ConstantValueDescription
MAX_HEADERS32Maximum 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

FunctionSignatureDescription
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) -> []i8Convert status code to reason phrase
str_eq_ci(a: []i8, b: []i8) -> boolCase-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 Found

status_to_text

fn status_to_text(code: i32) -> []i8

Convert 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) -> bool

Case-insensitive string comparison. Useful for comparing HTTP header names.

let eq = str_eq_ci("Content-Type", "content-type")
// eq == true

read_token

fn read_token(buf: []i8, pos: &mut i64) -> []i8

Internal: 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) -> []i8

Internal: 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) -> []i8

Internal: 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) -> bool

Internal: Check if the buffer at pos contains the sequence \r\n.


find_char

fn find_char(s: []i8, c: i8) -> i64

Internal: 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

FunctionSignatureDescription
read_token(buf: []i8, pos: &mut i64) -> []i8Read a whitespace-delimited token
read_until(buf: []i8, pos: &mut i64, delim: i8) -> []i8Read until a delimiter character
read_until_crlf(buf: []i8, pos: &mut i64) -> []i8Read 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) -> boolCheck if position is at \r\n
find_char(s: []i8, c: i8) -> i64Find first occurrence of a character
tcp_write_raw(fd: i32, s: []i8)Write raw bytes to a TCP socket