em-websocket 0.0.1

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