///|
/// Native platform-specific context data
pub(all) struct PlatformContext {
/// The server connection for sending responses
conn : @http.ServerConnection
/// Request body reader
body_reader : &@async_io.Reader
}
///|
/// Create a new PlatformContext for native
pub fn PlatformContext::new(
conn : @http.ServerConnection,
body_reader : &@async_io.Reader,
) -> PlatformContext {
{ conn, body_reader }
}
///|
/// Create a new Context (native-specific constructor)
pub fn Context::new(
request : @http.Request,
params : @router.Params,
conn : @http.ServerConnection,
body_reader : &@async_io.Reader,
) -> Context {
let exec_ctx = ExecutionContext::new()
let env = EmptyEnv::new()
{
request,
params,
platform: PlatformContext::new(conn, body_reader),
data: Map::new(),
vars: Variables::new(),
exec_ctx,
env,
cached_body: None,
cached_query: None,
cached_cookies: None,
response_headers: {},
status: 200,
response_sent: false,
}
}
///|
/// Create a new Context with environment and execution context (native-specific)
pub fn[E : Env, C : ExecCtx] Context::with_env(
request : @http.Request,
params : @router.Params,
conn : @http.ServerConnection,
body_reader : &@async_io.Reader,
env : E,
exec_ctx : C,
) -> Context {
{
request,
params,
platform: PlatformContext::new(conn, body_reader),
data: Map::new(),
vars: Variables::new(),
exec_ctx,
env,
cached_body: None,
cached_query: None,
cached_cookies: None,
response_headers: {},
status: 200,
response_sent: false,
}
}
///|
/// Read body from reader as string (native implementation)
async fn read_body_as_string(reader : &@async_io.Reader) -> String {
let buf = StringBuilder::new()
let chunk = FixedArray::make(4096, b'\x00')
for {
let n = reader.read(chunk) catch { _ => break }
if n == 0 {
break
}
for i = 0; i < n; i = i + 1 {
buf.write_char(chunk[i].to_int().unsafe_to_char())
}
}
buf.to_string()
}
///|
/// Read request body as string (native implementation)
pub async fn Context::body(self : Context) -> String {
match self.cached_body {
Some(body) => body
None => {
let body = read_body_as_string(self.platform.body_reader)
self.cached_body = Some(body)
body
}
}
}
///|
/// Send a text response (native implementation)
pub async fn Context::text(
self : Context,
body : String,
status? : Int,
) -> Unit {
let status = status.unwrap_or(self.status)
let headers = merge_headers(self.response_headers, {
"Content-Type": "text/plain; charset=utf-8",
})
self.platform.conn.send_response(
status,
reason_for_status(status),
extra_headers=headers,
)
self.platform.conn.write(@encoding.encode(@encoding.UTF8, body))
self.platform.conn.end_response()
self.response_sent = true
}
///|
/// Send a JSON response (native implementation)
pub async fn Context::json(self : Context, data : Json, status? : Int) -> Unit {
let status = status.unwrap_or(self.status)
let headers = merge_headers(self.response_headers, {
"Content-Type": "application/json",
})
self.platform.conn.send_response(
status,
reason_for_status(status),
extra_headers=headers,
)
self.platform.conn.write(@encoding.encode(@encoding.UTF8, data.stringify()))
self.platform.conn.end_response()
self.response_sent = true
}
///|
/// Send an HTML response (native implementation)
pub async fn Context::html(
self : Context,
body : String,
status? : Int,
) -> Unit {
let status = status.unwrap_or(self.status)
let headers = merge_headers(self.response_headers, {
"Content-Type": "text/html; charset=utf-8",
})
self.platform.conn.send_response(
status,
reason_for_status(status),
extra_headers=headers,
)
self.platform.conn.write(@encoding.encode(@encoding.UTF8, body))
self.platform.conn.end_response()
self.response_sent = true
}
///|
/// Send a redirect response (native implementation)
pub async fn Context::redirect(self : Context, url : String) -> Unit {
let headers = merge_headers(self.response_headers, { "Location": url })
self.platform.conn.send_response(302, "Found", extra_headers=headers)
self.platform.conn.end_response()
self.response_sent = true
}
///|
/// Send a 404 Not Found response (native implementation)
pub async fn Context::not_found(self : Context) -> Unit {
self.text("Not Found", status=404)
}
///|
/// Start an SSE response (native implementation)
pub async fn Context::sse_start(self : Context) -> Unit {
let headers = merge_headers(self.response_headers, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
})
self.platform.conn.send_response(200, "OK", extra_headers=headers)
self.response_sent = true
}
///|
/// Write raw data to the response (native implementation)
pub async fn Context::write_raw(self : Context, data : String) -> Unit {
self.platform.conn.write(@encoding.encode(@encoding.UTF8, data))
self.platform.conn.flush()
}
///|
/// End the response (native implementation)
pub async fn Context::end_response(self : Context) -> Unit {
self.platform.conn.end_response()
}
///|
/// Get server connection (native only)
pub fn Context::conn(self : Context) -> @http.ServerConnection {
self.platform.conn
}
///|
/// Get HTTP status reason phrase
fn reason_for_status(status : Int) -> String {
match status {
200 => "OK"
201 => "Created"
204 => "No Content"
301 => "Moved Permanently"
302 => "Found"
304 => "Not Modified"
400 => "Bad Request"
401 => "Unauthorized"
403 => "Forbidden"
404 => "Not Found"
405 => "Method Not Allowed"
500 => "Internal Server Error"
502 => "Bad Gateway"
503 => "Service Unavailable"
_ => "Unknown"
}
}