Skip to content

Commit 7c095ca

Browse files
committed
wip
1 parent 046d2db commit 7c095ca

File tree

4 files changed

+95
-43
lines changed

4 files changed

+95
-43
lines changed

client/client.odin

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ Connection_State :: enum {
6666
Failed,
6767
}
6868

69+
// TODO: should response `Set-Cookie` headers automatically be stored on the connection and passed
70+
// along next requests?
71+
6972
Connection :: struct {
7073
allocator: mem.Allocator,
7174

@@ -106,9 +109,6 @@ Response :: struct {
106109
status: http.Status,
107110
cookies: [dynamic]http.Cookie,
108111
body: http.Body,
109-
110-
// Below is for internal use only:
111-
112112
using _: http.Has_Body,
113113
}
114114

@@ -181,8 +181,8 @@ connection_init :: proc(conn: ^Connection, client: ^Client, target: string, sche
181181
return true
182182
}
183183

184-
connection_make :: proc(client: ^Client, host: string, scheme := Scheme.From_Target, allocator := context.allocator) -> (conn: Connection) {
185-
connection_init(&conn, client, host, scheme, allocator)
184+
connection_make :: proc(client: ^Client, target: string, scheme := Scheme.From_Target, allocator := context.allocator) -> (conn: Connection) {
185+
connection_init(&conn, client, target, scheme, allocator)
186186
return
187187
}
188188

@@ -395,9 +395,10 @@ callback_and_process_next :: proc(r: ^Request, err: net.Network_Error) {
395395

396396
log.infof("request done: %v %v", r.res.status, err)
397397

398+
r.on_response(r, r.user_data, err)
399+
398400
r.conn.request = r.next
399401
connection_process(r.conn)
400-
r.on_response(r, r.user_data, err)
401402
}
402403

403404
@(private="file")
@@ -719,7 +720,7 @@ prepare_request :: proc(r: ^Request) {
719720
// Escape newlines in headers, if we don't, an attacker can find an endpoint
720721
// that returns a header with user input, and inject headers into the response.
721722
esc_value, was_allocation := strings.replace_all(value, "\n", "\\n", r.allocator)
722-
defer if was_allocation do delete(esc_value)
723+
defer if was_allocation do delete(esc_value, r.allocator)
723724

724725
ws(&r.buf, esc_value)
725726
ws(&r.buf, "\r\n")

client/dns/dns.odin

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ Client :: struct {
2323
io: ^nbio.IO,
2424

2525
// Hosts/Name servers configuration.
26-
using config: net.DNS_Configuration,
26+
name_servers: []net.Endpoint,
2727
hosts: []net.DNS_Host_Entry,
28+
// init_cb: proc(^Client, rawptr),
29+
// init_ud: rawptr,
30+
// init_state: int,
2831

2932
// Cache.
3033
cache: map[string]Cache_Entry,
@@ -50,7 +53,7 @@ Callback :: struct {
5053
ud: rawptr,
5154
}
5255

53-
// TODO: provide a callback, OR allow `resolve` before these things are done with some sort of request queue.
56+
// TODO: callback.
5457
init :: proc(c: ^Client, allocator := context.allocator) {
5558
c.allocator = allocator
5659
c.cache.allocator = allocator
@@ -59,6 +62,33 @@ init :: proc(c: ^Client, allocator := context.allocator) {
5962
load_hosts(c)
6063
}
6164

65+
// Waits until all requests are done and frees all related resources.
66+
destroy :: proc {
67+
destroy_cb,
68+
destroy_no_cb,
69+
}
70+
71+
destroy_no_cb :: proc(c: ^Client) {
72+
destroy_cb(c, nil, proc(_: rawptr) {})
73+
}
74+
75+
destroy_cb :: proc(c: ^Client, user: rawptr, cb: proc(user: rawptr)) {
76+
cache_clear(c)
77+
78+
// Try to clear again next tick, we don't want to interrupt in progress requests.
79+
if len(c.cache) > 0 {
80+
nbio.next_tick(c.io, c, user, cb, destroy_cb)
81+
} else {
82+
delete(c.cache)
83+
delete(c.name_servers, c.allocator)
84+
for h in c.hosts {
85+
delete(h.name, c.allocator)
86+
}
87+
delete(c.hosts, c.allocator)
88+
cb(user)
89+
}
90+
}
91+
6292
// Removes any cache entries that aren't currently being resolved.
6393
cache_clear :: proc(c: ^Client) {
6494
for hostname, entry in c.cache {
@@ -358,14 +388,15 @@ resolve :: proc(c: ^Client, hostname: string, user: rawptr, cb: On_Resolve) {
358388
// Loads the name servers from the OS, this is called implicitly during `init`.
359389
@(private)
360390
load_name_servers :: proc(c: ^Client) {
361-
if c.resolv_conf == "" {
362-
log.error("empty resolv_conf file path")
391+
resolv_conf := net.DEFAULT_DNS_CONFIGURATION.resolv_conf
392+
if resolv_conf == "" {
393+
log.error("empty resolv_conf file path") // TODO: this is not an error on Windows.
363394
return
364395
}
365396

366-
fd, err := nbio.open(c.io, c.resolv_conf)
397+
fd, err := nbio.open(c.io, resolv_conf)
367398
if err != os.ERROR_NONE {
368-
log.errorf("error opening %q: %v", c.resolv_conf, err)
399+
log.errorf("error opening %q: %v", resolv_conf, err)
369400
return
370401
}
371402

@@ -388,14 +419,15 @@ load_name_servers :: proc(c: ^Client) {
388419
// Loads the hosts file from the OS, this is implicitly called during `init`.
389420
@(private)
390421
load_hosts :: proc(c: ^Client) {
391-
if c.hosts_file == "" {
422+
hosts_file := net.DEFAULT_DNS_CONFIGURATION.hosts_file
423+
if hosts_file == "" {
392424
log.error("empty hosts file")
393425
return
394426
}
395427

396-
fd, err := nbio.open(c.io, c.hosts_file)
428+
fd, err := nbio.open(c.io, hosts_file)
397429
if err != os.ERROR_NONE {
398-
log.errorf("error opening %q: %v", c.hosts_file, err)
430+
log.errorf("error opening %q: %v", hosts_file, err)
399431
return
400432
}
401433

client/test_client.odin

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,35 +19,53 @@ test_client :: proc(t: ^testing.T) {
1919
nbio.init(&io)
2020
defer nbio.destroy(&io)
2121

22-
client: dns.Client
23-
client.io = &io
24-
client.config = net.dns_configuration // TODO: make default if not set.
25-
dns.init(&client)
22+
// client: dns.Client
23+
// client.io = &io
24+
// client.config = net.dns_configuration // TODO: make default if not set.
25+
// dns.init(&client)
26+
//
27+
// nbio.timeout(client.io, time.Second, &client, proc(client: ^dns.Client, _: Maybe(time.Time)) {
28+
// log.info("Resolving")
29+
//
30+
// dns.resolve(client, "github.com", nil, proc(_: rawptr, recs: dns.Record, err: net.Network_Error) {
31+
// log.info("github.com", recs, err)
32+
// })
33+
// dns.resolve(client, "laytanlaats.com", nil, proc(_: rawptr, recs: dns.Record, err: net.Network_Error) {
34+
// log.info("laytanlaats.com", recs, err)
35+
// })
36+
// dns.resolve(client, "laytan.dev", nil, proc(_: rawptr, recs: dns.Record, err: net.Network_Error) {
37+
// log.info("laytan.dev", recs, err)
38+
// })
39+
// dns.resolve(client, "laytan.dev", nil, proc(_: rawptr, recs: dns.Record, err: net.Network_Error) {
40+
// log.info("laytan.dev", recs, err)
41+
// })
42+
// dns.resolve(client, "odin-http.laytan.dev", nil, proc(_: rawptr, recs: dns.Record, err: net.Network_Error) {
43+
// log.info("odin-http.laytan.dev", recs, err)
44+
// })
45+
// dns.resolve(client, "odin-http.laytan.dev", nil, proc(_: rawptr, recs: dns.Record, err: net.Network_Error) {
46+
// log.info("odin-http.laytan.dev", recs, err)
47+
// })
48+
// dns.resolve(client, "github.com", nil, proc(_: rawptr, recs: dns.Record, err: net.Network_Error) {
49+
// log.info("github.com", recs, err)
50+
// })
51+
// })
52+
53+
client := client_make(&io)
54+
conn := connection_make(&client, "https://github.com")
2655

27-
nbio.timeout(client.io, time.Second, &client, proc(client: ^dns.Client, _: Maybe(time.Time)) {
28-
log.info("Resolving")
56+
req := request_make(&conn, "/laytan")
57+
request(&req, nil, proc(r: ^Request, _: rawptr, err: net.Network_Error) {
58+
log.infof("%v %v %#v %#v", r.res.status, err, r.res.headers._kv, r.res.cookies)
59+
})
60+
61+
req = request_make(&conn, "/laytan/odin-http")
62+
request(&req, nil, proc(r: ^Request, _: rawptr, err: net.Network_Error) {
63+
log.infof("%v %v %#v %#v", r.res.status, err, r.res.headers._kv, r.res.cookies)
64+
})
2965

30-
dns.resolve(client, "github.com", nil, proc(_: rawptr, recs: dns.Record, err: net.Network_Error) {
31-
log.info("github.com", recs, err)
32-
})
33-
dns.resolve(client, "laytanlaats.com", nil, proc(_: rawptr, recs: dns.Record, err: net.Network_Error) {
34-
log.info("laytanlaats.com", recs, err)
35-
})
36-
dns.resolve(client, "laytan.dev", nil, proc(_: rawptr, recs: dns.Record, err: net.Network_Error) {
37-
log.info("laytan.dev", recs, err)
38-
})
39-
dns.resolve(client, "laytan.dev", nil, proc(_: rawptr, recs: dns.Record, err: net.Network_Error) {
40-
log.info("laytan.dev", recs, err)
41-
})
42-
dns.resolve(client, "odin-http.laytan.dev", nil, proc(_: rawptr, recs: dns.Record, err: net.Network_Error) {
43-
log.info("odin-http.laytan.dev", recs, err)
44-
})
45-
dns.resolve(client, "odin-http.laytan.dev", nil, proc(_: rawptr, recs: dns.Record, err: net.Network_Error) {
46-
log.info("odin-http.laytan.dev", recs, err)
47-
})
48-
dns.resolve(client, "github.com", nil, proc(_: rawptr, recs: dns.Record, err: net.Network_Error) {
49-
log.info("github.com", recs, err)
50-
})
66+
req = request_make(&conn, "/laytan/Odin")
67+
request(&req, nil, proc(r: ^Request, _: rawptr, err: net.Network_Error) {
68+
log.infof("%v %v %#v %#v", r.res.status, err, r.res.headers._kv, r.res.cookies)
5169
})
5270

5371
// log.info(net.resolve("github.com"))

responses.odin

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ If any other error occurs, a 500 is sent and the error is logged.
4242
*/
4343
respond_file :: proc(r: ^Response, path: string, content_type: Maybe(Mime_Type) = nil, loc := #caller_location) {
4444
// PERF: we are still putting the content into the body buffer, we could stream it.
45+
// PERF: could even use `splice`/`sendfile` on Linux https://tinselcity.github.io/Sendfile-W-Iouring/
4546

4647
assert_has_td(loc)
4748
assert(!r.sent, "response has already been sent", loc)

0 commit comments

Comments
 (0)