extension.py 4.3 KB

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