deflate_message.py 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import zlib
  2. from extension import Extension
  3. from deflate_frame import DeflateFrame
  4. class DeflateMessage(Extension):
  5. """
  6. Implementation of the "permessage-deflate" extension, as defined by
  7. http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-17.
  8. Note: this implementetion is only eligible for server sockets, client
  9. sockets must NOT use it.
  10. """
  11. name = 'permessage-deflate'
  12. rsv1 = True
  13. defaults = {
  14. 'client_max_window_bits': zlib.MAX_WBITS,
  15. 'client_no_context_takeover': False,
  16. 'server_max_window_bits': zlib.MAX_WBITS,
  17. 'server_no_context_takeover': False
  18. }
  19. before_fragmentation = True
  20. compression_threshold = 20 # minimal message payload size for compression
  21. def negotiate(self, name, params):
  22. default = self.defaults['client_max_window_bits']
  23. if 'client_max_window_bits' in params:
  24. mwb = params['client_max_window_bits']
  25. if mwb is True:
  26. if default != zlib.MAX_WBITS:
  27. yield 'client_max_window_bits', default
  28. else:
  29. mwb = int(mwb)
  30. assert 8 <= mwb <= zlib.MAX_WBITS
  31. yield 'client_max_window_bits', min(mwb, default)
  32. elif default != zlib.MAX_WBITS:
  33. yield 'client_max_window_bits', default
  34. if 'client_no_context_takeover' in params:
  35. assert params['client_no_context_takeover'] is True
  36. yield 'client_no_context_takeover', True
  37. elif self.defaults['client_no_context_takeover']:
  38. yield 'client_no_context_takeover', True
  39. default = self.defaults['server_max_window_bits']
  40. if 'server_max_window_bits' in params:
  41. mwb = int(params['server_max_window_bits'])
  42. assert 8 <= mwb <= zlib.MAX_WBITS
  43. yield 'server_max_window_bits', min(mwb, default)
  44. elif default != zlib.MAX_WBITS:
  45. yield 'server_max_window_bits', default
  46. if 'server_no_context_takeover' in params:
  47. assert params['server_no_context_takeover'] is True
  48. yield 'server_no_context_takeover', True
  49. elif self.defaults['server_no_context_takeover']:
  50. yield 'server_no_context_takeover', True
  51. class Instance(DeflateFrame.Instance):
  52. def init(self):
  53. if not self.server_no_context_takeover:
  54. self.defl = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
  55. zlib.DEFLATED, -self.server_max_window_bits)
  56. if not self.client_no_context_takeover:
  57. self.dec = zlib.decompressobj(-self.client_max_window_bits)
  58. def deflate(self, data):
  59. if self.server_no_context_takeover:
  60. self.defl = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
  61. zlib.DEFLATED, -self.server_max_window_bits)
  62. compressed = self.defl.compress(data)
  63. compressed += self.defl.flush(zlib.Z_SYNC_FLUSH)
  64. assert compressed[-4:] == '\x00\x00\xff\xff'
  65. return compressed[:-4]
  66. def inflate(self, data):
  67. data = str(data + '\x00\x00\xff\xff')
  68. if self.client_no_context_takeover:
  69. self.dec = zlib.decompressobj(-self.client_max_window_bits)
  70. return self.dec.decompress(data)