cramp 0.15.1 → 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YTU3MzczMjYwZmVjOWNlODI4MDViMDkzODFkYmRlNTI2ZGFmY2E3Yw==
5
+ data.tar.gz: !binary |-
6
+ ZmExZWQwMTJiMGRkMTNjZWVlNmJlY2FhZmNmNjNjYTMzMWFhZWI0ZA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YjM0NjdiMDZjOWI0ZWZiMjBkMzg5ZDgwYWRhZWRiMjBjYjlhNTQwNGM2ODk3
10
+ ZmI4NzNhM2IyNmZjODBhMDIwNmZkODFjMTdhZmI0YWJjNTBmMzMyNzAwNzc2
11
+ NzJmOTMyMWEzZmI4OGNhOTEzZmM0MWQxODYyZWNmMjM1NmE4MjU=
12
+ data.tar.gz: !binary |-
13
+ MjA5NjZmN2I5NDAyNTNkYWM1YWU1NDliZTkwYzJlY2NlZTI5MzBhYmMzYzgz
14
+ ZmE4MzRlYTk0N2U5MThhNmFkZmY4YTlkYzlhMmUwZmE4YjVlOWQ1YmIyZGY0
15
+ MDM1ZDFjZTIyOTRmZGU1MmE5NzcwNDQwODQyYmFhZjQ2YTE5NTk=
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009-2010 Pratik Naik
1
+ Copyright (c) 2009-2011 Pratik Naik
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/lib/cramp.rb CHANGED
@@ -3,7 +3,6 @@ EM.epoll
3
3
 
4
4
  require 'active_support'
5
5
  require 'active_support/core_ext/class/attribute'
6
- require 'active_support/core_ext/class/inheritable_attributes'
7
6
  require 'active_support/core_ext/class/attribute_accessors'
8
7
  require 'active_support/core_ext/module/aliasing'
9
8
  require 'active_support/core_ext/module/attribute_accessors'
@@ -14,6 +13,7 @@ require 'active_support/core_ext/hash/except'
14
13
  require 'active_support/buffered_logger'
15
14
 
16
15
  require 'rack'
16
+ require 'faye/websocket'
17
17
 
18
18
  begin
19
19
  require 'fiber'
@@ -21,24 +21,24 @@ begin
21
21
  rescue LoadError
22
22
  # No fibers available!
23
23
  end
24
-
24
+
25
25
  module Cramp
26
- VERSION = '0.15.1'
26
+ VERSION = '0.15.2'
27
27
 
28
28
  mattr_accessor :logger
29
29
 
30
- autoload :Action, "cramp/action"
31
- autoload :Websocket, "cramp/websocket"
32
- autoload :WebsocketExtension, "cramp/websocket/extension"
33
- autoload :Protocol10FrameParser, "cramp/websocket/protocol10_frame_parser"
34
- autoload :SSE, "cramp/sse"
35
- autoload :LongPolling, "cramp/long_polling"
36
- autoload :Body, "cramp/body"
37
- autoload :PeriodicTimer, "cramp/periodic_timer"
38
- autoload :KeepConnectionAlive, "cramp/keep_connection_alive"
39
- autoload :Abstract, "cramp/abstract"
40
- autoload :Callbacks, "cramp/callbacks"
41
- autoload :FiberPool, "cramp/fiber_pool"
42
- autoload :ExceptionHandler, "cramp/exception_handler"
43
- autoload :TestCase, "cramp/test_case"
30
+ autoload :Action, 'cramp/action'
31
+ autoload :Websocket, 'cramp/websocket'
32
+ autoload :WebsocketExtension, 'cramp/websocket/extension'
33
+ autoload :Protocol10FrameParser, 'cramp/websocket/protocol10_frame_parser'
34
+ autoload :SSE, 'cramp/sse'
35
+ autoload :LongPolling, 'cramp/long_polling'
36
+ autoload :Body, 'cramp/body'
37
+ autoload :PeriodicTimer, 'cramp/periodic_timer'
38
+ autoload :KeepConnectionAlive, 'cramp/keep_connection_alive'
39
+ autoload :Abstract, 'cramp/abstract'
40
+ autoload :Callbacks, 'cramp/callbacks'
41
+ autoload :FiberPool, 'cramp/fiber_pool'
42
+ autoload :ExceptionHandler, 'cramp/exception_handler'
43
+ autoload :TestCase, 'cramp/test_case'
44
44
  end
@@ -63,6 +63,7 @@ module Cramp
63
63
  end
64
64
 
65
65
  def finish
66
+ @_state = :finishing
66
67
  @body.succeed if is_finishable?
67
68
  ensure
68
69
  @_state = :finished
data/lib/cramp/action.rb CHANGED
@@ -5,7 +5,23 @@ module Cramp
5
5
 
6
6
  def initialize(env)
7
7
  super
8
- @env['websocket.receive_callback'] = method(:_on_data_receive)
8
+
9
+ case
10
+ when Faye::EventSource.eventsource?(env)
11
+ # request has Accept: text/event-stream
12
+ # faye server adapter intercepts headers - need to send them in send_initial_response or use faye's implementation
13
+ @eventsource_detected = true
14
+ unless transport == :sse
15
+ err = "WARNING: Cramp got request with EventSource header on action with transport #{transport} (not sse)! Response may not contain valid http headers!"
16
+ Cramp.logger ? Cramp.logger.error(err) : $stderr.puts(err)
17
+ end
18
+ when Faye::WebSocket.websocket?(env)
19
+ @web_socket = Faye::WebSocket.new(env)
20
+ @web_socket.onmessage = lambda do |event|
21
+ message = event.data
22
+ _invoke_data_callbacks(message) if message.is_a?(String)
23
+ end
24
+ end
9
25
  end
10
26
 
11
27
  protected
@@ -20,13 +36,20 @@ module Cramp
20
36
  # Dont send no initial response. Just cache it for later.
21
37
  @_lp_status = status
22
38
  @_lp_headers = headers
39
+ when :sse
40
+ super
41
+ if @eventsource_detected
42
+ # Reconstruct headers that were killed by faye server adapter:
43
+ @body.call("HTTP/1.1 200 OK\r\n#{headers.map{|(k,v)| "#{k}: #{v.is_a?(Time) ? v.httpdate : v.to_s}"}.join("\r\n")}\r\n\r\n")
44
+ end
45
+ # send retry? @body.call("retry: #{ (@retry * 1000).floor }\r\n\r\n")
23
46
  else
24
47
  super
25
48
  end
26
49
  end
27
50
 
28
51
  class_attribute :default_sse_headers
29
- self.default_sse_headers = {'Content-Type' => 'text/event-stream', 'Cache-Control' => 'no-cache', 'Connection' => 'keep-alive'}
52
+ self.default_sse_headers = {'Content-Type' => 'text/event-stream', 'Cache-Control' => 'no-cache, no-store', 'Connection' => 'keep-alive'}
30
53
 
31
54
  class_attribute :default_chunked_headers
32
55
  self.default_chunked_headers = {'Transfer-Encoding' => 'chunked', 'Connection' => 'keep-alive'}
@@ -63,7 +86,9 @@ module Cramp
63
86
  end
64
87
 
65
88
  def render_sse(data, options = {})
89
+ #TODO: Faye uses \r\n for newlines, some compatibility?
66
90
  result = "id: #{sse_event_id}\n"
91
+ result << "event: #{options[:event]}\n" if options[:event]
67
92
  result << "retry: #{options[:retry]}\n" if options[:retry]
68
93
 
69
94
  data.split(/\n/).each {|d| result << "data: #{d}\n" }
@@ -73,13 +98,7 @@ module Cramp
73
98
  end
74
99
 
75
100
  def render_websocket(body, *)
76
- if websockets_protocol_10?
77
- data = encode(protocol10_parser.send_text_frame(body), 'BINARY')
78
- else
79
- data = ["\x00", body, "\xFF"].map(&method(:encode)) * ''
80
- end
81
-
82
- @body.call(data)
101
+ @web_socket.send(body)
83
102
  end
84
103
 
85
104
  CHUNKED_TERM = "\r\n"
@@ -111,13 +130,5 @@ module Cramp
111
130
  super
112
131
  end
113
132
 
114
- def websockets_protocol_10?
115
- [7, 8, 9, 10].include?(@env['HTTP_SEC_WEBSOCKET_VERSION'].to_i)
116
- end
117
-
118
- def protocol10_parser
119
- @protocol10_parser ||= Protocol10FrameParser.new
120
- end
121
-
122
133
  end
123
134
  end
@@ -4,7 +4,7 @@ module Cramp
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- class_inheritable_accessor :before_start_callbacks, :on_finish_callbacks, :on_start_callback, :on_data_callbacks, :instance_reader => false
7
+ class_attribute :before_start_callbacks, :on_finish_callbacks, :on_start_callback, :on_data_callbacks, :instance_reader => false
8
8
 
9
9
  self.before_start_callbacks = []
10
10
  self.on_finish_callbacks = []
@@ -62,26 +62,8 @@ module Cramp
62
62
  end
63
63
  end
64
64
 
65
- def _on_data_receive(data)
66
- websockets_protocol_10? ? _receive_protocol10_data(data) : _receive_protocol76_data(data)
67
- end
68
-
69
65
  protected
70
66
 
71
- def _receive_protocol10_data(data)
72
- protocol10_parser.data << data
73
-
74
- messages = @protocol10_parser.process_data
75
- messages.each do |type, content|
76
- _invoke_data_callbacks(content) if type == :text
77
- end
78
- end
79
-
80
- def _receive_protocol76_data(data)
81
- data = data.split(/\000([^\377]*)\377/).select{|d| !d.empty? }.collect{|d| d.gsub(/^\x00|\xff$/, '') }
82
- data.each {|message| _invoke_data_callbacks(message) }
83
- end
84
-
85
67
  def _invoke_data_callbacks(message)
86
68
  self.class.on_data_callbacks.each do |callback|
87
69
  callback_wrapper { send(callback, message) }
@@ -106,4 +88,4 @@ module Cramp
106
88
  end
107
89
 
108
90
  end
109
- end
91
+ end
@@ -4,13 +4,13 @@ module Cramp
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- class_inheritable_accessor :periodic_timers, :instance_reader => false
7
+ class_attribute :periodic_timers, :instance_reader => false
8
8
  self.periodic_timers ||= []
9
9
  end
10
10
 
11
11
  module ClassMethods
12
12
  def periodic_timer(method, options = {})
13
- self.periodic_timers << [method, options]
13
+ self.periodic_timers += [ [method, options] ]
14
14
  end
15
15
  end
16
16
 
@@ -5,7 +5,7 @@ module Cramp
5
5
  class << self
6
6
  def backend=(backend)
7
7
  raise "Websocket backend #{backend} is unknown" unless [:thin, :rainbows].include?(backend.to_sym)
8
- require "cramp/websocket/#{backend}_backend.rb"
8
+ Faye::WebSocket.load_adapter(backend.to_s)
9
9
  end
10
10
  end
11
11
 
metadata CHANGED
@@ -1,62 +1,90 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cramp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.1
5
- prerelease:
4
+ version: 0.15.2
6
5
  platform: ruby
7
6
  authors:
8
7
  - Pratik Naik
8
+ - Lucas Allan Amorim
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-08-30 00:00:00.000000000 Z
12
+ date: 2013-12-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
16
- requirement: &2155605620 !ruby/object:Gem::Requirement
17
- none: false
16
+ requirement: !ruby/object:Gem::Requirement
18
17
  requirements:
19
18
  - - ~>
20
19
  - !ruby/object:Gem::Version
21
- version: 3.0.9
20
+ version: 3.2.16
22
21
  type: :runtime
23
22
  prerelease: false
24
- version_requirements: *2155605620
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ version: 3.2.16
25
28
  - !ruby/object:Gem::Dependency
26
29
  name: rack
27
- requirement: &2155604860 !ruby/object:Gem::Requirement
28
- none: false
30
+ requirement: !ruby/object:Gem::Requirement
29
31
  requirements:
30
32
  - - ~>
31
33
  - !ruby/object:Gem::Version
32
- version: 1.3.2
34
+ version: 1.5.2
33
35
  type: :runtime
34
36
  prerelease: false
35
- version_requirements: *2155604860
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ version: 1.5.2
36
42
  - !ruby/object:Gem::Dependency
37
43
  name: eventmachine
38
- requirement: &2155604140 !ruby/object:Gem::Requirement
39
- none: false
44
+ requirement: !ruby/object:Gem::Requirement
40
45
  requirements:
41
46
  - - ~>
42
47
  - !ruby/object:Gem::Version
43
- version: 1.0.0.beta.3
48
+ version: 1.0.3
44
49
  type: :runtime
45
50
  prerelease: false
46
- version_requirements: *2155604140
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: 1.0.3
56
+ - !ruby/object:Gem::Dependency
57
+ name: faye-websocket
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ version: 0.7.1
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 0.7.1
47
70
  - !ruby/object:Gem::Dependency
48
71
  name: thor
49
- requirement: &2155603680 !ruby/object:Gem::Requirement
50
- none: false
72
+ requirement: !ruby/object:Gem::Requirement
51
73
  requirements:
52
74
  - - ~>
53
75
  - !ruby/object:Gem::Version
54
- version: 0.14.6
76
+ version: 0.18.0
55
77
  type: :runtime
56
78
  prerelease: false
57
- version_requirements: *2155603680
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ~>
82
+ - !ruby/object:Gem::Version
83
+ version: 0.18.0
58
84
  description: Cramp is a framework for developing asynchronous web applications.
59
- email: pratiknaik@gmail.com
85
+ email:
86
+ - pratiknaik@gmail.com
87
+ - lucas.allan@gmail.com
60
88
  executables:
61
89
  - cramp
62
90
  extensions: []
@@ -82,37 +110,31 @@ files:
82
110
  - lib/cramp/rendering.rb
83
111
  - lib/cramp/sse.rb
84
112
  - lib/cramp/test_case.rb
85
- - lib/cramp/websocket/extension.rb
86
- - lib/cramp/websocket/protocol10_frame_parser.rb
87
- - lib/cramp/websocket/rainbows.rb
88
- - lib/cramp/websocket/rainbows_backend.rb
89
- - lib/cramp/websocket/thin_backend.rb
90
113
  - lib/cramp/websocket.rb
91
114
  - lib/cramp.rb
92
115
  - lib/vendor/fiber_pool.rb
93
116
  - bin/cramp
94
117
  homepage: http://cramp.in
95
118
  licenses: []
119
+ metadata: {}
96
120
  post_install_message:
97
121
  rdoc_options: []
98
122
  require_paths:
99
123
  - lib
100
124
  required_ruby_version: !ruby/object:Gem::Requirement
101
- none: false
102
125
  requirements:
103
126
  - - ! '>='
104
127
  - !ruby/object:Gem::Version
105
128
  version: '0'
106
129
  required_rubygems_version: !ruby/object:Gem::Requirement
107
- none: false
108
130
  requirements:
109
131
  - - ! '>='
110
132
  - !ruby/object:Gem::Version
111
133
  version: '0'
112
134
  requirements: []
113
135
  rubyforge_project:
114
- rubygems_version: 1.8.6
136
+ rubygems_version: 2.1.9
115
137
  signing_key:
116
- specification_version: 3
138
+ specification_version: 4
117
139
  summary: Asynchronous web framework.
118
140
  test_files: []
@@ -1,92 +0,0 @@
1
- require 'base64'
2
- require 'digest/sha1'
3
-
4
- module Cramp
5
- module WebsocketExtension
6
- WEBSOCKET_RECEIVE_CALLBACK = 'websocket.receive_callback'.freeze
7
-
8
- def protocol_class
9
- @env['HTTP_SEC_WEBSOCKET_VERSION'] ? Protocol10 : Protocol76
10
- end
11
-
12
- def websocket?
13
- ['WebSocket', 'websocket'].include?(@env['HTTP_UPGRADE'])
14
- end
15
-
16
- def secure_websocket?
17
- if @env.has_key?('HTTP_X_FORWARDED_PROTO')
18
- @env['HTTP_X_FORWARDED_PROTO'] == 'https'
19
- else
20
- @env['HTTP_ORIGIN'] =~ /^https:/i
21
- end
22
- end
23
-
24
- def websocket_url
25
- scheme = secure_websocket? ? 'wss:' : 'ws:'
26
- @env['websocket.url'] = "#{ scheme }//#{ @env['HTTP_HOST'] }#{ @env['REQUEST_URI'] }"
27
- end
28
-
29
- class WebSocketHandler
30
- def initialize(env, websocket_url, body = nil)
31
- @env = env
32
- @websocket_url = websocket_url
33
- @body = body
34
- end
35
- end
36
-
37
- class Protocol10 < WebSocketHandler
38
- MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".freeze
39
-
40
- def handshake
41
- digest = Base64.encode64(Digest::SHA1.digest("#{@env['HTTP_SEC_WEBSOCKET_KEY']}#{MAGIC_GUID}")).chomp
42
-
43
- upgrade = "HTTP/1.1 101 Switching Protocols\r\n"
44
- upgrade << "Upgrade: websocket\r\n"
45
- upgrade << "Connection: Upgrade\r\n"
46
- upgrade << "Sec-WebSocket-Accept: #{digest}\r\n\r\n"
47
- upgrade
48
- end
49
- end
50
-
51
- class Protocol76 < WebSocketHandler
52
- def handshake
53
- key1 = @env['HTTP_SEC_WEBSOCKET_KEY1']
54
- value1 = number_from_key(key1) / spaces_in_key(key1)
55
-
56
- key2 = @env['HTTP_SEC_WEBSOCKET_KEY2']
57
- value2 = number_from_key(key2) / spaces_in_key(key2)
58
-
59
- hash = Digest::MD5.digest(big_endian(value1) +
60
- big_endian(value2) +
61
- @body)
62
-
63
- upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
64
- upgrade << "Upgrade: WebSocket\r\n"
65
- upgrade << "Connection: Upgrade\r\n"
66
- upgrade << "Sec-WebSocket-Origin: #{@env['HTTP_ORIGIN']}\r\n"
67
- upgrade << "Sec-WebSocket-Location: #{@websocket_url}\r\n\r\n"
68
- upgrade << hash
69
- upgrade
70
- end
71
-
72
- private
73
-
74
- def number_from_key(key)
75
- key.scan(/[0-9]/).join('').to_i(10)
76
- end
77
-
78
- def spaces_in_key(key)
79
- key.scan(/ /).size
80
- end
81
-
82
- def big_endian(number)
83
- string = ''
84
- [24,16,8,0].each do |offset|
85
- string << (number >> offset & 0xFF).chr
86
- end
87
- string
88
- end
89
- end
90
-
91
- end
92
- end
@@ -1,241 +0,0 @@
1
- # encoding: BINARY
2
-
3
- # The MIT License - Copyright (c) 2009 Ilya Grigorik
4
- # Thank you https://github.com/igrigorik/em-websocket
5
- #
6
- # Copyright (c) 2009 Ilya Grigorik
7
- #
8
- # Permission is hereby granted, free of charge, to any person obtaining
9
- # a copy of this software and associated documentation files (the
10
- # "Software"), to deal in the Software without restriction, including
11
- # without limitation the rights to use, copy, modify, merge, publish,
12
- # distribute, sublicense, and/or sell copies of the Software, and to
13
- # permit persons to whom the Software is furnished to do so, subject to
14
- # the following conditions:
15
- #
16
- # The above copyright notice and this permission notice shall be
17
- # included in all copies or substantial portions of the Software.
18
- #
19
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23
- # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24
- # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25
- # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
-
27
- module Cramp
28
- class Protocol10FrameParser
29
- class WebSocketError < RuntimeError; end
30
-
31
- class MaskedString < String
32
- # Read a 4 bit XOR mask - further requested bytes will be unmasked
33
- def read_mask
34
- if respond_to?(:encoding) && encoding.name != "ASCII-8BIT"
35
- raise "MaskedString only operates on BINARY strings"
36
- end
37
- raise "Too short" if bytesize < 4 # TODO - change
38
- @masking_key = String.new(self[0..3])
39
- end
40
-
41
- # Removes the mask, behaves like a normal string again
42
- def unset_mask
43
- @masking_key = nil
44
- end
45
-
46
- def slice_mask
47
- slice!(0, 4)
48
- end
49
-
50
- def getbyte(index)
51
- if @masking_key
52
- masked_char = super
53
- masked_char ? masked_char ^ @masking_key.getbyte(index % 4) : nil
54
- else
55
- super
56
- end
57
- end
58
-
59
- def getbytes(start_index, count)
60
- data = ''
61
- count.times do |i|
62
- data << getbyte(start_index + i)
63
- end
64
- data
65
- end
66
- end
67
-
68
- attr_accessor :data
69
-
70
- def initialize
71
- @data = MaskedString.new
72
- @application_data_buffer = '' # Used for MORE frames
73
- end
74
-
75
- def process_data
76
- messages = []
77
- error = false
78
-
79
- while !error && @data.size >= 2
80
- pointer = 0
81
-
82
- fin = (@data.getbyte(pointer) & 0b10000000) == 0b10000000
83
- # Ignoring rsv1-3 for now
84
- opcode = @data.getbyte(pointer) & 0b00001111
85
- pointer += 1
86
-
87
- mask = (@data.getbyte(pointer) & 0b10000000) == 0b10000000
88
- length = @data.getbyte(pointer) & 0b01111111
89
- pointer += 1
90
-
91
- raise WebSocketError, 'Data from client must be masked' unless mask
92
-
93
- payload_length = case length
94
- when 127 # Length defined by 8 bytes
95
- # Check buffer size
96
- if @data.getbyte(pointer+8-1) == nil
97
- debug [:buffer_incomplete, @data]
98
- error = true
99
- next
100
- end
101
-
102
- # Only using the last 4 bytes for now, till I work out how to
103
- # unpack 8 bytes. I'm sure 4GB frames will do for now :)
104
- l = @data.getbytes(pointer+4, 4).unpack('N').first
105
- pointer += 8
106
- l
107
- when 126 # Length defined by 2 bytes
108
- # Check buffer size
109
- if @data.getbyte(pointer+2-1) == nil
110
- debug [:buffer_incomplete, @data]
111
- error = true
112
- next
113
- end
114
-
115
- l = @data.getbytes(pointer, 2).unpack('n').first
116
- pointer += 2
117
- l
118
- else
119
- length
120
- end
121
-
122
- # Compute the expected frame length
123
- frame_length = pointer + payload_length
124
- frame_length += 4 if mask
125
-
126
- # Check buffer size
127
- if @data.getbyte(frame_length - 1) == nil
128
- debug [:buffer_incomplete, @data]
129
- error = true
130
- next
131
- end
132
-
133
- # Remove frame header
134
- @data.slice!(0...pointer)
135
- pointer = 0
136
-
137
- # Read application data (unmasked if required)
138
- @data.read_mask if mask
139
- pointer += 4 if mask
140
- application_data = @data.getbytes(pointer, payload_length)
141
- pointer += payload_length
142
- @data.unset_mask if mask
143
-
144
- # Throw away data up to pointer
145
- @data.slice!(0...pointer)
146
-
147
- frame_type = opcode_to_type(opcode)
148
-
149
- if frame_type == :continuation && !@frame_type
150
- raise WebSocketError, 'Continuation frame not expected'
151
- end
152
-
153
- if !fin
154
- debug [:moreframe, frame_type, application_data]
155
- @application_data_buffer << application_data
156
- @frame_type = frame_type
157
- else
158
- # Message is complete
159
- if frame_type == :continuation
160
- @application_data_buffer << application_data
161
- messages << [@frame_type, @application_data_buffer]
162
- @application_data_buffer = ''
163
- @frame_type = nil
164
- else
165
- messages << [frame_type, application_data]
166
- end
167
- end
168
- end # end while
169
-
170
- messages
171
- end
172
-
173
- def send_frame(frame_type, application_data)
174
- debug [:sending_frame, frame_type, application_data]
175
-
176
- # Protocol10FrameParser doesn't have any knowledge of :closing in Cramp
177
- # if @state == :closing && data_frame?(frame_type)
178
- # raise WebSocketError, "Cannot send data frame since connection is closing"
179
- # end
180
-
181
- frame = ''
182
-
183
- opcode = type_to_opcode(frame_type)
184
- byte1 = opcode | 0b10000000 # fin bit set, rsv1-3 are 0
185
- frame << byte1
186
-
187
- length = application_data.size
188
- if length <= 125
189
- byte2 = length # since rsv4 is 0
190
- frame << byte2
191
- elsif length < 65536 # write 2 byte length
192
- frame << 126
193
- frame << [length].pack('n')
194
- else # write 8 byte length
195
- frame << 127
196
- frame << [length >> 32, length & 0xFFFFFFFF].pack("NN")
197
- end
198
-
199
- frame << application_data
200
- end
201
-
202
- def send_text_frame(data)
203
- send_frame(:text, data)
204
- end
205
-
206
- private
207
-
208
- FRAME_TYPES = {
209
- :continuation => 0,
210
- :text => 1,
211
- :binary => 2,
212
- :close => 8,
213
- :ping => 9,
214
- :pong => 10,
215
- }
216
- FRAME_TYPES_INVERSE = FRAME_TYPES.invert
217
- # Frames are either data frames or control frames
218
- DATA_FRAMES = [:text, :binary, :continuation]
219
-
220
- def type_to_opcode(frame_type)
221
- FRAME_TYPES[frame_type] || raise("Unknown frame type")
222
- end
223
-
224
- def opcode_to_type(opcode)
225
- FRAME_TYPES_INVERSE[opcode] || raise(DataError, "Unknown opcode")
226
- end
227
-
228
- def data_frame?(type)
229
- DATA_FRAMES.include?(type)
230
- end
231
-
232
- def debug(*data)
233
- if @debug
234
- require 'pp'
235
- pp data
236
- puts
237
- end
238
- end
239
-
240
- end
241
- end
@@ -1,42 +0,0 @@
1
- class Cramp::Websocket::Rainbows < Rainbows::EventMachine::Client
2
- include Cramp::WebsocketExtension
3
-
4
- def receive_data(data)
5
- case @state
6
- when :websocket
7
- callback = @env[WEBSOCKET_RECEIVE_CALLBACK]
8
- callback.call(data) if callback
9
- else
10
- super
11
- end
12
- end
13
-
14
- def on_read(data)
15
- if @state == :headers
16
- @hp.add_parse(data) or return want_more
17
- @state = :body
18
- if 0 == @hp.content_length && !websocket?
19
- app_call NULL_IO # common case
20
- else # nil or len > 0
21
- prepare_request_body
22
- end
23
- elsif @state == :body && websocket? && @hp.body_eof?
24
- @state = :websocket
25
- @input.rewind
26
-
27
- write(protocol_class.new(@env, websocket_url, @buf).handshake)
28
- app_call NULL_IO
29
- else
30
- super
31
- end
32
- rescue => e
33
- handle_error(e)
34
- end
35
-
36
- def write_response(status, headers, body, alive)
37
- write_headers(status, headers, alive) unless websocket?
38
- write_body_each(body)
39
- ensure
40
- body.close if body.respond_to?(:close)
41
- end
42
- end
@@ -1,8 +0,0 @@
1
- # :enddoc:
2
- require "rainbows"
3
- class Cramp::Websocket
4
- # we use autoload since Rainbows::EventMachine::Client should only be
5
- # loaded in the worker proceses and we want to be preload_app-friendly
6
- autoload :Rainbows, "cramp/websocket/rainbows"
7
- end
8
- Rainbows::O[:em_client_class] = "Cramp::Websocket::Rainbows"
@@ -1,53 +0,0 @@
1
- require 'thin'
2
-
3
- silence_warnings { Thin::Server::DEFAULT_TIMEOUT = 0 }
4
-
5
- class Thin::Connection
6
- # Called when data is received from the client.
7
- def receive_data(data)
8
- trace { data }
9
-
10
- case @serving
11
- when :websocket
12
- callback = @request.env[Thin::Request::WEBSOCKET_RECEIVE_CALLBACK]
13
- callback.call(data) if callback
14
- else
15
- if @request.parse(data)
16
- if @request.websocket?
17
- @response.persistent!
18
- @response.websocket_upgrade_data = @request.websocket_upgrade_data
19
- @serving = :websocket
20
- end
21
-
22
- process
23
- end
24
- end
25
- rescue Thin::InvalidRequest => e
26
- log "!! Invalid request"
27
- log_error e
28
- close_connection
29
- end
30
- end
31
-
32
- class Thin::Request
33
- include Cramp::WebsocketExtension
34
-
35
- def websocket_upgrade_data
36
- protocol_class.new(@env, websocket_url, body.read).handshake
37
- end
38
-
39
- end
40
-
41
- class Thin::Response
42
- # Headers for sending Websocket upgrade
43
- attr_accessor :websocket_upgrade_data
44
-
45
- def each
46
- websocket_upgrade_data ? yield(websocket_upgrade_data) : yield(head)
47
- if @body.is_a?(String)
48
- yield @body
49
- else
50
- @body.each { |chunk| yield chunk }
51
- end
52
- end
53
- end