#![allow(non_camel_case_types)] use std::{ io::{self, Read, Write}, mem, os::unix::prelude::OsStrExt, path::Path, }; mod sys { use core::ffi::{c_size_t, c_ssize_t}; pub use std::{ ffi::c_void, os::raw::{c_char, c_int, c_long, c_ushort}, }; pub type sa_family_t = u8; pub type in_port_t = u16; pub type in_addr_t = u32; pub type socklen_t = u32; pub type off_t = i64; pub type dev_t = i32; pub type ino_t = u64; pub type mode_t = u16; pub type nlink_t = u16; pub type blksize_t = i32; pub type uid_t = u32; pub type gid_t = u32; pub type time_t = c_long; pub type blkcnt_t = i64; #[repr(C)] #[derive(Default)] pub struct in_addr { pub s_addr: in_addr_t, } #[repr(C)] #[derive(Default)] pub struct sockaddr_in { pub sin_len: u8, pub sin_family: sa_family_t, pub sin_port: in_port_t, pub sin_addr: in_addr, pub sin_zero: [c_char; 8], } #[repr(C)] #[derive(Default)] pub struct sockaddr { pub sa_len: u8, pub sa_family: sa_family_t, pub sa_data: [c_char; 14], } #[repr(C)] #[derive(Default)] pub struct stat { pub st_dev: dev_t, pub st_mode: mode_t, pub st_nlink: nlink_t, pub st_ino: ino_t, pub st_uid: uid_t, pub st_gid: gid_t, pub st_rdev: dev_t, pub st_atime: time_t, pub st_atime_nsec: c_long, pub st_mtime: time_t, pub st_mtime_nsec: c_long, pub st_ctime: time_t, pub st_ctime_nsec: c_long, pub st_birthtime: time_t, pub st_birthtime_nsec: c_long, pub st_size: off_t, pub st_blocks: blkcnt_t, pub st_blksize: blksize_t, pub st_flags: u32, pub st_gen: u32, pub st_lspare: i32, pub st_qspare: [i64; 2], } pub const AF_INET: c_int = 2; pub const SOCK_STREAM: c_int = 1; pub const O_RDONLY: c_int = 0; extern "C" { pub fn socket(domain: c_int, ty: c_int, protocol: c_int) -> c_int; pub fn bind(sockfd: c_int, addr: *const sockaddr, addrlen: socklen_t) -> c_int; pub fn htons(hostshort: c_ushort) -> c_ushort; pub fn listen(socket: c_int, backlog: c_int) -> c_int; pub fn accept(socket: c_int, address: *mut sockaddr, address_len: *mut socklen_t) -> c_int; pub fn read(fd: c_int, buf: *mut c_void, count: c_size_t) -> c_ssize_t; pub fn write(fd: c_int, buf: *const c_void, count: c_size_t) -> c_ssize_t; pub fn sendfile( out_fd: c_int, in_fd: c_int, offset: *mut off_t, count: c_size_t, ) -> c_ssize_t; pub fn stat(path: *const c_char, buf: *mut stat) -> c_int; pub fn open(path: *const c_char, oflag: c_int, ...) -> c_int; pub fn close(fd: c_int) -> c_int; } } pub struct Socket { descriptor: i32, } impl Socket { pub fn new(port: u16, backlog: usize) -> io::Result { let socket = Self::create()?; socket.bind(port)?; socket.listen(backlog)?; Ok(socket) } fn create() -> io::Result { let descriptor = unsafe { sys::socket(sys::AF_INET, sys::SOCK_STREAM, 0) }; if descriptor == -1 { return Err(io::Error::last_os_error()); } Ok(Self { descriptor }) } fn bind(&self, port: u16) -> io::Result<()> { let server_addr = sys::sockaddr_in { sin_family: sys::AF_INET as u8, sin_port: unsafe { sys::htons(port) }, ..Default::default() }; let result = unsafe { sys::bind( self.descriptor, mem::transmute(&server_addr), mem::size_of::() as sys::socklen_t, ) }; if result != 0 { return Err(io::Error::last_os_error()); } Ok(()) } fn listen(&self, backlog: usize) -> io::Result<()> { let result = unsafe { sys::listen(self.descriptor, backlog as i32) }; if result != 0 { return Err(io::Error::last_os_error()); } Ok(()) } pub fn accept(&self) -> io::Result { let mut client_addr = sys::sockaddr_in::default(); let mut client_addrlen = 0; let descriptor = unsafe { sys::accept( self.descriptor, mem::transmute(&mut client_addr), &mut client_addrlen, ) }; if descriptor < 0 { return Err(io::Error::last_os_error()); } assert_eq!( client_addrlen, mem::size_of::() as sys::socklen_t ); Ok(Connection { descriptor }) } } pub struct Connection { descriptor: i32, } impl Connection { pub fn serve_file( mut self: &mut Self, file_path: impl AsRef, content_type: &str, ) -> io::Result<()> { let mut file_stat = sys::stat::default(); let file_path = file_path.as_ref().as_os_str().as_bytes(); let result = unsafe { sys::stat(file_path.as_ptr() as *const i8, &mut file_stat) }; if result < 0 { return self.error(); } let src_fd = unsafe { sys::open(file_path.as_ptr() as *const i8, sys::O_RDONLY) }; if src_fd < 0 { return self.error(); } write!(&mut self, "HTTP/1.1 200\r\n")?; write!(&mut self, "Content-Type: {}\r\n", content_type)?; write!(&mut self, "Content-Length: {}\r\n", file_stat.st_size)?; write!(&mut self, "\r\n")?; let mut offset = 0; while offset < file_stat.st_size { let n = unsafe { sys::sendfile(self.descriptor, src_fd, &mut offset, 1024) }; if n < 0 { break; } } unsafe { sys::close(src_fd) }; Ok(()) } fn error(mut self: &mut Self) -> io::Result<()> { write!(&mut self, "HTTP/1.1 500\r\n\r\n") } pub fn close(self) -> io::Result<()> { let result = unsafe { sys::close(self.descriptor) }; if result < 0 { return Err(io::Error::last_os_error()); } Ok(()) } } impl Read for Connection { fn read(&mut self, buf: &mut [u8]) -> io::Result { let result = unsafe { sys::read( self.descriptor, buf.as_mut_ptr() as *mut sys::c_void, buf.len(), ) }; if result == -1 { return Err(io::Error::last_os_error()); } Ok(result as usize) } } impl Write for Connection { fn write(&mut self, buf: &[u8]) -> io::Result { let result = unsafe { sys::write( self.descriptor, buf.as_ptr() as *const sys::c_void, buf.len(), ) }; if result == -1 { return Err(io::Error::last_os_error()); } Ok(result as usize) } fn flush(&mut self) -> io::Result<()> { Ok(()) } }