diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..6147d1d
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,7 @@
+bundler_args: --without development
+language: ruby
+env: COVERAGE=on
+script: bundle exec rspec spec
+rvm:
+ - 1.9.2
+ - 1.9.3
diff --git a/README.textile b/README.textile
index ba21d8c..3ddb6b2 100644
--- a/README.textile
+++ b/README.textile
@@ -116,3 +116,4 @@ h1. Links
* "SockJS: WebSocket emulation":http://www.rabbitmq.com/blog/2011/09/13/sockjs-websocket-emulation
* "SockJS: web messaging ain't easy":http://www.rabbitmq.com/blog/2011/08/22/sockjs-web-messaging-aint-easy
* "PubSubHuddle Realtime Web talk":http://www.rabbitmq.com/blog/2011/09/26/pubsubhuddle-realtime-web-talk
+
diff --git a/lib/rack/sockjs.rb b/lib/rack/sockjs.rb
index 444fc29..c51efbb 100644
--- a/lib/rack/sockjs.rb
+++ b/lib/rack/sockjs.rb
@@ -47,7 +47,7 @@ module Rack
class SockJS
SERVER_SESSION_REGEXP = %r{/([^/]*)/([^/]*)}
DEFAULT_OPTIONS = {
- :sockjs_url => "http://cdn.sockjs.org/sockjs-#{::SockJS::PROTOCOL_VERSION}.min.js"
+ :sockjs_url => "http://cdn.sockjs.org/sockjs-#{::SockJS::PROTOCOL_VERSION_STRING}.min.js"
}
diff --git a/lib/sockjs/faye.rb b/lib/sockjs/faye.rb
index 7bbec92..0d3f87e 100644
--- a/lib/sockjs/faye.rb
+++ b/lib/sockjs/faye.rb
@@ -5,11 +5,4 @@
class Thin::Request
WEBSOCKET_RECEIVE_CALLBACK = 'websocket.receive_callback'.freeze
GET = 'GET'.freeze
-
- def websocket?
- @env['REQUEST_METHOD'] == GET and
- @env['HTTP_CONNECTION'] and
- @env['HTTP_CONNECTION'].split(/\s*,\s*/).include?('Upgrade') and
- ['WebSocket', 'websocket'].include?(@env['HTTP_UPGRADE'])
- end
end
diff --git a/lib/sockjs/protocol.rb b/lib/sockjs/protocol.rb
index 72a2d91..7e0b83c 100644
--- a/lib/sockjs/protocol.rb
+++ b/lib/sockjs/protocol.rb
@@ -1,6 +1,6 @@
# encoding: utf-8
-require "json"
+require 'multi_json'
module SockJS
module Protocol
@@ -30,7 +30,7 @@ class Frame
# with Unicode surrogates 0xD800-0xDFFF:
# http://en.wikipedia.org/wiki/Mapping_of_Unicode_characters#Surrogates
def escape(string)
- string.gsub(CHARS_TO_BE_ESCAPED) do |match|
+ string.encode("UTF-8", :invalid => :replace, :undef => :replace).gsub(CHARS_TO_BE_ESCAPED) do |match|
'\u%04x' % (match.ord)
end
end
@@ -76,7 +76,7 @@ def initialize(array)
attr_reader :array
def to_s
- "a#{escape(array.to_json)}"
+ "a#{escape(MultiJson.dump(array))}"
end
end
diff --git a/lib/sockjs/servers/response.rb b/lib/sockjs/servers/response.rb
index e4fab45..d457c5b 100644
--- a/lib/sockjs/servers/response.rb
+++ b/lib/sockjs/servers/response.rb
@@ -134,6 +134,10 @@ def set_no_cache
self.set_header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0")
end
+ def set_allow_options_headers
+ self.set_header("Access-Control-Allow-Headers", "Content-Type")
+ end
+
CONTENT_TYPES ||= {
plain: "text/plain; charset=UTF-8",
html: "text/html; charset=UTF-8",
diff --git a/lib/sockjs/session.rb b/lib/sockjs/session.rb
index 56a15dd..8d0fb70 100644
--- a/lib/sockjs/session.rb
+++ b/lib/sockjs/session.rb
@@ -15,7 +15,7 @@ def initialize(response, transport)
#Close the *response* not the *session*
def disconnect
- @response.finish
+ @response.finish if @response.respond_to?(:finish)
end
def heartbeat
@@ -67,7 +67,7 @@ def send(*messages)
@outbox += messages
end
- def close(status = nil, message = nil)
+ def close(status = 1002, message = "Connection closed")
@close_status = status
@close_message = message
transition_to(:closed)
@@ -102,7 +102,7 @@ def send_heartbeat
@consumer.heartbeat
end
- def close(status = nil, message = nil)
+ def close(status = 1002, message = "Connection closed")
@close_status = status
@close_message = message
@consumer.closing(@close_status, @close_message)
@@ -117,6 +117,7 @@ def on_enter
@close_message ||= "Go away!"
clear_all_timers
set_close_timer
+ on_close
end
def attach_consumer(response, transport)
@@ -188,6 +189,9 @@ def opened
def after_app_run
end
+ def on_close
+ end
+
attr_accessor :disconnect_delay, :interval
attr_reader :transport, :response, :outbox, :closing_frame, :data
@@ -215,12 +219,6 @@ def alive?
!!@alive
end
- #XXX This is probably important - need to examine this case
- def on_close
- SockJS.debug "The connection has been closed on the client side (current status: #{@status})."
- close_session(1002, "Connection interrupted")
- end
-
def max_permitted_content_length
@max_permitted_content_length ||= ($DEBUG ? 4096 : 128_000)
end
@@ -230,9 +228,9 @@ def parse_json(data)
return []
end
- JSON.parse("[#{data}]")[0]
- rescue JSON::ParserError => error
- raise SockJS::InvalidJSON.new(500, "Broken JSON encoding.")
+ MultiJson.load("[#{data}]")[0]
+ rescue MultiJson::LoadError => error
+ raise SockJS::InvalidJSON.new(500, "Broken JSON encoding: #{error.message}")
end
#Timers:
diff --git a/lib/sockjs/transport.rb b/lib/sockjs/transport.rb
index 99f1cca..d13fada 100644
--- a/lib/sockjs/transport.rb
+++ b/lib/sockjs/transport.rb
@@ -127,7 +127,11 @@ def format_frame(session, payload)
"#{payload}\n"
end
+ attr_reader :remote_addr, :http_origin
+
def call(env)
+ @remote_addr = env["REMOTE_ADDR"]
+ @http_origin = env["HTTP_ORIGIN"]
SockJS.debug "Request for #{self.class}: #{env["REQUEST_METHOD"]}/#{env["PATH_INFO"]}"
request = ::SockJS::Request.new(env)
EM.next_tick do
diff --git a/lib/sockjs/transports/htmlfile.rb b/lib/sockjs/transports/htmlfile.rb
index 987fd76..db8b449 100644
--- a/lib/sockjs/transports/htmlfile.rb
+++ b/lib/sockjs/transports/htmlfile.rb
@@ -1,6 +1,6 @@
# encoding: utf-8
-require "json"
+require "multi_json"
require "sockjs/transport"
module SockJS
@@ -62,7 +62,7 @@ def handle_http_error(request, error)
def format_frame(response, frame)
raise TypeError.new("Payload must not be nil!") if frame.nil?
- "\r\n"
+ "\r\n"
end
end
end
diff --git a/lib/sockjs/transports/info.rb b/lib/sockjs/transports/info.rb
index 6a0e798..aba5200 100644
--- a/lib/sockjs/transports/info.rb
+++ b/lib/sockjs/transports/info.rb
@@ -14,7 +14,7 @@ def setup_response(request, response)
response.set_access_control(request.origin)
response.set_allow_options_post
response.set_no_cache
- response.write(self.info.to_json)
+ response.write(MultiJson.dump(self.info))
end
def info
diff --git a/lib/sockjs/transports/jsonp.rb b/lib/sockjs/transports/jsonp.rb
index c1ae0d7..bc3ab5c 100644
--- a/lib/sockjs/transports/jsonp.rb
+++ b/lib/sockjs/transports/jsonp.rb
@@ -33,7 +33,7 @@ def format_frame(response, payload)
# Yes, JSONed twice, there isn't a better way, we must pass
# a string back, and the script, will be evaled() by the browser.
- "#{response.request.callback}(#{super.chomp.to_json});\r\n"
+ "#{response.request.callback}(#{MultiJson.dump(super.chomp)});\r\n"
end
end
diff --git a/lib/sockjs/transports/websocket.rb b/lib/sockjs/transports/websocket.rb
index 6382232..b678829 100644
--- a/lib/sockjs/transports/websocket.rb
+++ b/lib/sockjs/transports/websocket.rb
@@ -146,7 +146,7 @@ def heartbeat_frame(response)
def extract_message(event)
SockJS.debug "Received message event: #{event.data.inspect}"
- event.data.to_json
+ MultiJson.dump(event.data)
end
def messages_frame(websocket, messages)
diff --git a/lib/sockjs/transports/xhr.rb b/lib/sockjs/transports/xhr.rb
index d95fa90..ef7834f 100644
--- a/lib/sockjs/transports/xhr.rb
+++ b/lib/sockjs/transports/xhr.rb
@@ -13,6 +13,7 @@ def setup_response(request, response)
response.set_cache_control
response.set_access_control(request.origin)
response.set_session_id(request.session_id)
+ response.set_allow_options_headers
end
end
diff --git a/lib/sockjs/version.rb b/lib/sockjs/version.rb
index f4cf996..a920704 100644
--- a/lib/sockjs/version.rb
+++ b/lib/sockjs/version.rb
@@ -7,7 +7,7 @@ module SockJS
PROTOCOL_VERSION_STRING = PROTOCOL_VERSION.join(".")
# Patch version of the gem.
- PATCH_VERSION = []
+ PATCH_VERSION = ['beta', 3]
GEM_VERSION = (PROTOCOL_VERSION + PATCH_VERSION).join(".")
end
diff --git a/sockjs.gemspec b/sockjs.gemspec
index 30e6625..e5ef348 100755
--- a/sockjs.gemspec
+++ b/sockjs.gemspec
@@ -26,6 +26,15 @@ Gem::Specification.new do |s|
s.add_dependency "faye-websocket", "~> 0.4.3"
s.add_dependency "regin", "~> 0.3.8"
s.add_dependency "rack-mount", "~> 0.8.3"
+ s.add_dependency "multi_json", ">=1.3.6"
+
+ begin
+ if !defined?(JRUBY_VERSION)
+ s.add_dependency("oj", ">= 2.0.12")
+ end
+ rescue
+ p "Could not detect ruby version"
+ end
# Files
s.files = %w{
diff --git a/spec/sockjs/protocol_spec.rb b/spec/sockjs/protocol_spec.rb
index 96f2197..016ac3c 100755
--- a/spec/sockjs/protocol_spec.rb
+++ b/spec/sockjs/protocol_spec.rb
@@ -24,6 +24,16 @@
SockJS::Protocol::ArrayFrame.new([1, 2, 3]).to_s.should eql("a[1,2,3]")
SockJS::Protocol::ArrayFrame.new(["tests"]).to_s.should eql('a["tests"]')
end
+
+ it "should not blow up if passed invalid utf-8" do
+ expect {
+ SockJS::Protocol::ArrayFrame.new(["tests\255"])
+ }.to_not raise_error
+ end
+
+ it "should still handle utf-8 strings though" do
+ SockJS::Protocol::ArrayFrame.new(["lämpö"]).to_s.should eql('a["lämpö"]')
+ end
end
describe "ClosingFrame" do