mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-28 14:43:28 +00:00
url: search query dynamic and encoded
This commit is contained in:
@@ -79,19 +79,42 @@ pub const Values = struct {
|
||||
pub fn deleteValue(self: *Values, k: []const u8, v: []const u8) void {
|
||||
const list = self.map.getPtr(k) orelse return;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < list.items.len) {
|
||||
if (std.mem.eql(u8, v, list.items[i])) {
|
||||
for (list.items, 0..) |vv, i| {
|
||||
if (std.mem.eql(u8, v, vv)) {
|
||||
_ = list.swapRemove(i);
|
||||
return;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn count(self: *Values) usize {
|
||||
return self.map.count();
|
||||
}
|
||||
|
||||
// the caller owned the returned string.
|
||||
pub fn encode(self: *Values, writer: anytype) !void {
|
||||
var i: usize = 0;
|
||||
var it = self.map.iterator();
|
||||
while (it.next()) |entry| {
|
||||
defer i += 1;
|
||||
if (i > 0) try writer.writeByte('&');
|
||||
|
||||
if (entry.value_ptr.items.len == 0) {
|
||||
try escape(writer, entry.key_ptr.*);
|
||||
continue;
|
||||
}
|
||||
|
||||
const start = i;
|
||||
for (entry.value_ptr.items) |v| {
|
||||
defer i += 1;
|
||||
if (start < i) try writer.writeByte('&');
|
||||
|
||||
try escape(writer, entry.key_ptr.*);
|
||||
if (v.len > 0) try writer.writeByte('=');
|
||||
try escape(writer, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn unhex(c: u8) u8 {
|
||||
@@ -137,6 +160,19 @@ test "unescape" {
|
||||
alloc.free(v);
|
||||
}
|
||||
|
||||
pub fn escape(writer: anytype, raw: []const u8) !void {
|
||||
var start: usize = 0;
|
||||
for (raw, 0..) |char, index| {
|
||||
if ('a' <= char and char <= 'z' or 'A' <= char and char <= 'Z' or '0' <= char and char <= '9') {
|
||||
continue;
|
||||
}
|
||||
|
||||
try writer.print("{s}%{X:0>2}", .{ raw[start..index], char });
|
||||
start = index + 1;
|
||||
}
|
||||
try writer.writeAll(raw[start..]);
|
||||
}
|
||||
|
||||
// Parse the given query.
|
||||
pub fn parseQuery(alloc: std.mem.Allocator, s: []const u8) !Values {
|
||||
var values = Values.init(alloc);
|
||||
@@ -213,3 +249,17 @@ test "parse query dup" {
|
||||
try std.testing.expect(std.mem.eql(u8, values.first("a"), "b"));
|
||||
try std.testing.expect(values.get("a").len == 2);
|
||||
}
|
||||
|
||||
test "encode query" {
|
||||
var values = try parseQuery(std.testing.allocator, "a=b&b=c");
|
||||
defer values.deinit();
|
||||
|
||||
try values.append("a", "~");
|
||||
|
||||
var buf: std.ArrayListUnmanaged(u8) = .{};
|
||||
defer buf.deinit(std.testing.allocator);
|
||||
|
||||
try values.encode(buf.writer(std.testing.allocator));
|
||||
|
||||
try std.testing.expect(std.mem.eql(u8, buf.items, "a=b&a=%7E&b=c"));
|
||||
}
|
||||
|
||||
@@ -56,10 +56,18 @@ pub const URL = struct {
|
||||
// the caller must free the returned string.
|
||||
// TODO return a disposable string
|
||||
// https://github.com/lightpanda-io/jsruntime-lib/issues/195
|
||||
pub fn get_href(self: URL, alloc: std.mem.Allocator) ![]const u8 {
|
||||
pub fn get_href(self: *URL, alloc: std.mem.Allocator) ![]const u8 {
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
defer buf.deinit();
|
||||
|
||||
// retrieve the query search from search_params.
|
||||
const cur = self.uri.query;
|
||||
defer self.uri.query = cur;
|
||||
var q = std.ArrayList(u8).init(alloc);
|
||||
defer q.deinit();
|
||||
try self.search_params.values.encode(q.writer());
|
||||
self.uri.query = q.items;
|
||||
|
||||
try self.uri.writeToStream(.{
|
||||
.scheme = true,
|
||||
.authentication = true,
|
||||
@@ -116,9 +124,14 @@ pub const URL = struct {
|
||||
// TODO return a disposable string
|
||||
// https://github.com/lightpanda-io/jsruntime-lib/issues/195
|
||||
pub fn get_search(self: *URL, alloc: std.mem.Allocator) ![]const u8 {
|
||||
if (self.uri.query == null) return try alloc.dupe(u8, "");
|
||||
if (self.search_params.get_size() == 0) return try alloc.dupe(u8, "");
|
||||
|
||||
return try std.mem.concat(alloc, u8, &[_][]const u8{ "?", self.uri.query.? });
|
||||
var buf: std.ArrayListUnmanaged(u8) = .{};
|
||||
defer buf.deinit(alloc);
|
||||
|
||||
try buf.append(alloc, '?');
|
||||
try self.search_params.values.encode(buf.writer(alloc));
|
||||
return buf.toOwnedSlice(alloc);
|
||||
}
|
||||
|
||||
// the caller must free the returned string.
|
||||
@@ -207,12 +220,18 @@ pub fn testExecFn(
|
||||
try checkCases(js_env, &url);
|
||||
|
||||
var qs = [_]Case{
|
||||
.{ .src = "var url = new URL('https://foo.bar/path?a=~&b=%7E')", .ex = "undefined" },
|
||||
.{ .src = "var url = new URL('https://foo.bar/path?a=~&b=%7E#fragment')", .ex = "undefined" },
|
||||
.{ .src = "url.searchParams.get('a')", .ex = "~" },
|
||||
.{ .src = "url.searchParams.get('b')", .ex = "~" },
|
||||
.{ .src = "url.searchParams.append('c', 'foo')", .ex = "undefined" },
|
||||
.{ .src = "url.searchParams.get('c')", .ex = "foo" },
|
||||
.{ .src = "url.searchParams.size", .ex = "3" },
|
||||
|
||||
// search is dynamic
|
||||
.{ .src = "url.search", .ex = "?a=%7E&b=%7E&c=foo" },
|
||||
// href is dynamic
|
||||
.{ .src = "url.href", .ex = "https://foo.bar/path?a=%7E&b=%7E&c=foo#fragment" },
|
||||
|
||||
.{ .src = "url.searchParams.delete('c', 'foo')", .ex = "undefined" },
|
||||
.{ .src = "url.searchParams.get('c')", .ex = "" },
|
||||
.{ .src = "url.searchParams.delete('a')", .ex = "undefined" },
|
||||
|
||||
Reference in New Issue
Block a user