Lindenii Project Forge
Warning: Due to various recent migrations, viewing non-HEAD refs may be broken.
/ev/server/http.ha (raw)
use ev;
use bufio;
use errors;
use io;
use memio;
use net::http;
use net::ip;
use net::tcp;
use net;
use strings;
use types;
// A callback for an [[http_serve]] operation.
export type http_servecb = fn(user: nullable *opaque, request: http::request, rw: http::response_writer) void;
// Schedules an http serve operation on a socket. The user must free resources
// when done with [[server_finish]]
export fn http_serve(
sock: *ev::file,
cb: *http_servecb,
user: nullable *opaque,
) (*server | nomem) = {
const serv = newserver(sock, cb, user)?;
ev::setuser(sock, serv);
ev::accept(sock, &http_accept);
return serv;
};
fn http_accept(sock: *ev::file, r: (*ev::file | net::error)) (void | nomem) = {
const server = ev::getuser(sock): *server;
const sock = match (r) {
case let sock: *ev::file =>
yield sock;
case let err: net::error =>
// TODO handle it
return;
};
const client = newclient(
server,
sock,
)?;
ev::setuser(client.sock, client);
ev::read(client.sock, &http_read, client.rbuf);
append(server.clients, client)?;
ev::accept(server.sock, &http_accept);
};
fn http_read(sock: *ev::file, r: (size | io::EOF | io::error)) (void | nomem) = {
const client = ev::getuser(sock): *server_client;
const n = match (r) {
case let err: io::error =>
client_close(client);
return;
case io::EOF =>
client_close(client);
return;
case let n: size =>
yield n;
};
io::seek(&client.buf, 0, io::whence::END)!;
io::write(&client.buf, client.rbuf[..n])!;
io::seek(&client.buf, 0, io::whence::SET)!;
let scan = bufio::newscanner(&client.buf, types::SIZE_MAX);
defer bufio::finish(&scan);
let req = match (http::request_scan(&scan)) {
case let req: http::request => yield req;
case io::EOF =>
ev::read(client.sock, &http_read, client.rbuf);
return;
case let err: (http::protoerr | errors::unsupported | io::error) =>
client_close(client);
return;
};
defer http::parsed_request_finish(&req);
memio::reset(&client.buf);
const resp = http::response {
version = (1, 1),
status = http::STATUS_OK,
reason = strings::dup(http::status_reason(http::STATUS_OK))?,
header = [],
body = io::empty,
};
defer io::close(resp.body)!;
const cb = client.server.cb: *http_servecb;
cb(client.server.user, req, http::response_writer {
sock = ev::getfd(client.sock),
resp = resp,
writeto = &client.buf,
});
client.wbuf = memio::buffer(&client.buf);
ev::write(client.sock, &http_write, client.wbuf);
};
fn http_write(sock: *ev::file, r: (size | io::error)) (void | nomem) = {
const client = ev::getuser(sock): *server_client;
const n = match (r) {
case let err: io::error =>
client_close(client);
return;
case let n: size =>
yield n;
};
static delete(client.wbuf[..n]);
if (len(client.wbuf) != 0) {
ev::write(client.sock, &http_write, client.wbuf);
return;
};
memio::reset(&client.buf);
ev::read(client.sock, &http_read, client.rbuf);
};