184 lines
5 KiB
Python
184 lines
5 KiB
Python
"""
|
|
|
|
"""
|
|
|
|
"""
|
|
websocket - WebSocket client library for Python
|
|
|
|
Copyright (C) 2010 Hiroki Ohtani(liris)
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
"""
|
|
import errno
|
|
import selectors
|
|
import socket
|
|
|
|
from ._exceptions import *
|
|
from ._ssl_compat import *
|
|
from ._utils import *
|
|
|
|
DEFAULT_SOCKET_OPTION = [(socket.SOL_TCP, socket.TCP_NODELAY, 1)]
|
|
if hasattr(socket, "SO_KEEPALIVE"):
|
|
DEFAULT_SOCKET_OPTION.append((socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1))
|
|
if hasattr(socket, "TCP_KEEPIDLE"):
|
|
DEFAULT_SOCKET_OPTION.append((socket.SOL_TCP, socket.TCP_KEEPIDLE, 30))
|
|
if hasattr(socket, "TCP_KEEPINTVL"):
|
|
DEFAULT_SOCKET_OPTION.append((socket.SOL_TCP, socket.TCP_KEEPINTVL, 10))
|
|
if hasattr(socket, "TCP_KEEPCNT"):
|
|
DEFAULT_SOCKET_OPTION.append((socket.SOL_TCP, socket.TCP_KEEPCNT, 3))
|
|
|
|
_default_timeout = None
|
|
|
|
__all__ = ["DEFAULT_SOCKET_OPTION", "sock_opt", "setdefaulttimeout", "getdefaulttimeout",
|
|
"recv", "recv_line", "send"]
|
|
|
|
|
|
class sock_opt(object):
|
|
|
|
def __init__(self, sockopt, sslopt):
|
|
if sockopt is None:
|
|
sockopt = []
|
|
if sslopt is None:
|
|
sslopt = {}
|
|
self.sockopt = sockopt
|
|
self.sslopt = sslopt
|
|
self.timeout = None
|
|
|
|
|
|
def setdefaulttimeout(timeout):
|
|
"""
|
|
Set the global timeout setting to connect.
|
|
|
|
Parameters
|
|
----------
|
|
timeout: int or float
|
|
default socket timeout time (in seconds)
|
|
"""
|
|
global _default_timeout
|
|
_default_timeout = timeout
|
|
|
|
|
|
def getdefaulttimeout():
|
|
"""
|
|
Get default timeout
|
|
|
|
Returns
|
|
----------
|
|
_default_timeout: int or float
|
|
Return the global timeout setting (in seconds) to connect.
|
|
"""
|
|
return _default_timeout
|
|
|
|
|
|
def recv(sock, bufsize):
|
|
if not sock:
|
|
raise WebSocketConnectionClosedException("socket is already closed.")
|
|
|
|
def _recv():
|
|
try:
|
|
return sock.recv(bufsize)
|
|
except SSLWantReadError:
|
|
pass
|
|
except socket.error as exc:
|
|
error_code = extract_error_code(exc)
|
|
if error_code is None:
|
|
raise
|
|
if error_code != errno.EAGAIN or error_code != errno.EWOULDBLOCK:
|
|
raise
|
|
|
|
sel = selectors.DefaultSelector()
|
|
sel.register(sock, selectors.EVENT_READ)
|
|
|
|
r = sel.select(sock.gettimeout())
|
|
sel.close()
|
|
|
|
if r:
|
|
return sock.recv(bufsize)
|
|
|
|
try:
|
|
if sock.gettimeout() == 0:
|
|
bytes_ = sock.recv(bufsize)
|
|
else:
|
|
bytes_ = _recv()
|
|
except socket.timeout as e:
|
|
message = extract_err_message(e)
|
|
raise WebSocketTimeoutException(message)
|
|
except SSLError as e:
|
|
message = extract_err_message(e)
|
|
if isinstance(message, str) and 'timed out' in message:
|
|
raise WebSocketTimeoutException(message)
|
|
else:
|
|
raise
|
|
|
|
if not bytes_:
|
|
raise WebSocketConnectionClosedException(
|
|
"Connection to remote host was lost.")
|
|
|
|
return bytes_
|
|
|
|
|
|
def recv_line(sock):
|
|
line = []
|
|
while True:
|
|
c = recv(sock, 1)
|
|
line.append(c)
|
|
if c == b'\n':
|
|
break
|
|
return b''.join(line)
|
|
|
|
|
|
def send(sock, data):
|
|
if isinstance(data, str):
|
|
data = data.encode('utf-8')
|
|
|
|
if not sock:
|
|
raise WebSocketConnectionClosedException("socket is already closed.")
|
|
|
|
def _send():
|
|
try:
|
|
return sock.send(data)
|
|
except SSLWantWriteError:
|
|
pass
|
|
except socket.error as exc:
|
|
error_code = extract_error_code(exc)
|
|
if error_code is None:
|
|
raise
|
|
if error_code != errno.EAGAIN or error_code != errno.EWOULDBLOCK:
|
|
raise
|
|
|
|
sel = selectors.DefaultSelector()
|
|
sel.register(sock, selectors.EVENT_WRITE)
|
|
|
|
w = sel.select(sock.gettimeout())
|
|
sel.close()
|
|
|
|
if w:
|
|
return sock.send(data)
|
|
|
|
try:
|
|
if sock.gettimeout() == 0:
|
|
return sock.send(data)
|
|
else:
|
|
return _send()
|
|
except socket.timeout as e:
|
|
message = extract_err_message(e)
|
|
raise WebSocketTimeoutException(message)
|
|
except Exception as e:
|
|
message = extract_err_message(e)
|
|
if isinstance(message, str) and "timed out" in message:
|
|
raise WebSocketTimeoutException(message)
|
|
else:
|
|
raise
|