Skip to content

Commit a9842fd

Browse files
committed
url: decode query
1 parent f704015 commit a9842fd

File tree

1 file changed

+58
-2
lines changed

1 file changed

+58
-2
lines changed

src/url/query.zig

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,17 @@ pub const Values = struct {
4141
try self.map.put(self.alloc, kk, list);
4242
}
4343

44+
// append by taking the ownership of the key and the value
45+
fn appendOwned(self: *Values, k: []const u8, v: []const u8) !void {
46+
if (self.map.getPtr(k)) |list| {
47+
return try list.append(self.alloc, v);
48+
}
49+
50+
var list = List{};
51+
try list.append(self.alloc, v);
52+
try self.map.put(self.alloc, k, list);
53+
}
54+
4455
pub fn get(self: *Values, k: []const u8) [][]const u8 {
4556
if (self.map.get(k)) |list| {
4657
return list.items;
@@ -83,6 +94,49 @@ pub const Values = struct {
8394
}
8495
};
8596

97+
fn unhex(c: u8) u8 {
98+
if ('0' <= c and c <= '9') return c - '0';
99+
if ('a' <= c and c <= 'f') return c - 'a' + 10;
100+
if ('A' <= c and c <= 'F') return c - 'A' + 10;
101+
return 0;
102+
}
103+
104+
// unescape decodes a percent encoded string.
105+
// The caller owned the returned string.
106+
pub fn unescape(alloc: std.mem.Allocator, s: []const u8) ![]const u8 {
107+
var buf: std.ArrayListUnmanaged(u8) = .{};
108+
defer buf.deinit(alloc);
109+
110+
var i: usize = 0;
111+
while (i < s.len) {
112+
defer i += 1;
113+
114+
switch (s[i]) {
115+
'%' => {
116+
if (i + 2 > s.len) return error.EscapeError;
117+
if (!std.ascii.isHex(s[i + 1])) return error.EscapeError;
118+
if (!std.ascii.isHex(s[i + 2])) return error.EscapeError;
119+
120+
try buf.append(alloc, unhex(s[i + 1]) << 4 | unhex(s[i + 2]));
121+
i += 2;
122+
},
123+
'+' => try buf.append(alloc, ' '), // TODO should we decode or keep as it?
124+
else => try buf.append(alloc, s[i]),
125+
}
126+
}
127+
128+
return try buf.toOwnedSlice(alloc);
129+
}
130+
131+
test "unescape" {
132+
var v: []const u8 = undefined;
133+
const alloc = std.testing.allocator;
134+
135+
v = try unescape(alloc, "%7E");
136+
try std.testing.expect(std.mem.eql(u8, "~", v));
137+
alloc.free(v);
138+
}
139+
86140
// Parse the given query.
87141
pub fn parseQuery(alloc: std.mem.Allocator, s: []const u8) !Values {
88142
var values = Values.init(alloc);
@@ -103,9 +157,11 @@ pub fn parseQuery(alloc: std.mem.Allocator, s: []const u8) !Values {
103157
_ = rr.skip();
104158
const v = rr.tail();
105159

106-
// TODO decode k and v
160+
// decode k and v
161+
const kk = try unescape(alloc, k);
162+
const vv = try unescape(alloc, v);
107163

108-
try values.append(k, v);
164+
try values.appendOwned(kk, vv);
109165

110166
if (!r.skip()) break;
111167
}

0 commit comments

Comments
 (0)