mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
pico.v and dependencies
This commit is contained in:

committed by
Alexander Medvednikov

parent
5c6032d272
commit
7b345e207d
234
vlib/picoev/picoev.v
Normal file
234
vlib/picoev/picoev.v
Normal file
@ -0,0 +1,234 @@
|
||||
// Copyright (c) 2019 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 picohttpparser
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
|
||||
#flag -I @VROOT/thirdparty/picoev
|
||||
#flag -L @VROOT/thirdparty/picoev
|
||||
#flag @VROOT/thirdparty/picoev/picoev.o
|
||||
|
||||
#include "src/picoev.h"
|
||||
|
||||
const (
|
||||
MAX_FDS = 1024
|
||||
TIMEOUT_SECS = 8
|
||||
MAX_TIMEOUT = 10
|
||||
MAX_READ = 4096
|
||||
MAX_WRITE = 8192
|
||||
)
|
||||
|
||||
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.socket() int
|
||||
fn C.setsockopt() int
|
||||
fn C.htonl() int
|
||||
fn C.htons() int
|
||||
fn C.bind() int
|
||||
fn C.listen() int
|
||||
fn C.accept() int
|
||||
fn C.getaddrinfo() int
|
||||
fn C.connect() int
|
||||
fn C.send() int
|
||||
fn C.recv() int
|
||||
fn C.read() int
|
||||
fn C.shutdown() int
|
||||
fn C.close() int
|
||||
fn C.ntohs() int
|
||||
fn C.getsockname() int
|
||||
|
||||
fn C.fcntl() int
|
||||
fn C.write() int
|
||||
|
||||
struct C.picoev_loop {}
|
||||
|
||||
struct Picoev {
|
||||
loop *C.picoev_loop
|
||||
cb fn(req picohttpparser.Request, res mut picohttpparser.Response)
|
||||
mut:
|
||||
date byteptr
|
||||
buf byteptr
|
||||
idx [1024]int
|
||||
out byteptr
|
||||
oidx [1024]int
|
||||
}
|
||||
|
||||
fn picoev_del(*C.picoev_loop, int) int
|
||||
fn picoev_set_timeout(*C.picoev_loop, int, int)
|
||||
fn picoev_add(*C.picoev_loop, int, int, int, *C.picoev_handler, voidptr) int
|
||||
fn picoev_init(int) int
|
||||
fn picoev_create_loop(int) *C.picoev_loop
|
||||
fn picoev_loop_once(*C.picoev_loop, int) int
|
||||
fn picoev_destroy_loop(*C.picoev_loop) int
|
||||
fn picoev_deinit() int
|
||||
|
||||
[inline]
|
||||
fn setup_sock(fd int) {
|
||||
on := 1
|
||||
if C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_NODELAY, &on, sizeof(int)) < 0 {
|
||||
println('setup_sock.setup_sock failed')
|
||||
}
|
||||
if C.fcntl(fd, C.F_SETFL, C.O_NONBLOCK) != 0 {
|
||||
println('fcntl failed')
|
||||
}
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn close_conn(loop *C.picoev_loop, fd int) {
|
||||
C.picoev_del(loop, fd)
|
||||
C.close(fd)
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn myread(fd int, b byteptr, max_len, idx int) int {
|
||||
return C.read(fd, b + idx, max_len - idx)
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn mysubstr(s byteptr, from, len int) string {
|
||||
return tos(s + from, len)
|
||||
}
|
||||
|
||||
fn rw_callback(loop *C.picoev_loop, fd, events int, cb_arg voidptr) {
|
||||
mut p := *Picoev(cb_arg)
|
||||
if (events & C.PICOEV_TIMEOUT) != 0 {
|
||||
close_conn(loop, fd)
|
||||
p.idx[fd] = 0
|
||||
return
|
||||
}
|
||||
else if (events & C.PICOEV_READ) != 0 {
|
||||
C.picoev_set_timeout(loop, fd, TIMEOUT_SECS)
|
||||
buf := (p.buf + fd * MAX_READ)
|
||||
idx := p.idx[fd]
|
||||
mut r := myread(fd, buf, MAX_READ, idx)
|
||||
if (r == 0) {
|
||||
close_conn(loop, fd)
|
||||
p.idx[fd] = 0
|
||||
return
|
||||
} else if (r == -1) {
|
||||
if errno == C.EAGAIN || errno == C.EWOULDBLOCK {
|
||||
//
|
||||
} else {
|
||||
close_conn(loop, fd)
|
||||
p.idx[fd] = 0
|
||||
return
|
||||
}
|
||||
} else {
|
||||
r += idx
|
||||
mut s := tos(buf, r)
|
||||
out := (p.out + fd * MAX_WRITE)
|
||||
mut res := picohttpparser.Response{
|
||||
fd: fd
|
||||
date: p.date
|
||||
buf_start: out
|
||||
buf: out + p.oidx[fd]
|
||||
}
|
||||
mut req := picohttpparser.Request{}
|
||||
for {
|
||||
pret := req.parse_request_path_pipeline(s)
|
||||
if pret <= 0 && s.len > 0 {
|
||||
C.memmove(buf, s.str, s.len)
|
||||
p.idx[fd] = s.len
|
||||
p.oidx[fd] = int(res.buf - res.buf_start)
|
||||
break
|
||||
}
|
||||
p.cb(req, mut res)
|
||||
if pret >= s.len {
|
||||
p.idx[fd] = 0
|
||||
p.oidx[fd] = 0
|
||||
if res.end() < 0 {
|
||||
close_conn(loop, fd)
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
s = mysubstr(buf, pret, s.len - pret)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn accept_callback(loop *C.picoev_loop, fd, events int, cb_arg voidptr) {
|
||||
newfd := C.accept(fd, 0, 0)
|
||||
if newfd != -1 {
|
||||
setup_sock(newfd)
|
||||
C.picoev_add(loop, newfd, C.PICOEV_READ, TIMEOUT_SECS, rw_callback, cb_arg)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(port int, cb voidptr) &Picoev {
|
||||
fd := C.socket(C.AF_INET, C.SOCK_STREAM, 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(port)
|
||||
addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY)
|
||||
size := 16 // sizeof(C.sockaddr_in)
|
||||
bind_res := C.bind(fd, &addr, size)
|
||||
assert bind_res == 0
|
||||
|
||||
listen_res := C.listen(fd, C.SOMAXCONN)
|
||||
assert listen_res == 0
|
||||
|
||||
setup_sock(fd)
|
||||
|
||||
C.picoev_init(MAX_FDS)
|
||||
loop := C.picoev_create_loop(MAX_TIMEOUT)
|
||||
pv := &Picoev{
|
||||
loop: loop
|
||||
cb: cb
|
||||
date: C.get_date()
|
||||
buf: malloc(MAX_FDS * MAX_READ + 1)
|
||||
out: malloc(MAX_FDS * MAX_WRITE + 1)
|
||||
}
|
||||
C.picoev_add(loop, fd, C.PICOEV_READ, 0, accept_callback, pv)
|
||||
|
||||
go update_date(pv)
|
||||
|
||||
return pv
|
||||
}
|
||||
|
||||
pub fn (p Picoev) serve() {
|
||||
for {
|
||||
C.picoev_loop_once(p.loop, 1)
|
||||
}
|
||||
}
|
||||
|
||||
fn update_date(p mut Picoev) {
|
||||
for {
|
||||
p.date = C.get_date()
|
||||
C.usleep(1000000)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user