From e229050d5aae07607d56746892da75afe3417f64 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Wed, 12 Mar 2025 23:47:53 +0800 Subject: Add a basic HTTP server --- .gitignore | 1 + Makefile | 2 ++ main.ha | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 main.ha diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e90b0c9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/forge diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..acb67a1 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +forge: main.ha + hare build -o $@ $^ diff --git a/main.ha b/main.ha new file mode 100644 index 0000000..4359c46 --- /dev/null +++ b/main.ha @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu +// Adapted from template by Willow Barraco + +use getopt; +use log; +use net; +use net::ip; +use net::http; +use net::dial; +use os; +use memio; +use io; +use fmt; +use bufio; +use strings; + +const usage: [_]getopt::help = [ + "HTTP server", + ('a', "address", "listened address (ex: 127.0.0.1:8080)") +]; + +export fn main() void = { + const cmd = getopt::parse(os::args, usage...); + defer getopt::finish(&cmd); + + let port: u16 = 8080; + let ip_addr: ip::addr4 = [127, 0, 0, 1]; + + for (let i = 0z; i < len(cmd.opts); i += 1) { + const opt = cmd.opts[i]; + switch (opt.0) { + case 'a' => + match (dial::splitaddr(opt.1, "")) { + case let value: (str, u16) => + ip_addr = ip::parsev4(value.0)!; + port = value.1; + case dial::invalid_address => + abort("Invalid address"); + }; + case => abort(); // unreachable + }; + }; + + const server = match (http::listen(ip_addr, port)) { + case let this: *http::server => + yield this; + case net::error => abort("failure while listening"); + }; + defer http::server_finish(server); + + for (true) { + const serv_req = match (http::serve(server)) { + case let this: *http::server_request => + yield this; + case net::error => abort("failure while serving"); + }; + defer http::serve_finish(serv_req); + + match (handlereq(serv_req.socket, &serv_req.request)) { + case void => yield; + case io::error => log::println("error while handling request"); + }; + }; +}; + +export fn handlereq(conn: io::handle, request: *http::request) (void | io::error) = { + fmt::fprint(conn, "HTTP/1.1 200 OK\r\n")?; + fmt::fprint(conn, "Content-Type: text/plain\r\n\r\n")?; + + fmt::fprintln(conn, "Hi there!")?; +}; -- cgit v1.2.3