class Vines::Stream::Http
Attributes
Public Class Methods
Vines::Stream::Client::new
# File lib/vines/stream/http.rb, line 8 def initialize(config) super @session = Http::Session.new(self) end
Public Instance Methods
Override Stream#create_parser
to provide an HTTP parser rather than a Nokogiri XML parser.
Returns nothing.
# File lib/vines/stream/http.rb, line 17 def create_parser @parser = ::Http::Parser.new.tap do |parser| body = '' parser.on_body = proc {|data| body << data } parser.on_message_complete = proc do process_request(Request.new(self, @parser, body)) body = '' end end end
Parse the one or more stanzas from a single body element. BOSH clients buffer stanzas sent in quick succession, and send them as a bundle, to save on the request/response cycle.
TODO This parses the XML again just to strip namespaces. Figure out Nokogiri namespace handling instead.
body - The XML::Node containing the BOSH `body` element.
Returns an Array of XML::Node stanzas.
# File lib/vines/stream/http.rb, line 94 def parse_body(body) body.namespace = nil body.elements.map do |node| Nokogiri::XML(node.to_s.sub(' xmlns="jabber:client"', '')).root end end
# File lib/vines/stream/http.rb, line 48 def process_request(request) if request.method == 'POST' if request.path == self.bind && request.options? request.reply_to_options elsif request.path == self.bind body = Nokogiri::XML(request.body).root if session = Sessions[body['sid']] @session = session else @session = Http::Session.new(self) end @session.request(request) @nodes.push(body) end else request.reply('It works!', 'text/plain') end end
# File lib/vines/stream/http.rb, line 101 def start(node) domain, type, hold, wait, rid = %w[to content hold wait rid].map {|a| (node[a] || '').strip } version = node.attribute_with_ns('version', NAMESPACES[:bosh]).value rescue nil @session.inactivity = 20 @session.domain = domain @session.content_type = type unless type.empty? @session.hold = hold.to_i unless hold.empty? @session.wait = wait.to_i unless wait.empty? raise StreamErrors::UndefinedCondition.new('rid required') if rid.empty? raise StreamErrors::UnsupportedVersion unless version == '1.0' raise StreamErrors::ImproperAddressing unless valid_address?(domain) raise StreamErrors::HostUnknown unless config.vhost?(domain) raise StreamErrors::InvalidNamespace unless node.namespaces['xmlns'] == NAMESPACES[:http_bind] Sessions[@session.id] = @session send_stream_header end
Alias the Stream#write
method before overriding it so we can call it later from a Session
instance.
# File lib/vines/stream/http.rb, line 121 def terminate doc = Nokogiri::XML::Document.new node = doc.create_element('body', 'type' => 'terminate', 'xmlns' => NAMESPACES[:http_bind]) @session.reply(node) close_stream end
If the session ID is valid, switch this stream's session to the new ID and return true. Some clients, like Google Chrome, reuse one stream for multiple sessions.
sid - The String session ID.
Returns true if the server previously distributed this SID to a client.
# File lib/vines/stream/http.rb, line 35 def valid_session?(sid) if session = Sessions[sid] @session = session end !!session end
Override Stream#write
to queue stanzas rather than immediately writing to the stream. Stanza
responses must be paired with a queued request.
If a request is not waiting, the written stanzas will buffer until they can be sent in the next response.
data - The XML String or XML::Node to write to the HTTP socket.
Returns nothing.
# File lib/vines/stream/http.rb, line 80 def write(data) @session.write(data) end
Private Instance Methods
Override Stream#close_stream
to simply close the connection without writing a closing stream tag.
Returns nothing.
# File lib/vines/stream/http.rb, line 179 def close_stream close_connection_after_writing @session.close end
Override Stream#send_stream_error
to wrap the error XML in a BOSH terminate body tag.
e - The StreamError
that caused the connection to close.
Returns nothing.
# File lib/vines/stream/http.rb, line 164 def send_stream_error(e) doc = Nokogiri::XML::Document.new node = doc.create_element('body', 'condition' => 'remote-stream-error', 'type' => 'terminate', 'xmlns' => NAMESPACES[:http_bind], 'xmlns:stream' => NAMESPACES[:stream]) node.inner_html = e.to_xml @session.reply(node) end
# File lib/vines/stream/http.rb, line 132 def send_stream_header doc = Nokogiri::XML::Document.new node = doc.create_element('body', 'charsets' => 'UTF-8', 'from' => @session.domain, 'hold' => @session.hold, 'inactivity' => @session.inactivity, 'polling' => '5', 'requests' => '2', 'sid' => @session.id, 'ver' => '1.6', 'wait' => @session.wait, 'xmpp:version' => '1.0', 'xmlns' => NAMESPACES[:http_bind], 'xmlns:xmpp' => NAMESPACES[:bosh], 'xmlns:stream' => NAMESPACES[:stream]) node << doc.create_element('stream:features') do |el| el << doc.create_element('mechanisms') do |mechanisms| mechanisms.default_namespace = NAMESPACES[:sasl] mechanisms << doc.create_element('mechanism', 'PLAIN') end end @session.reply(node) end