Current section

Files

Jump to
midas src magpie server.gleam
Raw

src/magpie/server.gleam

import gleam/list
import gleam/int
import gleam/result
import gleam/string
import process/process
import process/process.{From, Pid, BarePid, ExitReason, Normal, Kill, Infinity, Milliseconds, TrapExit}
import midas_utils
import midas/http
import net/http as wire
fn parse_method(method_string) -> Result(http.Method, Nil) {
case method_string {
"CONNECT" -> Ok(http.Connect)
"DELETE" -> Ok(http.Delete)
"GET" -> Ok(http.Get)
"HEAD" -> Ok(http.Head)
"OPTIONS" -> Ok(http.Options)
"PATCH" -> Ok(http.Patch)
"POST" -> Ok(http.Post)
"PUT" -> Ok(http.Put)
"TRACE" -> Ok(http.Trace)
_ -> Error(Nil)
}
}
// TODO reanme supervisor -> endpoint
fn read_request(socket) {
// TODO https://erlang.org/doc/man/timer.html#exit_after-2
let Ok(
tuple(method_string, wire.AbsPath(raw_path), raw_headers),
) = wire.read_request_head(socket, 1000)
let Ok(method) = parse_method(method_string)
let tuple(path, query_string) = midas_utils.split_on(raw_path, "?")
let [tuple("host", authority), ..headers] = raw_headers
let content_length = result.unwrap(
http.get_header(headers, "content-length"),
or: "0",
)
let Ok(content_length) = int.parse(content_length)
let body = case content_length {
0 -> ""
_ -> {
let Ok(body) = wire.read_body(socket, content_length, 5000)
body
}
}
Ok(
http.Request(
method: method,
authority: authority,
headers: headers,
path: path,
query: query_string,
body: body,
),
)
}
// completely ignore reson phrases
fn response_to_string(response) {
let http.Response(status: status, headers: headers, body: body) = response
let status_line = string.concat(["HTTP/1.1 ", int.to_string(status), " \r\n"])
let response_head = list.fold(
headers,
status_line,
fn(header, buffer) {
let tuple(name, value) = header
string.concat([buffer, name, ": ", value, "\r\n"])
},
)
let response = string.concat([response_head, "\r\n", body])
response
}
// TODO needs to add content length Or does it!, use the set_body function
pub type Accept {
Accept(From(Nil))
}
fn run(receive, handler, listen_socket) {
let Ok(Accept(from)) = receive(Infinity)
let Ok(socket) = wire.accept(listen_socket)
process.reply(from, Nil)
let Ok(request) = read_request(socket)
let response = handler(request)
let Ok(Nil) = wire.send(socket, response_to_string(response))
Normal
}
pub fn spawn_link(handler, listen_socket) {
let pid = process.spawn_link(
fn(receive) { run(receive, handler, listen_socket) },
)
pid
}
pub fn accept(pid) {
process.call(pid, Accept(_), Infinity)
}