cramp 0.15.1 → 0.15.2

Sign up to get free protection for your applications and to get access to all the features.
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