working on fixing dialing issue with http library
CI / Detect changed paths (pull_request) Successful in 4s
CI / Odin unit tests and build (pull_request) Successful in 1m16s
CI / API unit tests and lint (pull_request) Failing after 1m12s
CI / Infra unit tests, vet, and preview (pull_request) Successful in 51s

This commit is contained in:
2026-09-13 13:26:27 -06:00
parent de842fdc2d
commit c6bfb966b7
5 changed files with 172 additions and 57 deletions
+43 -27
View File
@@ -1,12 +1,14 @@
package http
import "core:fmt"
import "core:log"
import "core:net"
import "core:strconv"
import "core:strings"
import "core:time"
DEFAULT_HTTP_PORT :: 80
DEFAULT_HTTPS_PORT :: 443
MAX_REDIRECTS :: 5
HTTP_TIMEOUT :: 5 * time.Second
@@ -33,27 +35,31 @@ Error_Code :: enum int {
Too_Many_Redirects,
}
error_make :: proc(code: Error_Code) -> ^Error {
error_make :: proc(code: Error_Code, msg: string = "") -> ^Error {
err := new(Error)
err.code = int(code)
err.message = msg
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"
if msg == "" {
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
}
@@ -83,12 +89,12 @@ http_get :: proc(url: string) -> (resp: Response, err: ^Error) {
}
perform_request :: proc(url: string) -> (next_url: string, resp: Response, err: ^Error) {
host, port, path, parse_ok := parse_http_url(url)
_, host, port, path, parse_ok := parse_http_url(url)
if !parse_ok {
return "", {}, error_make(.Invalid_Url)
}
conn, dial_err := net.dial_tcp_from_hostname_with_port_override(host, port)
conn, dial_err := net.dial_tcp_from_host_or_endpoint(net.Host{host, port})
if dial_err != nil {
return "", {}, error_make(.Dial_Failed)
}
@@ -214,12 +220,21 @@ parse_status_code :: proc(status_line: string) -> int {
return code if ok else 0
}
parse_http_url :: proc(url: string) -> (host: string, port: int, path: string, ok: bool) {
if strings.has_prefix(url, "https://") || !strings.has_prefix(url, "http://") {
return "", 0, "", false
parse_http_url :: proc(
url: string,
) -> (
scheme: string,
host: string,
port: int,
path: string,
ok: bool,
) {
if !strings.has_prefix(url, "https://") && !strings.has_prefix(url, "http://") {
return "", "", 0, "", false
}
rest := url[len("http://"):]
https := strings.has_prefix(url, "https://")
rest := url[https ? len("https://") : len("http://"):]
host_and_port := rest
if slash := strings.index_byte(rest, '/'); slash >= 0 {
host_and_port = rest[:slash]
@@ -229,20 +244,20 @@ parse_http_url :: proc(url: string) -> (host: string, port: int, path: string, o
}
host = host_and_port
port = DEFAULT_HTTP_PORT
port = https ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT
if colon := strings.last_index_byte(host_and_port, ':'); colon >= 0 {
parsed, p_ok := strconv.parse_int(host_and_port[colon + 1:])
if !p_ok {
return "", 0, "", false
return "", "", 0, "", false
}
host = host_and_port[:colon]
port = parsed
}
if host == "" {
return "", 0, "", false
return "", "", 0, "", false
}
return host, port, path, true
return https ? "https" : "http", host, port, path, true
}
resolve_redirect :: proc(base_url, location: string) -> string {
@@ -308,4 +323,5 @@ fold_eq :: proc(a, b: string) -> bool {
if ca != cb do return false
}
return true
}
}
+77 -4
View File
@@ -9,7 +9,8 @@ import "core:thread"
import http "../src"
SERVER_BODY :: "hello world"
SERVER_RESPONSE := "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n" +
SERVER_RESPONSE :=
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n" +
"Content-Length: 11\r\n" +
"Connection: close\r\n\r\n" +
SERVER_BODY
@@ -57,7 +58,9 @@ test_http_get_request :: proc(t: ^testing.T) {
return
}
args: Serve_Args = {listener = listener}
args: Serve_Args = {
listener = listener,
}
server_thread := thread.create_and_start_with_data(&args, serve_proc)
defer thread.destroy(server_thread)
@@ -66,11 +69,81 @@ test_http_get_request :: proc(t: ^testing.T) {
log.infof("requesting %s", url)
resp, req_err := http.http_get(url)
if !testing.expect(t, req_err == nil, "http_get returned an error") {
if !testing.expectf(t, req_err == nil, "http_get returned an error: %w", req_err) {
return
}
defer delete(resp.body)
testing.expectf(t, resp.status == 200, "expected status 200, got %d", resp.status)
testing.expectf(t, resp.body == SERVER_BODY, "expected %q, got %q", SERVER_BODY, resp.body)
}
}
@(test)
test_http_parse_url :: proc(t: ^testing.T) {
testURL :: struct {
url: string,
expectedScheme: string,
expectedOK: bool,
expectedHost: string,
expectedPort: int,
expectedPath: string,
}
testURLs := []testURL {
{"http://localhost:8080", "http", true, "localhost", 8080, "/"},
{"http://localhost", "http", true, "localhost", 80, "/"},
{"https://localhost:443", "https", true, "localhost", 443, "/"},
{"https://localhost", "https", true, "localhost", 443, "/"},
{"http://test:6969", "http", true, "test", 6969, "/"},
{
"http://localhost:3000/api/v1/catalogs",
"http",
true,
"localhost",
3000,
"/api/v1/catalogs",
},
}
for tu in testURLs {
scheme, host, port, path, ok := http.parse_http_url(tu.url)
testing.expectf(
t,
ok == tu.expectedOK,
"expected ok from parsing to be %v but got %v",
tu.expectedOK,
ok,
)
testing.expectf(
t,
host == tu.expectedHost,
"expected host url to be '%s' but got '%s'",
tu.expectedHost,
host,
)
testing.expectf(
t,
scheme == tu.expectedScheme,
"expected scheme to be '%s' but got '%s'",
tu.expectedScheme,
scheme,
)
testing.expectf(
t,
port == tu.expectedPort,
"expected port to be %d but got %d",
tu.expectedPort,
port,
)
testing.expectf(
t,
path == tu.expectedPath,
"expected path to be '%s' but got '%s'",
tu.expectedPath,
path,
)
}
}