Lindenii Project Forge
Login

hare-ev

Temporary fork of hare-ev for... reasons
Commit info
ID
3fcedc4ca8e505adcb95f36a9958ab5ca21d74fe
Author
Willow Barraco <contact@willowbarraco.fr>
Author date
Wed, 13 Sep 2023 16:30:27 +0200
Committer
Drew DeVault <sir@cmpwn.com>
Committer date
Wed, 13 Sep 2023 16:31:33 +0200
Actions
Rename BUFSZ

Signed-off-by: Willow Barraco <contact@willowbarraco.fr>
use errors;
use ev;
use fmt;
use io;
use net;
use net::ip;
use os;

type state = struct {
	loop: *ev::loop,
	stdin: *ev::file,
	stdout: *ev::file,
	sock: *ev::file,
	stdbuf: [os::BUFSIZ]u8,
	netbuf: [os::BUFSIZ]u8,
	stdbuf: [os::BUFSZ]u8,
	netbuf: [os::BUFSZ]u8,
	wbuf: []u8,
};

export fn main() void = {
	const loop = ev::newloop()!;
	defer ev::finish(&loop);

	let state = state {
		loop = &loop,
		stdin = ev::register(&loop, os::stdin_file)!,
		stdout = ev::register(&loop, os::stdout_file)!,
		sock = null: *ev::file, // populated by [[connected]]
		...
	};
	ev::setuser(state.stdin, &state);
	ev::setuser(state.stdout, &state);

	match (ev::connect_tcp(&loop, &connected, ip::LOCAL_V4, 12345, &state)) {
	case let err: net::error =>
		fmt::fatal("Error: connect:", net::strerror(err));
	case let err: errors::error =>
		fmt::fatal("Error: connect:", errors::strerror(err));
	case =>
		yield;
	};

	for (ev::dispatch(&loop, -1)!) void;
};

fn connected(result: (*ev::file | net::error), user: nullable *opaque) void = {
	const state = user: *state;
	const sock = match (result) {
	case let err: net::error =>
		fmt::fatal("Error: connect:", net::strerror(err));
	case let sock: *ev::file =>
		yield sock;
	};
	ev::setuser(sock, state);
	state.sock = sock;

	ev::read(state.sock, &sock_read, state.netbuf);
	ev::readable(state.stdin, &stdin_readable);
};

fn stdin_readable(file: *ev::file) void = {
	const state = ev::getuser(file): *state;
	const n = match (io::read(os::stdin_file, state.stdbuf)) {
	case let n: size =>
		yield n;
	case io::EOF =>
		ev::stop(state.loop);
		return;
	case let err: io::error =>
		fmt::errorln("Error: read:", io::strerror(err))!;
		ev::stop(state.loop);
		return;
	};
	state.wbuf = state.stdbuf[..n];
	ev::write(state.sock, &sock_write, state.wbuf);
};

fn sock_read(file: *ev::file, r: (size | io::EOF | io::error)) void = {
	const state = ev::getuser(file): *state;
	const n = match (r) {
	case let n: size =>
		yield n;
	case io::EOF =>
		ev::stop(state.loop);
		return;
	case let err: io::error =>
		fmt::errorln("Error: read:", io::strerror(err))!;
		ev::stop(state.loop);
		return;
	};
	io::write(os::stdout_file, state.netbuf[..n])!;
	ev::read(state.sock, &sock_read, state.netbuf);
};

fn sock_write(file: *ev::file, r: (size | io::error)) void = {
	const state = ev::getuser(file): *state;
	const n = match (r) {
	case let n: size =>
		yield n;
	case let err: io::error =>
		fmt::errorln("Error: read:", io::strerror(err))!;
		ev::stop(state.loop);
		return;
	};
	static delete(state.wbuf[..n]);
	if (len(state.wbuf) != 0) {
		ev::write(state.sock, &sock_write, state.wbuf);
	} else {
		ev::readable(state.stdin, &stdin_readable);
	};
};
use errors;
use ev;
use io;
use log;
use net;
use net::ip;
use net::tcp;
use os;
use unix::signal;

type server = struct {
	loop: *ev::loop,
	sock: *ev::file,
	clients: []*client,
	exit: int,
};

type client = struct {
	server: *server,
	sock: *ev::file,
	addr: ip::addr,
	port: u16,
	buf: [os::BUFSIZ]u8,
	buf: [os::BUFSZ]u8,
	wbuf: []u8,
};

export fn main() void = {
	const loop = ev::newloop()!;
	defer ev::finish(&loop);

	const sock = match (ev::listen_tcp(&loop, ip::LOCAL_V4, 12345)) {
	case let err: net::error =>
		log::fatalf("Error: listen: {}", net::strerror(err));
	case let err: errors::error =>
		log::fatalf("Error: listen: {}", errors::strerror(err));
	case let sock: *ev::file =>
		yield sock;
	};
	defer ev::close(sock);

	let state = server {
		loop = &loop,
		sock = sock,
		...
	};
	ev::setuser(sock, &state);
	ev::accept(sock, &server_accept);

	const sig = ev::signal(&loop, &signal, signal::sig::INT, signal::sig::TERM)!;
	defer ev::close(sig);
	ev::setuser(sig, &state);

	log::println("Listening on 127.0.0.1:12345");
	for (ev::dispatch(&loop, -1)!) void;
	os::exit(state.exit);
};

fn signal(file: *ev::file, sig: signal::sig) void = {
	log::printfln("Exiting due to {}", signal::signame(sig));
	const server = ev::getuser(file): *server;
	ev::stop(server.loop);
};

fn server_accept(sock: *ev::file, r: (*ev::file | net::error)) void = {
	let server = ev::getuser(sock): *server;
	const sock = match (r) {
	case let sock: *ev::file =>
		yield sock;
	case let err: net::error =>
		log::printfln("Error: accept: {}", net::strerror(err));
		ev::stop(server.loop);
		server.exit = 1;
		return;
	};
	const file = ev::getfd(sock);
	const (addr, port) = tcp::peeraddr(file) as (ip::addr, u16);
	log::printfln("Connection from {}:{}", ip::string(addr), port);

	const client = alloc(client {
		server = server,
		sock = sock,
		addr = addr,
		port = port,
		...
	});
	append(server.clients, client);
	ev::setuser(client.sock, client);
	ev::read(client.sock, &client_read, client.buf);
	ev::accept(server.sock, &server_accept);
};

fn client_read(sock: *ev::file, r: (size | io::EOF | io::error)) void = {
	const client = ev::getuser(sock): *client;
	const n = match (r) {
	case let err: io::error =>
		log::printfln("{}:{}: Error: read: {}",
			ip::string(client.addr), client.port,
			io::strerror(err));
		client_close(client);
		return;
	case io::EOF =>
		client_close(client);
		return;
	case let n: size =>
		yield n;
	};
	client.wbuf = client.buf[..n];
	ev::write(client.sock, &client_write, client.wbuf);
};

fn client_write(sock: *ev::file, r: (size | io::error)) void = {
	const client = ev::getuser(sock): *client;
	const n = match (r) {
	case let err: io::error =>
		log::printfln("{}:{}: Error: write: {}",
			ip::string(client.addr), client.port,
			io::strerror(err));
		client_close(client);
		return;
	case let n: size =>
		yield n;
	};
	static delete(client.wbuf[..n]);
	if (len(client.wbuf) != 0) {
		ev::write(client.sock, &client_write, client.wbuf);
	} else {
		ev::read(client.sock, &client_read, client.buf);
	};
};

fn client_close(client: *client) void = {
	const server = client.server;
	for (let i = 0z; i < len(server.clients); i += 1) {
		if (server.clients[i] == client) {
			delete(server.clients[i]);
			break;
		};
	};
	log::printfln("{}:{}: Connection closed",
		ip::string(client.addr), client.port);
	ev::close(client.sock);
	free(client);
};
// TODO: Update me when we can send/recv in parallel
use ev;
use fmt;
use io;
use net::ip;
use net;
use os;

type state = struct {
	loop: *ev::loop,
	stdin: *ev::file,
	stdout: *ev::file,
	sock: *ev::file,
	stdbuf: [os::BUFSIZ]u8,
	netbuf: [os::BUFSIZ]u8,
	stdbuf: [os::BUFSZ]u8,
	netbuf: [os::BUFSZ]u8,
	wbuf: []u8,
};

export fn main() void = {
	const loop = ev::newloop()!;
	defer ev::finish(&loop);

	const sock = ev::connect_udp(&loop, ip::LOCAL_V4, 12345)!;
	defer ev::close(sock);

	let state = state {
		loop = &loop,
		stdin = ev::register(&loop, os::stdin_file)!,
		stdout = ev::register(&loop, os::stdout_file)!,
		sock = sock,
		...
	};
	ev::setuser(state.stdin, &state);
	ev::setuser(state.stdout, &state);
	ev::setuser(state.sock, &state);

	ev::readable(state.stdin, &stdin_readable);

	for (ev::dispatch(&loop, -1)!) void;
};

fn stdin_readable(file: *ev::file) void = {
	const state = ev::getuser(file): *state;
	const n = match (io::read(os::stdin_file, state.stdbuf)) {
	case let n: size =>
		yield n;
	case io::EOF =>
		ev::stop(state.loop);
		return;
	case let err: io::error =>
		fmt::errorln("Error: read:", io::strerror(err))!;
		ev::stop(state.loop);
		return;
	};
	state.wbuf = state.stdbuf[..n];
	ev::send(state.sock, &sock_send, state.wbuf);
};

fn sock_recv(file: *ev::file, r: (size | net::error)) void = {
	const state = ev::getuser(file): *state;
	const n = match (r) {
	case let n: size =>
		yield n;
	case let err: net::error =>
		fmt::errorln("Error: recv:", net::strerror(err))!;
		ev::stop(state.loop);
		return;
	};
	io::write(os::stdout_file, state.netbuf[..n])!;
};

fn sock_send(file: *ev::file, r: (size | net::error)) void = {
	const state = ev::getuser(file): *state;
	const n = match (r) {
	case let n: size =>
		yield n;
	case let err: net::error =>
		fmt::errorln("Error: send:", net::strerror(err))!;
		ev::stop(state.loop);
		return;
	};
	static delete(state.wbuf[..n]);
	if (len(state.wbuf) != 0) {
		ev::send(state.sock, &sock_send, state.wbuf);
	} else {
		ev::recv(state.sock, &sock_recv, state.netbuf);
		ev::readable(state.stdin, &stdin_readable);
	};
};
use errors;
use ev;
use log;
use net;
use net::ip;
use os;

type state = struct {
	loop: *ev::loop,
	src: ip::addr,
	port: u16,
	buf: [os::BUFSIZ]u8,
	buf: [os::BUFSZ]u8,
	wbuf: []u8,
};

export fn main() void = {
	const loop = ev::newloop()!;
	defer ev::finish(&loop);

	const sock = match (ev::listen_udp(&loop, ip::LOCAL_V4, 12345)) {
	case let err: net::error =>
		log::fatalf("Error: listen: {}", net::strerror(err));
	case let err: errors::error =>
		log::fatalf("Error: listen: {}", errors::strerror(err));
	case let sock: *ev::file =>
		yield sock;
	};
	defer ev::close(sock);

	let state = state {
		loop = &loop,
		src = [0: u8, 0: u8, 0: u8, 0: u8],
		...
	};
	ev::setuser(sock, &state);
	ev::recvfrom(sock, &recv, state.buf);

	log::println("Listening on 127.0.0.1:12345");
	for (ev::dispatch(&loop, -1)!) void;
};

fn recv(sock: *ev::file, r: ((size, ip::addr, u16) | net::error)) void = {
	const state = ev::getuser(sock): *state;
	const (n, src, port) = match (r) {
	case let err: net::error =>
		log::println("Error: recv:", net::strerror(err));
		ev::stop(state.loop);
		return;
	case let packet: (size, ip::addr, u16) =>
		yield packet;
	};

	// TODO: Make ev send/write all data and drop these fields and the need
	// to manually manage a write buffer and re-call sendto in &send
	state.src = src;
	state.port = port;

	log::printfln("{} bytes from {}:{}", n, ip::string(src), port);
	state.wbuf = state.buf[..n];
	ev::sendto(sock, &send, state.wbuf, src, port);
};

fn send(sock: *ev::file, r: (size | net::error)) void = {
	const state = ev::getuser(sock): *state;
	const n = match (r) {
	case let err: net::error =>
		log::println("Error: send:", net::strerror(err));
		ev::stop(state.loop);
		return;
	case let n: size =>
		yield n;
	};

	static delete(state.wbuf[..n]);

	if (len(state.wbuf) != 0) {
		ev::sendto(sock, &send, state.wbuf, state.src, state.port);
	} else {
		ev::recvfrom(sock, &recv, state.buf);
	};
};