hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit c8e9c390e4a1baa9f39d15deb6450d5c1e492366
parent 9a956e93568ad7e38a9eab0b7fb39f85db9e1c8b
Author: Mykyta Holubakha <hilobakho@gmail.com>
Date:   Thu,  6 Jun 2024 21:09:11 +0300

net/uri: fix some leaks

Diffstat:
Mnet/uri/+test.ha | 3+++
Mnet/uri/parse.ha | 27++++++++++++++++++++++++++-
2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/net/uri/+test.ha b/net/uri/+test.ha @@ -103,6 +103,9 @@ use net::ip; // Unexpected character assert(parse("https://^harelang.org") is invalid); + // Trailing stuff after port + assert(parse("https://harelang.org:1foo2") is invalid); + // Something other than IPv6 address inside [ ... ] assert(parse("https://[1.2.3.4]") is invalid); assert(parse("https://[example]") is invalid); diff --git a/net/uri/parse.ha b/net/uri/parse.ha @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 // (c) Hare authors <https://harelang.org> +use fmt; use ascii; use encoding::utf8; use io; @@ -12,16 +13,29 @@ use strings; // The URI provided to [[parse]] is invalid. export type invalid = !void; +fn free_non_empty(in: (str | ip::addr6)) void = { + match (in) { + case let s: str => if (len(s) > 0) free(s); + case => void; + }; +}; + // Parses a URI string into [[uri]] structure. The return value must be freed // using [[finish]]. export fn parse(in: str) (uri | invalid) = { + let success = false; let in = strings::iter(in); const scheme = parse_scheme(&in)?; + defer if (!success) free(scheme); // Determine hier-part variant let path = ""; let authority: ((str | ip::addr6), u16, str) = ("", 0u16, ""); + defer if (!success) { + free_non_empty(authority.0); + free_non_empty(authority.2); + }; match (strings::next(&in)) { case let r: rune => switch (r) { @@ -82,6 +96,7 @@ export fn parse(in: str) (uri | invalid) = { case => void; }; + success = true; return uri { scheme = scheme, @@ -133,12 +148,14 @@ fn parse_authority( in: *strings::iterator, ) (((str | ip::addr6), u16, str) | invalid) = { // Scan everything until '@' or ':' or '/', then decide what it is + let success = false; let buf = memio::dynamic(); defer io::close(&buf)!; let host: (str | ip::addr6) = ""; let port = 0u16; let userinfo = ""; let has_userinfo = false; + let want_port = false; for (let r => strings::next(in)) { if (r == '[') { @@ -176,6 +193,7 @@ fn parse_authority( }; // This was userinfo+host[+port] userinfo = percent_decode(memio::string(&buf)!)?; + defer if (!success) free(userinfo); memio::reset(&buf); has_userinfo = true; case '/' => @@ -186,7 +204,7 @@ fn parse_authority( case ':' => // This was host+port host = percent_decode(memio::string(&buf)!)?; - port = parse_port(in)?; + want_port = true; break; case => return invalid; @@ -196,6 +214,12 @@ fn parse_authority( }; }; + defer if (!success) free_non_empty(host); + + if (want_port) { + port = parse_port(in)?; + }; + match (host) { case let s: str => // In end of string case @@ -205,6 +229,7 @@ fn parse_authority( case => void; }; + success = true; return (host, port, userinfo); };