Merge pull request #7234 from michtesar/fix/net-percent-encode

Fix percent encoding for bytes < 0x10
This commit is contained in:
Jeroen van Rijn
2026-08-06 13:54:36 +02:00
committed by GitHub
2 changed files with 35 additions and 5 deletions

View File

@@ -19,7 +19,6 @@ package net
*/
import "core:strings"
import "core:strconv"
import "core:unicode/utf8"
import "core:encoding/hex"
@@ -114,6 +113,8 @@ join_url :: proc(scheme, host, path: string, queries: map[string]string, fragmen
}
percent_encode :: proc(s: string, allocator := context.allocator) -> string {
HEX_DIGITS_UPPER := "0123456789ABCDEF" // NOTE(michtesar): RFC 3986 §2.1
b := strings.builder_make(allocator)
strings.builder_grow(&b, len(s) + 16) // NOTE(tetra): A reasonable number to allow for the number of things we need to escape.
@@ -124,10 +125,9 @@ percent_encode :: proc(s: string, allocator := context.allocator) -> string {
case:
bytes, n := utf8.encode_rune(ch)
for byte in bytes[:n] {
buf: [2]u8 = ---
t := strconv.write_int(buf[:], i64(byte), 16)
strings.write_rune(&b, '%')
strings.write_string(&b, t)
strings.write_byte(&b, '%')
strings.write_byte(&b, HEX_DIGITS_UPPER[byte >> 4])
strings.write_byte(&b, HEX_DIGITS_UPPER[byte & 0xF])
}
}
}

View File

@@ -525,6 +525,36 @@ join_url_test :: proc(t: ^testing.T) {
}
}
@test
percent_encode_test :: proc(t: ^testing.T) {
test_cases := []struct{input, expected: string} {
// Bytes < 0x10 must be zero-padded to two hex digits
{"\n", "%0A"},
{"\t", "%09"},
{"\r", "%0D"},
{"a\nb", "a%0Ab"},
{"\x00", "%00"},
// Bytes >= 0x10
{" ", "%20"},
{"😃", "%F0%9F%98%83"},
// Unreserved characters pass through unescaped
{"AZaz09-_.~", "AZaz09-_.~"},
}
for test in test_cases {
encoded := net.percent_encode(test.input)
defer delete(encoded)
testing.expectf(t, encoded == test.expected, "Expected `net.percent_encode(%q)` to return %q, got %q", test.input, test.expected, encoded)
decoded, ok := net.percent_decode(encoded)
defer delete(decoded)
testing.expectf(t, ok, "Expected `net.percent_decode(%q)` to succeed", encoded)
testing.expectf(t, decoded == test.input, "Expected percent-encoding roundtrip for %q, got %q", test.input, decoded)
}
}
@test
test_udp_echo :: proc(t: ^testing.T) {
endpoint := net.Endpoint{address=net.IP4_Address{127, 0, 0, 1}, port=0}