Use detection rather than python version checking.

Try importing from the newest location/name and then fallback if that
fails instead of using python version switches.

Still use version switch for the buffer/bytes to string wrapper
routines since python 2.6 has intermediate support for buffer/bytes
and I want to know if full support (ala python 3.0) is there.
This commit is contained in:
Joel Martin 2011-10-06 09:37:09 -05:00
parent 1658649d0c
commit eac2c9fadd
1 changed files with 40 additions and 45 deletions

View File

@ -17,60 +17,54 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates
''' '''
import os, sys, time, errno, signal, socket, traceback, select import os, sys, time, errno, signal, socket, traceback, select
import array import array, struct
from cgi import parse_qsl from cgi import parse_qsl
from base64 import b64encode, b64decode from base64 import b64encode, b64decode
# Imports that vary by python version # Imports that vary by python version
# python 3.0 differences
if sys.hexversion > 0x3000000: if sys.hexversion > 0x3000000:
# python >= 3.0
from io import StringIO
from http.server import SimpleHTTPRequestHandler
from urllib.parse import urlsplit
b2s = lambda buf: buf.decode('latin_1') b2s = lambda buf: buf.decode('latin_1')
s2b = lambda s: s.encode('latin_1') s2b = lambda s: s.encode('latin_1')
s2a = lambda s: s s2a = lambda s: s
else: else:
# python 2.X b2s = lambda buf: buf # No-op
from cStringIO import StringIO s2b = lambda s: s # No-op
from SimpleHTTPServer import SimpleHTTPRequestHandler
from urlparse import urlsplit
# No-ops
b2s = lambda buf: buf
s2b = lambda s: s
s2a = lambda s: [ord(c) for c in s] s2a = lambda s: [ord(c) for c in s]
try: from io import StringIO
except: from cStringIO import StringIO
try: from http.server import SimpleHTTPRequestHandler
except: from SimpleHTTPServer import SimpleHTTPRequestHandler
try: from urllib.parse import urlsplit
except: from urlparse import urlsplit
if sys.hexversion >= 0x2060000: # python 2.6 differences
# python >= 2.6 try: from hashlib import md5, sha1
from multiprocessing import Process except: from md5 import md5; from sha import sha as sha1
from hashlib import md5, sha1
else:
# python < 2.6
Process = None
from md5 import md5
from sha import sha as sha1
if sys.hexversion >= 0x2050000: # python 2.5 differences
# python >= 2.5 try:
import struct from struct import pack, unpack_from
else: except:
# python < 2.5 from struct import pack
import struct def unpack_from(fmt, buf, offset=0):
# unpack_from was introduced in python 2.5
def _unpack_from(fmt, buf, offset=0):
slice = buffer(buf, offset, struct.calcsize(fmt)) slice = buffer(buf, offset, struct.calcsize(fmt))
return struct.unpack(fmt, slice) return struct.unpack(fmt, slice)
struct.unpack_from = _unpack_from
# Degraded functionality if these imports are missing # Degraded functionality if these imports are missing
for mod, sup in [('numpy', 'HyBi protocol'), for mod, sup in [('numpy', 'HyBi protocol'), ('ssl', 'TLS/SSL/wss'),
('ssl', 'TLS/SSL/wss'), ('resource', 'daemonizing')]: ('multiprocessing', 'Multi-Processing'),
('resource', 'daemonizing')]:
try: try:
globals()[mod] = __import__(mod) globals()[mod] = __import__(mod)
except ImportError: except ImportError:
globals()[mod] = None globals()[mod] = None
print("WARNING: no '%s' module, %s decode may be slower" % ( print("WARNING: no '%s' module, %s is slower or disabled" % (
mod, sup)) mod, sup))
if multiprocessing and sys.platform == 'win32':
# make sockets pickle-able/inheritable
import multiprocessing.reduction
class WebSocketServer(object): class WebSocketServer(object):
@ -277,11 +271,11 @@ Sec-WebSocket-Accept: %s\r
b1 = 0x80 | (opcode & 0x0f) # FIN + opcode b1 = 0x80 | (opcode & 0x0f) # FIN + opcode
payload_len = len(buf) payload_len = len(buf)
if payload_len <= 125: if payload_len <= 125:
header = struct.pack('>BB', b1, payload_len) header = pack('>BB', b1, payload_len)
elif payload_len > 125 and payload_len < 65536: elif payload_len > 125 and payload_len < 65536:
header = struct.pack('>BBH', b1, 126, payload_len) header = pack('>BBH', b1, 126, payload_len)
elif payload_len >= 65536: elif payload_len >= 65536:
header = struct.pack('>BBQ', b1, 127, payload_len) header = pack('>BBQ', b1, 127, payload_len)
#print("Encoded: %s" % repr(header + buf)) #print("Encoded: %s" % repr(header + buf))
@ -318,7 +312,7 @@ Sec-WebSocket-Accept: %s\r
if blen < f['hlen']: if blen < f['hlen']:
return f # Incomplete frame header return f # Incomplete frame header
b1, b2 = struct.unpack_from(">BB", buf) b1, b2 = unpack_from(">BB", buf)
f['opcode'] = b1 & 0x0f f['opcode'] = b1 & 0x0f
f['fin'] = (b1 & 0x80) >> 7 f['fin'] = (b1 & 0x80) >> 7
has_mask = (b2 & 0x80) >> 7 has_mask = (b2 & 0x80) >> 7
@ -329,12 +323,12 @@ Sec-WebSocket-Accept: %s\r
f['hlen'] = 4 f['hlen'] = 4
if blen < f['hlen']: if blen < f['hlen']:
return f # Incomplete frame header return f # Incomplete frame header
(f['length'],) = struct.unpack_from('>xxH', buf) (f['length'],) = unpack_from('>xxH', buf)
elif f['length'] == 127: elif f['length'] == 127:
f['hlen'] = 10 f['hlen'] = 10
if blen < f['hlen']: if blen < f['hlen']:
return f # Incomplete frame header return f # Incomplete frame header
(f['length'],) = struct.unpack_from('>xxQ', buf) (f['length'],) = unpack_from('>xxQ', buf)
full_len = f['hlen'] + has_mask * 4 + f['length'] full_len = f['hlen'] + has_mask * 4 + f['length']
@ -363,7 +357,7 @@ Sec-WebSocket-Accept: %s\r
if f['opcode'] == 0x08: if f['opcode'] == 0x08:
if f['length'] >= 2: if f['length'] >= 2:
f['close_code'] = struct.unpack_from(">H", f['payload']) f['close_code'] = unpack_from(">H", f['payload'])
if f['length'] > 3: if f['length'] > 3:
f['close_reason'] = f['payload'][2:] f['close_reason'] = f['payload'][2:]
@ -393,7 +387,7 @@ Sec-WebSocket-Accept: %s\r
num1 = int("".join([c for c in key1 if c.isdigit()])) / spaces1 num1 = int("".join([c for c in key1 if c.isdigit()])) / spaces1
num2 = int("".join([c for c in key2 if c.isdigit()])) / spaces2 num2 = int("".join([c for c in key2 if c.isdigit()])) / spaces2
return b2s(md5(struct.pack('>II8s', return b2s(md5(pack('>II8s',
int(num1), int(num2), key3)).digest()) int(num1), int(num2), key3)).digest())
# #
@ -544,7 +538,7 @@ Sec-WebSocket-Accept: %s\r
if self.version.startswith("hybi"): if self.version.startswith("hybi"):
msg = s2b('') msg = s2b('')
if code != None: if code != None:
msg = struct.pack(">H%ds" % (len(reason)), code) msg = pack(">H%ds" % (len(reason)), code)
buf, h, t = self.encode_hybi(msg, opcode=0x08, base64=False) buf, h, t = self.encode_hybi(msg, opcode=0x08, base64=False)
self.client.send(buf) self.client.send(buf)
@ -803,7 +797,7 @@ Sec-WebSocket-Accept: %s\r
# Allow override of SIGINT # Allow override of SIGINT
signal.signal(signal.SIGINT, self.do_SIGINT) signal.signal(signal.SIGINT, self.do_SIGINT)
if not Process: if not multiprocessing:
# os.fork() (python 2.4) child reaper # os.fork() (python 2.4) child reaper
signal.signal(signal.SIGCHLD, self.fallback_SIGCHLD) signal.signal(signal.SIGCHLD, self.fallback_SIGCHLD)
@ -849,9 +843,10 @@ Sec-WebSocket-Accept: %s\r
self.msg('%s: exiting due to --run-once' self.msg('%s: exiting due to --run-once'
% address[0]) % address[0])
break break
elif Process: elif multiprocessing:
self.vmsg('%s: new handler Process' % address[0]) self.vmsg('%s: new handler Process' % address[0])
p = Process(target=self.top_new_client, p = multiprocessing.Process(
target=self.top_new_client,
args=(startsock, address)) args=(startsock, address))
p.start() p.start()
# child will not return # child will not return