commit - 5b302645b69e633643b7810d0de85cec0b54973d
commit + 4ea63b457e8a1e2ee9e96e354fa90da2c7aa88e7
blob - 23594b7dd753141b992b762e15e3d96e46f60a8f
blob + 5dbdc740fb3a5ef07ae666039271cdd4501ccb2d
--- http/http.ha
+++ http/http.ha
export type routefn = *fn(*request, *reswriter) (void | io::error);
-type client = struct {
+type connection = struct {
fp: io::file,
in: bufio::scanner,
first_line: bool,
// http::listen("unix:/run/foo.sock")
export fn listen_sock(f: io::file) void = {
- ev::add(f, ev::READ, &acceptclt, &global);
+ ev::add(f, ev::READ, &acceptconn, &global);
};
-fn acceptclt(f: io::file, ev: ev::event, data: nullable *opaque) void = {
+fn acceptconn(f: io::file, ev: ev::event, data: nullable *opaque) void = {
let sock = match(net::accept(f, net::sockflag::NONBLOCK)) {
case let err: net::error =>
log::printfln("accept: {}", net::strerror(err));
};
let mux = data: *mux;
- let clt = alloc(client {
+ let conn = alloc(connection {
fp = sock,
mux = mux,
in = bufio::newscanner(sock, types::SIZE_MAX),
...
});
- ev::add(sock, ev::READ, &handleclt, clt);
+ ev::add(sock, ev::READ, &handleconn, conn);
};
fn parse_req(
- c: *client
+ conn: *connection
) (bool | badrequest | io::error | encoding::utf8::invalid) = {
+ let req = &conn.request;
+ let res = &conn.response;
+
for (true) {
- let line = match(bufio::scan_string(&c.in, "\r\n")) {
+ let line = match(bufio::scan_string(&conn.in, "\r\n")) {
case io::EOF => return badrequest;
case let err: io::error => return err;
case let line: const str => yield line;
};
- if (c.first_line) {
- c.first_line = false;
+ if (conn.first_line) {
+ conn.first_line = false;
let (method, rest) = strings::cut(line, " ");
switch (method) {
- case "GET" => c.request.method = method::GET;
- case "HEAD" => c.request.method = method::HEAD;
- case "POST" => c.request.method = method::POST;
- case "PUT" => c.request.method = method::PUT;
- case "DELETE" => c.request.method = method::DELETE;
- case "CONNECT" => c.request.method = method::CONNECT;
- case "OPTIONS" => c.request.method = method::OPTIONS;
- case "TRACE" => c.request.method = method::TRACE;
- case "PATCH" => c.request.method = method::PATCH;
+ case "GET" => req.method = method::GET;
+ case "HEAD" => req.method = method::HEAD;
+ case "POST" => req.method = method::POST;
+ case "PUT" => req.method = method::PUT;
+ case "DELETE" => req.method = method::DELETE;
+ case "CONNECT" => req.method = method::CONNECT;
+ case "OPTIONS" => req.method = method::OPTIONS;
+ case "TRACE" => req.method = method::TRACE;
+ case "PATCH" => req.method = method::PATCH;
case => return badrequest;
};
let (path, version) = strings::cut(rest, " ");
- c.request.path = strings::dup(path);
+ req.path = strings::dup(path);
switch (version) {
case "HTTP/1.0" =>
- c.request.version = version::HTTP_1_0;
+ req.version = version::HTTP_1_0;
case "HTTP/1.1" =>
- c.request.version = version::HTTP_1_1;
- c.response.chunked = true;
+ req.version = version::HTTP_1_1;
+ res.chunked = true;
case => return badrequest;
};
};
};
-fn delclient(c: *client) void = {
- ev::del(c.fp);
+fn close_conn(conn: *connection) void = {
+ ev::del(conn.fp);
- bufio::finish(&c.in);
- match (io::close(c.fp)) {
+ bufio::finish(&conn.in);
+ match (io::close(conn.fp)) {
case => yield;
};
- match (io::close(&c.response)) {
+ match (io::close(&conn.response)) {
case => yield;
};
- free(c);
+ free(conn);
};
-fn handleclt(f: io::file, ev: ev::event, data: nullable *opaque) void = {
- let clt = data: *client;
+fn handleconn(f: io::file, ev: ev::event, data: nullable *opaque) void = {
+ let conn = data: *connection;
- match (respond(clt)) {
+ match (respond(conn)) {
case let err: io::error =>
log::printfln("failure: {}", io::strerror(err));
- delclient(clt);
+ close_conn(conn);
return;
case =>
// XXX wait if there is still data pending.
- if (clt.response.done) {
- delclient(clt);
+ if (conn.response.done) {
+ close_conn(conn);
return;
};
- ev::add(clt.fp, ev::READ | ev::WRITE, &handleclt, clt);
+ ev::add(conn.fp, ev::READ | ev::WRITE, &handleconn, conn);
};
};
-fn handle_request(clt: *client) (void | io::error) = {
- match (parse_req(clt)) {
+fn handle_request(conn: *connection) (void | io::error) = {
+ match (parse_req(conn)) {
case let done: bool =>
if (!done)
return; // try again later
yield;
case errors::again => return; // try again later
case badrequest =>
- route_badrequest(&clt.request, &clt.response)?;
- clt.response.done = true;
- reswriter_flush(&clt.response)?;
+ route_badrequest(&conn.request, &conn.response)?;
+ conn.response.done = true;
+ reswriter_flush(&conn.response)?;
return;
case let err: io::error =>
log::printfln("parse_req: {}", io::strerror(err));
return;
};
- let path = clt.request.path;
+ let path = conn.request.path;
log::printfln("< GET {}", path);
- for (let i = 0z; i < len(clt.mux); i += 1) {
- if (fnmatch::fnmatch(clt.mux[i].pattern, path)) {
- clt.routefn = clt.mux[i].routefn;
+ for (let i = 0z; i < len(conn.mux); i += 1) {
+ if (fnmatch::fnmatch(conn.mux[i].pattern, path)) {
+ conn.routefn = conn.mux[i].routefn;
break;
};
};
- clt.request_done = true;
+ conn.request_done = true;
};
-fn respond(clt: *client) (void | io::error) = {
- if (!clt.request_done)
- handle_request(clt)?;
- if (!clt.request_done)
+fn respond(conn: *connection) (void | io::error) = {
+ if (!conn.request_done)
+ handle_request(conn)?;
+ if (!conn.request_done)
return;
- if (!clt.response.done) {
- clt.response.done = true;
- clt.routefn(&clt.request, &clt.response)?;
+ if (!conn.response.done) {
+ conn.response.done = true;
+ conn.routefn(&conn.request, &conn.response)?;
};
- reswriter_flush(&clt.response)?;
+ reswriter_flush(&conn.response)?;
};
fn route_notfound(req: *request, res: *reswriter) (void | io::error) = {