diff --git a/main.ha b/main.ha new file mode 100644 index 0000000..48babb8 --- /dev/null +++ b/main.ha @@ -0,0 +1,143 @@ +use bufio; +use io; +use log; +use net; +use net::ip; +use net::tcp; +use strings; +use time; +use unix::poll; + +// our message to send to clients as a constant +def tick: [5]u8 = ['t', 'i', 'c', 'k', '\n']; + +// tryconn attempts to add a client to a pollfd slice. +fn tryconn(fds: []poll::pollfd) void = { + let socket = fds[0]; + if (socket.revents & poll::event::POLLIN > 0) { + + // try to accept the connection. + let conn = match(net::accept(socket.fd)) { + case let err: net::error => + log::println("failed to accept connection"); + return; + case let sock: net::socket => + yield sock; + }; + + // the events we want to listen for + let events = (poll::event::POLLIN | poll::event::POLLPRI); + + for (let i = 1z; i < len(fds); i += 1) { + // empty slot is indicated by a + // file descriptor equal to 0 + if (fds[i].fd == 0) { + + fds[i].fd = conn; + fds[i].events = events; + + return; + }; + }; + + // close the connection if there is no room + io::close(conn)!; + }; +}; + +// tryread will attempt to read from all connected clients. +fn tryread(fds: []poll::pollfd) void = { + for (let i = 1z; i < len(fds); i += 1) { + + let revents = fds[i].revents; + + // just close the file descriptor + // on error or hangup events + if (revents & poll::event::POLLERR + & poll::event::POLLHUP > 0) { + closefd(&fds[i]); + continue; + }; + + // if there is data to read... + if (revents & poll::event::POLLIN > 0) { + + let scanner = bufio::newscanner(fds[i].fd, 1024z); + defer bufio::finish(&scanner); + + match(bufio::scan_line(&scanner)) { + case let msg: const str => + log::printfln("client said: {}", msg); + case => + // io::EOF, io::error, or utf8::invalid + closefd(&fds[i]); + }; + + }; + }; +}; + +// write sends a 'tick' message to all connected clients. +fn write(fds: []poll::pollfd) void = { + for (let i = 1z; i < len(fds); i += 1) { + if (fds[i].fd > 0) { + io::writeall(fds[i].fd, tick)!; + }; + }; +}; + +// closefd closes a provided file descriptor +// and resets all fields to 0. +fn closefd(fd: *poll::pollfd) void = { + + match(io::close(fd.fd)) { + case void => + void; + case io::error => + log::println("failed to close socket"); + }; + + fd.fd = 0; + fd.events = 0; + fd.revents = 0; +}; + +export fn main() void = { + + let socket = tcp::listen(ip::LOCAL_V4, 8080)!; + + // initialize all pollfds with zero values + let fds: [15]poll::pollfd = [ + poll::pollfd { + fd = 0, + events = 0, + revents = 0, + } + ... + ]; + + // overwrite the first pollfd with our socket + fds[0] = poll::pollfd { + fd = socket, + events = (poll::event::POLLIN | poll::event::POLLPRI), + revents = 0 + }; + + for (true) { + match(poll::poll(fds, time::SECOND)) { + + case let n: uint => + if (n > 0) { + tryconn(fds); + tryread(fds); + } else { + write(fds); + }; + + case let err: poll::error => + log::fatal("poll failed"); + }; + + }; + +};