Class: Blather::Stream

Inherits:
EventMachine::Connection
  • Object
show all
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/resource.rb

Overview

:nodoc:

Direct Known Subclasses

Client, Component

Defined Under Namespace

Classes: Client, Component, Features, NoConnection, Parser, Resource, SASL, Session, TLS

Constant Summary collapse

STREAM_NS =
'http://etherx.jabber.org/streams'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(client, jid, pass) ⇒ Stream

Called by EM.connect to initialize stream variables



123
124
125
126
127
128
129
130
131
132
# File 'lib/blather/stream.rb', line 123

def initialize(client, jid, pass)
  super()

  @error = nil
  @receiver = @client = client

  self.jid = jid
  @to = self.jid.domain
  @password = pass
end

Instance Attribute Details

#jidObject

Returns the value of attribute jid.



55
56
57
# File 'lib/blather/stream.rb', line 55

def jid
  @jid
end

#passwordObject

Returns the value of attribute password.



54
55
56
# File 'lib/blather/stream.rb', line 54

def password
  @password
end

Class Method Details

.connect(host, port, conn, client, jid, pass) ⇒ 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



100
101
102
103
104
# File 'lib/blather/stream.rb', line 100

def self.connect(host, port, conn, client, jid, pass)
  EM.connect host, port, conn, client, jid, pass
rescue NoConnection
  false
end

.start(client, jid, pass, host = nil, port = 5222) ⇒ Object

Start the stream between client and server

#unbind #receive_data to use the domain on the JID default of 5222

Parameters:

  • client (Object)

    an object that will respond to #post_init,

  • jid (Blather::JID, #to_s)

    the jid to authenticate with

  • pass (String)

    the password to authenticate with

  • host (String, nil) (defaults to: nil)

    the hostname or IP to connect to. Default is

  • port (Fixnum, nil) (defaults to: 5222)

    the port to connect on. Default is the XMPP



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/blather/stream.rb', line 67

def self.start(client, jid, pass, host = nil, port = 5222)
  jid = JID.new jid
  if host
    connect host, port, self, client, jid, pass
  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
    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) === false
      end
    end
  end
end

Instance Method Details

#connection_completedObject

Called when EM completes the connection to the server this kicks off the starttls/authorize/bind process



137
138
139
140
# File 'lib/blather/stream.rb', line 137

def connection_completed
#      @keepalive = EM::PeriodicTimer.new(60) { send_data ' ' }
  start
end

#post_initObject

Called by EM after the connection has started



157
158
159
# File 'lib/blather/stream.rb', line 157

def post_init
  @connected = true
end

#receive(node) ⇒ Object

Called by the parser with parsed nodes



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/blather/stream.rb', line 174

def receive(node)
  Blather.logger.debug "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



144
145
146
147
148
149
150
151
152
153
# File 'lib/blather/stream.rb', line 144

def receive_data(data)
  Blather.logger.debug "\n#{'-'*30}\n"
  Blather.logger.debug "STREAM IN: #{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

TODO:

Queue if not ready

Send data over the wire

Parameters:

  • stanza (#to_xml, #to_s)

    the stanza to send over the wire



115
116
117
118
119
# File 'lib/blather/stream.rb', line 115

def send(stanza)
  data = stanza.respond_to?(:to_xml) ? stanza.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::AS_XML) : stanza.to_s
  Blather.logger.debug "SENDING: (#{caller[1]}) #{data}"
  send_data data
end

#unbindObject

Called by EM when the connection is closed

Raises:



163
164
165
166
167
168
169
170
# File 'lib/blather/stream.rb', line 163

def unbind
  raise NoConnection unless @connected

#      @keepalive.cancel
  @state = :stopped
  @client.receive_data @error if @error
  @client.unbind
end