updated http library to return error and response structs; updated how the GUI works to have a 'wait' UI; updated Makefile to ensure API is started before starting GUI
This commit is contained in:
@@ -7,8 +7,8 @@ import "core:strings"
|
||||
import "core:time"
|
||||
|
||||
DEFAULT_HTTP_PORT :: 80
|
||||
MAX_REDIRECTS :: 5
|
||||
HTTP_TIMEOUT :: 5 * time.Second
|
||||
MAX_REDIRECTS :: 5
|
||||
HTTP_TIMEOUT :: 5 * time.Second
|
||||
|
||||
Response :: struct {
|
||||
status: int,
|
||||
@@ -18,42 +18,79 @@ Response :: struct {
|
||||
chunked: bool,
|
||||
}
|
||||
|
||||
http_get :: proc(url: string) -> (data: string, ok: bool) {
|
||||
Error :: struct {
|
||||
code: int,
|
||||
message: string,
|
||||
}
|
||||
|
||||
Error_Code :: enum int {
|
||||
Invalid_Url,
|
||||
Dial_Failed,
|
||||
Send_Failed,
|
||||
Invalid_Response,
|
||||
Invalid_Redirect,
|
||||
Invalid_Chunked,
|
||||
Too_Many_Redirects,
|
||||
}
|
||||
|
||||
error_make :: proc(code: Error_Code) -> ^Error {
|
||||
err := new(Error)
|
||||
err.code = int(code)
|
||||
|
||||
switch code {
|
||||
case .Invalid_Url:
|
||||
err.message = "invalid url"
|
||||
case .Dial_Failed:
|
||||
err.message = "could not connect to host"
|
||||
case .Send_Failed:
|
||||
err.message = "could not send request"
|
||||
case .Invalid_Response:
|
||||
err.message = "could not parse response"
|
||||
case .Invalid_Redirect:
|
||||
err.message = "invalid redirect"
|
||||
case .Invalid_Chunked:
|
||||
err.message = "invalid chunked response"
|
||||
case .Too_Many_Redirects:
|
||||
err.message = "too many redirects"
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
http_get :: proc(url: string) -> (resp: Response, err: ^Error) {
|
||||
current := url
|
||||
owns := false
|
||||
|
||||
for _ in 0 ..= MAX_REDIRECTS {
|
||||
next_url, body, _, request_ok := perform_request(current)
|
||||
next_url, next_resp, request_err := perform_request(current)
|
||||
if owns do delete(current)
|
||||
|
||||
if !request_ok {
|
||||
if next_url != "" do delete(next_url)
|
||||
if body != "" do delete(body)
|
||||
return "", false
|
||||
if request_err != nil {
|
||||
return {}, request_err
|
||||
}
|
||||
|
||||
if next_url != "" {
|
||||
if body != "" do delete(body)
|
||||
current = next_url
|
||||
owns = true
|
||||
continue
|
||||
}
|
||||
|
||||
return body, true
|
||||
return next_resp, nil
|
||||
}
|
||||
|
||||
return "", false
|
||||
if owns do delete(current)
|
||||
return {}, error_make(.Too_Many_Redirects)
|
||||
}
|
||||
|
||||
perform_request :: proc(url: string) -> (next_url: string, body: string, status: int, ok: bool) {
|
||||
perform_request :: proc(url: string) -> (next_url: string, resp: Response, err: ^Error) {
|
||||
host, port, path, parse_ok := parse_http_url(url)
|
||||
if !parse_ok {
|
||||
return "", "", 0, false
|
||||
return "", {}, error_make(.Invalid_Url)
|
||||
}
|
||||
|
||||
conn, dial_err := net.dial_tcp_from_hostname_with_port_override(host, port)
|
||||
if dial_err != nil {
|
||||
return "", "", 0, false
|
||||
return "", {}, error_make(.Dial_Failed)
|
||||
}
|
||||
defer net.close(conn)
|
||||
|
||||
@@ -75,7 +112,7 @@ perform_request :: proc(url: string) -> (next_url: string, body: string, status:
|
||||
)
|
||||
|
||||
if _, send_err := net.send_tcp(conn, transmute([]u8)request); send_err != nil {
|
||||
return "", "", 0, false
|
||||
return "", {}, error_make(.Send_Failed)
|
||||
}
|
||||
|
||||
buf: [dynamic]u8
|
||||
@@ -87,34 +124,35 @@ perform_request :: proc(url: string) -> (next_url: string, body: string, status:
|
||||
if recv_err != nil || n == 0 do break
|
||||
}
|
||||
|
||||
resp, parsed := parse_response(buf[:])
|
||||
if !parsed {
|
||||
return "", "", 0, false
|
||||
parsed, response_ok := parse_response(buf[:])
|
||||
if !response_ok {
|
||||
return "", {}, error_make(.Invalid_Response)
|
||||
}
|
||||
resp = parsed
|
||||
|
||||
if resp.status >= 300 && resp.status < 400 && resp.location != "" {
|
||||
resolved := resolve_redirect(url, resp.location)
|
||||
if resolved == "" {
|
||||
return "", "", resp.status, false
|
||||
return "", {}, error_make(.Invalid_Redirect)
|
||||
}
|
||||
return resolved, "", resp.status, true
|
||||
resp.body = ""
|
||||
return resolved, resp, nil
|
||||
}
|
||||
|
||||
if resp.chunked {
|
||||
decoded, decode_ok := decode_chunked(resp.body)
|
||||
if !decode_ok {
|
||||
return "", "", resp.status, false
|
||||
return "", {}, error_make(.Invalid_Chunked)
|
||||
}
|
||||
return "", decoded, resp.status, true
|
||||
resp.body = decoded
|
||||
return "", resp, nil
|
||||
}
|
||||
|
||||
if resp.content_length >= 0 && resp.content_length < len(resp.body) {
|
||||
resp.body = resp.body[:resp.content_length]
|
||||
}
|
||||
if resp.body == "" {
|
||||
return "", "", resp.status, true
|
||||
}
|
||||
return "", strings.clone(resp.body), resp.status, true
|
||||
resp.body = strings.clone(resp.body)
|
||||
return "", resp, nil
|
||||
}
|
||||
|
||||
parse_response :: proc(raw: []byte) -> (resp: Response, ok: bool) {
|
||||
@@ -255,7 +293,7 @@ decode_chunked :: proc(data: string) -> (body: string, ok: bool) {
|
||||
if len(decoded) == 0 {
|
||||
return "", true
|
||||
}
|
||||
return string(decoded[:]), true
|
||||
return strings.clone(string(decoded[:])), true
|
||||
}
|
||||
|
||||
fold_eq :: proc(a, b: string) -> bool {
|
||||
|
||||
Reference in New Issue
Block a user