aboutsummaryrefslogtreecommitdiff
path: root/src/url.lua
diff options
context:
space:
mode:
authorThijs Schreijer <thijs@thijsschreijer.nl>2026-08-30 15:34:15 +0200
committerThijs Schreijer <thijs@thijsschreijer.nl>2026-08-30 15:34:15 +0200
commit6fe110be47c0fc34bc9dce6d377ce9b48ab27c2e (patch)
tree15eabb11745033d916e61c565ea4205bae84d0fe /src/url.lua
parent84c2bb905bda5f1fc58195171c75723b5fec6e87 (diff)
downloadluasocket-6fe110be47c0fc34bc9dce6d377ce9b48ab27c2e.tar.gz
luasocket-6fe110be47c0fc34bc9dce6d377ce9b48ab27c2e.tar.bz2
luasocket-6fe110be47c0fc34bc9dce6d377ce9b48ab27c2e.zip
feat(url): classify host as name/ipv4/ipv6 in parse and build
parse() now sets hosttype ("name"/"ipv4"/"ipv6") plus a matching hostname/ipv4/ipv6 field alongside host. build() accepts those same fields when host is absent, erroring if more than one is set. classify_host is exported for standalone use; it classifies by shape (colon present -> ipv6, dotted-quad shape -> ipv4) rather than validating the address.
Diffstat (limited to 'src/url.lua')
-rw-r--r--src/url.lua53
1 files changed, 47 insertions, 6 deletions
diff --git a/src/url.lua b/src/url.lua
index aef1698..be82421 100644
--- a/src/url.lua
+++ b/src/url.lua
@@ -123,6 +123,25 @@ local function absolute_path(base_path, relative_path)
123end 123end
124 124
125----------------------------------------------------------------------------- 125-----------------------------------------------------------------------------
126-- Classifies a raw host string into "name", "ipv4" or "ipv6"
127-- Input
128-- raw: host part of the authority, as found in the URL (with its
129-- enclosing [...] brackets, if any)
130-- Returns
131-- hosttype: "name", "ipv4" or "ipv6"
132-- host: raw, with any enclosing [...] brackets stripped
133-----------------------------------------------------------------------------
134function _M.classify_host(raw)
135 if string.find(raw, ":", 1, true) then
136 return "ipv6", string.match(raw, "^%[?(.-)%]?$")
137 end
138 if string.match(raw, "^%d+%.%d+%.%d+%.%d+$") then
139 return "ipv4", raw
140 end
141 return "name", raw
142end
143
144-----------------------------------------------------------------------------
126-- Parses a url and returns a table with all its parts according to RFC 2396 145-- Parses a url and returns a table with all its parts according to RFC 2396
127-- The following grammar describes the names given to the URL parts 146-- The following grammar describes the names given to the URL parts
128-- <url> ::= <scheme>://<authority>/<path>;<params>?<query>#<fragment> 147-- <url> ::= <scheme>://<authority>/<path>;<params>?<query>#<fragment>
@@ -137,6 +156,10 @@ end
137-- been preserved: 156-- been preserved:
138-- scheme, authority, userinfo, user, password, host, port, 157-- scheme, authority, userinfo, user, password, host, port,
139-- path, params, query, fragment 158-- path, params, query, fragment
159-- plus, when a host is present:
160-- hosttype: "name", "ipv4" or "ipv6", describing the syntax of host
161-- hostname, ipv4 or ipv6: same value as host, keyed by hosttype
162-- (only one of these three is ever set)
140-- Obs: 163-- Obs:
141-- the leading '/' in {/<path>} is considered part of <path> 164-- the leading '/' in {/<path>} is considered part of <path>
142----------------------------------------------------------------------------- 165-----------------------------------------------------------------------------
@@ -180,8 +203,10 @@ function _M.parse(url, default)
180 authority = string.gsub(authority, ":([^:%]]*)$", 203 authority = string.gsub(authority, ":([^:%]]*)$",
181 function(p) parsed.port = p; return "" end) 204 function(p) parsed.port = p; return "" end)
182 if authority ~= "" then 205 if authority ~= "" then
183 -- IPv6? 206 parsed.hosttype, parsed.host = _M.classify_host(authority)
184 parsed.host = string.match(authority, "^%[(.+)%]$") or authority 207 if parsed.hosttype == "name" then parsed.hostname = parsed.host
208 elseif parsed.hosttype == "ipv4" then parsed.ipv4 = parsed.host
209 else parsed.ipv6 = parsed.host end
185 end 210 end
186 local userinfo = parsed.userinfo 211 local userinfo = parsed.userinfo
187 if not userinfo then return parsed end 212 if not userinfo then return parsed end
@@ -195,7 +220,11 @@ end
195-- Rebuilds a parsed URL from its components. 220-- Rebuilds a parsed URL from its components.
196-- Components are protected if any reserved or unallowed characters are found 221-- Components are protected if any reserved or unallowed characters are found
197-- Input 222-- Input
198-- parsed: parsed URL, as returned by parse 223-- parsed: parsed URL, as returned by parse. If parsed.host is absent,
224-- the host is taken from whichever single one of hostname/ipv4/ipv6
225-- is set (parsed.hosttype is ignored for building). It is an error
226-- (raised with error()) for more than one of hostname/ipv4/ipv6 to
227-- be set when parsed.host is absent.
199-- Returns 228-- Returns
200-- a stringing with the corresponding URL 229-- a stringing with the corresponding URL
201----------------------------------------------------------------------------- 230-----------------------------------------------------------------------------
@@ -206,9 +235,21 @@ function _M.build(parsed)
206 if parsed.params then url = url .. ";" .. parsed.params end 235 if parsed.params then url = url .. ";" .. parsed.params end
207 if parsed.query then url = url .. "?" .. parsed.query end 236 if parsed.query then url = url .. "?" .. parsed.query end
208 local authority = parsed.authority 237 local authority = parsed.authority
209 if parsed.host then 238 local host = parsed.host
210 authority = parsed.host 239 if not host then
211 if string.find(authority, ":") then -- IPv6? 240 local set = 0
241 if parsed.hostname then host, set = parsed.hostname, set+1 end
242 if parsed.ipv4 then host, set = parsed.ipv4, set+1 end
243 if parsed.ipv6 then host, set = parsed.ipv6, set+1 end
244 if set > 1 then
245 base.error("url.build: ambiguous host, more than one of " ..
246 "hostname/ipv4/ipv6 is set")
247 end
248 end
249 if host then
250 local hosttype
251 hosttype, authority = _M.classify_host(host)
252 if hosttype == "ipv6" then
212 authority = "[" .. authority .. "]" 253 authority = "[" .. authority .. "]"
213 end 254 end
214 if parsed.port then authority = authority .. ":" .. base.tostring(parsed.port) end 255 if parsed.port then authority = authority .. ":" .. base.tostring(parsed.port) end