15. HTTP
🚀 IMPLEMENTATION STATUS: HTTP functions are implemented and working. WebSocket functions are implemented but undergoing testing. Fiber operations are partially implemented.
Table of Contents #
- 15. HTTP
- Table of Contents
- 15.1 HTTP Core Types
- 15.2 HTTP Server Functions
- 15.2.1 HTTP Request Handling Bridge
- 15.3 HTTP Client Functions
httpCreateClient(baseUrl: String, timeout: Int) -> Result<ClientID, String>
httpGet(clientID: Int, path: String, headers: String) -> Result<HttpResponse, String>
httpPost(clientID: Int, path: String, body: String, headers: String) -> Result<HttpResponse, String>
httpPut(clientID: Int, path: String, body: String, headers: String) -> Result<HttpResponse, String>
httpDelete(clientID: Int, path: String, headers: String) -> Result<HttpResponse, String>
httpRequest(clientID: Int, method: HttpMethod, path: String, headers: String, body: String) -> Result<HttpResponse, String>
httpCloseClient(clientID: Int) -> Result<Success, String>
- 15.4 Complete HTTP Server Example
Osprey provides first-class support for HTTP servers and clients, designed with performance, safety, and streaming as core principles. All HTTP functions follow Osprey’s functional programming paradigms and comply with:
-
RFC 7230: HTTP/1.1 Message Syntax and Routing (https://tools.ietf.org/html/rfc7230)
-
RFC 7231: HTTP/1.1 Semantics and Content (https://tools.ietf.org/html/rfc7231)
-
RFC 7232: HTTP/1.1 Conditional Requests (https://tools.ietf.org/html/rfc7232)
-
RFC 7233: HTTP/1.1 Range Requests (https://tools.ietf.org/html/rfc7233)
-
RFC 7234: HTTP/1.1 Caching (https://tools.ietf.org/html/rfc7234)
-
RFC 7235: HTTP/1.1 Authentication (https://tools.ietf.org/html/rfc7235)
-
Result types instead of exceptions for error handling
-
Immutable response objects that cannot be modified after creation
-
Streaming by default for large response bodies to prevent memory issues
-
Fiber-based concurrency for handling thousands of concurrent connections
15.1 HTTP Core Types #
HTTP Method Union Type #
type HttpMethod = GET | POST | PUT | DELETE | PATCH | HEAD | OPTIONS
HTTP Request Type (Immutable) #
type HttpRequest = {
method: HttpMethod,
path: String,
headers: String,
body: String,
queryParams: String
}
HTTP Response Type (Immutable with Streaming - SIMPLIFIED!) #
type HttpResponse = {
status: Int,
headers: String,
contentType: String,
streamFd: Int, // File descriptor for streaming
isComplete: Bool, // Whether response is fully loaded
partialBody: String // Body data (runtime auto-calculates length using strlen)
}
IMPORTANT: The contentLength
and partialLength
fields have been removed to prevent hardcoded length bugs. The C runtime automatically calculates string lengths using strlen()
for security and simplicity.
15.2 HTTP Server Functions #
httpCreateServer(port: Int, address: String) -> Result<ServerID, String>
#
Creates an HTTP server bound to the specified port and address.
Parameters:
port
: Port number (1-65535)address
: IP address to bind to (e.g., “127.0.0.1”, “0.0.0.0”)
Returns:
Success(serverID)
: Unique server identifierErr(message)
: Error description (invalid port, bind failure, etc.)
Implementation Status: ⚠️ INCORRECT - Current C runtime returns raw int64_t
instead of Result<ServerID, String>
Example:
let serverResult = httpCreateServer(port: 8080, address: "127.0.0.1")
match serverResult {
Success serverId => print("Server created with ID: ${serverId}")
Err message => print("Failed to create server: ${message}")
}
httpListen(serverID: Int, handler: fn(String, String, String, String) -> HttpResponse) -> Result<Success, String>
#
Starts the HTTP server listening for requests. Each request is handled in a separate fiber for maximum concurrency.
CRITICAL: The handler function receives structured HTTP request data and must return an HttpResponse object. The C runtime handles HTTP parsing and response formatting.
Parameters:
serverID
: Server identifier fromhttpCreateServer
handler
: Request handler function that takes:method: String
- HTTP method (GET, POST, PUT, DELETE, etc.)path: String
- Request path (e.g., “/api/users”, “/health”)headers: String
- Raw HTTP headers as receivedbody: String
- Raw request body data
Returns:
Success()
: Server started successfullyErr(message)
: Error description
Implementation Status: ⚠️ INCORRECT - Current C runtime expects raw string return from handler, not HttpResponse object. Also returns raw int64_t
instead of Result<Success, String>
Example:
fn handleHttpRequest(method: String, path: String, headers: String, body: String) -> HttpResponse =
match method {
"GET" => match path {
"/health" => HttpResponse {
status: 200,
headers: "Content-Type: application/json",
contentType: "application/json",
streamFd: -1,
isComplete: true,
partialBody: "{\"status\": \"healthy\"}"
}
"/api/users" => HttpResponse {
status: 200,
headers: "Content-Type: application/json",
contentType: "application/json",
streamFd: -1,
isComplete: true,
partialBody: "{\"users\": [{\"id\": 1, \"name\": \"Alice\"}, {\"id\": 2, \"name\": \"Bob\"}]}"
}
_ => HttpResponse {
status: 404,
headers: "Content-Type: application/json",
contentType: "application/json",
streamFd: -1,
isComplete: true,
partialBody: "{\"error\": \"Not found\"}",
}
}
"POST" => match path {
"/api/users" => HttpResponse {
status: 201,
headers: "Content-Type: application/json",
contentType: "application/json",
streamFd: -1,
isComplete: true,
partialBody: "{\"id\": 3, \"name\": \"New User\", \"message\": \"User created successfully\"}",
}
_ => HttpResponse {
status: 404,
headers: "Content-Type: application/json",
contentType: "application/json",
streamFd: -1,
isComplete: true,
partialBody: "{\"error\": \"Not found\"}",
}
}
_ => HttpResponse {
status: 405,
headers: "Content-Type: application/json",
contentType: "application/json",
streamFd: -1,
isComplete: true,
partialBody: "{\"error\": \"Method not allowed\"}",
}
}
let listenResult = httpListen(serverID: serverId, handler: handleHttpRequest)
match listenResult {
Success _ => print("Server started successfully")
Err message => print("Failed to start server: ${message}")
}
HTTP Handler Architecture:
The HTTP server uses a structured callback architecture where:
- C Runtime handles TCP connections, HTTP parsing, and response formatting
- Osprey Handler receives structured request data and returns HttpResponse object
- Type-safe abstraction - handler works with HttpResponse types, not raw strings
- Maximum performance - direct function pointer callbacks with zero overhead
Handler Function Signature:
fn myHandler(method: String, path: String, headers: String, body: String) -> HttpResponse
Response Handling:
- Return HttpResponse object with complete response data
- HTTP status codes are taken from the
status
field - Content-Type headers are taken from the
headers
orcontentType
fields - Response body is taken from
partialBody
field
httpStopServer(serverID: Int) -> Result<Success, String>
#
Stops the HTTP server and cleans up resources.
Parameters:
serverID
: Server identifier to stop
Returns:
Success()
: Server stopped successfullyErr(message)
: Error description
Implementation Status: ⚠️ INCORRECT - Current C runtime returns raw int64_t
instead of Result<Success, String>
15.2.1 HTTP Request Handling Bridge #
CRITICAL REQUIREMENT: HTTP servers in Osprey must call back into Osprey code to handle requests. NO ROUTING LOGIC SHALL BE IMPLEMENTED IN C RUNTIME. The C runtime provides only the transport layer; all application logic, routing, and request handling must be implemented in Osprey.
Request Handling Architecture #
When an HTTP server receives a request, the C runtime must:
- Parse the HTTP request (method, path, headers, body)
- Call the Osprey handler function with structured data
- Receive HttpResponse object from Osprey
- Send HTTP response back to the client
Function Pointer Callbacks #
When httpListen()
is called, the Osprey handler function is passed directly to the C runtime as a function pointer:
C Runtime Function Signature:
int64_t http_listen(int64_t server_id, HttpRequestHandler handler);
Handler Function Pointer Type:
typedef HttpResponse* (*HttpRequestHandler)(char* method, char* path, char* headers, char* body);
Implementation Status: ⚠️ INCORRECT - Current C runtime expects char*
return, not HttpResponse*
Callback Architecture Flow #
1. Osprey Code:
fn handleHttpRequest(method: String, path: String, headers: String, body: String) -> HttpResponse =
// ... handler implementation
let listenResult = httpListen(serverId, handleHttpRequest)
2. LLVM Code Generation:
- Generates function pointer for
handleHttpRequest
- Passes function pointer to
http_listen()
C function
3. C Runtime Implementation (Needs to be fixed):
// In request processing loop:
void handle_client_request(int client_fd, char* method, char* path, char* headers, char* body) {
if (server->handler) {
// Call Osprey function directly
HttpResponse* response = server->handler(method, path, headers, body);
// Format and send HTTP response using response object
char http_response[8192];
snprintf(http_response, sizeof(http_response),
"HTTP/1.1 %d %s\r\n"
"%s"
"Connection: close\r\n"
"\r\n",
response->status,
get_status_text(response->status),
response->headers ? response->headers : "");
// Send headers and body
send(client_fd, http_response, strlen(http_response), 0);
send(client_fd, response->partialBody, response->partialLength, 0);
}
}
Architecture Benefits:
- Type safety: Handler returns structured HttpResponse object
- Zero overhead: Direct function calls, no serialization
- Complete control: Osprey has full control over response format
- Simple debugging: Direct call stack from C to Osprey
- Memory efficient: No intermediate data structures
15.3 HTTP Client Functions #
httpCreateClient(baseUrl: String, timeout: Int) -> Result<ClientID, String>
#
Creates an HTTP client for making requests.
Parameters:
baseUrl
: Base URL for requests (e.g., “http://api.example.com”)timeout
: Request timeout in milliseconds
Returns:
Success(clientID)
: Unique client identifierErr(message)
: Error description
Implementation Status: ⚠️ INCORRECT - Current C runtime returns raw int64_t
instead of Result<ClientID, String>
Example:
let clientResult = httpCreateClient(baseUrl: "http://jsonplaceholder.typicode.com", timeout: 5000)
match clientResult {
Success clientId => print("Client created with ID: ${clientId}")
Err message => print("Failed to create client: ${message}")
}
httpGet(clientID: Int, path: String, headers: String) -> Result<HttpResponse, String>
#
Makes an HTTP GET request.
Parameters:
clientID
: Client identifier fromhttpCreateClient
path
: Request path (e.g., “/users/1”)headers
: Additional headers (e.g., “Authorization: Bearer token\r\n”)
Returns:
Success(response)
: HttpResponse object with status, headers, and bodyErr(message)
: Error description
Implementation Status: ⚠️ INCORRECT - Current C runtime returns raw status code int64_t
instead of Result<HttpResponse, String>
Example:
let getResult = httpGet(clientID: clientId, path: "/users", headers: "")
match getResult {
Success response => print("Request completed with status: ${response.status}")
Err message => print("Request failed: ${message}")
}
httpPost(clientID: Int, path: String, body: String, headers: String) -> Result<HttpResponse, String>
#
Makes an HTTP POST request with a request body.
Parameters:
clientID
: Client identifierpath
: Request pathbody
: Request body dataheaders
: Additional headers
Implementation Status: ⚠️ INCORRECT - Current C runtime returns raw status code instead of Result<HttpResponse, String>
Example:
let postData = "{\"name\": \"John\", \"email\": \"john@example.com\"}"
let headers = "Content-Type: application/json\r\n"
let postResult = httpPost(clientID: clientId, path: "/users", body: postData, headers: headers)
match postResult {
Success response => print("POST completed with status: ${response.status}")
Err message => print("POST failed: ${message}")
}
httpPut(clientID: Int, path: String, body: String, headers: String) -> Result<HttpResponse, String>
#
Makes an HTTP PUT request.
Implementation Status: ⚠️ INCORRECT - Current C runtime returns raw status code instead of Result<HttpResponse, String>
httpDelete(clientID: Int, path: String, headers: String) -> Result<HttpResponse, String>
#
Makes an HTTP DELETE request.
Implementation Status: ⚠️ INCORRECT - Current C runtime returns raw status code instead of Result<HttpResponse, String>
httpRequest(clientID: Int, method: HttpMethod, path: String, headers: String, body: String) -> Result<HttpResponse, String>
#
Generic HTTP request function for any HTTP method.
Implementation Status: ⚠️ INCORRECT - Current C runtime returns raw status code instead of Result<HttpResponse, String>
httpCloseClient(clientID: Int) -> Result<Success, String>
#
Closes the HTTP client and cleans up resources.
Returns:
Success()
: Client closed successfullyErr(message)
: Error description
Implementation Status: ⚠️ INCORRECT - Current C runtime returns raw int64_t
instead of Result<Success, String>
15.4 Complete HTTP Server Example #
A practical HTTP server that demonstrates functional programming with HTTP functions:
fn main() -> Int = {
let serverResult = httpCreateServer(8080, "127.0.0.1")
match serverResult {
Success serverId => {
print("Server created, starting listener...")
let listenResult = httpListen(serverId, handleRequest)
match listenResult {
Success _ => print("Server stopped")
Err message => print("Server error: ${message}")
}
}
Err message => print("Failed to create server: ${message}")
}
0
}
fn handleRequest(method: String, path: String, headers: String, body: String) -> HttpResponse =
match method {
"GET" => match path {
"/health" => createHealthResponse()
"/users" => createUserListResponse()
"/api/stats" => createStatsResponse()
_ => createNotFoundResponse()
}
"POST" => match path {
"/users" => createUserFromBody(body)
"/api/data" => processDataFromBody(body)
_ => createNotFoundResponse()
}
"PUT" => match path {
"/users/1" => updateUserFromBody(body)
_ => createNotFoundResponse()
}
"DELETE" => match path {
"/users/1" => createDeleteResponse()
_ => createNotFoundResponse()
}
_ => createMethodNotAllowedResponse()
}
fn createHealthResponse() -> HttpResponse = HttpResponse {
status: 200,
headers: "Content-Type: application/json",
contentType: "application/json",
streamFd: -1,
isComplete: true,
partialBody: "{\"status\": \"healthy\"}",
}
fn createUserListResponse() -> HttpResponse = HttpResponse {
status: 200,
headers: "Content-Type: application/json",
contentType: "application/json",
streamFd: -1,
isComplete: true,
partialBody: "{\"users\": [{\"id\": 1, \"name\": \"Alice\"}, {\"id\": 2, \"name\": \"Bob\"}]}",
}
fn createNotFoundResponse() -> HttpResponse = HttpResponse {
status: 404,
headers: "Content-Type: application/json",
contentType: "application/json",
streamFd: -1,
isComplete: true,
partialBody: "{\"error\": \"Not found\"}",
}
fn createMethodNotAllowedResponse() -> HttpResponse = HttpResponse {
status: 405,
headers: "Content-Type: application/json",
contentType: "application/json",
streamFd: -1,
isComplete: true,
partialBody: "{\"error\": \"Method not allowed\"}",
}
fn createUserFromBody(body: String) -> HttpResponse = HttpResponse {
status: 201,
headers: "Content-Type: application/json",
contentType: "application/json",
streamFd: -1,
isComplete: true,
partialBody: "{\"id\": 3, \"name\": \"New User\", \"message\": \"User created successfully\"}",
}
fn updateUserFromBody(body: String) -> HttpResponse = HttpResponse {
status: 200,
headers: "Content-Type: application/json",
contentType: "application/json",
streamFd: -1,
isComplete: true,
partialBody: "{\"id\": 1, \"name\": \"Alice Updated\", \"message\": \"User updated successfully\"}",
}
fn createDeleteResponse() -> HttpResponse = HttpResponse {
status: 200,
headers: "Content-Type: application/json",
contentType: "application/json",
streamFd: -1,
isComplete: true,
partialBody: "{\"message\": \"User deleted successfully\"}",
}
fn processDataFromBody(body: String) -> HttpResponse = HttpResponse {
status: 200,
headers: "Content-Type: application/json",
contentType: "application/json",
streamFd: -1,
isComplete: true,
partialBody: "{\"processed\": true, \"length\": ${length(body)}}",
}
fn createStatsResponse() -> HttpResponse = HttpResponse {
status: 200,
headers: "Content-Type: application/json",
contentType: "application/json",
streamFd: -1,
isComplete: true,
partialBody: "{\"server\": \"osprey\", \"version\": \"1.0\", \"uptime\": \"3600\"}",
}
This example demonstrates:
- HttpResponse object handling with complete response structure
- Functional programming patterns using pattern matching for routing
- Type-safe HTTP responses with structured data
- Modular design with separate functions for different operations
- Error handling through the Result type system
- Real-world API patterns (CRUD operations, health checks, stats)