Skip to content

Commit 23fa030

Browse files
giacomocavalierilpil
authored andcommitted
make sure empty port is parsed correctly on both targets
1 parent 4af6d60 commit 23fa030

File tree

4 files changed

+78
-11
lines changed

4 files changed

+78
-11
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
- The performance of the `string.repeat` function has been improved. It now runs
44
in loglinear time.
5+
- Fixed a bug in the `uri.parse` function where parsing a uri with an empty port
6+
would produce an invalid value.
57

68
## v0.62.1 - 2025-08-07
79

src/gleam/uri.gleam

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -363,20 +363,23 @@ fn parse_port(uri_string: String, pieces: Uri) -> Result(Uri, Nil) {
363363
":8" <> rest -> parse_port_loop(rest, pieces, 8)
364364
":9" <> rest -> parse_port_loop(rest, pieces, 9)
365365

366-
// It means the port segment is not composed of numbers, the port is invalid
367-
// and so is the uri!
368-
":" <> _ -> Error(Nil)
366+
// The port could be empty and be followed by any of the next delimiters.
367+
// Like `:#`, `:?` or `:/`
368+
":" | "" -> Ok(pieces)
369369

370370
// `?` marks the beginning of the query with question mark.
371-
"?" <> rest -> parse_query_with_question_mark(rest, pieces)
371+
"?" <> rest | ":?" <> rest -> parse_query_with_question_mark(rest, pieces)
372372

373373
// `#` marks the beginning of the fragment part.
374-
"#" <> rest -> parse_fragment(rest, pieces)
374+
"#" <> rest | ":#" <> rest -> parse_fragment(rest, pieces)
375375

376376
// `/` marks the beginning of a path.
377377
"/" <> _ -> parse_path(uri_string, pieces)
378-
379-
"" -> Ok(pieces)
378+
":" <> rest ->
379+
case rest {
380+
"/" <> _ -> parse_path(rest, pieces)
381+
_ -> Error(Nil)
382+
}
380383

381384
_ -> Error(Nil)
382385
}

src/gleam_stdlib.erl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
(X == 95)).
3434
-define(is_digit_char(X),
3535
(X > 47 andalso X < 58)).
36-
-define(is_ascii_character(X),
36+
-define(is_ascii_character(X),
3737
(erlang:is_integer(X) andalso X >= 32 andalso X =< 126)).
3838

3939
uppercase(X) -> X - 32.
@@ -229,11 +229,17 @@ uri_parse(String) ->
229229
case uri_string:parse(String) of
230230
{error, _, _} -> {error, nil};
231231
Uri ->
232+
Port =
233+
try maps:get(port, Uri) of
234+
undefined -> none;
235+
Value -> {some, Value}
236+
catch _:_ -> none
237+
end,
232238
{ok, {uri,
233239
maps_get_optional(Uri, scheme),
234240
maps_get_optional(Uri, userinfo),
235241
maps_get_optional(Uri, host),
236-
maps_get_optional(Uri, port),
242+
Port,
237243
maps_get_or(Uri, path, <<>>),
238244
maps_get_optional(Uri, query),
239245
maps_get_optional(Uri, fragment)
@@ -482,13 +488,13 @@ index([_, _, _, _, _, _, _, X | _], 7) ->
482488
index(Tuple, Index) when is_tuple(Tuple) andalso is_integer(Index) ->
483489
{ok, try
484490
{some, element(Index + 1, Tuple)}
485-
catch _:_ ->
491+
catch _:_ ->
486492
none
487493
end};
488494
index(Map, Key) when is_map(Map) ->
489495
{ok, try
490496
{some, maps:get(Key, Map)}
491-
catch _:_ ->
497+
catch _:_ ->
492498
none
493499
end};
494500
index(_, Index) when is_integer(Index) ->

test/gleam/uri_test.gleam

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,62 @@ pub fn empty_query_to_string_test() {
418418
assert query_string == ""
419419
}
420420

421+
pub fn empty_port_test() {
422+
let assert Ok(uri) = uri.parse("//:")
423+
assert uri
424+
== uri.Uri(
425+
scheme: None,
426+
userinfo: None,
427+
host: Some(""),
428+
port: None,
429+
path: "",
430+
query: None,
431+
fragment: None,
432+
)
433+
}
434+
435+
pub fn empty_port_followed_by_query_test() {
436+
let assert Ok(uri) = uri.parse("//:?")
437+
assert uri
438+
== uri.Uri(
439+
scheme: None,
440+
userinfo: None,
441+
host: Some(""),
442+
port: None,
443+
path: "",
444+
query: Some(""),
445+
fragment: None,
446+
)
447+
}
448+
449+
pub fn empty_port_followed_by_fragment_test() {
450+
let assert Ok(uri) = uri.parse("//:#")
451+
assert uri
452+
== uri.Uri(
453+
scheme: None,
454+
userinfo: None,
455+
host: Some(""),
456+
port: None,
457+
path: "",
458+
query: None,
459+
fragment: Some(""),
460+
)
461+
}
462+
463+
pub fn empty_port_followed_by_path_test() {
464+
let assert Ok(uri) = uri.parse("//:/")
465+
assert uri
466+
== uri.Uri(
467+
scheme: None,
468+
userinfo: None,
469+
host: Some(""),
470+
port: None,
471+
path: "/",
472+
query: None,
473+
fragment: None,
474+
)
475+
}
476+
421477
const percent_codec_fixtures = [
422478
#(" ", "%20"),
423479
#(",", "%2C"),

0 commit comments

Comments
 (0)