Add websocket transport
This commit is contained in:
parent
601ba59971
commit
978a669d16
13 changed files with 250 additions and 78 deletions
|
|
@ -8,13 +8,12 @@ before_install:
|
||||||
- sudo apt-get install nodejs
|
- sudo apt-get install nodejs
|
||||||
install:
|
install:
|
||||||
- npm install -G socket.io
|
- npm install -G socket.io
|
||||||
- npm install -G http-proxy
|
- npm install -G yargs
|
||||||
- pip install -U requests
|
- pip install -U requests
|
||||||
- pip install -U six
|
- pip install -U six
|
||||||
- pip install -U websocket-client
|
- pip install -U websocket-client
|
||||||
- pip install -U coverage
|
- pip install -U coverage
|
||||||
before_script:
|
before_script:
|
||||||
- DEBUG=* node socketIO_client/tests/serve.js &
|
- DEBUG=* node socketIO_client/tests/serve.js &
|
||||||
- DEBUG=* node socketIO_client/tests/proxy.js &
|
|
||||||
- sleep 3
|
- sleep 3
|
||||||
script: nosetests
|
script: nosetests
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
.. image:: https://travis-ci.org/invisibleroads/socketIO-client.svg?branch=master
|
.. image:: https://travis-ci.org/invisibleroads/socketIO-client.svg?branch=master
|
||||||
:target: https://travis-ci.org/invisibleroads/socketIO-client
|
:target: https://travis-ci.org/invisibleroads/socketIO-client
|
||||||
|
|
||||||
.. image:: https://www.bountysource.com/badge/tracker?tracker_id=388415
|
|
||||||
:target: https://www.bountysource.com/trackers/388415-invisibleroads-socketio-client?utm_source=388415&utm_medium=shield&utm_campaign=TRACKER_BADGE
|
|
||||||
|
|
||||||
|
|
||||||
socketIO-client
|
socketIO-client
|
||||||
===============
|
===============
|
||||||
|
|
@ -139,7 +136,8 @@ Specify params, headers, cookies, proxies thanks to the `requests <http://python
|
||||||
from socketIO_client import SocketIO
|
from socketIO_client import SocketIO
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
|
|
||||||
SocketIO('localhost', 8000,
|
SocketIO(
|
||||||
|
localhost', 8000,
|
||||||
params={'q': 'qqq'},
|
params={'q': 'qqq'},
|
||||||
headers={'Authorization': 'Basic ' + b64encode('username:password')},
|
headers={'Authorization': 'Basic ' + b64encode('username:password')},
|
||||||
cookies={'a': 'aaa'},
|
cookies={'a': 'aaa'},
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
Add Websocket transport
|
Fix https tests for xhr-polling
|
||||||
Update proxy to include websocket depending on argument
|
Fix https tests for websocket
|
||||||
Use prepared request to get headers from http_session
|
|
||||||
Include https://github.com/invisibleroads/socketIO-client/issues/68
|
|
||||||
Add test for on_reconnect using sarietta's bash scripts
|
|
||||||
Consider logging packets sent and received
|
|
||||||
Implement rooms #65
|
Implement rooms #65
|
||||||
Implement binary event
|
Implement binary event
|
||||||
Implement binary ack
|
Implement binary ack
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ from .parsers import (
|
||||||
format_socketIO_packet_data, parse_socketIO_packet_data,
|
format_socketIO_packet_data, parse_socketIO_packet_data,
|
||||||
get_namespace_path)
|
get_namespace_path)
|
||||||
from .symmetries import get_character
|
from .symmetries import get_character
|
||||||
from .transports import XHR_PollingTransport, prepare_http_session, TRANSPORTS
|
from .transports import (
|
||||||
|
WebsocketTransport, XHR_PollingTransport, prepare_http_session, TRANSPORTS)
|
||||||
|
|
||||||
|
|
||||||
__all__ = 'SocketIO', 'SocketIONamespace'
|
__all__ = 'SocketIO', 'SocketIONamespace'
|
||||||
|
|
@ -54,7 +55,7 @@ class EngineIO(LoggingMixin):
|
||||||
if self._opened:
|
if self._opened:
|
||||||
return self._transport_instance
|
return self._transport_instance
|
||||||
self._engineIO_session = self._get_engineIO_session()
|
self._engineIO_session = self._get_engineIO_session()
|
||||||
self._transport_instance = self._negotiate_transport()
|
self._negotiate_transport()
|
||||||
self._connect_namespaces()
|
self._connect_namespaces()
|
||||||
self._opened = True
|
self._opened = True
|
||||||
self._reset_heartbeat()
|
self._reset_heartbeat()
|
||||||
|
|
@ -79,8 +80,22 @@ class EngineIO(LoggingMixin):
|
||||||
return parse_engineIO_session(engineIO_packet_data)
|
return parse_engineIO_session(engineIO_packet_data)
|
||||||
|
|
||||||
def _negotiate_transport(self):
|
def _negotiate_transport(self):
|
||||||
self._transport_name = 'xhr-polling'
|
self._transport_instance = self._get_transport('xhr-polling')
|
||||||
return self._get_transport(self._transport_name)
|
self.transport_name = 'xhr-polling'
|
||||||
|
is_ws_client = 'websocket' in self._client_transports
|
||||||
|
is_ws_server = 'websocket' in self._engineIO_session.transport_upgrades
|
||||||
|
if is_ws_client and is_ws_server:
|
||||||
|
try:
|
||||||
|
transport = self._get_transport('websocket')
|
||||||
|
transport.send_packet(2, 'probe')
|
||||||
|
for packet_type, packet_data in transport.recv_packet():
|
||||||
|
if packet_type == 3 and packet_data == b'probe':
|
||||||
|
transport.send_packet(5, '')
|
||||||
|
self._transport_instance = transport
|
||||||
|
self.transport_name = 'websocket'
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
self._debug('[transport selected] %s', self.transport_name)
|
||||||
|
|
||||||
def _reset_heartbeat(self):
|
def _reset_heartbeat(self):
|
||||||
try:
|
try:
|
||||||
|
|
@ -88,10 +103,7 @@ class EngineIO(LoggingMixin):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
ping_interval = self._engineIO_session.ping_interval
|
ping_interval = self._engineIO_session.ping_interval
|
||||||
if self._transport_name.endswith('-polling'):
|
hurry_interval_in_seconds = self._hurry_interval_in_seconds
|
||||||
hurry_interval_in_seconds = self._hurry_interval_in_seconds
|
|
||||||
else:
|
|
||||||
hurry_interval_in_seconds = ping_interval
|
|
||||||
self._heartbeat_thread = HeartbeatThread(
|
self._heartbeat_thread = HeartbeatThread(
|
||||||
send_heartbeat=self._ping,
|
send_heartbeat=self._ping,
|
||||||
relax_interval_in_seconds=ping_interval,
|
relax_interval_in_seconds=ping_interval,
|
||||||
|
|
@ -102,9 +114,9 @@ class EngineIO(LoggingMixin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _get_transport(self, transport_name):
|
def _get_transport(self, transport_name):
|
||||||
self._debug('[transport selected] %s', transport_name)
|
|
||||||
SelectedTransport = {
|
SelectedTransport = {
|
||||||
'xhr-polling': XHR_PollingTransport,
|
'xhr-polling': XHR_PollingTransport,
|
||||||
|
'websocket': WebsocketTransport,
|
||||||
}[transport_name]
|
}[transport_name]
|
||||||
return SelectedTransport(
|
return SelectedTransport(
|
||||||
self._http_session, self._is_secure, self._url,
|
self._http_session, self._is_secure, self._url,
|
||||||
|
|
@ -146,7 +158,7 @@ class EngineIO(LoggingMixin):
|
||||||
@retry
|
@retry
|
||||||
def _open(self):
|
def _open(self):
|
||||||
engineIO_packet_type = 0
|
engineIO_packet_type = 0
|
||||||
self._transport.send_packet(engineIO_packet_type, '')
|
self._transport_instance.send_packet(engineIO_packet_type)
|
||||||
|
|
||||||
def _close(self):
|
def _close(self):
|
||||||
self._wants_to_close = True
|
self._wants_to_close = True
|
||||||
|
|
@ -154,34 +166,37 @@ class EngineIO(LoggingMixin):
|
||||||
if not self._opened:
|
if not self._opened:
|
||||||
return
|
return
|
||||||
engineIO_packet_type = 1
|
engineIO_packet_type = 1
|
||||||
self._transport.send_packet(engineIO_packet_type, '')
|
self._transport_instance.send_packet(engineIO_packet_type)
|
||||||
self._opened = False
|
self._opened = False
|
||||||
|
|
||||||
@retry
|
@retry
|
||||||
def _ping(self, engineIO_packet_data=''):
|
def _ping(self, engineIO_packet_data=''):
|
||||||
engineIO_packet_type = 2
|
engineIO_packet_type = 2
|
||||||
self._transport.send_packet(engineIO_packet_type, engineIO_packet_data)
|
self._transport_instance.send_packet(
|
||||||
|
engineIO_packet_type, engineIO_packet_data)
|
||||||
|
|
||||||
@retry
|
@retry
|
||||||
def _pong(self, engineIO_packet_data=''):
|
def _pong(self, engineIO_packet_data=''):
|
||||||
engineIO_packet_type = 3
|
engineIO_packet_type = 3
|
||||||
self._transport.send_packet(engineIO_packet_type, engineIO_packet_data)
|
self._transport_instance.send_packet(
|
||||||
|
engineIO_packet_type, engineIO_packet_data)
|
||||||
|
|
||||||
@retry
|
@retry
|
||||||
def _message(self, engineIO_packet_data):
|
def _message(self, engineIO_packet_data):
|
||||||
engineIO_packet_type = 4
|
engineIO_packet_type = 4
|
||||||
self._transport.send_packet(engineIO_packet_type, engineIO_packet_data)
|
self._transport_instance.send_packet(
|
||||||
|
engineIO_packet_type, engineIO_packet_data)
|
||||||
self._debug('[socket.io packet sent] %s', engineIO_packet_data)
|
self._debug('[socket.io packet sent] %s', engineIO_packet_data)
|
||||||
|
|
||||||
@retry
|
@retry
|
||||||
def _upgrade(self):
|
def _upgrade(self):
|
||||||
engineIO_packet_type = 5
|
engineIO_packet_type = 5
|
||||||
self._transport.send_packet(engineIO_packet_type, '')
|
self._transport_instance.send_packet(engineIO_packet_type)
|
||||||
|
|
||||||
@retry
|
@retry
|
||||||
def _noop(self):
|
def _noop(self):
|
||||||
engineIO_packet_type = 6
|
engineIO_packet_type = 6
|
||||||
self._transport.send_packet(engineIO_packet_type, '')
|
self._transport_instance.send_packet(engineIO_packet_type)
|
||||||
|
|
||||||
# React
|
# React
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,15 +22,15 @@ class HeartbeatThread(Thread):
|
||||||
def run(self):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
while not self._halt.is_set():
|
while not self._halt.is_set():
|
||||||
try:
|
|
||||||
self._send_heartbeat()
|
|
||||||
except TimeoutError:
|
|
||||||
pass
|
|
||||||
if self._adrenaline.is_set():
|
if self._adrenaline.is_set():
|
||||||
interval_in_seconds = self._hurry_interval_in_seconds
|
interval_in_seconds = self._hurry_interval_in_seconds
|
||||||
else:
|
else:
|
||||||
interval_in_seconds = self._relax_interval_in_seconds
|
interval_in_seconds = self._relax_interval_in_seconds
|
||||||
self._rest.wait(interval_in_seconds)
|
self._rest.wait(interval_in_seconds)
|
||||||
|
try:
|
||||||
|
self._send_heartbeat()
|
||||||
|
except TimeoutError:
|
||||||
|
pass
|
||||||
except ConnectionError:
|
except ConnectionError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,8 @@ def parse_engineIO_session(engineIO_packet_data):
|
||||||
def encode_engineIO_content(engineIO_packets):
|
def encode_engineIO_content(engineIO_packets):
|
||||||
content = bytearray()
|
content = bytearray()
|
||||||
for packet_type, packet_data in engineIO_packets:
|
for packet_type, packet_data in engineIO_packets:
|
||||||
packet_string = encode_string(str(packet_type) + packet_data)
|
packet_text = format_packet_text(packet_type, packet_data)
|
||||||
content.extend(_make_packet_header(packet_string) + packet_string)
|
content.extend(_make_packet_prefix(packet_text) + packet_text)
|
||||||
return content
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -46,10 +46,10 @@ def decode_engineIO_content(content):
|
||||||
content, content_index)
|
content, content_index)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
break
|
break
|
||||||
content_index, packet_string = _read_packet_string(
|
content_index, packet_text = _read_packet_text(
|
||||||
content, content_index, packet_length)
|
content, content_index, packet_length)
|
||||||
engineIO_packet_type = int(get_character(packet_string, 0))
|
engineIO_packet_type, engineIO_packet_data = parse_packet_text(
|
||||||
engineIO_packet_data = packet_string[1:]
|
packet_text)
|
||||||
yield engineIO_packet_type, engineIO_packet_data
|
yield engineIO_packet_type, engineIO_packet_data
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -85,6 +85,16 @@ def parse_socketIO_packet_data(socketIO_packet_data):
|
||||||
return SocketIOData(path=path, ack_id=ack_id, args=args)
|
return SocketIOData(path=path, ack_id=ack_id, args=args)
|
||||||
|
|
||||||
|
|
||||||
|
def format_packet_text(packet_type, packet_data):
|
||||||
|
return encode_string(str(packet_type) + packet_data)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_packet_text(packet_text):
|
||||||
|
packet_type = int(get_character(packet_text, 0))
|
||||||
|
packet_data = packet_text[1:]
|
||||||
|
return packet_type, packet_data
|
||||||
|
|
||||||
|
|
||||||
def get_namespace_path(socketIO_packet_data):
|
def get_namespace_path(socketIO_packet_data):
|
||||||
if not socketIO_packet_data.startswith(b'/'):
|
if not socketIO_packet_data.startswith(b'/'):
|
||||||
return ''
|
return ''
|
||||||
|
|
@ -98,8 +108,8 @@ def get_namespace_path(socketIO_packet_data):
|
||||||
return ''.join(parts)
|
return ''.join(parts)
|
||||||
|
|
||||||
|
|
||||||
def _make_packet_header(packet_string):
|
def _make_packet_prefix(packet):
|
||||||
length_string = str(len(packet_string))
|
length_string = str(len(packet))
|
||||||
header_digits = bytearray([0])
|
header_digits = bytearray([0])
|
||||||
for i in range(len(length_string)):
|
for i in range(len(length_string)):
|
||||||
header_digits.append(ord(length_string[i]) - 48)
|
header_digits.append(ord(length_string[i]) - 48)
|
||||||
|
|
@ -120,8 +130,8 @@ def _read_packet_length(content, content_index):
|
||||||
return content_index, int(packet_length_string)
|
return content_index, int(packet_length_string)
|
||||||
|
|
||||||
|
|
||||||
def _read_packet_string(content, content_index, packet_length):
|
def _read_packet_text(content, content_index, packet_length):
|
||||||
while get_byte(content, content_index) == 255:
|
while get_byte(content, content_index) == 255:
|
||||||
content_index += 1
|
content_index += 1
|
||||||
packet_string = content[content_index:content_index + packet_length]
|
packet_text = content[content_index:content_index + packet_length]
|
||||||
return content_index + packet_length, packet_string
|
return content_index + packet_length, packet_text
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,22 @@
|
||||||
import six
|
import six
|
||||||
try:
|
try:
|
||||||
from urllib.parse import urlparse as parse_url
|
from urllib import urlencode as format_query
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
from urllib.parse import urlencode as format_query
|
||||||
|
try:
|
||||||
from urlparse import urlparse as parse_url
|
from urlparse import urlparse as parse_url
|
||||||
|
except ImportError:
|
||||||
|
from urllib.parse import urlparse as parse_url
|
||||||
|
|
||||||
|
|
||||||
|
def get_character(x, index):
|
||||||
|
return chr(get_byte(x, index))
|
||||||
|
|
||||||
|
|
||||||
def get_byte(x, index):
|
def get_byte(x, index):
|
||||||
return six.indexbytes(x, index)
|
return six.indexbytes(x, index)
|
||||||
|
|
||||||
|
|
||||||
def get_character(x, index):
|
|
||||||
return chr(six.indexbytes(x, index))
|
|
||||||
|
|
||||||
|
|
||||||
def encode_string(x):
|
def encode_string(x):
|
||||||
return x.encode('utf-8')
|
return x.encode('utf-8')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from .. import SocketIO, LoggingNamespace, find_callback
|
||||||
|
|
||||||
|
|
||||||
HOST = 'localhost'
|
HOST = 'localhost'
|
||||||
PORT = 8000
|
PORT = 9000
|
||||||
DATA = 'xxx'
|
DATA = 'xxx'
|
||||||
PAYLOAD = {'xxx': 'yyy'}
|
PAYLOAD = {'xxx': 'yyy'}
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
@ -159,10 +159,21 @@ class Test_XHR_PollingTransport(BaseMixin, TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(Test_XHR_PollingTransport, self).setUp()
|
super(Test_XHR_PollingTransport, self).setUp()
|
||||||
self.socketIO = SocketIO(HOST, PORT, LoggingNamespace, transports=[
|
self.socketIO = SocketIO(HOST, PORT, LoggingNamespace, transports=[
|
||||||
'xhr-polling'])
|
'xhr-polling'], verify=False)
|
||||||
|
self.assertEqual(self.socketIO.transport_name, 'xhr-polling')
|
||||||
self.wait_time_in_seconds = 1
|
self.wait_time_in_seconds = 1
|
||||||
|
|
||||||
|
|
||||||
|
class Test_WebsocketTransport(BaseMixin, TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(Test_WebsocketTransport, self).setUp()
|
||||||
|
self.socketIO = SocketIO(HOST, PORT, LoggingNamespace, transports=[
|
||||||
|
'xhr-polling', 'websocket'], verify=False)
|
||||||
|
self.assertEqual(self.socketIO.transport_name, 'websocket')
|
||||||
|
self.wait_time_in_seconds = 0.1
|
||||||
|
|
||||||
|
|
||||||
class Namespace(LoggingNamespace):
|
class Namespace(LoggingNamespace):
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script src="/socket.io/socket.io.js"></script>
|
<script src="/socket.io/socket.io.js"></script>
|
||||||
<script>
|
<script>
|
||||||
var socket = io('http://localhost');
|
var socket = io('//localhost');
|
||||||
var chat = io('/chat');
|
var chat = io('/chat');
|
||||||
var news = io('/news');
|
var news = io('/news');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,18 @@
|
||||||
// DEBUG=* node serve.js
|
// DEBUG=* node serve.js
|
||||||
|
|
||||||
var app = require('http').createServer(serve).listen(9000);
|
var argv = require('yargs').argv;
|
||||||
|
if (argv.secure) {
|
||||||
|
var fs = require('fs');
|
||||||
|
var app = require('https').createServer({
|
||||||
|
key: fs.readFileSync('ssl.key'),
|
||||||
|
cert: fs.readFileSync('ssl.crt')
|
||||||
|
}, serve);
|
||||||
|
} else {
|
||||||
|
var app = require('http').createServer(serve);
|
||||||
|
}
|
||||||
|
app.listen(9000);
|
||||||
|
|
||||||
var io = require('socket.io')(app);
|
var io = require('socket.io')(app);
|
||||||
var fs = require('fs');
|
|
||||||
var PAYLOAD = {'xxx': 'yyy'};
|
var PAYLOAD = {'xxx': 'yyy'};
|
||||||
|
|
||||||
io.on('connection', function(socket) {
|
io.on('connection', function(socket) {
|
||||||
|
|
|
||||||
21
socketIO_client/tests/ssl.crt
Normal file
21
socketIO_client/tests/ssl.crt
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDbTCCAlWgAwIBAgIJALVDKggptosBMA0GCSqGSIb3DQEBBQUAME0xCzAJBgNV
|
||||||
|
BAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazERMA8GA1UEBwwITmV3IFlvcmsxGDAW
|
||||||
|
BgNVBAoMD0V4YW1wbGUgQ29tcGFueTAeFw0xNTA0MTQyMzE5MDlaFw0xNjA0MTMy
|
||||||
|
MzE5MDlaME0xCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazERMA8GA1UE
|
||||||
|
BwwITmV3IFlvcmsxGDAWBgNVBAoMD0V4YW1wbGUgQ29tcGFueTCCASIwDQYJKoZI
|
||||||
|
hvcNAQEBBQADggEPADCCAQoCggEBALUOviKWvseO7gbqOXOBp+v6lUmReL2xFTiK
|
||||||
|
vrbdi9BMUMbl1CE+yiP7JTmSHCFty72xv/iwRgPG3rfUgEOZfuedhYXwl8hsnBzI
|
||||||
|
ZK3quQiBMxPzpGOO6w5I2moMI+u45gOFpmf8CySjlcnTsKaaMAAr+8IiP+NpiW3U
|
||||||
|
zKYLPtTkGdwBnilFEdLPlL3N5+yZ1zLA9WVMoEVLkg2Jbo5NEDK3JaGh34zxBOxh
|
||||||
|
WbhvarAgb8PYBcXJaf9Ctp6M077cr8qIVq/dtBjUQcBNspeDJksuhpMzIRQy0FFX
|
||||||
|
Qsso132LUsGBZqF4hVuLiSl5m0kjxxY3SOrNcvcEyLhxHJG+jLMCAwEAAaNQME4w
|
||||||
|
HQYDVR0OBBYEFAEu7HcWyPZYVWHp5NhUlmHTirV8MB8GA1UdIwQYMBaAFAEu7HcW
|
||||||
|
yPZYVWHp5NhUlmHTirV8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB
|
||||||
|
AJIUPUwx4sYn1aDTukMzcaGu1uElSdeayCe5mOmKd7XBald0PuUFqboWsTKmqELH
|
||||||
|
usFDAJNmWS/zCuEuW0huqb1c3orw9IfVFx+oPXAEGFvRmBhKd9UmFpqLmhhRcP25
|
||||||
|
RT2By00vN1A1f/XW0H2Rj5pgIBdbKGwzabQVy8RTMmmtiNlQqHwElUzggq+EbS2m
|
||||||
|
XwNJ3bMumwrjciZTbMo0MMTStqF5oqacCfvt2vTT1c2IdpiCafPMYqrldXOJddx5
|
||||||
|
uJ/Tu75ZHf+pjfg7SRGq5WmoKP36VCKbQJSP6kXcbTMP4KvlWlbmQgDsWvmnA0Nm
|
||||||
|
283Ms7he6efOphFh2XHxysU=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
28
socketIO_client/tests/ssl.key
Normal file
28
socketIO_client/tests/ssl.key
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC1Dr4ilr7Hju4G
|
||||||
|
6jlzgafr+pVJkXi9sRU4ir623YvQTFDG5dQhPsoj+yU5khwhbcu9sb/4sEYDxt63
|
||||||
|
1IBDmX7nnYWF8JfIbJwcyGSt6rkIgTMT86RjjusOSNpqDCPruOYDhaZn/Asko5XJ
|
||||||
|
07CmmjAAK/vCIj/jaYlt1MymCz7U5BncAZ4pRRHSz5S9zefsmdcywPVlTKBFS5IN
|
||||||
|
iW6OTRAytyWhod+M8QTsYVm4b2qwIG/D2AXFyWn/QraejNO+3K/KiFav3bQY1EHA
|
||||||
|
TbKXgyZLLoaTMyEUMtBRV0LLKNd9i1LBgWaheIVbi4kpeZtJI8cWN0jqzXL3BMi4
|
||||||
|
cRyRvoyzAgMBAAECggEAWZMEoAoiMopU2ljwuWNw1z9usinlKTu9uu5xcfjjXdcT
|
||||||
|
s4Mq0pPE3841WWkev68ZOC0DQ5651M6Di0D3f/olnaMB1wTzorWQ+nBt1tkkV7/L
|
||||||
|
rFhCgPrI8ZU7cXG2J72fFij97b31KVhBF8vCPnpTuxIHiMD3CKRC2HKKGK/BkwK9
|
||||||
|
cxlPjUlh2LawIULQM0jl/oLGC6jwTupNrbZogVLBf6Koi3S5o0e9F9KaBl127Mh9
|
||||||
|
COZIFDnD0exGHCvH49YI/cuwHYeyDlU6Xi7Wv2f3cdmC/enUG5n8Ts/OpDTPiSgf
|
||||||
|
cZOrVQ8nCm/4jc/qhTubBZwPGqkMpFbE+DRSGDsgKQKBgQDbndwec9lrnyHUgJ/r
|
||||||
|
hBoMFwFMc6rZvAug4CF1ry7nZqLOI+Xg6rHjseu1Vc8aQMJCuK1YAr6ImYbN1Jwq
|
||||||
|
5cta4cPlPKis4xBbwhcELfVJB550LIwv3hgN/94Sh8aojPueIJcoUsOrG87wJ2KS
|
||||||
|
RCPK+Pz7W5b+cBc/Ii9ROGPAdwKBgQDTDY8E0P4Yp/okc2IyNU+gurSP61DITEpq
|
||||||
|
N51QNQ1BmCID/G7yyErsRJeQHhCKVvGEijh/DB4nQKO9lJcVSh4v511NojwpQ35V
|
||||||
|
FtSePXmhBY8J7vq/DAkvISi8g1/A6A10XEsPcO4ga7EL5CtzYWC4Vh14a0tWNGxW
|
||||||
|
vUJWNoiApQKBgQCMxSsK+gcrTN1KcQgQ1qQ7i6NxddLVrgtmG0RXQus1uDwzFh+L
|
||||||
|
g+RypuEHYvFVSp06V1YFS/0FYiNeXCGd/Z3Tq1L95VvZNdKOfmJyc2L+ZLvUi5lw
|
||||||
|
NVQF5TRbfFyCPZwrR4iceDCjxTdoCFbOmo6209KU66hlf0PnW+oojZSjEQKBgDTY
|
||||||
|
nkkcc6OE3BOoeJwN0URzu6aVy4J759400sU0o38bMtlAqh9Mm8YRXsoNXSLmpk8D
|
||||||
|
tSXKyPoXK2ja/gGsr2ZbTneT+fBzH/z6XH7K8dup4qkgF9UilGIisWqSkrVg5Y2P
|
||||||
|
VpQlONsRXCGYHnEjnu5JUdPHOfP56G7HsQaZXRCpAoGBAI2E2ui3nyzUWmnpT4gI
|
||||||
|
vTZE4DqtLSWRL85xSJJdw7phI/dgAtVe5I7vnv8kapD+m4xMoa0J1O5jBwNdC4/C
|
||||||
|
6rCmFC+wh3qu1f3DRwaUsM/hdkQyA7DQcYzayhiicPQCi0xuJuA8FuYHBkQCmOfN
|
||||||
|
77zJjF6uScFwr+2Ozpb0H30V
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
|
@ -1,12 +1,30 @@
|
||||||
import requests
|
import requests
|
||||||
|
import six
|
||||||
|
import socket
|
||||||
|
import ssl
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import websocket
|
||||||
|
|
||||||
from .exceptions import ConnectionError, TimeoutError
|
from .exceptions import ConnectionError, TimeoutError
|
||||||
from .parsers import decode_engineIO_content, encode_engineIO_content
|
from .parsers import (
|
||||||
|
encode_engineIO_content, decode_engineIO_content,
|
||||||
|
format_packet_text, parse_packet_text)
|
||||||
|
from .symmetries import format_query, parse_url
|
||||||
|
|
||||||
|
|
||||||
|
if not hasattr(websocket, 'create_connection'):
|
||||||
|
sys.exit("""\
|
||||||
|
An incompatible websocket library is conflicting with the one we need.
|
||||||
|
You can remove the incompatible library and install the correct one
|
||||||
|
by running the following commands:
|
||||||
|
|
||||||
|
yes | pip uninstall websocket websocket-client
|
||||||
|
pip install -U websocket-client""")
|
||||||
|
|
||||||
|
|
||||||
ENGINEIO_PROTOCOL = 3
|
ENGINEIO_PROTOCOL = 3
|
||||||
TRANSPORTS = 'websocket', 'xhr-polling'
|
TRANSPORTS = 'xhr-polling', 'websocket'
|
||||||
|
|
||||||
|
|
||||||
class AbstractTransport(object):
|
class AbstractTransport(object):
|
||||||
|
|
@ -20,7 +38,7 @@ class AbstractTransport(object):
|
||||||
def recv_packet(self):
|
def recv_packet(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def send_packet(self, engineIO_packet_type, engineIO_packet_data):
|
def send_packet(self, engineIO_packet_type, engineIO_packet_data=''):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -29,53 +47,115 @@ class XHR_PollingTransport(AbstractTransport):
|
||||||
def __init__(self, http_session, is_secure, url, engineIO_session=None):
|
def __init__(self, http_session, is_secure, url, engineIO_session=None):
|
||||||
super(XHR_PollingTransport, self).__init__(
|
super(XHR_PollingTransport, self).__init__(
|
||||||
http_session, is_secure, url, engineIO_session)
|
http_session, is_secure, url, engineIO_session)
|
||||||
self.http_url = '%s://%s/' % ('https' if is_secure else 'http', url)
|
self._params = {
|
||||||
self.params = {
|
'EIO': ENGINEIO_PROTOCOL, 'transport': 'polling'}
|
||||||
'EIO': ENGINEIO_PROTOCOL,
|
|
||||||
'transport': 'polling',
|
|
||||||
}
|
|
||||||
if engineIO_session:
|
if engineIO_session:
|
||||||
self.request_index = 1
|
self._request_index = 1
|
||||||
self.kw_get = dict(timeout=engineIO_session.ping_timeout)
|
self._kw_get = dict(timeout=engineIO_session.ping_timeout)
|
||||||
self.kw_post = dict(headers={
|
self._kw_post = dict(headers={
|
||||||
'content-type': 'application/octet-stream',
|
'content-type': 'application/octet-stream',
|
||||||
})
|
})
|
||||||
self.params['sid'] = engineIO_session.id
|
self._params['sid'] = engineIO_session.id
|
||||||
else:
|
else:
|
||||||
self.request_index = 0
|
self._request_index = 0
|
||||||
self.kw_get = {}
|
self._kw_get = {}
|
||||||
self.kw_post = {}
|
self._kw_post = {}
|
||||||
|
http_scheme = 'https' if is_secure else 'http'
|
||||||
|
self._http_url = '%s://%s/' % (http_scheme, url)
|
||||||
|
|
||||||
def recv_packet(self):
|
def recv_packet(self):
|
||||||
params = dict(self.params)
|
params = dict(self._params)
|
||||||
params['t'] = self._get_timestamp()
|
params['t'] = self._get_timestamp()
|
||||||
response = get_response(
|
response = get_response(
|
||||||
self.http_session.get,
|
self.http_session.get,
|
||||||
self.http_url,
|
self._http_url,
|
||||||
params=params,
|
params=params,
|
||||||
**self.kw_get)
|
**self._kw_get)
|
||||||
for engineIO_packet in decode_engineIO_content(response.content):
|
for engineIO_packet in decode_engineIO_content(response.content):
|
||||||
yield engineIO_packet
|
engineIO_packet_type, engineIO_packet_data = engineIO_packet
|
||||||
|
yield engineIO_packet_type, engineIO_packet_data
|
||||||
|
|
||||||
def send_packet(self, engineIO_packet_type, engineIO_packet_data):
|
def send_packet(self, engineIO_packet_type, engineIO_packet_data=''):
|
||||||
params = dict(self.params)
|
params = dict(self._params)
|
||||||
params['t'] = self._get_timestamp()
|
params['t'] = self._get_timestamp()
|
||||||
response = get_response(
|
response = get_response(
|
||||||
self.http_session.post,
|
self.http_session.post,
|
||||||
self.http_url,
|
self._http_url,
|
||||||
params=params,
|
params=params,
|
||||||
data=encode_engineIO_content([
|
data=encode_engineIO_content([
|
||||||
(engineIO_packet_type, engineIO_packet_data),
|
(engineIO_packet_type, engineIO_packet_data),
|
||||||
]),
|
]),
|
||||||
**self.kw_post)
|
**self._kw_post)
|
||||||
assert response.content == b'ok'
|
assert response.content == b'ok'
|
||||||
|
|
||||||
def _get_timestamp(self):
|
def _get_timestamp(self):
|
||||||
timestamp = '%s-%s' % (int(time.time() * 1000), self.request_index)
|
timestamp = '%s-%s' % (int(time.time() * 1000), self._request_index)
|
||||||
self.request_index += 1
|
self._request_index += 1
|
||||||
return timestamp
|
return timestamp
|
||||||
|
|
||||||
|
|
||||||
|
class WebsocketTransport(AbstractTransport):
|
||||||
|
|
||||||
|
def __init__(self, http_session, is_secure, url, engineIO_session=None):
|
||||||
|
super(WebsocketTransport, self).__init__(
|
||||||
|
http_session, is_secure, url, engineIO_session)
|
||||||
|
params = dict(http_session.params, **{
|
||||||
|
'EIO': ENGINEIO_PROTOCOL, 'transport': 'websocket'})
|
||||||
|
request = http_session.prepare_request(requests.Request('GET', url))
|
||||||
|
kw = {'header': ['%s: %s' % x for x in request.headers.items()]}
|
||||||
|
if engineIO_session:
|
||||||
|
params['sid'] = engineIO_session.id
|
||||||
|
kw['timeout'] = engineIO_session.ping_timeout
|
||||||
|
ws_url = '%s://%s/?%s' % (
|
||||||
|
'wss' if is_secure else 'ws', url, format_query(params))
|
||||||
|
http_scheme = 'https' if is_secure else 'http'
|
||||||
|
if http_scheme in http_session.proxies: # Use the correct proxy
|
||||||
|
proxy_url_pack = parse_url(http_session.proxies[http_scheme])
|
||||||
|
kw['http_proxy_host'] = proxy_url_pack.hostname
|
||||||
|
kw['http_proxy_port'] = proxy_url_pack.port
|
||||||
|
if proxy_url_pack.username:
|
||||||
|
kw['http_proxy_auth'] = (
|
||||||
|
proxy_url_pack.username, proxy_url_pack.password)
|
||||||
|
if http_session.verify:
|
||||||
|
if http_session.cert: # Specify certificate path on disk
|
||||||
|
if isinstance(http_session.cert, basestring):
|
||||||
|
kw['ca_certs'] = http_session.cert
|
||||||
|
else:
|
||||||
|
kw['ca_certs'] = http_session.cert[0]
|
||||||
|
else: # Do not verify the SSL certificate
|
||||||
|
kw['sslopt'] = {'cert_reqs': ssl.CERT_NONE}
|
||||||
|
try:
|
||||||
|
self._connection = websocket.create_connection(ws_url, **kw)
|
||||||
|
except Exception as e:
|
||||||
|
raise ConnectionError(e)
|
||||||
|
|
||||||
|
def recv_packet(self):
|
||||||
|
try:
|
||||||
|
packet_text = self._connection.recv()
|
||||||
|
except websocket.WebSocketTimeoutException as e:
|
||||||
|
raise TimeoutError('recv timed out (%s)' % e)
|
||||||
|
except websocket.SSLError as e:
|
||||||
|
raise ConnectionError('recv disconnected (%s)' % e)
|
||||||
|
except websocket.WebSocketConnectionClosedException as e:
|
||||||
|
raise ConnectionError('recv disconnected (%s)' % e)
|
||||||
|
except socket.error as e:
|
||||||
|
raise ConnectionError('recv disconnected (%s)' % e)
|
||||||
|
engineIO_packet_type, engineIO_packet_data = parse_packet_text(
|
||||||
|
six.b(packet_text))
|
||||||
|
yield engineIO_packet_type, engineIO_packet_data
|
||||||
|
|
||||||
|
def send_packet(self, engineIO_packet_type, engineIO_packet_data=''):
|
||||||
|
packet = format_packet_text(engineIO_packet_type, engineIO_packet_data)
|
||||||
|
try:
|
||||||
|
self._connection.send(packet)
|
||||||
|
except websocket.WebSocketTimeoutException as e:
|
||||||
|
raise TimeoutError('send timed out (%s)' % e)
|
||||||
|
except socket.error as e:
|
||||||
|
raise ConnectionError('send disconnected (%s)' % e)
|
||||||
|
except websocket.WebSocketConnectionClosedException as e:
|
||||||
|
raise ConnectionError('send disconnected (%s)' % e)
|
||||||
|
|
||||||
|
|
||||||
def get_response(request, *args, **kw):
|
def get_response(request, *args, **kw):
|
||||||
try:
|
try:
|
||||||
response = request(*args, **kw)
|
response = request(*args, **kw)
|
||||||
|
|
@ -98,7 +178,7 @@ def prepare_http_session(kw):
|
||||||
http_session.proxies.update(kw.get('proxies', {}))
|
http_session.proxies.update(kw.get('proxies', {}))
|
||||||
http_session.hooks.update(kw.get('hooks', {}))
|
http_session.hooks.update(kw.get('hooks', {}))
|
||||||
http_session.params.update(kw.get('params', {}))
|
http_session.params.update(kw.get('params', {}))
|
||||||
http_session.verify = kw.get('verify')
|
http_session.verify = kw.get('verify', True)
|
||||||
http_session.cert = kw.get('cert')
|
http_session.cert = kw.get('cert')
|
||||||
http_session.cookies.update(kw.get('cookies', {}))
|
http_session.cookies.update(kw.get('cookies', {}))
|
||||||
return http_session
|
return http_session
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue