aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/old/vlib/picoev/picoev.v
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/old/vlib/picoev/picoev.v')
-rw-r--r--v_windows/v/old/vlib/picoev/picoev.v265
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)
+ }
+}