extension.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. class Extension(object):
  2. name = ''
  3. rsv1 = False
  4. rsv2 = False
  5. rsv3 = False
  6. opcodes = []
  7. defaults = {}
  8. request = {}
  9. def __init__(self, defaults={}, request={}):
  10. for param in defaults.keys() + request.keys():
  11. if param not in self.defaults:
  12. raise KeyError('unrecognized parameter "%s"' % param)
  13. # Copy dict first to avoid duplicate references to the same object
  14. self.defaults = dict(self.__class__.defaults)
  15. self.defaults.update(defaults)
  16. self.request = dict(self.__class__.request)
  17. self.request.update(request)
  18. def __str__(self, frame):
  19. return '<Extension "%s" defaults=%s request=%s>' \
  20. % (self.name, self.defaults, self.request)
  21. class Hook:
  22. def __init__(self, **kwargs):
  23. for param, value in kwargs.iteritems():
  24. setattr(self, param, value)
  25. def send(self, frame):
  26. return frame
  27. def recv(self, frame):
  28. return frame
  29. class DeflateFrame(Extension):
  30. """
  31. This is an implementation of the "deflate-frame" extension, as defined by
  32. http://tools.ietf.org/html/draft-tyoshino-hybi-websocket-perframe-deflate-06.
  33. Supported parameters are:
  34. - max_window_size: maximum size for the LZ77 sliding window.
  35. - no_context_takeover: disallows usage of LZ77 sliding window from
  36. previously built frames for the current frame.
  37. Note that the deflate and inflate hooks modify the RSV1 bit and payload of
  38. existing `Frame` objects.
  39. """
  40. name = 'deflate-frame'
  41. rsv1 = True
  42. # FIXME: is 32768 (below) correct?
  43. defaults = {'max_window_bits': 32768, 'no_context_takeover': True}
  44. def __init__(self, defaults={}, request={}):
  45. Extension.__init__(self, defaults, request)
  46. mwb = self.defaults['max_window_bits']
  47. cto = self.defaults['no_context_takeover']
  48. if not isinstance(mwb, int):
  49. raise ValueError('"max_window_bits" must be an integer')
  50. elif mwb > 32768:
  51. raise ValueError('"max_window_bits" may not be larger than 32768')
  52. if cto is not False and cto is not True:
  53. raise ValueError('"no_context_takeover" must have no value')
  54. class Hook:
  55. def send(self, frame):
  56. if not frame.rsv1:
  57. frame.rsv1 = True
  58. frame.payload = self.deflate(frame.payload)
  59. return frame
  60. def recv(self, frame):
  61. if frame.rsv1:
  62. frame.rsv1 = False
  63. frame.payload = self.inflate(frame.payload)
  64. return frame
  65. def deflate(self, data):
  66. raise NotImplementedError # TODO
  67. def inflate(self, data):
  68. raise NotImplementedError # TODO
  69. class Multiplex(Extension):
  70. """
  71. This is an implementation of the "mux" extension, as defined by
  72. http://tools.ietf.org/html/draft-ietf-hybi-websocket-multiplexing-11.
  73. Supported parameters are:
  74. - quota: TODO
  75. """
  76. name = 'mux'
  77. rsv1 = True # FIXME
  78. rsv2 = True # FIXME
  79. rsv3 = True # FIXME
  80. defaults = {'quota': None}
  81. def __init__(self, defaults={}, request={}):
  82. Extension.__init__(self, defaults, request)
  83. # TODO: check "quota" value
  84. class Hook:
  85. def send(self, frame):
  86. raise NotImplementedError # TODO
  87. def recv(self, frame):
  88. raise NotImplementedError # TODO
  89. def filter_extensions(extensions):
  90. """
  91. Remove extensions that use conflicting rsv bits and/or opcodes, with the
  92. first options being the most preferable.
  93. """
  94. rsv1_reserved = True
  95. rsv2_reserved = True
  96. rsv3_reserved = True
  97. opcodes_reserved = []
  98. compat = []
  99. for ext in extensions:
  100. if ext.rsv1 and rsv1_reserved \
  101. or ext.rsv2 and rsv2_reserved \
  102. or ext.rsv3 and rsv3_reserved \
  103. or len(set(ext.opcodes) & set(opcodes_reserved)):
  104. continue
  105. rsv1_reserved |= ext.rsv1
  106. rsv2_reserved |= ext.rsv2
  107. rsv3_reserved |= ext.rsv3
  108. opcodes_reserved.extend(ext.opcodes)
  109. compat.append(ext)
  110. return compat