deflate_frame.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import zlib
  2. from extension import Extension
  3. from frame import ControlFrame
  4. class DeflateFrame(Extension):
  5. """
  6. This is an implementation of the "deflate-frame" extension, as defined by
  7. http://tools.ietf.org/html/draft-tyoshino-hybi-websocket-perframe-deflate-06.
  8. Supported parameters are:
  9. - max_window_size: maximum size for the LZ77 sliding window.
  10. - no_context_takeover: disallows usage of LZ77 sliding window from
  11. previously built frames for the current frame.
  12. Note that the deflate and inflate hooks modify the RSV1 bit and payload of
  13. existing `Frame` objects.
  14. """
  15. names = ('deflate-frame', 'x-webkit-deflate-frame')
  16. rsv1 = True
  17. defaults = {
  18. 'max_window_bits': zlib.MAX_WBITS,
  19. 'no_context_takeover': False
  20. }
  21. compression_threshold = 64 # minimal payload size for compression
  22. def negotiate(self, name, params):
  23. if 'max_window_bits' in params:
  24. mwb = int(params['max_window_bits'])
  25. assert 8 <= mwb <= zlib.MAX_WBITS
  26. yield 'max_window_bits', mwb
  27. if 'no_context_takeover' in params:
  28. assert params['no_context_takeover'] is True
  29. yield 'no_context_takeover', True
  30. class Instance(Extension.Instance):
  31. def init(self):
  32. if not self.no_context_takeover:
  33. self.defl = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
  34. zlib.DEFLATED, -self.max_window_bits)
  35. self.dec = zlib.decompressobj(-self.max_window_bits)
  36. def onsend_frame(self, frame):
  37. if not frame.rsv1 and not isinstance(frame, ControlFrame) and \
  38. len(frame.payload) > self.extension.compression_threshold:
  39. frame.rsv1 = True
  40. frame.payload = self.deflate(frame)
  41. def onrecv_frame(self, frame):
  42. if frame.rsv1:
  43. if isinstance(frame, ControlFrame):
  44. raise ValueError('received compressed control frame')
  45. frame.rsv1 = False
  46. frame.payload = self.inflate(frame.payload)
  47. def deflate(self, frame):
  48. if self.no_context_takeover:
  49. print 'no_context_takeover'
  50. compressed = zlib.compress(frame.payload)
  51. else:
  52. compressed = self.defl.compress(frame.payload)
  53. compressed += self.defl.flush(zlib.Z_SYNC_FLUSH)
  54. assert compressed[-4:] == '\x00\x00\xff\xff'
  55. return compressed[:-4]
  56. def inflate(self, data):
  57. data = str(data + '\x00\x00\xff\xff')
  58. if self.no_context_takeover:
  59. dec = zlib.decompressobj(-self.max_window_bits)
  60. return dec.decompress(data) + dec.flush()
  61. return self.dec.decompress(data)