diff options
Diffstat (limited to 'v_windows/v/old/vlib/picoev')
| -rw-r--r-- | v_windows/v/old/vlib/picoev/picoev.v | 265 | 
1 files changed, 265 insertions, 0 deletions
diff --git a/v_windows/v/old/vlib/picoev/picoev.v b/v_windows/v/old/vlib/picoev/picoev.v new file mode 100644 index 0000000..7c11d03 --- /dev/null +++ b/v_windows/v/old/vlib/picoev/picoev.v @@ -0,0 +1,265 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module picoev + +import net +import picohttpparser + +#include <errno.h> +#include <netinet/tcp.h> +#include <signal.h> +#flag -I @VEXEROOT/thirdparty/picoev +#flag -L @VEXEROOT/thirdparty/picoev +#flag @VEXEROOT/thirdparty/picoev/picoev.o +#include "src/picoev.h" + +struct C.in_addr { +mut: +	s_addr int +} + +struct C.sockaddr_in { +mut: +	sin_family int +	sin_port   int +	sin_addr   C.in_addr +} + +struct C.sockaddr_storage {} + +fn C.atoi() int + +fn C.strncasecmp(s1 &char, s2 &char, n size_t) int + +struct C.picoev_loop {} + +fn C.picoev_del(&C.picoev_loop, int) int + +fn C.picoev_set_timeout(&C.picoev_loop, int, int) + +// fn C.picoev_handler(loop &C.picoev_loop, fd int, revents int, cb_arg voidptr) +// TODO: (sponge) update to C.picoev_handler with C type def update +type Cpicoev_handler = fn (loop &C.picoev_loop, fd int, revents int, context voidptr) + +fn C.picoev_add(&C.picoev_loop, int, int, int, &Cpicoev_handler, voidptr) int + +fn C.picoev_init(int) int + +fn C.picoev_create_loop(int) &C.picoev_loop + +fn C.picoev_loop_once(&C.picoev_loop, int) int + +fn C.picoev_destroy_loop(&C.picoev_loop) int + +fn C.picoev_deinit() int + +const ( +	max_fds     = 1024 +	max_timeout = 10 +	max_read    = 4096 +	max_write   = 8192 +) + +enum Event { +	read = C.PICOEV_READ +	write = C.PICOEV_WRITE +	timeout = C.PICOEV_TIMEOUT +	add = C.PICOEV_ADD +	del = C.PICOEV_DEL +	readwrite = C.PICOEV_READWRITE +} + +pub struct Config { +pub: +	port         int = 8080 +	cb           fn (voidptr, picohttpparser.Request, mut picohttpparser.Response) +	err_cb       fn (voidptr, picohttpparser.Request, mut picohttpparser.Response, IError) = default_err_cb +	user_data    voidptr = voidptr(0) +	timeout_secs int     = 8 +	max_headers  int     = 100 +} + +struct Picoev { +	loop         &C.picoev_loop +	cb           fn (voidptr, picohttpparser.Request, mut picohttpparser.Response) +	err_cb       fn (voidptr, picohttpparser.Request, mut picohttpparser.Response, IError) +	user_data    voidptr +	timeout_secs int +	max_headers  int +mut: +	date &byte +	buf  &byte +	idx  [1024]int +	out  &byte +} + +[inline] +fn setup_sock(fd int) ? { +	flag := 1 +	if C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_NODELAY, &flag, sizeof(int)) < 0 { +		return error('setup_sock.setup_sock failed') +	} +	if C.fcntl(fd, C.F_SETFL, C.O_NONBLOCK) != 0 { +		return error('fcntl failed') +	} +} + +[inline] +fn close_conn(loop &C.picoev_loop, fd int) { +	C.picoev_del(loop, fd) +	C.close(fd) +} + +[inline] +fn req_read(fd int, b &byte, max_len int, idx int) int { +	unsafe { +		return C.read(fd, b + idx, max_len - idx) +	} +} + +fn rw_callback(loop &C.picoev_loop, fd int, events int, context voidptr) { +	mut p := unsafe { &Picoev(context) } +	defer { +		p.idx[fd] = 0 +	} +	if (events & int(Event.timeout)) != 0 { +		close_conn(loop, fd) +		return +	} else if (events & int(Event.read)) != 0 { +		C.picoev_set_timeout(loop, fd, p.timeout_secs) + +		// Request init +		mut buf := p.buf +		unsafe { +			buf += fd * picoev.max_read // pointer magic +		} +		mut req := picohttpparser.Request{} + +		// Response init +		mut out := p.out +		unsafe { +			out += fd * picoev.max_write // pointer magic +		} +		mut res := picohttpparser.Response{ +			fd: fd +			date: p.date +			buf_start: out +			buf: out +		} + +		for { +			// Request parsing loop +			r := req_read(fd, buf, picoev.max_read, p.idx[fd]) // Get data from socket +			if r == 0 { +				// connection closed by peer +				close_conn(loop, fd) +				return +			} else if r == -1 { +				// error +				if C.errno == C.EAGAIN || C.errno == C.EWOULDBLOCK { +					// try again later +					return +				} else { +					// fatal error +					close_conn(loop, fd) +					return +				} +			} +			p.idx[fd] += r + +			mut s := unsafe { tos(buf, p.idx[fd]) } +			pret := req.parse_request(s, p.max_headers) // Parse request via picohttpparser +			if pret > 0 { // Success +				break +			} else if pret == -1 { // Parse error +				p.err_cb(mut p.user_data, req, mut &res, error('ParseError')) +				return +			} + +			assert pret == -2 +			// request is incomplete, continue the loop +			if p.idx[fd] == sizeof(buf) { +				p.err_cb(mut p.user_data, req, mut &res, error('RequestIsTooLongError')) +				return +			} +		} + +		// Callback (should call .end() itself) +		p.cb(mut p.user_data, req, mut &res) +	} +} + +fn accept_callback(loop &C.picoev_loop, fd int, events int, cb_arg voidptr) { +	mut p := unsafe { &Picoev(cb_arg) } +	newfd := C.accept(fd, 0, 0) +	if newfd != -1 { +		setup_sock(newfd) or { +			p.err_cb(mut p.user_data, picohttpparser.Request{}, mut &picohttpparser.Response{}, +				err) +		} +		C.picoev_add(loop, newfd, int(Event.read), p.timeout_secs, rw_callback, cb_arg) +	} +} + +fn default_err_cb(data voidptr, req picohttpparser.Request, mut res picohttpparser.Response, error IError) { +	eprintln('picoev: $error') +	res.end() +} + +pub fn new(config Config) &Picoev { +	fd := C.socket(net.AddrFamily.ip, net.SocketType.tcp, 0) +	assert fd != -1 +	flag := 1 +	assert C.setsockopt(fd, C.SOL_SOCKET, C.SO_REUSEADDR, &flag, sizeof(int)) == 0 +	assert C.setsockopt(fd, C.SOL_SOCKET, C.SO_REUSEPORT, &flag, sizeof(int)) == 0 +	$if linux { +		assert C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_QUICKACK, &flag, sizeof(int)) == 0 +		timeout := 10 +		assert C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_DEFER_ACCEPT, &timeout, sizeof(int)) == 0 +		queue_len := 4096 +		assert C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_FASTOPEN, &queue_len, sizeof(int)) == 0 +	} +	mut addr := C.sockaddr_in{} +	addr.sin_family = C.AF_INET +	addr.sin_port = C.htons(config.port) +	addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY) +	size := 16 // sizeof(C.sockaddr_in) +	bind_res := C.bind(fd, unsafe { &net.Addr(&addr) }, size) +	assert bind_res == 0 +	listen_res := C.listen(fd, C.SOMAXCONN) +	assert listen_res == 0 +	setup_sock(fd) or { +		config.err_cb(mut config.user_data, picohttpparser.Request{}, mut &picohttpparser.Response{}, +			err) +	} +	C.picoev_init(picoev.max_fds) +	loop := C.picoev_create_loop(picoev.max_timeout) +	mut pv := &Picoev{ +		loop: loop +		cb: config.cb +		err_cb: config.err_cb +		user_data: config.user_data +		timeout_secs: config.timeout_secs +		max_headers: config.max_headers +		date: C.get_date() +		buf: unsafe { malloc_noscan(picoev.max_fds * picoev.max_read + 1) } +		out: unsafe { malloc_noscan(picoev.max_fds * picoev.max_write + 1) } +	} +	C.picoev_add(loop, fd, int(Event.read), 0, accept_callback, pv) +	go update_date(mut pv) +	return pv +} + +pub fn (p Picoev) serve() { +	for { +		C.picoev_loop_once(p.loop, 1) +	} +} + +fn update_date(mut p Picoev) { +	for { +		p.date = C.get_date() +		C.usleep(1000000) +	} +}  | 
