Class: Blather::Stream
- Inherits:
-
EventMachine::Connection
- Object
- EventMachine::Connection
- Blather::Stream
- Defined in:
- lib/blather/stream.rb,
lib/blather/stream/client.rb,
lib/blather/stream/parser.rb,
lib/blather/stream/features.rb,
lib/blather/stream/component.rb,
lib/blather/stream/features/tls.rb,
lib/blather/stream/features/sasl.rb,
lib/blather/stream/features/session.rb,
lib/blather/stream/features/register.rb,
lib/blather/stream/features/resource.rb
Overview
# A pure XMPP stream.
Blather::Stream can be used to build your own handler system if Blather’s doesn’t suit your needs. It will take care of the entire connection process then start sending Stanza objects back to the registered client.
The client you register with Blather::Stream needs to implement the following methods:
-
#post_init(stream, jid = nil) Called after the stream has been initiated. @param [Blather::Stream] stream is the connected stream object @param [Blather::JID, nil] jid is the full JID as recognized by the server
-
#receive_data(stanza) Called every time the stream receives a new stanza @param [Blather::Stanza] stanza a stanza object from the server
-
#unbind Called when the stream is shutdown. This will be called regardless of which side shut the stream down.
Defined Under Namespace
Classes: Client, Component, ConnectionFailed, ConnectionTimeout, Features, NoConnection, Parser, Register, Resource, SASL, Session, TLS
Constant Summary collapse
- STREAM_NS =
'http://etherx.jabber.org/streams'
- @@store =
nil
Instance Attribute Summary collapse
-
#jid ⇒ Object
Returns the value of attribute jid.
-
#password ⇒ Object
Returns the value of attribute password.
Class Method Summary collapse
-
.connect(host, port, conn, client, jid, pass, connect_timeout = nil) ⇒ Object
Attempt a connection Stream will raise
NoConnection
if it receives #unbind before #post_init this catches that and returns false prompting for another attempt. -
.start(client, jid, pass, host = nil, port = nil, certs_directory = nil, connect_timeout = nil) ⇒ Object
Start the stream between client and server.
Instance Method Summary collapse
-
#connection_completed ⇒ Object
Called when EM completes the connection to the server this kicks off the starttls/authorize/bind process.
-
#initialize(client, jid, pass, connect_timeout = nil) ⇒ Stream
constructor
Called by EM.connect to initialize stream variables.
-
#post_init ⇒ Object
Called by EM after the connection has started.
-
#receive(node) ⇒ Object
Called by the parser with parsed nodes.
-
#receive_data(data) ⇒ Object
Called by EM with data from the wire.
-
#send(stanza) ⇒ Object
Send data over the wire.
-
#ssl_verify_peer(pem) ⇒ Object
Called by EM to verify the peer certificate.
-
#unbind ⇒ Object
Called by EM when the connection is closed.
Constructor Details
#initialize(client, jid, pass, connect_timeout = nil) ⇒ Stream
Called by EM.connect to initialize stream variables
135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/blather/stream.rb', line 135 def initialize(client, jid, pass, connect_timeout = nil) super() @error = nil @receiver = @client = client self.jid = jid @to = self.jid.domain @password = pass @connect_timeout = connect_timeout || 180 end |
Instance Attribute Details
#jid ⇒ Object
Returns the value of attribute jid.
59 60 61 |
# File 'lib/blather/stream.rb', line 59 def jid @jid end |
#password ⇒ Object
Returns the value of attribute password.
58 59 60 |
# File 'lib/blather/stream.rb', line 58 def password @password end |
Class Method Details
.connect(host, port, conn, client, jid, pass, connect_timeout = nil) ⇒ Object
Attempt a connection Stream will raise NoConnection
if it receives #unbind before #post_init this catches that and returns false prompting for another attempt
112 113 114 115 116 |
# File 'lib/blather/stream.rb', line 112 def self.connect(host, port, conn, client, jid, pass, connect_timeout = nil) EM.connect host, port, conn, client, jid, pass, connect_timeout rescue NoConnection false end |
.start(client, jid, pass, host = nil, port = nil, certs_directory = nil, connect_timeout = nil) ⇒ Object
Start the stream between client and server
#unbind #receive_data to use the domain on the JID default of 5222 communication with the server is trusted.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/blather/stream.rb', line 75 def self.start(client, jid, pass, host = nil, port = nil, certs_directory = nil, connect_timeout = nil) jid = JID.new jid port ||= 5222 if certs_directory @@store = CertStore.new(certs_directory) end if host connect host, port, self, client, jid, pass, connect_timeout else require 'resolv' srv = [] Resolv::DNS.open do |dns| srv = dns.getresources( "_xmpp-client._tcp.#{jid.domain}", Resolv::DNS::Resource::IN::SRV ) end if srv.empty? connect jid.domain, port, self, client, jid, pass, connect_timeout else srv.sort! do |a,b| (a.priority != b.priority) ? (a.priority <=> b.priority) : (b.weight <=> a.weight) end srv.detect do |r| not connect(r.target.to_s, r.port, self, client, jid, pass, connect_timeout) === false end end end end |
Instance Method Details
#connection_completed ⇒ Object
Called when EM completes the connection to the server this kicks off the starttls/authorize/bind process
150 151 152 153 154 155 156 157 158 159 |
# File 'lib/blather/stream.rb', line 150 def connection_completed if @connect_timeout @connect_timer = EM::Timer.new @connect_timeout do raise ConnectionTimeout, "Stream timed out after #{@connect_timeout} seconds." unless started? end end @connected = true # @keepalive = EM::PeriodicTimer.new(60) { send_data ' ' } start end |
#post_init ⇒ Object
Called by EM after the connection has started
189 190 191 |
# File 'lib/blather/stream.rb', line 189 def post_init @inited = true end |
#receive(node) ⇒ Object
Called by the parser with parsed nodes
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/blather/stream.rb', line 208 def receive(node) Blather.log "RECEIVING (#{node.element_name}) #{node}" @node = node if @node.namespace && @node.namespace.prefix == 'stream' case @node.element_name when 'stream' @state = :ready if @state == :stopped return when 'error' handle_stream_error return when 'end' stop return when 'features' @state = :negotiating @receiver = Features.new( self, proc { ready! }, proc { |err| @error = err; stop } ) end end @receiver.receive_data @node.to_stanza end |
#receive_data(data) ⇒ Object
Called by EM with data from the wire
163 164 165 166 167 168 169 170 |
# File 'lib/blather/stream.rb', line 163 def receive_data(data) @parser << data rescue ParseError => e @error = e send "<stream:error><xml-not-well-formed xmlns='#{StreamError::STREAM_ERR_NS}'/></stream:error>" stop end |
#send(stanza) ⇒ Object
Queue if not ready
Send data over the wire
127 128 129 130 131 |
# File 'lib/blather/stream.rb', line 127 def send(stanza) data = stanza.respond_to?(:to_xml) ? stanza.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::AS_XML) : stanza.to_s Blather.log "SENDING: (#{caller[1]}) #{stanza}" EM.next_tick { send_data data } end |
#ssl_verify_peer(pem) ⇒ Object
Called by EM to verify the peer certificate. If a certificate store directory has not been configured don’t worry about peer verification. At least it is encrypted We Log the certificate so that you can add it to the trusted store easily if desired
176 177 178 179 180 181 182 183 184 185 |
# File 'lib/blather/stream.rb', line 176 def ssl_verify_peer(pem) # EM is supposed to close the connection when this returns false, # but it only does that for inbound connections, not when we # make a connection to another server. Blather.log "Checking SSL cert: #{pem}" return true if !@@store @@store.trusted?(pem).tap do |trusted| close_connection unless trusted end end |
#unbind ⇒ Object
Called by EM when the connection is closed
195 196 197 198 199 200 201 202 203 204 |
# File 'lib/blather/stream.rb', line 195 def unbind raise NoConnection unless @inited raise ConnectionFailed unless @connected @connect_timer.cancel if @connect_timer # @keepalive.cancel @state = :stopped @client.receive_data @error if @error @client.unbind end |