Skip to content

Commit 67fa30a

Browse files
committed
Optimize loop.getaddrinfo(): see if the address is an IP address already
1 parent 011b129 commit 67fa30a

File tree

4 files changed

+102
-7
lines changed

4 files changed

+102
-7
lines changed

uvloop/dns.pyx

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
cdef __port_to_int(port, proto):
2+
if port is None or port == '' or port == b'':
3+
return 0
4+
5+
try:
6+
return int(port)
7+
except (ValueError, TypeError):
8+
pass
9+
10+
if isinstance(port, bytes):
11+
port = port.decode()
12+
13+
if isinstance(port, str) and proto is not None:
14+
if proto == uv.IPPROTO_TCP:
15+
return socket_getservbyname(port, 'tcp')
16+
elif proto == uv.IPPROTO_UDP:
17+
return socket_getservbyname(port, 'udp')
18+
19+
raise OSError('service/proto not found')
20+
21+
122
cdef __convert_sockaddr_to_pyaddr(const system.sockaddr* addr):
223
# Converts sockaddr structs into what Python socket
324
# module can understand:
@@ -54,13 +75,11 @@ cdef __convert_pyaddr_to_sockaddr(int family, object addr,
5475
raise ValueError('AF_INET address must be tuple of (host, port)')
5576
host, port = addr
5677
if isinstance(host, str):
57-
host = host.encode()
78+
host = host.encode('idna')
5879
if not isinstance(host, (bytes, bytearray)):
5980
raise TypeError('host must be a string or bytes object')
60-
if isinstance(port, bytes):
61-
port = port.decode()
62-
if isinstance(port, str):
63-
port = int(port)
81+
82+
port = __port_to_int(port, None)
6483

6584
err = uv.uv_ip4_addr(host, <int>port, <system.sockaddr_in*>res)
6685
if err < 0:
@@ -78,9 +97,10 @@ cdef __convert_pyaddr_to_sockaddr(int family, object addr,
7897

7998
host = addr[0]
8099
if isinstance(host, str):
81-
host = host.encode()
100+
host = host.encode('idna')
101+
102+
port = __port_to_int(addr[1], None)
82103

83-
port = addr[1]
84104
if addr_len > 2:
85105
flowinfo = addr[2]
86106
if addr_len > 3:
@@ -98,6 +118,64 @@ cdef __convert_pyaddr_to_sockaddr(int family, object addr,
98118
'epected AF_INET or AF_INET6 family, got {}'.format(family))
99119

100120

121+
cdef __static_getaddrinfo(object host, object port,
122+
int family, int type,
123+
int proto,
124+
system.sockaddr *addr):
125+
126+
if proto not in {0, uv.IPPROTO_TCP, uv.IPPROTO_UDP}:
127+
raise LookupError
128+
129+
type &= ~_SOCKET_TYPE_MASK
130+
if type == uv.SOCK_STREAM:
131+
proto = uv.IPPROTO_TCP
132+
elif type == uv.SOCK_DGRAM:
133+
proto = uv.IPPROTO_UDP
134+
else:
135+
raise LookupError
136+
137+
try:
138+
port = __port_to_int(port, proto)
139+
except:
140+
raise LookupError
141+
142+
if family == uv.AF_UNSPEC:
143+
afs = [uv.AF_INET, uv.AF_INET6]
144+
else:
145+
afs = [family]
146+
147+
for af in afs:
148+
try:
149+
__convert_pyaddr_to_sockaddr(af, (host, port), addr)
150+
except:
151+
continue
152+
else:
153+
return (af, type, proto)
154+
155+
raise LookupError
156+
157+
158+
cdef __static_getaddrinfo_pyaddr(object host, object port,
159+
int family, int type,
160+
int proto, int flags):
161+
162+
cdef:
163+
system.sockaddr addr
164+
165+
try:
166+
(af, type, proto) = __static_getaddrinfo(host, port, family, type,
167+
proto, &addr)
168+
except LookupError:
169+
return
170+
171+
try:
172+
pyaddr = __convert_sockaddr_to_pyaddr(&addr)
173+
except:
174+
return
175+
176+
return af, type, proto, '', pyaddr
177+
178+
101179
@cython.freelist(DEFAULT_FREELIST_SIZE)
102180
cdef class AddrInfo:
103181
cdef:

uvloop/includes/stdlib.pxi

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ cdef socket_gaierror = socket.gaierror
5555
cdef socket_error = socket.error
5656
cdef socket_timeout = socket.timeout
5757
cdef socket_socket = socket.socket
58+
cdef socket_getservbyname = socket.getservbyname
5859

5960
cdef int socket_EAI_ADDRFAMILY = getattr(socket, 'EAI_ADDRFAMILY', -1)
6061
cdef int socket_EAI_AGAIN = getattr(socket, 'EAI_AGAIN', -1)
@@ -72,6 +73,13 @@ cdef int socket_EAI_SERVICE = getattr(socket, 'EAI_SERVICE', -1)
7273
cdef int socket_EAI_SOCKTYPE = getattr(socket, 'EAI_SOCKTYPE', -1)
7374

7475

76+
cdef int _SOCKET_TYPE_MASK = 0
77+
if hasattr(socket, 'SOCK_NONBLOCK'):
78+
_SOCKET_TYPE_MASK |= socket.SOCK_NONBLOCK
79+
if hasattr(socket, 'SOCK_CLOEXEC'):
80+
_SOCKET_TYPE_MASK |= socket.SOCK_CLOEXEC
81+
82+
7583
cdef str os_name = os.name
7684
cdef os_environ = os.environ
7785
cdef os_dup = os.dup

uvloop/includes/uv.pxd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ cdef extern from "uv.h" nogil:
6262
cdef int IPPROTO_IPV6
6363
cdef int SOCK_STREAM
6464
cdef int SOCK_DGRAM
65+
cdef int IPPROTO_TCP
66+
cdef int IPPROTO_UDP
6567

6668
cdef int SIGINT
6769
cdef int SIGHUP

uvloop/loop.pyx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,13 @@ cdef class Loop:
11051105
def getaddrinfo(self, object host, object port, *,
11061106
int family=0, int type=0, int proto=0, int flags=0):
11071107

1108+
addr = __static_getaddrinfo_pyaddr(host, port, family,
1109+
type, proto, flags)
1110+
if addr is not None:
1111+
fut = self._new_future()
1112+
fut.set_result([addr])
1113+
return fut
1114+
11081115
return self._getaddrinfo(host, port, family, type, proto, flags, 1)
11091116

11101117
async def getnameinfo(self, sockaddr, int flags=0):

0 commit comments

Comments
 (0)