Skip to content

Commit 441c76f

Browse files
committed
feat(uri): add telnet URI validation per RFC 4248
1 parent 9bc7e82 commit 441c76f

File tree

2 files changed

+87
-1
lines changed

2 files changed

+87
-1
lines changed

src/validators/uri.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
# Read: https://stackoverflow.com/questions/176264
44
# https://www.rfc-editor.org/rfc/rfc3986#section-3
55

6+
# standard
7+
from urllib.parse import urlsplit
8+
69
# local
710
from .email import email
11+
from .hostname import hostname
812
from .url import url
913
from .utils import validator
1014

@@ -21,6 +25,46 @@ def _ipfs_url(value: str):
2125
return True
2226

2327

28+
def _telnet_url(value: str):
29+
"""Validate a telnet URL per RFC 4248.
30+
31+
Ref: https://www.rfc-editor.org/rfc/rfc4248"
32+
Examples:
33+
>>> _telnet_url('telnet://example.com')
34+
True
35+
>>> _telnet_url('telnet://example.com:23')
36+
True
37+
>>> _telnet_url('telnet://example.com:23/')
38+
True
39+
40+
Args:
41+
value:
42+
Telnet URL to validate.
43+
44+
Returns:
45+
(Literal[True]): If `value` is a Telnet URI.
46+
(ValidationError): If `value` is an invalid URI.
47+
"""
48+
try:
49+
scheme, netloc, path, query, fragment = urlsplit(value)
50+
except ValueError:
51+
return False
52+
53+
if scheme != "telnet":
54+
return False
55+
if not netloc:
56+
return False
57+
if query or fragment:
58+
return False
59+
if path and path != "/":
60+
return False
61+
62+
# handling any additional checks user/pass
63+
if "@" in netloc:
64+
netloc = netloc.rsplit("@", 1)[1]
65+
return hostname(netloc, may_have_port=True)
66+
67+
2468
@validator
2569
def uri(value: str, /):
2670
"""Return whether or not given value is a valid URI.
@@ -60,7 +104,6 @@ def uri(value: str, /):
60104
"rtsp",
61105
"sftp",
62106
"ssh",
63-
"telnet",
64107
}
65108
# fmt: on
66109
):
@@ -98,4 +141,8 @@ def uri(value: str, /):
98141
if value.startswith("urc:"):
99142
return True
100143

144+
# telnet
145+
if value.startswith("telnet:"):
146+
return _telnet_url(value)
147+
101148
return False

tests/test_uri.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""Test URI."""
2+
3+
# external
4+
import pytest
5+
6+
# local
7+
from validators.uri import uri
8+
9+
10+
@pytest.mark.parametrize(
11+
"value",
12+
[
13+
"telnet://example.com",
14+
"telnet://example.com/",
15+
"telnet://example.com:23",
16+
"telnet://example.com:23/",
17+
"telnet://192.168.1.1",
18+
"telnet://192.168.1.1:23",
19+
"telnet://user:password@example.com",
20+
"telnet://user:password@example.com:23/",
21+
],
22+
)
23+
def test_valid_telnet_uri(value: str):
24+
"""Test valid telnet URI."""
25+
assert uri(value)
26+
27+
28+
@pytest.mark.parametrize(
29+
"value",
30+
[
31+
"telnet://",
32+
"telnet://example.com/path",
33+
"telnet://example.com?query=1",
34+
"telnet://example.com#frag",
35+
],
36+
)
37+
def test_invalid_telnet_uri(value: str):
38+
"""Test invalid telnet URI."""
39+
assert not uri(value)

0 commit comments

Comments
 (0)