Skip to content

Commit 3f5019d

Browse files
committed
write unit tests
1 parent 6fcb025 commit 3f5019d

File tree

8 files changed

+123
-31
lines changed

8 files changed

+123
-31
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,12 @@ Once you have a Mojo project set up locally,
126126
fn main() raises:
127127
var server = Server()
128128
var handler = Welcome()
129-
server.listen_and_serve("0.0.0.0:8080", handler)
129+
server.listen_and_serve("localhost:8080", handler)
130130
```
131131
132132
Feel free to change the settings in `listen_and_serve()` to serve on a particular host and port.
133133
134-
Now send a request `0.0.0.0:8080`. You should see some details about the request printed out to the console.
134+
Now send a request `localhost:8080`. You should see some details about the request printed out to the console.
135135
136136
Congrats 🥳 You're using Lightbug!
137137

benchmark/bench_server.mojo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ def main():
66
try:
77
var server = Server(tcp_keep_alive=True)
88
var handler = TechEmpowerRouter()
9-
server.listen_and_serve("0.0.0.0:8080", handler)
9+
server.listen_and_serve("localhost:8080", handler)
1010
except e:
1111
print("Error starting server: " + e.__str__())
1212
return

lightbug.🔥

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ from lightbug_http import Welcome, Server
33
fn main() raises:
44
var server = Server()
55
var handler = Welcome()
6-
server.listen_and_serve("0.0.0.0:8080", handler)
6+
server.listen_and_serve("localhost:8080", handler)

lightbug_http/_libc.mojo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ fn inet_pton[address_family: Int32](src: UnsafePointer[c_char]) raises -> c_uint
574574
* This function is valid for `AF_INET` and `AF_INET6`.
575575
"""
576576
constrained[
577-
int(address_family) in [AF_INET, AF_INET6], "Address family must be either INET_ADDRSTRLEN or INET6_ADDRSTRLEN."
577+
int(address_family) in [AF_INET, AF_INET6], "Address family must be either AF_INET or AF_INET6."
578578
]()
579579
var ip_buffer: UnsafePointer[c_void]
580580

lightbug_http/address.mojo

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -313,11 +313,11 @@ fn is_ip_protocol(network: NetworkType) -> Bool:
313313

314314
fn is_ipv4(network: NetworkType) -> Bool:
315315
"""Check if the network type is IPv4."""
316-
return network in (NetworkType.tcp4, NetworkType.udp4)
316+
return network in (NetworkType.tcp4, NetworkType.udp4, NetworkType.ip4)
317317

318318
fn is_ipv6(network: NetworkType) -> Bool:
319319
"""Check if the network type is IPv6."""
320-
return network in (NetworkType.tcp6, NetworkType.udp6)
320+
return network in (NetworkType.tcp6, NetworkType.udp6, NetworkType.ip6)
321321

322322
fn resolve_localhost(host: String, network: NetworkType) -> String:
323323
"""Resolve localhost to the appropriate IP address based on network type."""
@@ -383,45 +383,51 @@ fn parse_address(network: NetworkType, address: String) raises -> (String, UInt1
383383
Returns:
384384
Tuple containing the host and port
385385
"""
386-
# Handle IP protocols separately
387386
if is_ip_protocol(network):
388-
if address.find(":") != -1:
389-
raise Error("IP protocol addresses should not include ports")
390-
391387
var host = resolve_localhost(address, network)
392388
if host == AddressConstants.EMPTY:
393389
raise Error("missing host")
390+
391+
# For IPv6 addresses in IP protocol mode, we need to handle the address as-is
392+
if network == NetworkType.ip6 and host.find(":") != -1:
393+
return host, DEFAULT_IP_PORT
394+
395+
# For other IP protocols, no colons allowed
396+
if host.find(":") != -1:
397+
raise Error("IP protocol addresses should not include ports")
398+
394399
return host, DEFAULT_IP_PORT
395400

396-
# Parse regular addresses
397401
var colon_index = address.rfind(":")
398402
if colon_index == -1:
399403
raise MissingPortError
400404

401405
var host: String
402-
var bracket_offset: Int
406+
var bracket_offset: Int = 0
403407

404408
# Handle IPv6 addresses
405-
try:
406-
(host, bracket_offset) = parse_ipv6_bracketed_address(address)
407-
except e:
408-
raise e
409-
410-
# Validate no unexpected brackets
411-
validate_no_brackets(address, bracket_offset)
409+
if address[0] == "[":
410+
try:
411+
(host, bracket_offset) = parse_ipv6_bracketed_address(address)
412+
except e:
413+
raise e
414+
415+
validate_no_brackets(address, bracket_offset)
416+
else:
417+
# For IPv4, simply split at the last colon
418+
host = address[:colon_index]
419+
if host.find(":") != -1:
420+
raise TooManyColonsError
412421

413-
# Parse and validate port
414422
var port = parse_port(address[colon_index + 1:])
415423

416-
# Resolve localhost if needed
417424
host = resolve_localhost(host, network)
418425
if host == AddressConstants.EMPTY:
419426
raise Error("missing host")
420427

421428
return host, port
422429

423430

424-
425431
# TODO: Support IPv6 long form.
426432
fn join_host_port(host: String, port: String) -> String:
427433
if host.find(":") != -1: # must be IPv6 literal

lightbug_http/server.mojo

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ struct Server(Movable):
9191
address: The address (host:port) to listen on.
9292
handler: An object that handles incoming HTTP requests.
9393
"""
94-
var net = ListenConfig()
95-
var listener = net.listen(address)
94+
var config = ListenConfig()
95+
var listener = config.listen(address)
9696
self.set_address(address)
9797
self.serve(listener^, handler)
9898

scripts/bench_server.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ echo "running server..."
99
sleep 2
1010

1111
echo "Running benchmark"
12-
wrk -t1 -c1 -d10s http://0.0.0.0:8080/ --header "User-Agent: wrk"
12+
wrk -t1 -c1 -d10s http://localhost:8080/ --header "User-Agent: wrk"
1313

1414
kill $!
1515
wait $! 2>/dev/null

tests/lightbug_http/test_host_port.mojo

Lines changed: 91 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,107 @@ from lightbug_http.strings import NetworkType
44

55

66
def test_split_host_port():
7-
# IPv4
8-
var hp = parse_address("127.0.0.1:8080")
7+
# TCP4
8+
var hp = parse_address(NetworkType.tcp4, "127.0.0.1:8080")
99
testing.assert_equal(hp[0], "127.0.0.1")
1010
testing.assert_equal(hp[1], 8080)
1111

12-
# IPv6
13-
hp = parse_address("[::1]:8080")
12+
# TCP4 with localhost
13+
hp = parse_address(NetworkType.tcp4, "localhost:8080")
14+
testing.assert_equal(hp[0], "127.0.0.1")
15+
testing.assert_equal(hp[1], 8080)
16+
17+
# TCP6
18+
hp = parse_address(NetworkType.tcp6, "[::1]:8080")
1419
testing.assert_equal(hp[0], "::1")
1520
testing.assert_equal(hp[1], 8080)
1621

17-
# # TODO: IPv6 long form - Not supported yet.
22+
# TCP6 with localhost
23+
hp = parse_address(NetworkType.tcp6, "localhost:8080")
24+
testing.assert_equal(hp[0], "::1")
25+
testing.assert_equal(hp[1], 8080)
26+
27+
# UDP4
28+
hp = parse_address(NetworkType.udp4, "192.168.1.1:53")
29+
testing.assert_equal(hp[0], "192.168.1.1")
30+
testing.assert_equal(hp[1], 53)
31+
32+
# UDP4 with localhost
33+
hp = parse_address(NetworkType.udp4, "localhost:53")
34+
testing.assert_equal(hp[0], "127.0.0.1")
35+
testing.assert_equal(hp[1], 53)
36+
37+
# UDP6
38+
hp = parse_address(NetworkType.udp6, "[2001:db8::1]:53")
39+
testing.assert_equal(hp[0], "2001:db8::1")
40+
testing.assert_equal(hp[1], 53)
41+
42+
# UDP6 with localhost
43+
hp = parse_address(NetworkType.udp6, "localhost:53")
44+
testing.assert_equal(hp[0], "::1")
45+
testing.assert_equal(hp[1], 53)
46+
47+
# IP4 (no port)
48+
hp = parse_address(NetworkType.ip4, "192.168.1.1")
49+
testing.assert_equal(hp[0], "192.168.1.1")
50+
testing.assert_equal(hp[1], 0)
51+
52+
# IP4 with localhost
53+
hp = parse_address(NetworkType.ip4, "localhost")
54+
testing.assert_equal(hp[0], "127.0.0.1")
55+
testing.assert_equal(hp[1], 0)
56+
57+
# IP6 (no port)
58+
hp = parse_address(NetworkType.ip6, "2001:db8::1")
59+
testing.assert_equal(hp[0], "2001:db8::1")
60+
testing.assert_equal(hp[1], 0)
61+
62+
# IP6 with localhost
63+
hp = parse_address(NetworkType.ip6, "localhost")
64+
testing.assert_equal(hp[0], "::1")
65+
testing.assert_equal(hp[1], 0)
66+
67+
# TODO: IPv6 long form - Not supported yet.
1868
# hp = parse_address("0:0:0:0:0:0:0:1:8080")
1969
# testing.assert_equal(hp[0], "0:0:0:0:0:0:0:1")
2070
# testing.assert_equal(hp[1], 8080)
2171

72+
# Error cases
73+
# IP protocol with port
74+
try:
75+
_ = parse_address(NetworkType.ip4, "192.168.1.1:80")
76+
testing.assert_false("Should have raised an error for IP protocol with port")
77+
except Error:
78+
testing.assert_true(True)
79+
80+
# Missing port
81+
try:
82+
_ = parse_address(NetworkType.tcp4, "192.168.1.1")
83+
testing.assert_false("Should have raised MissingPortError")
84+
except MissingPortError:
85+
testing.assert_true(True)
86+
87+
# Missing port
88+
try:
89+
_ = parse_address(NetworkType.tcp6, "[::1]")
90+
testing.assert_false("Should have raised MissingPortError")
91+
except MissingPortError:
92+
testing.assert_true(True)
93+
94+
# Port out of range
95+
try:
96+
_ = parse_address(NetworkType.tcp4, "192.168.1.1:70000")
97+
testing.assert_false("Should have raised error for invalid port")
98+
except Error:
99+
testing.assert_true(True)
100+
101+
# Missing closing bracket
102+
try:
103+
_ = parse_address(NetworkType.tcp6, "[::1:8080")
104+
testing.assert_false("Should have raised error for missing bracket")
105+
except Error:
106+
testing.assert_true(True)
107+
22108

23109
def test_join_host_port():
24110
# IPv4

0 commit comments

Comments
 (0)