Initial commit
This commit is contained in:
commit
1368b7b69b
10 changed files with 588 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
*.dSYM
|
||||||
|
fjord
|
||||||
|
*.bin
|
||||||
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
[submodule "radix_tree"]
|
||||||
|
path = radix_tree
|
||||||
|
url = https://codeberg.org/hugomardbrink/odin-radixtree
|
||||||
|
update = none
|
||||||
1
README.md
Normal file
1
README.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
I'll get back to this when Odin is more mature, I've ran into countless compiler seg faults and can't be bothered to find out why anymore.
|
||||||
43
fjord_test.odin
Normal file
43
fjord_test.odin
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
package fjord
|
||||||
|
|
||||||
|
import "core:log"
|
||||||
|
import "core:net"
|
||||||
|
|
||||||
|
import "core:testing"
|
||||||
|
|
||||||
|
import http "http"
|
||||||
|
|
||||||
|
Error :: enum {}
|
||||||
|
|
||||||
|
handler :: proc(request: ^http.Request) -> (http.Response, Error) {
|
||||||
|
body := "<div>Hello</div>"
|
||||||
|
response := http.make_response(.Ok, transmute([]byte)body, .Html)
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
get_path_var_handler :: proc(request: ^http.Request) -> (http.Response, Error) {
|
||||||
|
body := "<div>Hello</div>"
|
||||||
|
response := http.make_response(.Ok, transmute([]byte)body, .Html)
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
test_basic_ok :: proc(t: ^testing.T) {
|
||||||
|
context.logger = log.create_console_logger(.Info)
|
||||||
|
defer log.destroy_console_logger(context.logger)
|
||||||
|
|
||||||
|
endpoint := net.Endpoint {
|
||||||
|
address = net.IP4_Address{127, 0, 0, 1},
|
||||||
|
port = 8080,
|
||||||
|
}
|
||||||
|
|
||||||
|
server: Server(Error)
|
||||||
|
server_init(&server, endpoint, context.allocator)
|
||||||
|
defer server_destroy(&server)
|
||||||
|
|
||||||
|
server_add_handler(&server, .GET, {}, handler)
|
||||||
|
server_add_handler(&server, .GET, {"hello", "{name-thing}"}, handler)
|
||||||
|
listen_and_serve(&server)
|
||||||
|
}
|
||||||
79
http/http.odin
Normal file
79
http/http.odin
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
package http
|
||||||
|
|
||||||
|
import "core:strconv"
|
||||||
|
|
||||||
|
RequestError :: enum {
|
||||||
|
InvalidRequestLine,
|
||||||
|
InvalidMethod,
|
||||||
|
InvalidHeaderFormat,
|
||||||
|
IncompleteRequest,
|
||||||
|
ContentLengthMismatch,
|
||||||
|
FailedParsing,
|
||||||
|
NetworkError,
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseError :: enum {
|
||||||
|
InvalidContentType,
|
||||||
|
InvalidStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
Header :: map[string]string
|
||||||
|
|
||||||
|
Status :: enum u32 {
|
||||||
|
Ok = 200,
|
||||||
|
BadRequest = 400,
|
||||||
|
NotFound = 404,
|
||||||
|
InternalServerError = 500,
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentType :: enum {
|
||||||
|
Html,
|
||||||
|
Image,
|
||||||
|
}
|
||||||
|
|
||||||
|
Response :: struct {
|
||||||
|
header: Header,
|
||||||
|
proto_version: string,
|
||||||
|
status: Status,
|
||||||
|
body: []byte,
|
||||||
|
}
|
||||||
|
|
||||||
|
Method :: enum {
|
||||||
|
GET,
|
||||||
|
POST,
|
||||||
|
PUT,
|
||||||
|
DELETE,
|
||||||
|
}
|
||||||
|
|
||||||
|
Request :: struct {
|
||||||
|
header: Header,
|
||||||
|
proto_version: string,
|
||||||
|
method: Method,
|
||||||
|
path: string,
|
||||||
|
body: []byte,
|
||||||
|
path_variables: map[string]string,
|
||||||
|
}
|
||||||
|
|
||||||
|
make_response :: proc(
|
||||||
|
status: Status,
|
||||||
|
body: []byte,
|
||||||
|
content_type: ContentType,
|
||||||
|
allocator := context.temp_allocator,
|
||||||
|
) -> Response {
|
||||||
|
response := Response {
|
||||||
|
status = status,
|
||||||
|
proto_version = "HTTP/1.1",
|
||||||
|
header = make(Header, allocator),
|
||||||
|
body = body,
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 32, allocator)
|
||||||
|
response.header["Content-Length"] = strconv.write_uint(
|
||||||
|
buf[:],
|
||||||
|
u64(len(body)),
|
||||||
|
10,
|
||||||
|
)
|
||||||
|
response.header["Content-Type"] = str_from_content_type(content_type)
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
188
http/marshaller.odin
Normal file
188
http/marshaller.odin
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
package http
|
||||||
|
|
||||||
|
import "core:bufio"
|
||||||
|
import "core:bytes"
|
||||||
|
import "core:io"
|
||||||
|
import "core:log"
|
||||||
|
import "core:net"
|
||||||
|
import "core:strconv"
|
||||||
|
import "core:strings"
|
||||||
|
import "core:unicode"
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
method_from_str :: proc(method_str: string) -> Method {
|
||||||
|
switch method_str {
|
||||||
|
case "GET":
|
||||||
|
return .GET
|
||||||
|
case "DELETE":
|
||||||
|
return .DELETE
|
||||||
|
case "PUT":
|
||||||
|
return .PUT
|
||||||
|
case "POST":
|
||||||
|
return .POST
|
||||||
|
}
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
str_from_content_type :: proc(content_type: ContentType) -> string {
|
||||||
|
switch content_type {
|
||||||
|
case .Html:
|
||||||
|
return "text/html"
|
||||||
|
case .Image:
|
||||||
|
return "image/jpeg"
|
||||||
|
}
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
status_text :: proc(status: Status) -> string {
|
||||||
|
switch status {
|
||||||
|
case .Ok:
|
||||||
|
return "OK"
|
||||||
|
case .NotFound:
|
||||||
|
return "Not found"
|
||||||
|
case .BadRequest:
|
||||||
|
return "Bad request"
|
||||||
|
case .InternalServerError:
|
||||||
|
return "Internal server error"
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
unmarshall_request_line :: proc(
|
||||||
|
request: ^Request,
|
||||||
|
reader: ^bufio.Reader,
|
||||||
|
) -> RequestError {
|
||||||
|
request_line, io_err := bufio.reader_read_slice(reader, '\n')
|
||||||
|
if io_err != nil do return .InvalidRequestLine
|
||||||
|
|
||||||
|
request_line = sanitize_byte_slice(request_line)
|
||||||
|
|
||||||
|
parts := bytes.split(request_line, {' '})
|
||||||
|
if len(parts) != 3 do return .InvalidRequestLine
|
||||||
|
|
||||||
|
request.method = method_from_str(string(parts[0]))
|
||||||
|
request.path = string(parts[1])
|
||||||
|
request.proto_version = string(parts[2])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
unmarshall_request_headers :: proc(
|
||||||
|
request: ^Request,
|
||||||
|
reader: ^bufio.Reader,
|
||||||
|
allocator := context.temp_allocator,
|
||||||
|
) -> RequestError {
|
||||||
|
request.header = make(Header, allocator)
|
||||||
|
|
||||||
|
for {
|
||||||
|
header_line, io_err := bufio.reader_read_slice(reader, '\n')
|
||||||
|
if io_err != nil do return .InvalidHeaderFormat
|
||||||
|
|
||||||
|
header_line = sanitize_byte_slice(header_line)
|
||||||
|
if len(header_line) == 0 do break
|
||||||
|
|
||||||
|
parts := bytes.split_n(header_line, {':'}, 2)
|
||||||
|
if len(parts) != 2 do return .InvalidHeaderFormat
|
||||||
|
|
||||||
|
key := string(bytes.trim_space(parts[0]))
|
||||||
|
value := string(bytes.trim_space(parts[1]))
|
||||||
|
|
||||||
|
request.header[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
unmarshall_request_body :: proc(
|
||||||
|
request: ^Request,
|
||||||
|
reader: ^bufio.Reader,
|
||||||
|
allocator := context.temp_allocator,
|
||||||
|
) -> RequestError {
|
||||||
|
content_length_str, ok := request.header["Content-Length"]
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
content_length, ok := strconv.parse_uint(content_length_str)
|
||||||
|
if !ok do return .InvalidHeaderFormat
|
||||||
|
request.body = make([]byte, content_length, allocator)
|
||||||
|
|
||||||
|
_, err := bufio.reader_read(reader, request.body)
|
||||||
|
if err != nil do return .FailedParsing
|
||||||
|
|
||||||
|
} else {
|
||||||
|
request.body = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
sanitize_byte_slice :: proc(slice: []byte) -> []byte {
|
||||||
|
return bytes.trim(slice, {'\n', '\r'})
|
||||||
|
}
|
||||||
|
|
||||||
|
unmarshall_request :: proc(
|
||||||
|
data: []byte,
|
||||||
|
allocator := context.temp_allocator,
|
||||||
|
) -> (
|
||||||
|
request: Request,
|
||||||
|
request_err: RequestError,
|
||||||
|
) {
|
||||||
|
if len(data) == 0 do return request, .IncompleteRequest
|
||||||
|
|
||||||
|
byte_reader: bytes.Reader
|
||||||
|
stream := bytes.reader_init(&byte_reader, data)
|
||||||
|
|
||||||
|
reader: bufio.Reader
|
||||||
|
bufio.reader_init(&reader, io.to_reader(stream), allocator = allocator)
|
||||||
|
defer bufio.reader_destroy(&reader)
|
||||||
|
|
||||||
|
unmarshall_request_line(&request, &reader) or_return
|
||||||
|
unmarshall_request_headers(&request, &reader, allocator) or_return
|
||||||
|
unmarshall_request_body(&request, &reader, allocator) or_return
|
||||||
|
|
||||||
|
return request, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
marshall_response :: proc(
|
||||||
|
response: ^Response,
|
||||||
|
allocator := context.temp_allocator,
|
||||||
|
) -> []byte {
|
||||||
|
buffer: bytes.Buffer
|
||||||
|
bytes.buffer_init_allocator(&buffer, 0, 0, allocator)
|
||||||
|
|
||||||
|
bytes.buffer_write_string(&buffer, response.proto_version)
|
||||||
|
bytes.buffer_write_string(&buffer, " ")
|
||||||
|
|
||||||
|
status_code_buf := make([]byte, 4, allocator)
|
||||||
|
status_code_str := strconv.write_uint(
|
||||||
|
status_code_buf[:],
|
||||||
|
u64(response.status),
|
||||||
|
10,
|
||||||
|
)
|
||||||
|
|
||||||
|
bytes.buffer_write_string(&buffer, status_code_str)
|
||||||
|
bytes.buffer_write_string(&buffer, " ")
|
||||||
|
|
||||||
|
status_msg := status_text(response.status)
|
||||||
|
bytes.buffer_write_string(&buffer, status_msg)
|
||||||
|
bytes.buffer_write_string(&buffer, "\r\n")
|
||||||
|
|
||||||
|
for key, value in response.header {
|
||||||
|
bytes.buffer_write_string(&buffer, key)
|
||||||
|
bytes.buffer_write_string(&buffer, ": ")
|
||||||
|
bytes.buffer_write_string(&buffer, value)
|
||||||
|
bytes.buffer_write_string(&buffer, "\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes.buffer_write_string(&buffer, "\r\n")
|
||||||
|
|
||||||
|
bytes.buffer_write(&buffer, response.body)
|
||||||
|
|
||||||
|
return bytes.buffer_to_bytes(&buffer)
|
||||||
|
}
|
||||||
7
odinfmt.json
Normal file
7
odinfmt.json
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"character_width": 80,
|
||||||
|
"tabs": false,
|
||||||
|
"tabs_width": 4,
|
||||||
|
"newline_style": "lf",
|
||||||
|
"brace_style": "allman"
|
||||||
|
}
|
||||||
1
radix_tree
Submodule
1
radix_tree
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 6fe8dc79ee3aa57f3a8b9a427bedf7bb24cfd809
|
||||||
42
router.odin
Normal file
42
router.odin
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
package fjord
|
||||||
|
|
||||||
|
import "core:path/slashpath"
|
||||||
|
import http "http"
|
||||||
|
import rxt "radix_tree"
|
||||||
|
|
||||||
|
EndpointHandler :: struct($Error_Type: typeid) {
|
||||||
|
method_handlers: map[http.Method]#type proc(
|
||||||
|
request: ^http.Request,
|
||||||
|
) -> (
|
||||||
|
http.Response,
|
||||||
|
Error_Type,
|
||||||
|
),
|
||||||
|
path_variables: []string,
|
||||||
|
}
|
||||||
|
|
||||||
|
Router :: struct {
|
||||||
|
radix_tree: rxt.RadixTree(EndpointHandler),
|
||||||
|
}
|
||||||
|
|
||||||
|
router_init :: proc(router: ^Router, allocator := context.allocator) {
|
||||||
|
rxt.init(&router.radix_tree, allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
router_destroy :: proc(router : ^Router) {
|
||||||
|
rxt.destroy(&router.radix_tree)
|
||||||
|
}
|
||||||
|
|
||||||
|
router_lookup :: proc(router: ^Router, key: string) -> (endpoint_handler: EndpointHandler, ok: bool) {
|
||||||
|
return rxt.lookup(&router.radix_tree, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
router_add_route :: proc(router: ^Router, key: []string, value: EndpointHandler) {
|
||||||
|
joined_path := slashpath.join(key, context.allocator)
|
||||||
|
defer delete(joined_path)
|
||||||
|
|
||||||
|
rxt.insert(&router.radix_tree, joined_path[:], value)
|
||||||
|
}
|
||||||
|
|
||||||
|
router_remove_route :: proc(router: ^Router, key: string) -> (ok: bool){
|
||||||
|
return rxt.remove(&router.radix_tree, key)
|
||||||
|
}
|
||||||
220
server.odin
Normal file
220
server.odin
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
package fjord
|
||||||
|
|
||||||
|
import "core:bufio"
|
||||||
|
import "core:bytes"
|
||||||
|
import "core:io"
|
||||||
|
import "core:log"
|
||||||
|
import "core:net"
|
||||||
|
import "core:path/slashpath"
|
||||||
|
import "core:strconv"
|
||||||
|
import "core:strings"
|
||||||
|
import "core:unicode"
|
||||||
|
|
||||||
|
import http "http"
|
||||||
|
|
||||||
|
Server :: struct($Error_Type: typeid) {
|
||||||
|
endpoint: net.Endpoint,
|
||||||
|
router: Router(EndpointHandler(Error_Type)),
|
||||||
|
not_found_handler: #type proc(
|
||||||
|
request: ^http.Request,
|
||||||
|
) -> (
|
||||||
|
http.Response,
|
||||||
|
Error_Type,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
request_ended :: proc(buf: []byte) -> bool {
|
||||||
|
HTTP_END_SEQUENCE :: []byte{'\r', '\n', '\r', '\n'}
|
||||||
|
return bytes.index(buf, HTTP_END_SEQUENCE) != -1
|
||||||
|
}
|
||||||
|
|
||||||
|
read_connection :: proc(
|
||||||
|
client_socket: net.TCP_Socket,
|
||||||
|
allocator := context.temp_allocator,
|
||||||
|
) -> (
|
||||||
|
data: []byte,
|
||||||
|
err: http.RequestError,
|
||||||
|
) {
|
||||||
|
TCP_CHUNK_SIZE :: 1024
|
||||||
|
buffer: bytes.Buffer
|
||||||
|
bytes.buffer_init_allocator(&buffer, 0, 0, allocator)
|
||||||
|
|
||||||
|
chunk: [TCP_CHUNK_SIZE]byte
|
||||||
|
for {
|
||||||
|
bytes_read, net_err := net.recv_tcp(client_socket, chunk[:])
|
||||||
|
if net_err != nil do return data, .NetworkError
|
||||||
|
|
||||||
|
if bytes_read == 0 do break
|
||||||
|
bytes.buffer_write(&buffer, chunk[:bytes_read])
|
||||||
|
|
||||||
|
if request_ended(buffer.buf[:]) do break
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.buf[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
server_init :: proc(
|
||||||
|
server: ^Server($Error_Type),
|
||||||
|
endpoint: net.Endpoint,
|
||||||
|
allocator := context.allocator,
|
||||||
|
) {
|
||||||
|
server^ = Server(Error_Type) {
|
||||||
|
endpoint = endpoint,
|
||||||
|
not_found_handler = proc(
|
||||||
|
request: ^http.Request,
|
||||||
|
) -> (
|
||||||
|
http.Response,
|
||||||
|
Error_Type,
|
||||||
|
) {
|
||||||
|
body := "<div>Not found</div>"
|
||||||
|
http_response := http.make_response(
|
||||||
|
.NotFound,
|
||||||
|
transmute([]byte)body,
|
||||||
|
.Html,
|
||||||
|
)
|
||||||
|
return http_response, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
router_init(&server.router)
|
||||||
|
}
|
||||||
|
|
||||||
|
server_destroy :: proc(server: ^Server($Error_Type)) {
|
||||||
|
router_destroy(&server.router)
|
||||||
|
}
|
||||||
|
|
||||||
|
server_add_handler :: proc(
|
||||||
|
server: ^Server($Error_Type),
|
||||||
|
method: http.Method,
|
||||||
|
path: []string,
|
||||||
|
handle_proc: #type proc(
|
||||||
|
request: ^http.Request,
|
||||||
|
) -> (
|
||||||
|
http.Response,
|
||||||
|
Error_Type,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
path_variables := make([dynamic]string, context.allocator)
|
||||||
|
defer delete(path_variables)
|
||||||
|
|
||||||
|
for seg in path {
|
||||||
|
is_path_var :=
|
||||||
|
strings.has_prefix(seg, "{") && strings.has_suffix(seg, "}")
|
||||||
|
|
||||||
|
if is_path_var {
|
||||||
|
append(&path_variables, seg[1:len(seg) - 1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
joined_path := slashpath.join(path, context.allocator)
|
||||||
|
defer delete(joined_path)
|
||||||
|
|
||||||
|
if joined_path == "" {
|
||||||
|
router_add_route(
|
||||||
|
&server.router,
|
||||||
|
"/",
|
||||||
|
EndpointHandler(Error_Type){method},
|
||||||
|
)
|
||||||
|
server.handlers["/"] = EndpointHandler(Error_Type) {
|
||||||
|
method,
|
||||||
|
handle_proc,
|
||||||
|
path_variables[:],
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
server.handlers[joined_path] = Handler(Error_Type) {
|
||||||
|
method,
|
||||||
|
handle_proc,
|
||||||
|
path_variables[:],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is used instead of server_make param until compiler bug is fixed: https://github.com/odin-lang/Odin/issues/5792
|
||||||
|
server_set_not_found_handler :: proc(
|
||||||
|
server: ^Server($Error_Type),
|
||||||
|
not_found_handler: #type proc(
|
||||||
|
request: ^http.Request,
|
||||||
|
) -> (
|
||||||
|
http.Response,
|
||||||
|
Error_Type,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
server.not_found_handler = not_found_handler
|
||||||
|
}
|
||||||
|
|
||||||
|
match_handler_pattern :: proc(
|
||||||
|
server: ^Server($Error_Type),
|
||||||
|
method: http.Method,
|
||||||
|
path: string,
|
||||||
|
allocator := context.allocator,
|
||||||
|
) -> (
|
||||||
|
handler: #type proc(request: ^http.Request) -> (http.Response, Error_Type),
|
||||||
|
path_variables: map[string]string,
|
||||||
|
) {
|
||||||
|
path_variables = make(map[string]string, allocator)
|
||||||
|
segments := strings.split(path, "/", context.allocator)
|
||||||
|
defer delete(segments)
|
||||||
|
|
||||||
|
handler = server.handlers[identifier].procedure
|
||||||
|
|
||||||
|
return handler, path_variables
|
||||||
|
}
|
||||||
|
|
||||||
|
listen_and_serve :: proc(server: ^Server($Error_Type)) {
|
||||||
|
server_socket, net_err := net.listen_tcp(server.endpoint)
|
||||||
|
log.assert(net_err == nil, "Couldn't create TCP socket")
|
||||||
|
defer net.close(server_socket)
|
||||||
|
|
||||||
|
for {
|
||||||
|
client_socket, source, net_err := net.accept_tcp(server_socket)
|
||||||
|
if net_err != nil {
|
||||||
|
log.warnf("Failed to accept TCP connection, reason: %s", net_err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer net.close(client_socket)
|
||||||
|
|
||||||
|
data, read_err := read_connection(
|
||||||
|
client_socket,
|
||||||
|
context.temp_allocator,
|
||||||
|
)
|
||||||
|
if read_err != nil {
|
||||||
|
log.warnf("Failed to read request, reason: %s", read_err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
http_request, unmarshall_err := http.unmarshall_request(
|
||||||
|
data,
|
||||||
|
context.temp_allocator,
|
||||||
|
)
|
||||||
|
if unmarshall_err != nil {
|
||||||
|
log.warnf(
|
||||||
|
"Failed to unmarshall request, reason: %s",
|
||||||
|
unmarshall_err,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
handler: #type proc(
|
||||||
|
request: ^http.Request,
|
||||||
|
) -> (
|
||||||
|
http.Response,
|
||||||
|
Error_Type,
|
||||||
|
)
|
||||||
|
handler, http_request.path_variables = match_handler_pattern(
|
||||||
|
server,
|
||||||
|
http_request.method,
|
||||||
|
http_request.path,
|
||||||
|
)
|
||||||
|
|
||||||
|
http_response, err := handler(&http_request)
|
||||||
|
|
||||||
|
marshalled_response := http.marshall_response(
|
||||||
|
&http_response,
|
||||||
|
context.temp_allocator,
|
||||||
|
)
|
||||||
|
net.send_tcp(client_socket, marshalled_response)
|
||||||
|
|
||||||
|
free_all(context.temp_allocator)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue