From f34c2970913cdfad89e13237150eae73633a1604 Mon Sep 17 00:00:00 2001 From: Achille Roussel Date: Fri, 10 Jul 2015 11:55:05 -0700 Subject: [PATCH] use wsaccel if available --- websocket/_abnf.py | 28 ++++++++++----- websocket/_http.py | 2 +- websocket/_utils.py | 88 +++++++++++++++++++++++++-------------------- 3 files changed, 70 insertions(+), 48 deletions(-) diff --git a/websocket/_abnf.py b/websocket/_abnf.py index 5f5b1b8..04a8505 100644 --- a/websocket/_abnf.py +++ b/websocket/_abnf.py @@ -26,6 +26,24 @@ import os from ._exceptions import * from ._utils import validate_utf8 +try: + # If wsaccel is available we use compiled routines to mask data. + from wsaccel.xormask import XorMaskerSimple + + def _mask(_m, _d): + return XorMaskerSimple(_m).process(_d) + +except ImportError: + # wsaccel is not available, we rely on python implementations. + def _mask(_m, _d): + for i in range(len(_d)): + _d[i] ^= _m[i % 4] + + if six.PY3: + return _d.tobytes() + else: + return _d.tostring() + # closing frame status codes. STATUS_NORMAL = 1000 STATUS_GOING_AWAY = 1001 @@ -208,6 +226,7 @@ class ABNF(object): """ if data == None: data = "" + if isinstance(mask_key, six.text_type): mask_key = six.b(mask_key) @@ -216,14 +235,7 @@ class ABNF(object): _m = array.array("B", mask_key) _d = array.array("B", data) - for i in range(len(_d)): - _d[i] ^= _m[i % 4] - - if six.PY3: - return _d.tobytes() - else: - return _d.tostring() - + return _mask(_m, _d) class frame_buffer(object): _HEADER_MASK_INDEX = 5 diff --git a/websocket/_http.py b/websocket/_http.py index 662cb10..5c4a638 100644 --- a/websocket/_http.py +++ b/websocket/_http.py @@ -119,7 +119,7 @@ def _open_socket(addrinfo_list, sockopt, timeout): def _can_use_sni(): - return six.Py2 and sys.version_info >= (2, 7, 9) or sys.version_info >= (3, 2) + return six.PY2 and sys.version_info >= (2, 7, 9) or sys.version_info >= (3, 2) def _wrap_sni_socket(sock, sslopt, hostname, check_hostname): diff --git a/websocket/_utils.py b/websocket/_utils.py index 7ece785..ac14b4a 100644 --- a/websocket/_utils.py +++ b/websocket/_utils.py @@ -28,43 +28,63 @@ class NoLock(object): def __enter__(self): pass - def __exit__(self,type, value, traceback): + def __exit__(self, type, value, traceback): pass +try: + # If wsaccel is availabe we use compiled routines to validate UTF-8 + # strings. + from wsaccel.utf8validator import Utf8Validator -# UTF-8 validator -# python implementation of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + def _validate_utf8(utfbytes): + return Utf8Validator().validate(utfbytes)[0] -UTF8_ACCEPT = 0 -UTF8_REJECT=12 +except ImportError: + # UTF-8 validator + # python implementation of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ -_UTF8D = [ - # The first part of the table maps bytes to character classes that - # to reduce the size of the transition table and create bitmasks. - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + _UTF8_ACCEPT = 0 + _UTF8_REJECT = 12 - # The second part is a transition table that maps a combination - # of a state of the automaton and a character class to a state. - 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, - 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, - 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, - 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, - 12,36,12,12,12,12,12,12,12,12,12,12, ] + _UTF8D = [ + # The first part of the table maps bytes to character classes that + # to reduce the size of the transition table and create bitmasks. + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, -def _decode(state, codep, ch): - tp = _UTF8D[ch] + # The second part is a transition table that maps a combination + # of a state of the automaton and a character class to a state. + 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, + 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, + 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, + 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, + 12,36,12,12,12,12,12,12,12,12,12,12, ] - codep = (ch & 0x3f ) | (codep << 6) if (state != UTF8_ACCEPT) else (0xff >> tp) & (ch) - state = _UTF8D[256 + state + tp] + def _decode(state, codep, ch): + tp = _UTF8D[ch] - return state, codep; + codep = (ch & 0x3f ) | (codep << 6) if (state != _UTF8_ACCEPT) else (0xff >> tp) & (ch) + state = _UTF8D[256 + state + tp] + + return state, codep; + + def _validate_utf8(utfbytes): + state = _UTF8_ACCEPT + codep = 0 + for i in utfbytes: + if six.PY2: + i = ord(i) + state, codep = _decode(state, codep, i) + if state == _UTF8_REJECT: + return False + + return True def validate_utf8(utfbytes): """ @@ -72,17 +92,7 @@ def validate_utf8(utfbytes): utfbytes: utf byte string to check. return value: if valid utf8 string, return true. Otherwise, return false. """ - state = UTF8_ACCEPT - codep = 0 - for i in utfbytes: - if six.PY2: - i = ord(i) - state, codep = _decode(state, codep, i) - if state == UTF8_REJECT: - return False - - return True - + return _validate_utf8(utfbytes) def extract_err_message(exception): if exception.args: