permessage_deflate 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5a45dd8e4933a9a41365043f0b770ad2f9df343c
4
+ data.tar.gz: 59b4bbba3b0fc00a970604c1a307bf8b80fc034a
5
+ SHA512:
6
+ metadata.gz: b1c220ecbfe1f83d19c6a59d6045478bc827cb36f72e1f97ad914dc04002f188239bba1adde415f65cb85911a7c423e545e12d6ff1230d57e7bc62626c871af0
7
+ data.tar.gz: ae327691407e387275f0f0e8a5ce54817108a9f4b4dc2fbd9245f23514873ac3585e380ccfe04b38f3a64a5c93d9d8b6a828f87e06550b759cf2ba8f2a29e8f5
data/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # permessage_deflate [![Build status](https://secure.travis-ci.org/faye/permessage-deflate-ruby.svg)](http://travis-ci.org/faye/permessage-deflate-ruby)
2
+
3
+ Implements the
4
+ [permessage-deflate](https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression)
5
+ WebSocket protocol extension as a plugin for
6
+ [websocket-extensions](https://github.com/faye/websocket-extensions-ruby).
7
+
8
+ ## Installation
9
+
10
+ ```
11
+ $ gem install permessage_deflate
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ Add the plugin to your extensions:
17
+
18
+ ```rb
19
+ require 'websocket/extensions'
20
+ require 'permessage_deflate'
21
+
22
+ exts = WebSocket::Extensions.new
23
+ exts.add(PermessageDeflate)
24
+ ```
25
+
26
+ The extension can be configured, for example:
27
+
28
+ ```rb
29
+ require 'websocket/extensions'
30
+ require 'permessage_deflate'
31
+
32
+ deflate = PermessageDeflate.configure(
33
+ :level => Zlib::BEST_COMPRESSION,
34
+ :max_window_bits => 13
35
+ )
36
+
37
+ exts = WebSocket::Extensions.new
38
+ exts.add(deflate)
39
+ ```
40
+
41
+ The set of available options can be split into two sets: those that control the
42
+ session's compressor for outgoing messages and do not need to be communicated to
43
+ the peer, and those that are negoatiated as part of the protocol. The settings
44
+ only affecting the compressor are described fully in the [Zlib
45
+ documentation](http://ruby-doc.org/stdlib-2.1.0/libdoc/zlib/rdoc/Zlib/Deflate.html#method-c-new):
46
+
47
+ * `:level`: sets the compression level, can be an integer from `0` to `9`, or
48
+ one of the contants `Zlib::NO_COMPRESSION`, `Zlib::BEST_SPEED`,
49
+ `Zlib::BEST_COMPRESSION`, or `Zlib::DEFAULT_COMPRESSION`
50
+ * `:mem_level`: sets how much memory the compressor allocates, can be an integer
51
+ from `1` to `9`, or one of the constants `Zlib::MAX_MEM_LEVEL`, or
52
+ `Zlib::DEF_MEM_LEVEL`
53
+ * `:strategy`: can be one of the constants `Zlib::FILTERED`,
54
+ `Zlib::HUFFMAN_ONLY`, `Zlib::RLE`, `Zlib::FIXED`, or `Zlib::DEFAULT_STRATEGY`
55
+
56
+ The other options relate to settings that are negotiated via the protocol and
57
+ can be used to set the local session's behaviour and control that of the peer:
58
+
59
+ * `:no_context_takeover`: if `true`, stops the session reusing a deflate context
60
+ between messages
61
+ * `:request_no_context_takeover`: if `true`, makes the session tell the other
62
+ peer not to reuse a deflate context between messages
63
+ * `:max_window_bits`: an integer from `8` to `15` inclusive that sets the
64
+ maximum size of the session's sliding window; a lower window size will be used
65
+ if requested by the peer
66
+ * `:request_max_window_bits`: an integer from `8` to `15` inclusive to ask the
67
+ other peer to use to set its maximum sliding window size, if supported
68
+
69
+ ## License
70
+
71
+ (The MIT License)
72
+
73
+ Copyright (c) 2014 James Coglan
74
+
75
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
76
+ this software and associated documentation files (the 'Software'), to deal in
77
+ the Software without restriction, including without limitation the rights to
78
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
79
+ of the Software, and to permit persons to whom the Software is furnished to do
80
+ so, subject to the following conditions:
81
+
82
+ The above copyright notice and this permission notice shall be included in all
83
+ copies or substantial portions of the Software.
84
+
85
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
86
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
87
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
88
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
89
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
90
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
91
+ SOFTWARE.
@@ -0,0 +1,41 @@
1
+ require 'zlib'
2
+
3
+ class PermessageDeflate
4
+ root = File.expand_path('..', __FILE__)
5
+ require root + '/permessage_deflate/session'
6
+ require root + '/permessage_deflate/client_session'
7
+ require root + '/permessage_deflate/server_session'
8
+
9
+ ConfigurationError = Class.new(ArgumentError)
10
+
11
+ module Extension
12
+ define_method(:name) { 'permessage-deflate' }
13
+ define_method(:type) { 'permessage' }
14
+ define_method(:rsv1) { true }
15
+ define_method(:rsv2) { false }
16
+ define_method(:rsv3) { false }
17
+
18
+ def configure(options)
19
+ options = (@options || {}).merge(options)
20
+ PermessageDeflate.new(options)
21
+ end
22
+
23
+ def create_client_session
24
+ ClientSession.new(@options || {})
25
+ end
26
+
27
+ def create_server_session(offers)
28
+ offers.each do |offer|
29
+ return ServerSession.new(@options || {}, offer) if ServerSession.valid_params?(offer)
30
+ end
31
+ nil
32
+ end
33
+ end
34
+
35
+ include Extension
36
+ extend Extension
37
+
38
+ def initialize(options)
39
+ @options = options
40
+ end
41
+ end
@@ -0,0 +1,73 @@
1
+ class PermessageDeflate
2
+ class ClientSession < Session
3
+
4
+ def self.valid_params?(params)
5
+ return false unless super
6
+
7
+ if params.has_key?('client_max_window_bits')
8
+ return false unless VALID_WINDOW_BITS.include?(params['client_max_window_bits'])
9
+ end
10
+
11
+ true
12
+ end
13
+
14
+ def generate_offer
15
+ offer = {}
16
+
17
+ if @accept_no_context_takeover
18
+ offer['client_no_context_takeover'] = true
19
+ end
20
+
21
+ if @accept_max_window_bits
22
+ unless VALID_WINDOW_BITS.include?(@accept_max_window_bits)
23
+ raise ConfigurationError, 'Invalid value for max_window_bits'
24
+ end
25
+ offer['client_max_window_bits'] = @accept_max_window_bits
26
+ else
27
+ offer['client_max_window_bits'] = true
28
+ end
29
+
30
+ if @request_no_context_takeover
31
+ offer['server_no_context_takeover'] = true
32
+ end
33
+
34
+ if @request_max_window_bits
35
+ unless VALID_WINDOW_BITS.include?(@request_max_window_bits)
36
+ raise ConfigurationError, 'Invalid value for request_max_window_bits'
37
+ end
38
+ offer['server_max_window_bits'] = @request_max_window_bits
39
+ end
40
+
41
+ offer
42
+ end
43
+
44
+ def activate(params)
45
+ return false unless ClientSession.valid_params?(params)
46
+
47
+ if @accept_max_window_bits and params['client_max_window_bits']
48
+ return false if params['client_max_window_bits'] > @accept_max_window_bits
49
+ end
50
+
51
+ if @request_no_context_takeover and !params['server_no_context_takeover']
52
+ return false
53
+ end
54
+
55
+ if @request_max_window_bits
56
+ return false unless params['server_max_window_bits']
57
+ return false if params['server_max_window_bits'] > @request_max_window_bits
58
+ end
59
+
60
+ @own_context_takeover = !(@accept_no_context_takeover || params['client_no_context_takeover'])
61
+ @own_window_bits = [
62
+ @accept_max_window_bits || DEFAULT_MAX_WINDOW_BITS,
63
+ params['client_max_window_bits'] || DEFAULT_MAX_WINDOW_BITS
64
+ ].min
65
+
66
+ @peer_context_takeover = !params['server_no_context_takeover']
67
+ @peer_window_bits = params['server_max_window_bits'] || DEFAULT_MAX_WINDOW_BITS
68
+
69
+ true
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,59 @@
1
+ class PermessageDeflate
2
+ class ServerSession < Session
3
+
4
+ def self.valid_params?(params)
5
+ return false unless super
6
+
7
+ if params.has_key?('client_max_window_bits')
8
+ return false unless ([true] + VALID_WINDOW_BITS).include?(params['client_max_window_bits'])
9
+ end
10
+
11
+ true
12
+ end
13
+
14
+ def initialize(options, params)
15
+ super(options)
16
+ @params = params
17
+ end
18
+
19
+ def generate_response
20
+ params = {}
21
+
22
+ # https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression#section-8.1.1.1
23
+ if @accept_no_context_takeover or @params['server_no_context_takeover']
24
+ params['server_no_context_takeover'] = true
25
+ end
26
+
27
+ # https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression#section-8.1.1.2
28
+ if @request_no_context_takeover or @params['client_no_context_takeover']
29
+ params['client_no_context_takeover'] = true
30
+ end
31
+
32
+ # https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression#section-8.1.2.1
33
+ if @accept_max_window_bits or @params['server_max_window_bits']
34
+ accept_max = @accept_max_window_bits || DEFAULT_MAX_WINDOW_BITS
35
+ server_max = @params['server_max_window_bits'] || DEFAULT_MAX_WINDOW_BITS
36
+ params['server_max_window_bits'] = [accept_max, server_max].min
37
+ end
38
+
39
+ # https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression#section-8.1.2.2
40
+ if client_max = @params['client_max_window_bits']
41
+ if client_max == true
42
+ params['client_max_window_bits'] = @request_max_window_bits if @request_max_window_bits
43
+ else
44
+ request_max = @request_max_window_bits || DEFAULT_MAX_WINDOW_BITS
45
+ params['client_max_window_bits'] = [request_max, client_max].min
46
+ end
47
+ end
48
+
49
+ @own_context_takeover = !params['server_no_context_takeover']
50
+ @own_window_bits = params['server_max_window_bits'] || DEFAULT_MAX_WINDOW_BITS
51
+
52
+ @peer_context_takeover = !params['client_no_context_takeover']
53
+ @peer_window_bits = params['client_max_window_bits'] || DEFAULT_MAX_WINDOW_BITS
54
+
55
+ params
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,97 @@
1
+ class PermessageDeflate
2
+ class Session
3
+
4
+ VALID_PARAMS = [
5
+ 'server_no_context_takeover',
6
+ 'client_no_context_takeover',
7
+ 'server_max_window_bits',
8
+ 'client_max_window_bits'
9
+ ]
10
+
11
+ DEFAULT_MAX_WINDOW_BITS = 15
12
+ VALID_WINDOW_BITS = [8, 9, 10, 11, 12, 13, 14, 15]
13
+
14
+ def self.valid_params?(params)
15
+ return false unless params.keys.all? { |k| VALID_PARAMS.include?(k) }
16
+ return false if params.values.grep(Array).any?
17
+
18
+ if params.has_key?('server_no_context_takeover')
19
+ return false unless params['server_no_context_takeover'] == true
20
+ end
21
+
22
+ if params.has_key?('client_no_context_takeover')
23
+ return false unless params['client_no_context_takeover'] == true
24
+ end
25
+
26
+ if params.has_key?('server_max_window_bits')
27
+ return false unless VALID_WINDOW_BITS.include?(params['server_max_window_bits'])
28
+ end
29
+
30
+ true
31
+ end
32
+
33
+ def initialize(options)
34
+ @level = options.fetch(:level, Zlib::DEFAULT_COMPRESSION)
35
+ @mem_level = options.fetch(:mem_level, Zlib::DEF_MEM_LEVEL)
36
+ @strategy = options.fetch(:strategy, Zlib::DEFAULT_STRATEGY)
37
+
38
+ @accept_no_context_takeover = options.fetch(:no_context_takeover, false)
39
+ @accept_max_window_bits = options.fetch(:max_window_bits, nil)
40
+ @request_no_context_takeover = options.fetch(:request_no_context_takeover, false)
41
+ @request_max_window_bits = options.fetch(:request_max_window_bits, nil)
42
+ end
43
+
44
+ def process_incoming_message(message)
45
+ return message unless message.rsv1
46
+
47
+ inflate = get_inflate
48
+
49
+ message.data = inflate.inflate(message.data) +
50
+ inflate.inflate([0x00, 0x00, 0xff, 0xff].pack('C*'))
51
+
52
+ free(inflate) unless @inflate
53
+ message
54
+ end
55
+
56
+ def process_outgoing_message(message)
57
+ deflate = get_deflate
58
+
59
+ message.data = deflate.deflate(message.data, Zlib::SYNC_FLUSH)[0...-4]
60
+ message.rsv1 = true
61
+
62
+ free(deflate) unless @deflate
63
+ message
64
+ end
65
+
66
+ def close
67
+ free(@inflate)
68
+ @inflate = nil
69
+
70
+ free(@deflate)
71
+ @deflate = nil
72
+ end
73
+
74
+ private
75
+
76
+ def free(codec)
77
+ return if codec.nil?
78
+ codec.finish rescue nil
79
+ codec.close
80
+ end
81
+
82
+ def get_inflate
83
+ return @inflate if @inflate
84
+ inflate = Zlib::Inflate.new(-@peer_window_bits)
85
+ @inflate = inflate if @peer_context_takeover
86
+ inflate
87
+ end
88
+
89
+ def get_deflate
90
+ return @deflate if @deflate
91
+ deflate = Zlib::Deflate.new(@level, -@own_window_bits, @mem_level, @strategy)
92
+ @deflate = deflate if @own_context_takeover
93
+ deflate
94
+ end
95
+
96
+ end
97
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: permessage_deflate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - James Coglan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description:
28
+ email: jcoglan@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files:
32
+ - README.md
33
+ files:
34
+ - README.md
35
+ - lib/permessage_deflate.rb
36
+ - lib/permessage_deflate/client_session.rb
37
+ - lib/permessage_deflate/server_session.rb
38
+ - lib/permessage_deflate/session.rb
39
+ homepage: http://github.com/faye/permessage-deflate-ruby
40
+ licenses:
41
+ - MIT
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options:
45
+ - "--main"
46
+ - README.md
47
+ - "--markup"
48
+ - markdown
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements: []
62
+ rubyforge_project:
63
+ rubygems_version: 2.2.2
64
+ signing_key:
65
+ specification_version: 4
66
+ summary: Per-message DEFLATE compression extension for WebSocket connections
67
+ test_files: []