v/vlib/picoev/picoev.v

285 lines
5.8 KiB
V
Raw Normal View History

// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
2020-01-23 03:26:30 +01:00
// 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
2020-01-23 03:26:30 +01:00
)
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 {
}
2020-01-23 03:26:30 +01:00
fn C.atoi() int
fn C.strncasecmp() int
2020-01-23 03:26:30 +01:00
fn C.socket() int
2020-01-23 03:26:30 +01:00
fn C.setsockopt() int
2020-01-23 03:26:30 +01:00
fn C.htonl() int
2020-01-23 03:26:30 +01:00
fn C.htons() int
2020-01-23 03:26:30 +01:00
fn C.bind() int
2020-01-23 03:26:30 +01:00
fn C.listen() int
2020-01-23 03:26:30 +01:00
fn C.accept() int
2020-01-23 03:26:30 +01:00
fn C.getaddrinfo() int
2020-01-23 03:26:30 +01:00
fn C.connect() int
2020-01-23 03:26:30 +01:00
fn C.send() int
2020-01-23 03:26:30 +01:00
fn C.recv() int
// fn C.read() int
2020-01-23 03:26:30 +01:00
fn C.shutdown() int
// fn C.close() int
2020-01-23 03:26:30 +01:00
fn C.ntohs() int
2020-01-23 03:26:30 +01:00
fn C.getsockname() int
fn C.fcntl() int
// fn C.write() int
struct C.picoev_loop {
}
2020-01-23 03:26:30 +01:00
struct Picoev {
loop &C.picoev_loop
2020-06-04 10:35:40 +02:00
cb fn(req picohttpparser.Request, mut res picohttpparser.Response)
2020-01-23 03:26:30 +01:00
mut:
date byteptr
buf byteptr
idx [1024]int
out byteptr
oidx [1024]int
}
fn C.picoev_del(&C.picoev_loop, int) int
fn C.picoev_set_timeout(&C.picoev_loop, int, int)
fn C.picoev_add(&C.picoev_loop, int, int, int, &C.picoev_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
fn C.phr_parse_request() int
fn C.phr_parse_request_path_pipeline() int
fn C.phr_parse_request_path() int
2020-01-23 03:26:30 +01:00
[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) {
2020-01-23 03:26:30 +01:00
C.picoev_del(loop, fd)
C.close(fd)
}
[inline]
fn myread(fd int, b byteptr, max_len int, idx int) int {
unsafe {
return C.read(fd, b + idx, max_len - idx)
}
2020-01-23 03:26:30 +01:00
}
[inline]
fn mysubstr(s byteptr, from int, len int) string {
unsafe {
return tos(s + from, len)
}
2020-01-23 03:26:30 +01:00
}
fn rw_callback(loop &C.picoev_loop, fd int, events int, cb_arg voidptr) {
mut p := unsafe {&Picoev(cb_arg)}
2020-01-23 03:26:30 +01:00
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)
mut buf := p.buf
unsafe {
buf += fd * max_read
}
2020-01-23 03:26:30 +01:00
idx := p.idx[fd]
mut r := myread(fd, buf, max_read, idx)
if r == 0 {
2020-01-23 03:26:30 +01:00
close_conn(loop, fd)
p.idx[fd] = 0
return
} else if r == -1 {
if false { // errno == C.EAGAIN || errno == C.EWOULDBLOCK {
// TODO
2020-01-23 03:26:30 +01:00
} else {
close_conn(loop, fd)
p.idx[fd] = 0
return
}
} else {
r += idx
mut s := tos(buf, r)
mut out := p.out
unsafe {
out += fd * max_write
}
2020-01-23 03:26:30 +01:00
mut res := picohttpparser.Response{
fd: fd
date: p.date
buf_start: out
buf: out
}
unsafe {
res.buf += p.oidx[fd]
2020-01-23 03:26:30 +01:00
}
mut req := picohttpparser.Request{}
for {
pret := req.parse_request(s, 100)
2020-01-23 03:26:30 +01:00
if pret <= 0 && s.len > 0 {
unsafe {C.memmove(buf, s.str, s.len)}
2020-01-23 03:26:30 +01:00
p.idx[fd] = s.len
p.oidx[fd] = int(res.buf) - int(res.buf_start)
2020-01-23 03:26:30 +01:00
break
}
c0 := unsafe {req.method.str[0]}
if c0 == `p` || c0 == `P` || c0 == `d` || c0 == `D` {
2020-07-24 12:29:47 +02:00
mut j := 0
for {
if j == req.num_headers {
break
}
if req.headers[j].name_len == 14 &&
C.strncasecmp(req.headers[j].name, 'content-length', 14) == 0 {
// cont_length := C.atoi(tos(req.headers[j].value, req.headers[j].value_len).str)
// println('$cont_length')
// TODO need to maintain state of incomplete request to collect body later
}
j = j + 1
}
}
p.cb(req, mut &res)
2020-01-23 03:26:30 +01:00
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 int, events int, cb_arg voidptr) {
2020-01-23 03:26:30 +01:00
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)
2020-01-23 03:26:30 +01:00
}
}
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)
2020-07-24 12:29:47 +02:00
mut pv := &Picoev{
2020-01-23 03:26:30 +01:00
loop: loop
cb: cb
date: C.get_date()
buf: unsafe { malloc(max_fds * max_read + 1) }
out: unsafe { malloc(max_fds * max_write + 1) }
2020-01-23 03:26:30 +01:00
}
C.picoev_add(loop, fd, C.PICOEV_READ, 0, accept_callback, pv)
go update_date(mut pv)
2020-01-23 03:26:30 +01:00
return pv
}
pub fn (p Picoev) serve() {
for {
C.picoev_loop_once(p.loop, 1)
}
}
2020-06-04 10:35:40 +02:00
fn update_date(mut p Picoev) {
2020-01-23 03:26:30 +01:00
for {
p.date = C.get_date()
C.usleep(1000000)
}
}