aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRunxi Yu <me@runxiyu.org>2025-03-12 23:47:53 +0800
committerRunxi Yu <me@runxiyu.org>2025-03-13 00:00:05 +0800
commite229050d5aae07607d56746892da75afe3417f64 (patch)
tree96ca791015d2ad12c409a9a1284c40ce3e6b29e5
parentAdd LICENSE (diff)
downloadforge-e229050d5aae07607d56746892da75afe3417f64.tar.gz
forge-e229050d5aae07607d56746892da75afe3417f64.tar.zst
forge-e229050d5aae07607d56746892da75afe3417f64.zip
Add a basic HTTP server
-rw-r--r--.gitignore1
-rw-r--r--Makefile2
-rw-r--r--main.ha72
3 files changed, 75 insertions, 0 deletions
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 <https://runxiyu.org>
+// Adapted from template by Willow Barraco <contact@willowbarraco.fr>
+
+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!")?;
+};