Current section

Files

Jump to
smol src deno.ffi.mjs
Raw

src/deno.ffi.mjs

import { Ok, Error } from './gleam.mjs';
import { None } from '../gleam_stdlib/gleam/option.mjs';
import { adapt } from './adapt.ffi.mjs';
import {
// error
IsDir, NoEntry, NoAccess, UnknownFileError,
//
File, started
} from './smol.mjs';
import { upgrade } from './smol.ffi.mjs';
export function start(builder) {
const handler = adapt(builder.handler);
return new Promise((resolve) => {
const server = Deno.serve({
port: builder.port,
hostname: builder.interface,
async handler(req) {
const response = await handler(req)
if (response.body[upgrade]) {
const runtime = response.body[upgrade];
const { socket, response: wsResponse } = Deno.upgradeWebSocket(req)
runtime.send = message => socket.send(message);
runtime.close = () => socket.close();
socket.addEventListener('open', runtime.onOpen)
socket.addEventListener('message', (event) => runtime.onMessage(event.data))
socket.addEventListener('close', runtime.onClose)
return wsResponse
} else {
return response
}
},
onListen({ port, hostname }) {
let disposed = false
const stop = async () => {
if (disposed) {
return;
}
disposed = true;
console.log('smol: Gracefully shutting down. Please wait...');
await server.shutdown();
}
// TODO: doesn't seem like Deno lets us bind to SIGINT?
builder.after_start(port, hostname);
resolve(started(port, hostname, stop))
},
onError(error) {
console.error('smol:', error.stack)
return new Response('Internal Server Error', { status: 500 })
}
})
})
}
export async function from_file(path, offset, limit) {
try {
const file = await Deno.open(path);
const fileInfo = await file.stat();
if (fileInfo.isDirectory) {
return new Error(new IsDir());
}
await file.seek(offset, Deno.SeekMode.Start);
const stream = limit > 0
? file.readable.pipeThrough(new TransformStream({
transform(chunk, controller) {
if (chunk.byteLength > limit) {
chunk = chunk.slice(0, limit)
controller.enqueue(chunk);
controller.terminate();
} else {
controller.enqueue(chunk)
limit -= chunk.byteLength
}
}
}))
: file.readable;
return new Ok(new File(stream, fileInfo.size, new None()));
} catch(e) {
if (e.code === 'ENOENT') {
return new Error(new NoEntry());
} else if (e.code === 'EACCES') {
return new Error(new NoAccess());
} else {
return new Error(new UnknownFileError());
}
}
}