Skip to content

Commit 38a0b69

Browse files
authored
Merge pull request #949 from lightpanda-io/network_path_reference_stitch
Fix network-path reference stitching
2 parents d4d8773 + 8797549 commit 38a0b69

File tree

1 file changed

+117
-126
lines changed

1 file changed

+117
-126
lines changed

src/url.zig

Lines changed: 117 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,6 @@ pub const URL = struct {
8585
return WebApiURL.init(allocator, self.uri);
8686
}
8787

88-
const StitchOpts = struct {
89-
alloc: AllocWhen = .always,
90-
null_terminated: bool = false,
91-
92-
const AllocWhen = enum {
93-
always,
94-
if_needed,
95-
};
96-
};
97-
9888
/// Properly stitches two URL fragments together.
9989
///
10090
/// For URLs with a path, it will replace the last entry with the src.
@@ -106,25 +96,24 @@ pub const URL = struct {
10696
comptime opts: StitchOpts,
10797
) !StitchReturn(opts) {
10898
if (base.len == 0 or isComleteHTTPUrl(path)) {
109-
if (comptime opts.null_terminated) {
110-
return allocator.dupeZ(u8, path);
111-
}
112-
113-
if (opts.alloc == .always) {
114-
return allocator.dupe(u8, path);
115-
}
116-
return path;
99+
return simpleStitch(allocator, path, opts);
117100
}
118101

119102
if (path.len == 0) {
120-
if (comptime opts.null_terminated) {
121-
return allocator.dupeZ(u8, base);
122-
}
103+
return simpleStitch(allocator, base, opts);
104+
}
105+
106+
if (std.mem.startsWith(u8, path, "//")) {
107+
// network-path reference
108+
const index = std.mem.indexOfScalar(u8, base, ':') orelse {
109+
return simpleStitch(allocator, path, opts);
110+
};
123111

124-
if (opts.alloc == .always) {
125-
return allocator.dupe(u8, base);
112+
const protocol = base[0..index];
113+
if (comptime opts.null_terminated) {
114+
return std.fmt.allocPrintZ(allocator, "{s}:{s}", .{ protocol, path });
126115
}
127-
return base;
116+
return std.fmt.allocPrint(allocator, "{s}:{s}", .{ protocol, path });
128117
}
129118

130119
// Quick hack because domains have to be at least 3 characters.
@@ -191,10 +180,6 @@ pub const URL = struct {
191180
return out[0..out_i];
192181
}
193182

194-
fn StitchReturn(comptime opts: StitchOpts) type {
195-
return if (opts.null_terminated) [:0]const u8 else []const u8;
196-
}
197-
198183
pub fn concatQueryString(arena: Allocator, url: []const u8, query_string: []const u8) ![]const u8 {
199184
std.debug.assert(url.len != 0);
200185

@@ -221,11 +206,33 @@ pub const URL = struct {
221206
}
222207
};
223208

224-
fn isComleteHTTPUrl(url: []const u8) bool {
225-
if (std.mem.startsWith(u8, url, "://")) {
226-
return true;
209+
const StitchOpts = struct {
210+
alloc: AllocWhen = .always,
211+
null_terminated: bool = false,
212+
213+
const AllocWhen = enum {
214+
always,
215+
if_needed,
216+
};
217+
};
218+
219+
fn StitchReturn(comptime opts: StitchOpts) type {
220+
return if (opts.null_terminated) [:0]const u8 else []const u8;
221+
}
222+
223+
fn simpleStitch(allocator: Allocator, url: []const u8, comptime opts: StitchOpts) !StitchReturn(opts) {
224+
if (comptime opts.null_terminated) {
225+
return allocator.dupeZ(u8, url);
227226
}
228227

228+
if (comptime opts.alloc == .always) {
229+
return allocator.dupe(u8, url);
230+
}
231+
232+
return url;
233+
}
234+
235+
fn isComleteHTTPUrl(url: []const u8) bool {
229236
if (url.len < 8) {
230237
return false;
231238
}
@@ -243,8 +250,6 @@ fn isComleteHTTPUrl(url: []const u8) bool {
243250

244251
const testing = @import("testing.zig");
245252
test "URL: isComleteHTTPUrl" {
246-
try testing.expectEqual(true, isComleteHTTPUrl("://lightpanda.io"));
247-
try testing.expectEqual(true, isComleteHTTPUrl("://lightpanda.io/about"));
248253
try testing.expectEqual(true, isComleteHTTPUrl("http://lightpanda.io/about"));
249254
try testing.expectEqual(true, isComleteHTTPUrl("HttP://lightpanda.io/about"));
250255
try testing.expectEqual(true, isComleteHTTPUrl("httpS://lightpanda.io/about"));
@@ -253,6 +258,8 @@ test "URL: isComleteHTTPUrl" {
253258
try testing.expectEqual(false, isComleteHTTPUrl("/lightpanda.io"));
254259
try testing.expectEqual(false, isComleteHTTPUrl("../../about"));
255260
try testing.expectEqual(false, isComleteHTTPUrl("about"));
261+
try testing.expectEqual(false, isComleteHTTPUrl("//lightpanda.io"));
262+
try testing.expectEqual(false, isComleteHTTPUrl("//lightpanda.io/about"));
256263
}
257264

258265
test "URL: resolve size" {
@@ -280,99 +287,83 @@ test "URL: stitch" {
280287
expected: []const u8,
281288
};
282289

283-
const cases = [_]Case{
284-
.{
285-
.base = "https://lightpanda.io/xyz/abc/123",
286-
.path = "something.js",
287-
.expected = "https://lightpanda.io/xyz/abc/something.js",
288-
},
289-
.{
290-
.base = "https://lightpanda.io/xyz/abc/123",
291-
.path = "/something.js",
292-
.expected = "https://lightpanda.io/something.js",
293-
},
294-
.{
295-
.base = "https://lightpanda.io/",
296-
.path = "something.js",
297-
.expected = "https://lightpanda.io/something.js",
298-
},
299-
.{
300-
.base = "https://lightpanda.io/",
301-
.path = "/something.js",
302-
.expected = "https://lightpanda.io/something.js",
303-
},
304-
.{
305-
.base = "https://lightpanda.io",
306-
.path = "something.js",
307-
.expected = "https://lightpanda.io/something.js",
308-
},
309-
.{
310-
.base = "https://lightpanda.io",
311-
.path = "abc/something.js",
312-
.expected = "https://lightpanda.io/abc/something.js",
313-
},
314-
.{
315-
.base = "https://lightpanda.io/nested",
316-
.path = "abc/something.js",
317-
.expected = "https://lightpanda.io/abc/something.js",
318-
},
319-
.{
320-
.base = "https://lightpanda.io/nested/",
321-
.path = "abc/something.js",
322-
.expected = "https://lightpanda.io/nested/abc/something.js",
323-
},
324-
.{
325-
.base = "https://lightpanda.io/nested/",
326-
.path = "/abc/something.js",
327-
.expected = "https://lightpanda.io/abc/something.js",
328-
},
329-
.{
330-
.base = "https://lightpanda.io/nested/",
331-
.path = "http://www.github.com/lightpanda-io/",
332-
.expected = "http://www.github.com/lightpanda-io/",
333-
},
334-
.{
335-
.base = "https://lightpanda.io/nested/",
336-
.path = "",
337-
.expected = "https://lightpanda.io/nested/",
338-
},
339-
.{
340-
.base = "https://lightpanda.io/abc/aaa",
341-
.path = "./hello/./world",
342-
.expected = "https://lightpanda.io/abc/hello/world",
343-
},
344-
.{
345-
.base = "https://lightpanda.io/abc/aaa/",
346-
.path = "../hello",
347-
.expected = "https://lightpanda.io/abc/hello",
348-
},
349-
.{
350-
.base = "https://lightpanda.io/abc/aaa",
351-
.path = "../hello",
352-
.expected = "https://lightpanda.io/hello",
353-
},
354-
.{
355-
.base = "https://lightpanda.io/abc/aaa/",
356-
.path = "./.././.././hello",
357-
.expected = "https://lightpanda.io/hello",
358-
},
359-
.{
360-
.base = "some/page",
361-
.path = "hello",
362-
.expected = "some/hello",
363-
},
364-
.{
365-
.base = "some/page/",
366-
.path = "hello",
367-
.expected = "some/page/hello",
368-
},
369-
370-
.{
371-
.base = "some/page/other",
372-
.path = ".././hello",
373-
.expected = "some/hello",
374-
},
375-
};
290+
const cases = [_]Case{ .{
291+
.base = "https://lightpanda.io/xyz/abc/123",
292+
.path = "something.js",
293+
.expected = "https://lightpanda.io/xyz/abc/something.js",
294+
}, .{
295+
.base = "https://lightpanda.io/xyz/abc/123",
296+
.path = "/something.js",
297+
.expected = "https://lightpanda.io/something.js",
298+
}, .{
299+
.base = "https://lightpanda.io/",
300+
.path = "something.js",
301+
.expected = "https://lightpanda.io/something.js",
302+
}, .{
303+
.base = "https://lightpanda.io/",
304+
.path = "/something.js",
305+
.expected = "https://lightpanda.io/something.js",
306+
}, .{
307+
.base = "https://lightpanda.io",
308+
.path = "something.js",
309+
.expected = "https://lightpanda.io/something.js",
310+
}, .{
311+
.base = "https://lightpanda.io",
312+
.path = "abc/something.js",
313+
.expected = "https://lightpanda.io/abc/something.js",
314+
}, .{
315+
.base = "https://lightpanda.io/nested",
316+
.path = "abc/something.js",
317+
.expected = "https://lightpanda.io/abc/something.js",
318+
}, .{
319+
.base = "https://lightpanda.io/nested/",
320+
.path = "abc/something.js",
321+
.expected = "https://lightpanda.io/nested/abc/something.js",
322+
}, .{
323+
.base = "https://lightpanda.io/nested/",
324+
.path = "/abc/something.js",
325+
.expected = "https://lightpanda.io/abc/something.js",
326+
}, .{
327+
.base = "https://lightpanda.io/nested/",
328+
.path = "http://www.github.com/lightpanda-io/",
329+
.expected = "http://www.github.com/lightpanda-io/",
330+
}, .{
331+
.base = "https://lightpanda.io/nested/",
332+
.path = "",
333+
.expected = "https://lightpanda.io/nested/",
334+
}, .{
335+
.base = "https://lightpanda.io/abc/aaa",
336+
.path = "./hello/./world",
337+
.expected = "https://lightpanda.io/abc/hello/world",
338+
}, .{
339+
.base = "https://lightpanda.io/abc/aaa/",
340+
.path = "../hello",
341+
.expected = "https://lightpanda.io/abc/hello",
342+
}, .{
343+
.base = "https://lightpanda.io/abc/aaa",
344+
.path = "../hello",
345+
.expected = "https://lightpanda.io/hello",
346+
}, .{
347+
.base = "https://lightpanda.io/abc/aaa/",
348+
.path = "./.././.././hello",
349+
.expected = "https://lightpanda.io/hello",
350+
}, .{
351+
.base = "some/page",
352+
.path = "hello",
353+
.expected = "some/hello",
354+
}, .{
355+
.base = "some/page/",
356+
.path = "hello",
357+
.expected = "some/page/hello",
358+
}, .{
359+
.base = "some/page/other",
360+
.path = ".././hello",
361+
.expected = "some/hello",
362+
}, .{
363+
.path = "//static.lightpanda.io/hello.js",
364+
.base = "https://lightpanda.io/about/",
365+
.expected = "https://static.lightpanda.io/hello.js",
366+
} };
376367

377368
for (cases) |case| {
378369
const result = try stitch(testing.arena_allocator, case.path, case.base, .{});

0 commit comments

Comments
 (0)