PageRenderTime 45ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/gevent/socket.py

https://bitbucket.org/jjonte/gevent
Python | 660 lines | 550 code | 63 blank | 47 comment | 117 complexity | 3bd4581770379d9a1d44953fa39c07d6 MD5 | raw file
  1. # Copyright (c) 2005-2006, Bob Ippolito
  2. # Copyright (c) 2007, Linden Research, Inc.
  3. # Copyright (c) 2009-2010 Denis Bilenko
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a copy
  6. # of this software and associated documentation files (the "Software"), to deal
  7. # in the Software without restriction, including without limitation the rights
  8. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. # copies of the Software, and to permit persons to whom the Software is
  10. # furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. # THE SOFTWARE.
  22. """Cooperative socket module.
  23. This module provides socket operations and some related functions.
  24. The API of the functions and classes matches the API of the corresponding
  25. items in standard :mod:`socket` module exactly, but the synchronous functions
  26. in this module only block the current greenlet and let the others run.
  27. For convenience, exceptions (like :class:`error <socket.error>` and :class:`timeout <socket.timeout>`)
  28. as well as the constants from :mod:`socket` module are imported into this module.
  29. """
  30. __all__ = ['create_connection',
  31. 'error',
  32. 'fromfd',
  33. 'gaierror',
  34. 'getaddrinfo',
  35. 'gethostbyname',
  36. 'inet_aton',
  37. 'inet_ntoa',
  38. 'inet_pton',
  39. 'inet_ntop',
  40. 'socket',
  41. 'socketpair',
  42. 'timeout',
  43. 'ssl',
  44. 'sslerror',
  45. 'SocketType']
  46. import sys
  47. import errno
  48. import time
  49. import random
  50. import re
  51. import platform
  52. is_windows = platform.system() == 'Windows'
  53. if is_windows:
  54. # no such thing as WSAEPERM or error code 10001 according to winsock.h or MSDN
  55. from errno import WSAEINVAL as EINVAL
  56. from errno import WSAEWOULDBLOCK as EWOULDBLOCK
  57. from errno import WSAEINPROGRESS as EINPROGRESS
  58. from errno import WSAEALREADY as EALREADY
  59. from errno import WSAEISCONN as EISCONN
  60. from gevent.win32util import formatError as strerror
  61. EGAIN = EWOULDBLOCK
  62. else:
  63. from errno import EINVAL
  64. from errno import EWOULDBLOCK
  65. from errno import EINPROGRESS
  66. from errno import EALREADY
  67. from errno import EAGAIN
  68. from errno import EISCONN
  69. from os import strerror
  70. import _socket
  71. error = _socket.error
  72. timeout = _socket.timeout
  73. _realsocket = _socket.socket
  74. __socket__ = __import__('socket')
  75. _makefile = __socket__.socket.makefile
  76. gaierror = _socket.gaierror
  77. __socket__._socketmethods = (
  78. 'bind', 'connect', 'connect_ex', 'fileno', 'listen',
  79. 'getpeername', 'getsockname', 'getsockopt', 'setsockopt',
  80. 'recv', 'recvfrom', 'send', 'sendall', 'sendto', 'setblocking', 'shutdown',
  81. '_decref_socketios')
  82. # Import public constants from the standard socket (called __socket__ here) into this module.
  83. for name in __socket__.__all__:
  84. if name[:1].isupper():
  85. value = getattr(__socket__, name)
  86. if isinstance(value, (int, str)):
  87. globals()[name] = value
  88. __all__.append(name)
  89. del name, value
  90. inet_ntoa = _socket.inet_ntoa
  91. inet_aton = _socket.inet_aton
  92. try:
  93. inet_ntop = _socket.inet_ntop
  94. except AttributeError:
  95. def inet_ntop(address_family, packed_ip):
  96. if address_family == AF_INET:
  97. return inet_ntoa(packed_ip)
  98. # XXX: ipv6 won't work on windows
  99. raise NotImplementedError('inet_ntop() is not available on this platform')
  100. try:
  101. inet_pton = _socket.inet_pton
  102. except AttributeError:
  103. def inet_pton(address_family, ip_string):
  104. if address_family == AF_INET:
  105. return inet_aton(ip_string)
  106. # XXX: ipv6 won't work on windows
  107. raise NotImplementedError('inet_ntop() is not available on this platform')
  108. # XXX: import other non-blocking stuff, like ntohl
  109. # XXX: implement blocking functions that are not yet implemented
  110. # XXX: add test that checks that socket.__all__ matches gevent.socket.__all__ on all supported platforms
  111. from gevent.hub import getcurrent, get_hub
  112. from gevent.greenlet import Greenlet
  113. from gevent import core
  114. _ip4_re = re.compile('^[\d\.]+$')
  115. def _wait_helper(ev, evtype):
  116. current, timeout_exc = ev.arg
  117. if evtype & core.EV_TIMEOUT:
  118. current.throw(timeout_exc)
  119. else:
  120. current.switch(ev)
  121. def wait_read(fileno, timeout=-1, timeout_exc=_socket.timeout('timed out')):
  122. evt = core.read_event(fileno, _wait_helper, timeout, (getcurrent(), timeout_exc))
  123. try:
  124. switch_result = get_hub().switch()
  125. assert evt is switch_result, 'Invalid switch into wait_read(): %r' % (switch_result, )
  126. finally:
  127. evt.cancel()
  128. def wait_write(fileno, timeout=-1, timeout_exc=_socket.timeout('timed out')):
  129. evt = core.write_event(fileno, _wait_helper, timeout, (getcurrent(), timeout_exc))
  130. try:
  131. switch_result = get_hub().switch()
  132. assert evt is switch_result, 'Invalid switch into wait_write(): %r' % (switch_result, )
  133. finally:
  134. evt.cancel()
  135. def wait_readwrite(fileno, timeout=-1, timeout_exc=_socket.timeout('timed out')):
  136. evt = core.readwrite_event(fileno, _wait_helper, timeout, (getcurrent(), timeout_exc))
  137. try:
  138. switch_result = get_hub().switch()
  139. assert evt is switch_result, 'Invalid switch into wait_readwrite(): %r' % (switch_result, )
  140. finally:
  141. evt.cancel()
  142. class _closedsocket(object):
  143. __slots__ = []
  144. def _dummy(*args):
  145. raise error(errno.EBADF, 'Bad file descriptor')
  146. # All _delegate_methods must also be initialized here.
  147. send = recv = recv_into = sendto = recvfrom = recvfrom_into = _dummy
  148. __getattr__ = _dummy
  149. _delegate_methods = ("recv", "recvfrom", "recv_into", "recvfrom_into", "send", "sendto", 'sendall')
  150. timeout_default = object()
  151. class socket(object):
  152. def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, _sock=None):
  153. #self.__weakref__ = None
  154. self._io_refs = 0
  155. self._closed = False
  156. self._sock = None
  157. self.timeout = None
  158. if _sock is None:
  159. self._sock = _realsocket(family, type, proto)
  160. self.timeout = _socket.getdefaulttimeout()
  161. else:
  162. if hasattr(_sock, '_sock'):
  163. self._sock = _sock._sock
  164. self.timeout = getattr(_sock, 'timeout', False)
  165. if self.timeout is False:
  166. self.timeout = _socket.getdefaulttimeout()
  167. else:
  168. self._sock = _sock
  169. self.timeout = _socket.getdefaulttimeout()
  170. self._sock.setblocking(0)
  171. def __repr__(self):
  172. return '<%s at %s %s>' % (type(self).__name__, hex(id(self)), self._formatinfo())
  173. def __str__(self):
  174. return '<%s %s>' % (type(self).__name__, self._formatinfo())
  175. def _formatinfo(self):
  176. try:
  177. fileno = self.fileno()
  178. except Exception as ex:
  179. fileno = str(ex)
  180. try:
  181. sockname = self.getsockname()
  182. sockname = '%s:%s' % sockname
  183. except Exception:
  184. sockname = None
  185. try:
  186. peername = self.getpeername()
  187. peername = '%s:%s' % peername
  188. except Exception:
  189. peername = None
  190. result = 'fileno=%s' % fileno
  191. if sockname is not None:
  192. result += ' sock=' + str(sockname)
  193. if peername is not None:
  194. result += ' peer=' + str(peername)
  195. if self.timeout is not None:
  196. result += ' timeout=' + str(self.timeout)
  197. return result
  198. @property
  199. def fd(self):
  200. import warnings
  201. warnings.warn("socket.fd is deprecated; use socket._sock", DeprecationWarning, stacklevel=2)
  202. return self._sock
  203. def accept(self):
  204. while True:
  205. try:
  206. client, addr = self._sock.accept()
  207. break
  208. except error as ex:
  209. if ex.errno != errno.EWOULDBLOCK or self.timeout == 0.0:
  210. raise
  211. wait_read(self._sock.fileno(), timeout=self.timeout)
  212. return socket(_sock=client), addr
  213. def close(self):
  214. self._sock = _closedsocket()
  215. dummy = self._sock._dummy
  216. for method in _delegate_methods:
  217. setattr(self, method, dummy)
  218. def connect(self, address):
  219. if isinstance(address, tuple) and len(address)==2:
  220. address = gethostbyname(address[0]), address[1]
  221. if self.timeout == 0.0:
  222. return self._sock.connect(address)
  223. sock = self._sock
  224. if self.timeout is None:
  225. while True:
  226. err = sock.getsockopt(SOL_SOCKET, SO_ERROR)
  227. if err:
  228. raise error(err, strerror(err))
  229. result = sock.connect_ex(address)
  230. if not result or result == EISCONN:
  231. break
  232. elif (result in (EWOULDBLOCK, EINPROGRESS, EALREADY)) or (result == EINVAL and is_windows):
  233. wait_readwrite(sock.fileno())
  234. else:
  235. raise error(result, strerror(result))
  236. else:
  237. end = time.time() + self.timeout
  238. while True:
  239. err = sock.getsockopt(SOL_SOCKET, SO_ERROR)
  240. if err:
  241. raise error(err, strerror(err))
  242. result = sock.connect_ex(address)
  243. if not result or result == EISCONN:
  244. break
  245. elif (result in (EWOULDBLOCK, EINPROGRESS, EALREADY)) or (result == EINVAL and is_windows):
  246. timeleft = end - time.time()
  247. if timeleft <= 0:
  248. raise timeout('timed out')
  249. wait_readwrite(sock.fileno(), timeout=timeleft)
  250. else:
  251. raise error(result, strerror(result))
  252. def connect_ex(self, address):
  253. try:
  254. return self.connect(address) or 0
  255. except timeout:
  256. return EAGAIN
  257. except error as ex:
  258. if type(ex) is error:
  259. return ex.errno
  260. else:
  261. raise # gaierror is not silented by connect_ex
  262. def dup(self):
  263. """dup() -> socket object
  264. Return a new socket object connected to the same system resource."""
  265. return socket(_sock=self._sock)
  266. def makefile(self, mode='r', bufsize=-1):
  267. return _makefile(self.dup(), mode, bufsize)
  268. def recv(self, *args):
  269. while True:
  270. try:
  271. return self._sock.recv(*args)
  272. except error as ex:
  273. if ex.errno != EWOULDBLOCK or self.timeout == 0.0:
  274. raise
  275. # QQQ without clearing exc_info test__refcount.test_clean_exit fails
  276. wait_read(self.fileno(), timeout=self.timeout)
  277. def recvfrom(self, *args):
  278. while True:
  279. try:
  280. return self._sock.recvfrom(*args)
  281. except error as ex:
  282. if ex.errno != EWOULDBLOCK or self.timeout == 0.0:
  283. raise ex
  284. wait_read(self._sock.fileno(), timeout=self.timeout)
  285. def recvfrom_into(self, *args):
  286. while True:
  287. try:
  288. return self._sock.recvfrom_into(*args)
  289. except error as ex:
  290. if ex.errno != EWOULDBLOCK or self.timeout == 0.0:
  291. raise
  292. wait_read(self._sock.fileno(), timeout=self.timeout)
  293. def recv_into(self, *args):
  294. while True:
  295. try:
  296. return self._sock.recv_into(*args)
  297. except error as ex:
  298. if ex.errno != EWOULDBLOCK or self.timeout == 0.0:
  299. raise
  300. wait_read(self._sock.fileno(), timeout=self.timeout)
  301. def send(self, data, flags=0, timeout=timeout_default):
  302. if timeout is timeout_default:
  303. timeout = self.timeout
  304. try:
  305. return self._sock.send(data, flags)
  306. except error as ex:
  307. if ex.errno != EWOULDBLOCK or timeout == 0.0:
  308. raise
  309. wait_write(self._sock.fileno(), timeout=timeout)
  310. try:
  311. return self._sock.send(data, flags)
  312. except error as ex2:
  313. if ex2.errno == EWOULDBLOCK:
  314. return 0
  315. raise
  316. def sendall(self, data, flags=0):
  317. # this sendall is also reused by SSL subclasses (both from ssl and sslold modules),
  318. # so it should not call self._sock methods directly
  319. if self.timeout is None:
  320. data_sent = 0
  321. while data_sent < len(data):
  322. data_sent += self.send(data[data_sent:], flags)
  323. else:
  324. timeleft = self.timeout
  325. end = time.time() + timeleft
  326. data_sent = 0
  327. while True:
  328. data_sent += self.send(data[data_sent:], flags, timeout=timeleft)
  329. if data_sent >= len(data):
  330. break
  331. timeleft = end - time.time()
  332. if timeleft <= 0:
  333. raise timeout('timed out')
  334. def sendto(self, *args):
  335. try:
  336. return self._sock.sendto(*args)
  337. except error as ex:
  338. if ex.errno != EWOULDBLOCK or timeout == 0.0:
  339. raise
  340. wait_write(self.fileno(), timeout=self.timeout)
  341. try:
  342. return self._sock.sendto(*args)
  343. except error as ex2:
  344. if ex2.errno == EWOULDBLOCK:
  345. return 0
  346. raise
  347. def setblocking(self, flag):
  348. if flag:
  349. self.timeout = None
  350. else:
  351. self.timeout = 0.0
  352. def settimeout(self, howlong):
  353. if howlong is not None:
  354. try:
  355. f = howlong.__float__
  356. except AttributeError:
  357. raise TypeError('a float is required')
  358. howlong = f()
  359. if howlong < 0.0:
  360. raise ValueError('Timeout value out of range')
  361. self.timeout = howlong
  362. def gettimeout(self):
  363. return self.timeout
  364. def _decref_socketios(self):
  365. if self._io_refs > 0:
  366. self._io_refs -= 1
  367. if self._closed:
  368. self.close()
  369. family = property(lambda self: self._sock.family, doc="the socket family")
  370. type = property(lambda self: self._sock.type, doc="the socket type")
  371. proto = property(lambda self: self._sock.proto, doc="the socket protocol")
  372. # delegate the functions that we haven't implemented to the real socket object
  373. _s = ("def %s(self, *args): return self._sock.%s(*args)\n\n"
  374. "%s.__doc__ = _realsocket.%s.__doc__\n")
  375. for _m in set(__socket__._socketmethods) - set(locals()):
  376. exec(_s % (_m, _m, _m, _m))
  377. del _m, _s
  378. SocketType = socket
  379. def socketpair(*args):
  380. one, two = _socket.socketpair(*args)
  381. return socket(_sock=one), socket(_sock=two)
  382. def fromfd(*args):
  383. return socket(_sock=_socket.fromfd(*args))
  384. def bind_and_listen(descriptor, addr=('', 0), backlog=50, reuse_addr=True):
  385. if reuse_addr:
  386. try:
  387. descriptor.setsockopt(SOL_SOCKET, SO_REUSEADDR, descriptor.getsockopt(SOL_SOCKET, SO_REUSEADDR) | 1)
  388. except error:
  389. pass
  390. descriptor.bind(addr)
  391. descriptor.listen(backlog)
  392. def socket_bind_and_listen(*args, **kwargs):
  393. import warnings
  394. warnings.warn("gevent.socket.socket_bind_and_listen is renamed to bind_and_listen", DeprecationWarning, stacklevel=2)
  395. bind_and_listen(*args, **kwargs)
  396. return args[0]
  397. def set_reuse_addr(descriptor):
  398. import warnings
  399. warnings.warn("gevent.socket.set_reuse_addr is deprecated", DeprecationWarning, stacklevel=2)
  400. try:
  401. descriptor.setsockopt(SOL_SOCKET, SO_REUSEADDR, descriptor.getsockopt(SOL_SOCKET, SO_REUSEADDR) | 1)
  402. except error:
  403. pass
  404. def tcp_listener(address, backlog=50, reuse_addr=True):
  405. """A shortcut to create a TCP socket, bind it and put it into listening state."""
  406. sock = socket()
  407. bind_and_listen(sock, address, backlog=backlog, reuse_addr=reuse_addr)
  408. return sock
  409. def connect_tcp(address, localaddr=None):
  410. """
  411. Create a TCP connection to address (host, port) and return the socket.
  412. Optionally, bind to localaddr (host, port) first.
  413. """
  414. import warnings
  415. warnings.warn("gevent.socket.connect_tcp is deprecated", DeprecationWarning, stacklevel=2)
  416. desc = socket()
  417. if localaddr is not None:
  418. desc.bind(localaddr)
  419. desc.connect(address)
  420. return desc
  421. def tcp_server(listensocket, server, *args, **kw):
  422. """
  423. Given a socket, accept connections forever, spawning greenlets
  424. and executing *server* for each new incoming connection.
  425. When *listensocket* is closed, the ``tcp_server()`` greenlet will end.
  426. listensocket
  427. The socket from which to accept connections.
  428. server
  429. The callable to call when a new connection is made.
  430. \*args
  431. The positional arguments to pass to *server*.
  432. \*\*kw
  433. The keyword arguments to pass to *server*.
  434. """
  435. import warnings
  436. warnings.warn("gevent.socket.tcp_server is deprecated", DeprecationWarning, stacklevel=2)
  437. try:
  438. try:
  439. while True:
  440. client_socket = listensocket.accept()
  441. Greenlet.spawn(server, client_socket, *args, **kw)
  442. except error as e:
  443. # Broken pipe means it was shutdown
  444. if e.errno != 32:
  445. raise
  446. finally:
  447. listensocket.close()
  448. try:
  449. _GLOBAL_DEFAULT_TIMEOUT = __socket__._GLOBAL_DEFAULT_TIMEOUT
  450. except AttributeError:
  451. _GLOBAL_DEFAULT_TIMEOUT = object()
  452. def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT):
  453. """Connect to *address* and return the socket object.
  454. Convenience function. Connect to *address* (a 2-tuple ``(host,
  455. port)``) and return the socket object. Passing the optional
  456. *timeout* parameter will set the timeout on the socket instance
  457. before attempting to connect. If no *timeout* is supplied, the
  458. global default timeout setting returned by :func:`getdefaulttimeout`
  459. is used.
  460. """
  461. msg = "getaddrinfo returns an empty list"
  462. host, port = address
  463. for res in getaddrinfo(host, port, 0, SOCK_STREAM):
  464. af, socktype, proto, _canonname, sa = res
  465. sock = None
  466. try:
  467. sock = socket(af, socktype, proto)
  468. if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
  469. sock.settimeout(timeout)
  470. sock.connect(sa)
  471. return sock
  472. except error as msg:
  473. if sock is not None:
  474. sock.close()
  475. raise error(msg)
  476. try:
  477. from gevent.dns import resolve_ipv4, resolve_ipv6
  478. except Exception:
  479. import traceback
  480. traceback.print_exc()
  481. __all__.remove('gethostbyname')
  482. __all__.remove('getaddrinfo')
  483. else:
  484. def gethostbyname(hostname):
  485. """:func:`socket.gethostbyname` implemented using :mod:`gevent.dns`.
  486. Differs in the following ways:
  487. * raises :class:`DNSError` (a subclass of :class:`socket.gaierror`) with dns error
  488. codes instead of standard socket error codes
  489. * does not support ``/etc/hosts`` but calls the original :func:`socket.gethostbyname`
  490. if *hostname* has no dots
  491. * does not iterate through all addresses, instead picks a random one each time
  492. """
  493. # TODO: this is supposed to iterate through all the addresses
  494. # could use a global dict(hostname, iter)
  495. # - fix these nasty hacks for localhost, ips, etc.
  496. if not isinstance(hostname, str) or '.' not in hostname:
  497. return _socket.gethostbyname(hostname)
  498. if _ip4_re.match(hostname):
  499. return hostname
  500. if hostname == _socket.gethostname():
  501. return _socket.gethostbyname(hostname)
  502. _ttl, addrs = resolve_ipv4(hostname)
  503. return inet_ntoa(random.choice(addrs))
  504. def getaddrinfo(host, port, *args, **kwargs):
  505. """*Some* approximation of :func:`socket.getaddrinfo` implemented using :mod:`gevent.dns`.
  506. If *host* is not a string, does not has any dots or is a numeric IP address, then
  507. the standard :func:`socket.getaddrinfo` is called.
  508. Otherwise, calls either :func:`resolve_ipv4` or :func:`resolve_ipv6` and
  509. formats the result the way :func:`socket.getaddrinfo` does it.
  510. Differs in the following ways:
  511. * raises :class:`DNSError` (a subclass of :class:`gaierror`) with libevent-dns error
  512. codes instead of standard socket error codes
  513. * IPv6 support is untested.
  514. * AF_UNSPEC only tries IPv4
  515. * only supports TCP, UDP, IP protocols
  516. * port must be numeric, does not support string service names. see socket.getservbyname
  517. * *flags* argument is ignored
  518. Additionally, supports *evdns_flags* keyword arguments (default ``0``) that is passed
  519. to :mod:`dns` functions.
  520. """
  521. family, socktype, proto, _flags = args + (None, ) * (4 - len(args))
  522. if not isinstance(host, str) or '.' not in host or _ip4_re.match(host):
  523. return _socket.getaddrinfo(host, port, *args)
  524. evdns_flags = kwargs.pop('evdns_flags', 0)
  525. if kwargs:
  526. raise TypeError('Unsupported keyword arguments: %s' % (list(kwargs.keys()), ))
  527. if family in (None, AF_INET, AF_UNSPEC):
  528. family = AF_INET
  529. # TODO: AF_UNSPEC means try both AF_INET and AF_INET6
  530. _ttl, addrs = resolve_ipv4(host, evdns_flags)
  531. elif family == AF_INET6:
  532. _ttl, addrs = resolve_ipv6(host, evdns_flags)
  533. else:
  534. raise NotImplementedError('family is not among AF_UNSPEC/AF_INET/AF_INET6: %r' % (family, ))
  535. socktype_proto = [(SOCK_STREAM, 6), (SOCK_DGRAM, 17), (SOCK_RAW, 0)]
  536. if socktype is not None:
  537. socktype_proto = [(x, y) for (x, y) in socktype_proto if socktype == x]
  538. if proto is not None:
  539. socktype_proto = [(x, y) for (x, y) in socktype_proto if proto == y]
  540. result = []
  541. for addr in addrs:
  542. for socktype, proto in socktype_proto:
  543. result.append((family, socktype, proto, '', (inet_ntop(family, addr), port)))
  544. return result
  545. _have_ssl = False
  546. try:
  547. from gevent.ssl import sslwrap_simple as ssl, SSLError as sslerror
  548. _have_ssl = True
  549. except ImportError:
  550. try:
  551. from gevent.sslold import ssl, sslerror
  552. _have_ssl = True
  553. except ImportError:
  554. pass
  555. if not _have_ssl:
  556. __all__.remove('ssl')
  557. __all__.remove('sslerror')