em-websocket 0.0.1

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.
@@ -0,0 +1,25 @@
1
+ = EM-WebSocket
2
+
3
+ EventMachine based WebSocket server -- highly experimental, at this point, at least.
4
+
5
+ == Simple server example
6
+
7
+ EventMachine.run {
8
+
9
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080) do |ws|
10
+ ws.onopen {
11
+ puts "WebSocket connection open"
12
+
13
+ # publish message to the client
14
+ ws.send "Hello Client"
15
+ }
16
+
17
+ ws.onclose { puts "Connection closed" }
18
+ ws.onmessage { |msg|
19
+ puts "Recieved message: #{msg}"
20
+ ws.send "Pong: #{msg}"
21
+ }
22
+ end
23
+ }
24
+
25
+
@@ -0,0 +1,19 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gemspec|
6
+ gemspec.name = "em-websocket"
7
+ gemspec.summary = "EventMachine based WebSocket server"
8
+ gemspec.description = gemspec.summary
9
+ gemspec.email = "ilya@igvita.com"
10
+ gemspec.homepage = "http://github.com/igrigorik/em-websocket"
11
+ gemspec.authors = ["Ilya Grigorik"]
12
+ gemspec.add_dependency("eventmachine", ">= 0.12.9")
13
+ gemspec.rubyforge_project = "em-websocket"
14
+ end
15
+
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
19
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,7 @@
1
+ require 'lib/em-websocket'
2
+
3
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080) do |ws|
4
+ ws.onopen { ws.send "Hello Client!"}
5
+ ws.onmessage { |msg| ws.send "Pong: #{msg}" }
6
+ ws.onclose { puts "WebSocket closed" }
7
+ end
@@ -0,0 +1,22 @@
1
+ <html>
2
+ <head>
3
+ <script src='http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js'></script>
4
+ <script>
5
+ $(document).ready(function(){
6
+ function debug(str){ $("#debug").append("<p>" + str); };
7
+
8
+ ws = new WebSocket("ws://localhost:8080/");
9
+ ws.onmessage = function(evt) { $("#msg").append("<p>"+evt.data+"</p>"); };
10
+ ws.onclose = function() { debug("socket closed"); };
11
+ ws.onopen = function() {
12
+ debug("connected...");
13
+ ws.send("hello server");
14
+ };
15
+ });
16
+ </script>
17
+ </head>
18
+ <body>
19
+ <div id="debug"></div>
20
+ <div id="msg"></div>
21
+ </body>
22
+ </html>
@@ -0,0 +1,8 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require "rubygems"
4
+ require "eventmachine"
5
+
6
+ %w[ websocket connection ].each do |file|
7
+ require "em-websocket/#{file}"
8
+ end
@@ -0,0 +1,95 @@
1
+ module EventMachine
2
+ module WebSocket
3
+ class Connection < EventMachine::Connection
4
+
5
+ # define WebSocket callbacks
6
+ def onopen(&blk); @onopen = blk; end
7
+ def onclose(&blk); @onclode = blk; end
8
+ def onmessage(&blk); @onmessage = blk; end
9
+
10
+ def initialize(options)
11
+ @options = options
12
+ @debug = options[:debug] || false
13
+ @state = :handshake
14
+
15
+ debug [:initialize]
16
+ end
17
+
18
+ def receive_data(data)
19
+ debug [:receive_data, data]
20
+
21
+ dispatch(data)
22
+ end
23
+
24
+ def unbind
25
+ debug [:unbind, :connection]
26
+ @onclose.call if @onclose
27
+ end
28
+
29
+ def dispatch(data = nil)
30
+ while case @state
31
+ when :handshake
32
+ new_request(data)
33
+ when :upgrade
34
+ send_upgrade
35
+ when :connected
36
+ process_message(data)
37
+ else raise RuntimeError, "invalid state: #{@state}"
38
+ end
39
+ end
40
+ end
41
+
42
+ def new_request(data)
43
+ # TODO: verify WS headers
44
+ @state = :upgrade
45
+ end
46
+
47
+ def send_upgrade
48
+ upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
49
+ upgrade << "Upgrade: WebSocket\r\n"
50
+ upgrade << "Connection: Upgrade\r\n"
51
+ upgrade << "WebSocket-Origin: file://\r\n"
52
+ upgrade << "WebSocket-Location: ws://localhost:8080/\r\n\r\n"
53
+
54
+ # upgrade connection and notify client callback
55
+ # about completed handshake
56
+ send_data upgrade
57
+
58
+ @state = :connected
59
+ @onopen.call if @onopen
60
+
61
+ # stop dispatch, wait for messages
62
+ false
63
+ end
64
+
65
+ def process_message(data)
66
+ debug [:message, data]
67
+ @onmessage.call(data) if @onmessage
68
+
69
+ false
70
+ end
71
+
72
+ # should only be invoked after handshake, otherwise it
73
+ # will inject data into the header exchange
74
+ #
75
+ # frames need to start with 0x00-0x7f byte and end with
76
+ # an 0xFF byte. Per spec, we can also set the first
77
+ # byte to a value betweent 0x80 and 0xFF, followed by
78
+ # a leading length indicator
79
+ def send(data)
80
+ debug [:send, data]
81
+ send_data("\x00#{data}\xff")
82
+ end
83
+
84
+ private
85
+
86
+ def debug(*data)
87
+ if @debug
88
+ require 'pp'
89
+ pp data
90
+ puts
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,23 @@
1
+ module EventMachine
2
+ module WebSocket
3
+
4
+ def self.start(options, &blk)
5
+ EM.epoll
6
+ EM.run do
7
+
8
+ trap("TERM") { stop }
9
+ trap("INT") { stop }
10
+
11
+ EventMachine::start_server(options[:host], options[:port],
12
+ EventMachine::WebSocket::Connection, options) do |c|
13
+ blk.call(c)
14
+ end
15
+ end
16
+ end
17
+
18
+ def self.stop
19
+ puts "Terminating WebSocket Server"
20
+ EventMachine.stop
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'pp'
4
+ require 'em-http'
5
+
6
+ require 'lib/em-websocket'
@@ -0,0 +1,52 @@
1
+ require 'spec/helper'
2
+
3
+ describe EventMachine::WebSocket do
4
+
5
+ def failed
6
+ EventMachine.stop
7
+ fail
8
+ end
9
+
10
+ it "should automatically complete WebSocket handshake" do
11
+ EM.run do
12
+ MSG = "Hello World!"
13
+ EventMachine.add_timer(0.1) do
14
+ http = EventMachine::HttpRequest.new('ws://127.0.0.1:8080/').get :timeout => 0
15
+ http.errback { failed }
16
+ http.callback { http.response_header.status.should == 101 }
17
+
18
+ http.stream { |msg|
19
+ msg.should == MSG
20
+ EventMachine.stop
21
+ }
22
+ end
23
+
24
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080) do |ws|
25
+ ws.onopen {
26
+ puts "WebSocket connection open"
27
+ ws.send MSG
28
+ }
29
+
30
+ # TODO: need .terminate method on EM-http to invoke & test .onclose callback
31
+ end
32
+ end
33
+ end
34
+
35
+ it "should fail on non WebSocket requests" do
36
+ pending
37
+
38
+ EM.run do
39
+ EventMachine.add_timer(0.1) do
40
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get :timeout => 0
41
+ http.errback { failed }
42
+ http.callback {
43
+ http.response_header.status.should == 400
44
+ EventMachine.stop
45
+ }
46
+ end
47
+
48
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080, :debug => true) {}
49
+ end
50
+ end
51
+
52
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-websocket
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ilya Grigorik
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-22 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: eventmachine
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.12.9
24
+ version:
25
+ description: EventMachine based WebSocket server
26
+ email: ilya@igvita.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.rdoc
33
+ files:
34
+ - README.rdoc
35
+ - Rakefile
36
+ - VERSION
37
+ - examples/echo.rb
38
+ - examples/test.html
39
+ - lib/em-websocket.rb
40
+ - lib/em-websocket/connection.rb
41
+ - lib/em-websocket/websocket.rb
42
+ - spec/helper.rb
43
+ - spec/websocket_spec.rb
44
+ has_rdoc: true
45
+ homepage: http://github.com/igrigorik/em-websocket
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --charset=UTF-8
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ requirements: []
66
+
67
+ rubyforge_project: em-websocket
68
+ rubygems_version: 1.3.5
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: EventMachine based WebSocket server
72
+ test_files:
73
+ - spec/helper.rb
74
+ - spec/websocket_spec.rb
75
+ - examples/echo.rb