diff --git a/capc/src/codegen/intrinsics.rs b/capc/src/codegen/intrinsics.rs index f33b5e3..3958155 100644 --- a/capc/src/codegen/intrinsics.rs +++ b/capc/src/codegen/intrinsics.rs @@ -206,6 +206,14 @@ pub fn register_runtime_intrinsics(ptr_ty: Type) -> HashMap { params: vec![AbiType::Handle, AbiType::ResultString], ret: AbiType::ResultString, }; + let net_read = FnSig { + params: vec![AbiType::Handle, AbiType::I32], + ret: AbiType::Result(Box::new(AbiType::String), Box::new(AbiType::I32)), + }; + let net_read_abi = FnSig { + params: vec![AbiType::Handle, AbiType::I32, AbiType::ResultString], + ret: AbiType::ResultString, + }; let net_write = FnSig { params: vec![AbiType::Handle, AbiType::String], ret: AbiType::Result(Box::new(AbiType::Unit), Box::new(AbiType::I32)), @@ -727,6 +735,16 @@ pub fn register_runtime_intrinsics(ptr_ty: Type) -> HashMap { is_runtime: true, }, ); + map.insert( + "sys.net.TcpConn__read".to_string(), + FnInfo { + sig: net_read, + abi_sig: Some(net_read_abi), + symbol: "capable_rt_net_read".to_string(), + runtime_symbol: None, + is_runtime: true, + }, + ); map.insert( "sys.net.TcpConn__write".to_string(), FnInfo { diff --git a/examples/http_server/http_server.cap b/examples/http_server/http_server.cap index 7b876f3..b71dd76 100644 --- a/examples/http_server/http_server.cap +++ b/examples/http_server/http_server.cap @@ -9,29 +9,19 @@ use sys::args use sys::system fn arg_or_default(args: Args, index: i32, default: string) -> string { - if (args.len() > index) { - let res = args.at(index) - match (res) { - Ok(value) => { - return value - } - Err(_) => { - } - } + if (args.len() <= index) { + return default + } + match (args.at(index)) { + Ok(value) => { return value } + Err(_) => { return default } } - return default } fn strip_query(raw_path: string) -> string { - let parts = raw_path.split(63u8) - let res = parts.get(0) - return match (res) { - Ok(path) => { - path - } - Err(_) => { - "" - } + match (raw_path.split(63u8).get(0)) { + Ok(path) => { return path } + Err(_) => { return "" } } } @@ -48,77 +38,51 @@ fn sanitize_segment(parts: VecString, i: i32, acc: string, seg: string) -> Resul if (acc.len() == 0) { return sanitize_parts(parts, i + 1, seg) } - let next = fs::join(acc, seg) - return sanitize_parts(parts, i + 1, next) + return sanitize_parts(parts, i + 1, fs::join(acc, seg)) } fn sanitize_parts(parts: VecString, i: i32, acc: string) -> Result[string, unit] { if (i >= parts.len()) { return Ok(acc) } - let seg_res = parts.get(i) - return match (seg_res) { - Ok(seg) => { - sanitize_segment(parts, i, acc, seg) - } - Err(_) => { - Err(()) - } + match (parts.get(i)) { + Ok(seg) => { return sanitize_segment(parts, i, acc, seg) } + Err(_) => { return Err(()) } } } fn sanitize_path(raw_path: string) -> Result[string, unit] { - let parts = raw_path.split(47u8) - let res = sanitize_parts(parts, 0, "") - match (res) { + match (sanitize_parts(raw_path.split(47u8), 0, "")) { Ok(path) => { if (path.len() == 0) { return Ok("index.html") } return Ok(path) } - Err(_) => { - return Err(()) - } + Err(_) => { return Err(()) } } } fn parse_request_line(line: string) -> Result[string, unit] { - let trimmed = line.trim() - let parts = trimmed.split(32u8) - let method_res = parts.get(0) - match (method_res) { + let parts = line.trim().split(32u8) + match (parts.get(0)) { Ok(method) => { if (!method.eq("GET")) { return Err(()) } } - Err(_) => { - return Err(()) - } + Err(_) => { return Err(()) } } - let path_res = parts.get(1) - match (path_res) { - Ok(raw_path) => { - let cleaned = strip_query(raw_path) - return sanitize_path(cleaned) - } - Err(_) => { - return Err(()) - } + match (parts.get(1)) { + Ok(raw_path) => { return sanitize_path(strip_query(raw_path)) } + Err(_) => { return Err(()) } } } fn parse_request_path(req: string) -> Result[string, unit] { - let lines = req.lines() - let line_res = lines.get(0) - return match (line_res) { - Ok(line) => { - parse_request_line(line) - } - Err(_) => { - Err(()) - } + match (req.lines().get(0)) { + Ok(line) => { return parse_request_line(line) } + Err(_) => { return Err(()) } } } @@ -129,35 +93,27 @@ fn respond_ok(conn: &TcpConn, body: string) -> Result[unit, NetErr] { } fn respond_not_found(conn: &TcpConn) -> Result[unit, NetErr] { - conn.write("HTTP/1.0 404 Not Found\r\nContent-Type: text/plain\r\n\r\nnot found\n")? - return Ok(()) + return conn.write("HTTP/1.0 404 Not Found\r\nContent-Type: text/plain\r\n\r\nnot found\n") } fn respond_bad_request(conn: &TcpConn) -> Result[unit, NetErr] { - conn.write("HTTP/1.0 400 Bad Request\r\nContent-Type: text/plain\r\n\r\nbad request\n")? - return Ok(()) + return conn.write("HTTP/1.0 400 Bad Request\r\nContent-Type: text/plain\r\n\r\nbad request\n") +} + +fn handle_request(conn: &TcpConn, readfs: ReadFS, path: string) -> Result[unit, NetErr] { + match (readfs.read_to_string(path)) { + Ok(body) => { return respond_ok(conn, body) } + Err(_) => { return respond_not_found(conn) } + } } fn serve_once(c: Console, net: Net, readfs: ReadFS) -> Result[unit, NetErr] { let listener = net.listen("127.0.0.1", 8080)? let conn = listener.accept()? - let req = conn.read_to_string()? - let path_res = parse_request_path(req) - match (path_res) { - Ok(path) => { - let file_res = readfs.read_to_string(path) - match (file_res) { - Ok(body) => { - respond_ok(conn, body)? - } - Err(_) => { - respond_not_found(conn)? - } - } - } - Err(_) => { - respond_bad_request(conn)? - } + let req = conn.read(4096)? + match (parse_request_path(req)) { + Ok(path) => { handle_request(conn, readfs, path)? } + Err(_) => { respond_bad_request(conn)? } } conn.close() return Ok(()) @@ -170,13 +126,9 @@ pub fn main(rc: RootCap) -> i32 { let root = arg_or_default(args, 1, ".") let readfs = rc.mint_readfs(root) c.println("listening on 127.0.0.1:8080") - let res = serve_once(c, net, readfs) - match (res) { - Ok(_) => { - } - Err(_) => { - c.println("server error") - } + match (serve_once(c, net, readfs)) { + Ok(_) => {} + Err(_) => { c.println("server error") } } return 0 } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index d66a5b6..1a6752f 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -848,6 +848,39 @@ pub extern "C" fn capable_rt_net_read_to_string( ) } +#[no_mangle] +pub extern "C" fn capable_rt_net_read( + conn: Handle, + max_size: i32, + out_ptr: *mut *const u8, + out_len: *mut u64, + out_err: *mut i32, +) -> u8 { + let result = with_table(&TCP_CONNS, "tcp conn table", |table| { + let Some(stream) = table.get_mut(&conn) else { + return Err(NetErr::IoError); + }; + let max = max_size.max(0) as usize; + let mut buffer = vec![0u8; max]; + match stream.read(&mut buffer) { + Ok(n) => { + buffer.truncate(n); + match String::from_utf8(buffer) { + Ok(s) => Ok(s), + Err(_) => Err(NetErr::InvalidData), + } + } + Err(err) => Err(map_net_err(err)), + } + }); + write_string_result( + out_ptr, + out_len, + out_err, + result.map_err(|err| err as i32), + ) +} + #[no_mangle] pub extern "C" fn capable_rt_net_write( conn: Handle, diff --git a/stdlib/sys/net.cap b/stdlib/sys/net.cap index 0403b9a..9bbcf76 100644 --- a/stdlib/sys/net.cap +++ b/stdlib/sys/net.cap @@ -36,6 +36,10 @@ impl TcpConn { return Err(NetErr::IoError) } + pub fn read(self: &TcpConn, max_size: i32) -> Result[string, NetErr] { + return Err(NetErr::IoError) + } + pub fn write(self: &TcpConn, data: string) -> Result[unit, NetErr] { return Err(NetErr::IoError) }