HTTP
HTTP server and client. Bodies are streamable; every operation that can fail returns Result. See Error Handling.
Status
Function signatures below are the specified interface. The current C runtime returns raw int64_t for the create/listen/stop and request functions; the type system expects Result<T, string> and the bridge is being aligned. The handler bridge in httpListen currently expects a raw string return rather than the HttpResponse record described here.
Types
type HttpMethod = GET | POST | PUT | DELETE | PATCH | HEAD | OPTIONS
type HttpRequest = {
method: HttpMethod,
path: string,
headers: string,
body: string,
queryParams: string
}
type HttpResponse = {
status: int,
headers: string,
contentType: string,
streamFd: int, // file descriptor for streaming; -1 if not streaming
isComplete: bool,
partialBody: string // length computed by the runtime; do not pass an explicit length
}
type ServerID = int
type ClientID = int
Server Functions
httpCreateServer(port: int, address: string) -> ResultServerID, string>
httpListen(
serverID: ServerID,
handler: fn(method: string, path: string, headers: string, body: string) -> HttpResponse
) -> Resultunit, string>
httpStopServer(serverID: ServerID) -> Resultunit, string>
The C runtime parses incoming requests and calls handler per request. All routing and application logic lives in Osprey; the C side provides only the transport.
fn handleRequest(method: string, path: string, headers: string, body: string) -> HttpResponse =
match method {
"GET" => match path {
"/health" => jsonResponse(status: 200, body: "{\"status\":\"healthy\"}")
"/users" => jsonResponse(status: 200, body: "{\"users\":[...]}")
_ => jsonResponse(status: 404, body: "{\"error\":\"not found\"}")
}
"POST" => match path {
"/users" => jsonResponse(status: 201, body: "{\"id\":3}")
_ => jsonResponse(status: 404, body: "{\"error\":\"not found\"}")
}
_ => jsonResponse(status: 405, body: "{\"error\":\"method not allowed\"}")
}
fn jsonResponse(status: int, body: string) -> HttpResponse = HttpResponse {
status: status,
headers: "Content-Type: application/json",
contentType: "application/json",
streamFd: -1,
isComplete: true,
partialBody: body
}
match httpCreateServer(port: 8080, address: "127.0.0.1") {
Success { value: serverID } => httpListen(serverID: serverID, handler: handleRequest)
Error { message } => print("create failed: ${message}")
}
Client Functions
httpCreateClient(baseUrl: string, timeout: int) -> ResultClientID, string>
httpGet( clientID: ClientID, path: string, headers: string) -> ResultHttpResponse, string>
httpPost( clientID: ClientID, path: string, body: string, headers: string) -> ResultHttpResponse, string>
httpPut( clientID: ClientID, path: string, body: string, headers: string) -> ResultHttpResponse, string>
httpDelete(clientID: ClientID, path: string, headers: string) -> ResultHttpResponse, string>
httpRequest(clientID: ClientID, method: HttpMethod, path: string,
headers: string, body: string) -> ResultHttpResponse, string>
httpCloseClient(clientID: ClientID) -> Resultunit, string>
match httpCreateClient(baseUrl: "http://api.example.com", timeout: 5000) {
Success { value: clientID } => match httpGet(clientID: clientID, path: "/users", headers: "") {
Success { value: response } => print("status: ${response.status}")
Error { message } => print("request failed: ${message}")
}
Error { message } => print("client create failed: ${message}")
}