Initial commit
This commit is contained in:
commit
1368b7b69b
10 changed files with 588 additions and 0 deletions
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)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue