Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

480 строки
14 KiB

  1. cdef __port_to_int(port, proto):
  2. if type(port) is int:
  3. return port
  4. if port is None or port == '' or port == b'':
  5. return 0
  6. try:
  7. return int(port)
  8. except (ValueError, TypeError):
  9. pass
  10. if isinstance(port, bytes):
  11. port = port.decode()
  12. if isinstance(port, str) and proto is not None:
  13. if proto == uv.IPPROTO_TCP:
  14. return socket_getservbyname(port, 'tcp')
  15. elif proto == uv.IPPROTO_UDP:
  16. return socket_getservbyname(port, 'udp')
  17. raise OSError('service/proto not found')
  18. cdef __convert_sockaddr_to_pyaddr(const system.sockaddr* addr):
  19. # Converts sockaddr structs into what Python socket
  20. # module can understand:
  21. # - for IPv4 a tuple of (host, port)
  22. # - for IPv6 a tuple of (host, port, flowinfo, scope_id)
  23. cdef:
  24. char buf[128] # INET6_ADDRSTRLEN is usually 46
  25. int err
  26. system.sockaddr_in *addr4
  27. system.sockaddr_in6 *addr6
  28. system.sockaddr_un *addr_un
  29. if addr.sa_family == uv.AF_INET:
  30. addr4 = <system.sockaddr_in*>addr
  31. err = uv.uv_ip4_name(addr4, buf, sizeof(buf))
  32. if err < 0:
  33. raise convert_error(err)
  34. return (
  35. PyUnicode_FromString(buf),
  36. system.ntohs(addr4.sin_port)
  37. )
  38. elif addr.sa_family == uv.AF_INET6:
  39. addr6 = <system.sockaddr_in6*>addr
  40. err = uv.uv_ip6_name(addr6, buf, sizeof(buf))
  41. if err < 0:
  42. raise convert_error(err)
  43. return (
  44. PyUnicode_FromString(buf),
  45. system.ntohs(addr6.sin6_port),
  46. system.ntohl(addr6.sin6_flowinfo),
  47. addr6.sin6_scope_id
  48. )
  49. elif addr.sa_family == uv.AF_UNIX:
  50. addr_un = <system.sockaddr_un*>addr
  51. return system.MakeUnixSockPyAddr(addr_un)
  52. raise RuntimeError("cannot convert sockaddr into Python object")
  53. @cython.freelist(DEFAULT_FREELIST_SIZE)
  54. cdef class SockAddrHolder:
  55. cdef:
  56. int family
  57. system.sockaddr_storage addr
  58. Py_ssize_t addr_size
  59. cdef LruCache sockaddrs = LruCache(maxsize=DNS_PYADDR_TO_SOCKADDR_CACHE_SIZE)
  60. cdef __convert_pyaddr_to_sockaddr(int family, object addr,
  61. system.sockaddr* res):
  62. cdef:
  63. int err
  64. int addr_len
  65. int scope_id = 0
  66. int flowinfo = 0
  67. char *buf
  68. Py_ssize_t buflen
  69. SockAddrHolder ret
  70. ret = sockaddrs.get(addr, None)
  71. if ret is not None and ret.family == family:
  72. memcpy(res, &ret.addr, ret.addr_size)
  73. return
  74. ret = SockAddrHolder.__new__(SockAddrHolder)
  75. if family == uv.AF_INET:
  76. if not isinstance(addr, tuple):
  77. raise TypeError('AF_INET address must be tuple')
  78. if len(addr) != 2:
  79. raise ValueError('AF_INET address must be tuple of (host, port)')
  80. host, port = addr
  81. if isinstance(host, str):
  82. try:
  83. # idna codec is rather slow, so we try ascii first.
  84. host = host.encode('ascii')
  85. except UnicodeEncodeError:
  86. host = host.encode('idna')
  87. if not isinstance(host, (bytes, bytearray)):
  88. raise TypeError('host must be a string or bytes object')
  89. port = __port_to_int(port, None)
  90. ret.addr_size = sizeof(system.sockaddr_in)
  91. err = uv.uv_ip4_addr(host, <int>port, <system.sockaddr_in*>&ret.addr)
  92. if err < 0:
  93. raise convert_error(err)
  94. elif family == uv.AF_INET6:
  95. if not isinstance(addr, tuple):
  96. raise TypeError('AF_INET6 address must be tuple')
  97. addr_len = len(addr)
  98. if addr_len < 2 or addr_len > 4:
  99. raise ValueError(
  100. 'AF_INET6 must be a tuple of 2-4 parameters: '
  101. '(host, port, flowinfo?, scope_id?)')
  102. host = addr[0]
  103. if isinstance(host, str):
  104. try:
  105. # idna codec is rather slow, so we try ascii first.
  106. host = host.encode('ascii')
  107. except UnicodeEncodeError:
  108. host = host.encode('idna')
  109. if not isinstance(host, (bytes, bytearray)):
  110. raise TypeError('host must be a string or bytes object')
  111. port = __port_to_int(addr[1], None)
  112. if addr_len > 2:
  113. flowinfo = addr[2]
  114. if addr_len > 3:
  115. scope_id = addr[3]
  116. ret.addr_size = sizeof(system.sockaddr_in6)
  117. err = uv.uv_ip6_addr(host, port, <system.sockaddr_in6*>&ret.addr)
  118. if err < 0:
  119. raise convert_error(err)
  120. (<system.sockaddr_in6*>&ret.addr).sin6_flowinfo = flowinfo
  121. (<system.sockaddr_in6*>&ret.addr).sin6_scope_id = scope_id
  122. elif family == uv.AF_UNIX:
  123. if isinstance(addr, str):
  124. addr = addr.encode(sys_getfilesystemencoding())
  125. elif not isinstance(addr, bytes):
  126. raise TypeError('AF_UNIX address must be a str or a bytes object')
  127. PyBytes_AsStringAndSize(addr, &buf, &buflen)
  128. if buflen > 107:
  129. raise ValueError(
  130. f'unix socket path {addr!r} is longer than 107 characters')
  131. ret.addr_size = sizeof(system.sockaddr_un)
  132. memset(&ret.addr, 0, sizeof(system.sockaddr_un))
  133. (<system.sockaddr_un*>&ret.addr).sun_family = uv.AF_UNIX
  134. memcpy((<system.sockaddr_un*>&ret.addr).sun_path, buf, buflen)
  135. else:
  136. raise ValueError(
  137. f'expected AF_INET, AF_INET6, or AF_UNIX family, got {family}')
  138. ret.family = family
  139. sockaddrs[addr] = ret
  140. memcpy(res, &ret.addr, ret.addr_size)
  141. cdef __static_getaddrinfo(object host, object port,
  142. int family, int type,
  143. int proto,
  144. system.sockaddr *addr):
  145. if proto not in {0, uv.IPPROTO_TCP, uv.IPPROTO_UDP}:
  146. return
  147. if _is_sock_stream(type):
  148. proto = uv.IPPROTO_TCP
  149. elif _is_sock_dgram(type):
  150. proto = uv.IPPROTO_UDP
  151. else:
  152. return
  153. try:
  154. port = __port_to_int(port, proto)
  155. except Exception:
  156. return
  157. hp = (host, port)
  158. if family == uv.AF_UNSPEC:
  159. try:
  160. __convert_pyaddr_to_sockaddr(uv.AF_INET, hp, addr)
  161. except Exception:
  162. pass
  163. else:
  164. return (uv.AF_INET, type, proto)
  165. try:
  166. __convert_pyaddr_to_sockaddr(uv.AF_INET6, hp, addr)
  167. except Exception:
  168. pass
  169. else:
  170. return (uv.AF_INET6, type, proto)
  171. else:
  172. try:
  173. __convert_pyaddr_to_sockaddr(family, hp, addr)
  174. except Exception:
  175. pass
  176. else:
  177. return (family, type, proto)
  178. cdef __static_getaddrinfo_pyaddr(object host, object port,
  179. int family, int type,
  180. int proto, int flags):
  181. cdef:
  182. system.sockaddr_storage addr
  183. object triplet
  184. triplet = __static_getaddrinfo(
  185. host, port, family, type,
  186. proto, <system.sockaddr*>&addr)
  187. if triplet is None:
  188. return
  189. af, type, proto = triplet
  190. try:
  191. pyaddr = __convert_sockaddr_to_pyaddr(<system.sockaddr*>&addr)
  192. except Exception:
  193. return
  194. # When the host is an IP while type is one of TCP or UDP, different libc
  195. # implementations of getaddrinfo() behave differently:
  196. # 1. When AI_CANONNAME is set:
  197. # * glibc: returns ai_canonname
  198. # * musl: returns ai_canonname
  199. # * macOS: returns an empty string for ai_canonname
  200. # 2. When AI_CANONNAME is NOT set:
  201. # * glibc: returns an empty string for ai_canonname
  202. # * musl: returns ai_canonname
  203. # * macOS: returns an empty string for ai_canonname
  204. # At the same time, libuv and CPython both uses libc directly, even though
  205. # this different behavior is violating what is in the documentation.
  206. #
  207. # uvloop potentially should be a 100% drop-in replacement for asyncio,
  208. # doing whatever asyncio does, especially when the libc implementations are
  209. # also different in the same way. However, making our implementation to be
  210. # consistent with libc/CPython would be complex and hard to maintain
  211. # (including caching libc behaviors when flag is/not set), therefore we
  212. # decided to simply normalize the behavior in uvloop for this very marginal
  213. # case following the documentation, even though uvloop would behave
  214. # differently to asyncio on macOS and musl platforms, when again the host
  215. # is an IP and type is one of TCP or UDP.
  216. # All other cases are still asyncio-compatible.
  217. if flags & socket_AI_CANONNAME:
  218. if isinstance(host, str):
  219. canon_name = host
  220. else:
  221. canon_name = host.decode('ascii')
  222. else:
  223. canon_name = ''
  224. return (
  225. _intenum_converter(af, socket_AddressFamily),
  226. _intenum_converter(type, socket_SocketKind),
  227. proto,
  228. canon_name,
  229. pyaddr,
  230. )
  231. @cython.freelist(DEFAULT_FREELIST_SIZE)
  232. cdef class AddrInfo:
  233. cdef:
  234. system.addrinfo *data
  235. def __cinit__(self):
  236. self.data = NULL
  237. def __dealloc__(self):
  238. if self.data is not NULL:
  239. uv.uv_freeaddrinfo(self.data) # returns void
  240. self.data = NULL
  241. cdef void set_data(self, system.addrinfo *data) noexcept:
  242. self.data = data
  243. cdef unpack(self):
  244. cdef:
  245. list result = []
  246. system.addrinfo *ptr
  247. if self.data is NULL:
  248. raise RuntimeError('AddrInfo.data is NULL')
  249. ptr = self.data
  250. while ptr != NULL:
  251. if ptr.ai_addr.sa_family in (uv.AF_INET, uv.AF_INET6):
  252. result.append((
  253. _intenum_converter(ptr.ai_family, socket_AddressFamily),
  254. _intenum_converter(ptr.ai_socktype, socket_SocketKind),
  255. ptr.ai_protocol,
  256. ('' if ptr.ai_canonname is NULL else
  257. (<bytes>ptr.ai_canonname).decode()),
  258. __convert_sockaddr_to_pyaddr(ptr.ai_addr)
  259. ))
  260. ptr = ptr.ai_next
  261. return result
  262. @staticmethod
  263. cdef int isinstance(object other):
  264. return type(other) is AddrInfo
  265. cdef class AddrInfoRequest(UVRequest):
  266. cdef:
  267. system.addrinfo hints
  268. object callback
  269. uv.uv_getaddrinfo_t _req_data
  270. def __cinit__(self, Loop loop,
  271. bytes host, bytes port,
  272. int family, int type, int proto, int flags,
  273. object callback):
  274. cdef:
  275. int err
  276. char *chost
  277. char *cport
  278. if host is None:
  279. chost = NULL
  280. elif host == b'' and sys.platform == 'darwin':
  281. # It seems `getaddrinfo("", ...)` on macOS is equivalent to
  282. # `getaddrinfo("localhost", ...)`. This is inconsistent with
  283. # libuv 1.48 which treats empty nodename as EINVAL.
  284. chost = <char*>'localhost'
  285. else:
  286. chost = <char*>host
  287. if port is None:
  288. cport = NULL
  289. else:
  290. cport = <char*>port
  291. memset(&self.hints, 0, sizeof(system.addrinfo))
  292. self.hints.ai_flags = flags
  293. self.hints.ai_family = family
  294. self.hints.ai_socktype = type
  295. self.hints.ai_protocol = proto
  296. self.request = <uv.uv_req_t*> &self._req_data
  297. self.callback = callback
  298. self.request.data = <void*>self
  299. err = uv.uv_getaddrinfo(loop.uvloop,
  300. <uv.uv_getaddrinfo_t*>self.request,
  301. __on_addrinfo_resolved,
  302. chost,
  303. cport,
  304. &self.hints)
  305. if err < 0:
  306. self.on_done()
  307. try:
  308. if err == uv.UV_EINVAL:
  309. # Convert UV_EINVAL to EAI_NONAME to match libc behavior
  310. msg = system.gai_strerror(socket_EAI_NONAME).decode('utf-8')
  311. ex = socket_gaierror(socket_EAI_NONAME, msg)
  312. else:
  313. ex = convert_error(err)
  314. except Exception as ex:
  315. callback(ex)
  316. else:
  317. callback(ex)
  318. cdef class NameInfoRequest(UVRequest):
  319. cdef:
  320. object callback
  321. uv.uv_getnameinfo_t _req_data
  322. def __cinit__(self, Loop loop, callback):
  323. self.request = <uv.uv_req_t*> &self._req_data
  324. self.callback = callback
  325. self.request.data = <void*>self
  326. cdef query(self, system.sockaddr *addr, int flags):
  327. cdef int err
  328. err = uv.uv_getnameinfo(self.loop.uvloop,
  329. <uv.uv_getnameinfo_t*>self.request,
  330. __on_nameinfo_resolved,
  331. addr,
  332. flags)
  333. if err < 0:
  334. self.on_done()
  335. self.callback(convert_error(err))
  336. cdef _intenum_converter(value, enum_klass):
  337. try:
  338. return enum_klass(value)
  339. except ValueError:
  340. return value
  341. cdef void __on_addrinfo_resolved(
  342. uv.uv_getaddrinfo_t *resolver,
  343. int status,
  344. system.addrinfo *res,
  345. ) noexcept with gil:
  346. if resolver.data is NULL:
  347. aio_logger.error(
  348. 'AddrInfoRequest callback called with NULL resolver.data')
  349. return
  350. cdef:
  351. AddrInfoRequest request = <AddrInfoRequest> resolver.data
  352. Loop loop = request.loop
  353. object callback = request.callback
  354. AddrInfo ai
  355. try:
  356. if status < 0:
  357. callback(convert_error(status))
  358. else:
  359. ai = AddrInfo()
  360. ai.set_data(res)
  361. callback(ai)
  362. except (KeyboardInterrupt, SystemExit):
  363. raise
  364. except BaseException as ex:
  365. loop._handle_exception(ex)
  366. finally:
  367. request.on_done()
  368. cdef void __on_nameinfo_resolved(
  369. uv.uv_getnameinfo_t* req,
  370. int status,
  371. const char* hostname,
  372. const char* service,
  373. ) noexcept with gil:
  374. cdef:
  375. NameInfoRequest request = <NameInfoRequest> req.data
  376. Loop loop = request.loop
  377. object callback = request.callback
  378. try:
  379. if status < 0:
  380. callback(convert_error(status))
  381. else:
  382. callback(((<bytes>hostname).decode(),
  383. (<bytes>service).decode()))
  384. except (KeyboardInterrupt, SystemExit):
  385. raise
  386. except BaseException as ex:
  387. loop._handle_exception(ex)
  388. finally:
  389. request.on_done()