diff --git a/.gitignore b/.gitignore index 2f7896d..21ab51e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ target/ +backend/httpd +backend/proctest +backend/threads +backend/backend diff --git a/backend/Makefile b/backend/Makefile new file mode 100644 index 0000000..ed18223 --- /dev/null +++ b/backend/Makefile @@ -0,0 +1,45 @@ +.POSIX: +.SUFFIXES: +HARE=hare +HAREFLAGS= + +DESTDIR= +PREFIX=/usr/local +BINDIR=$(PREFIX)/bin + +CC ?= cc +CFLAGS = -O2 -Wall -fPIC + +classpathify = $(subst $(eval ) ,:,$1) + +HAREEXTRAPATH = $(HAREPATH) +HAREEXTRAPATH += $(wildcard vendor/*) + +all: proctest httpd threads + +proctest: $(wildcard cmd/proctest/*.ha) $(SOURCES) + HAREPATH=$(call classpathify,$(HAREEXTRAPATH)) \ + $(HARE) build $(HAREFLAGS) -o $@ cmd/$@/ + +httpd: $(wildcard cmd/httpd/*.ha) $(SOURCES) + HAREPATH=$(call classpathify,$(HAREEXTRAPATH)) \ + $(HARE) build $(HAREFLAGS) -o $@ cmd/$@/ + +threads: $(wildcard cmd/threads/*.ha) $(SOURCES) + HAREPATH=$(call classpathify,$(HAREEXTRAPATH)) \ + $(HARE) build $(HAREFLAGS) -o $@ cmd/$@/ + +check: + HAREPATH=$(HAREPATH):$(HAREEXTRAPATH) \ + $(HARE) test $(HAREFLAGS) + +clean: + rm -f proctest httpd threads + +install: httpd + install -Dm755 web $(DESTDIR)$(BINDIR)/httpd + +uninstall: + rm -f $(DESTDIR)$(BINDIR)/httpd + +.PHONY: all check clean install uninstall diff --git a/backend/cmd/httpd/main.ha b/backend/cmd/httpd/main.ha new file mode 100644 index 0000000..819a2f5 --- /dev/null +++ b/backend/cmd/httpd/main.ha @@ -0,0 +1,92 @@ +use getopt; +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); + + let buf = memio::dynamic(); + defer io::close(&buf)!; + handlereq(&buf, &serv_req.request); + + http::response_write( + serv_req.socket, + http::STATUS_OK, + &buf, + ("Content-Type", "text/plain") + )!; + }; +}; + +export fn handlereq(buf: *io::stream, request: *http::request) void = { + fmt::fprintfln(buf, "Method: {}", request.method)!; + fmt::fprintfln(buf, "Path: {}", request.target.path)!; + fmt::fprintfln(buf, "Fragment: {}", request.target.fragment)!; + fmt::fprintfln(buf, "Query: {}", request.target.query)!; + fmt::fprintfln(buf, "Headers: < void; + case let body: io::handle => + fmt::fprintfln(buf, "Body: < + fmt::fprintln(buf, strings::fromutf8(line)!)!; + break; + case io::EOF => + break; + }; + }; + fmt::fprintfln(buf, "EOF")!; + }; +}; diff --git a/backend/cmd/threads/main.ha b/backend/cmd/threads/main.ha new file mode 100644 index 0000000..9c259c4 --- /dev/null +++ b/backend/cmd/threads/main.ha @@ -0,0 +1,45 @@ +use types::c; +use fmt; + +export type pthread_t = u64; +export type pthread_attr_t = opaque; +export type pthread_fn = fn(nullable *opaque) nullable *opaque; + +export @symbol("pthread_create") fn pthread_create(thread: nullable *pthread_t, attr: nullable *pthread_attr_t, start_routine: *pthread_fn, arg: nullable *opaque) int; +export @symbol("pthread_join") fn pthread_join(thread: pthread_t, nullable *nullable *opaque) int; + +fn thread_start(arg: nullable *opaque) nullable *opaque = { + fmt::println("hello from another thread")!; + return null; +}; + +export fn main() void = { + fmt::println("starting a thread")!; + let tid: pthread_t = 0; + let ret = pthread_create(&tid, null, &thread_start, null); + fmt::printfln("ret: {}", ret)!; + + fmt::println("joining")!; + ret = pthread_join(tid, null); + fmt::printfln("ret: {}", ret)!; +}; + + +// spawn a thread +// +// returns a handle or an error +fn spawn(f: *pthread_fn) (join_handle | error) = { + void; +}; + +// join a thread +// +// no return value or an error on join +fn join(handle: join_handle) (void | error) = { + void; +}; + +// detach a thread. +fn detach(join_handle) (void | error) = { + void; +}; diff --git a/backend/vendor/hare-http b/backend/vendor/hare-http index 9237448..53a1384 160000 --- a/backend/vendor/hare-http +++ b/backend/vendor/hare-http @@ -1 +1 @@ -Subproject commit 923744872525b490925117ce597f10037208fc05 +Subproject commit 53a13840bddfbf8b11e4bb5704eb470d44bd63b9