|
const { epoll } = just.load('epoll') |
|
const { sys } = just.load('sys') |
|
const { net } = just.load('net') |
|
const { http } = just.library('http') |
|
const spawn = require('spawn.js') |
|
|
|
const { |
|
close, recv, send, accept4, setsockopt, socket, bind, listen, SOMAXCONN |
|
} = net |
|
const { |
|
O_NONBLOCK, AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR, SO_REUSEPORT, |
|
SOCK_NONBLOCK |
|
} = net |
|
const { |
|
create, wait, control, EPOLL_CLOEXEC, EPOLL_CTL_ADD, EPOLL_CTL_MOD, EPOLLIN, |
|
EPOLLERR, EPOLLHUP |
|
} = epoll |
|
const { |
|
parseRequestsHandle, createHandle |
|
} = http |
|
|
|
function add (fd, callback, events = EPOLLIN) { |
|
const r = control(loopfd, EPOLL_CTL_ADD, fd, events) |
|
if (r === 0) handles[fd] = callback |
|
return r |
|
} |
|
|
|
function update (fd, events = EPOLLIN) { |
|
return control(loopfd, EPOLL_CTL_MOD, fd, events) |
|
} |
|
|
|
function poll (timeout = -1, sigmask) { |
|
let r = 0 |
|
if (sigmask) { |
|
r = wait(loopfd, evbuf, timeout, sigmask) |
|
} else { |
|
r = wait(loopfd, evbuf, timeout) |
|
} |
|
if (r > 0) { |
|
let off = 0 |
|
for (let i = 0; i < r; i++) { |
|
const fd = events[off + 1] |
|
handles[fd] && handles[fd](fd, events[off]) |
|
off += 3 |
|
} |
|
} |
|
return r |
|
} |
|
|
|
function onSocketEvent (fd, event) { |
|
if (event & EPOLLERR || event & EPOLLHUP) { |
|
close(fd) |
|
return |
|
} |
|
const bytes = recv(fd, buf, 0, bufferSize) |
|
if (bytes > 0) { |
|
parseRequestsHandle(handle, bytes, 0) |
|
const [count] = u16 |
|
if (count > 1) { |
|
if (count > maxPipeline) { |
|
send(fd, r400, count * r400Len, 0) |
|
close(fd) |
|
return |
|
} |
|
send(fd, r200, count * r200Len, 0) |
|
return |
|
} |
|
send(fd, r200, r200Len, 0) |
|
return |
|
} |
|
if (bytes < 0) just.error('recv error') |
|
close(fd) |
|
} |
|
|
|
function onConnect (fd, event) { |
|
if (event & EPOLLERR || event & EPOLLHUP) { |
|
close(fd) |
|
return |
|
} |
|
const newfd = accept4(fd, O_NONBLOCK) |
|
add(newfd, onSocketEvent) |
|
update(newfd, EPOLLIN | EPOLLERR | EPOLLHUP) |
|
} |
|
|
|
const handles = {} |
|
const bufferSize = 16384 |
|
const r200 = sys.calloc(1, `HTTP/1.1 200 OK\r\nServer: j\r\nContent-Type: text/plain\r\nDate: ${(new Date()).toUTCString()}\r\nContent-Length: 11\r\n\r\nHello World`) |
|
const r400 = sys.calloc(1, `HTTP/1.1 400 Bad Request\r\nServer: j\r\nContent-Type: text/plain\r\n\r\nDate: ${(new Date()).toUTCString()}\r\nContent-Length: 0\r\nConnection: close\r\n\r\n`) |
|
const r200Len = r200.byteLength |
|
const r400Len = r400.byteLength |
|
const buf = new ArrayBuffer(bufferSize) |
|
const info = new ArrayBuffer(4) |
|
const u16 = new Uint16Array(info) |
|
const handle = createHandle(buf, info) |
|
const nevents = 1024 |
|
const evbuf = new ArrayBuffer(nevents * 12) |
|
const events = new Uint32Array(evbuf) |
|
let fd |
|
let loopfd |
|
|
|
function main () { |
|
fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0) |
|
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, 1) |
|
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, 1) |
|
bind(fd, '127.0.0.1', 3000) |
|
listen(fd, SOMAXCONN) |
|
loopfd = create(EPOLL_CLOEXEC) |
|
add(fd, onConnect) |
|
while (1) poll(-1) |
|
} |
|
|
|
spawn(main) |