# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://home.gna.org/xmpp4r/ # TODO: eval element' end @streamid = res_body.attributes['authid'] @status = CONNECTED @http_sid = res_body.attributes['sid'] @http_wait = res_body.attributes['wait'].to_i if res_body.attributes['wait'] @http_hold = res_body.attributes['hold'].to_i if res_body.attributes['hold'] @http_inactivity = res_body.attributes['inactivity'].to_i @http_polling = res_body.attributes['polling'].to_i @http_polling = 5 if @http_polling == 0 @http_requests = res_body.attributes['requests'].to_i @http_requests = 1 if @http_requests == 0 receive_elements_with_rid(@http_rid, res_body.children) @features_sem.run end ## # Ensure that there is one pending request # # Will be automatically called if you've sent # a stanza. def ensure_one_pending_request return if is_disconnected? if @lock.synchronize { @pending_requests } < 1 send_data('') end end ## # Close the session by sending # def close @status = DISCONNECTED req_body = nil @lock.synchronize { req_body = " @pending_rid cv = ConditionVariable.new @pending_rid_cv << [rid, cv] @pending_rid_cv.sort! while rid > @pending_rid cv.wait(@pending_rid_lock) end end end elements.each { |e| receive(e) } # Signal waiting elements @pending_rid_lock.synchronize do @pending_rid = rid + 1 # Ensure @pending_rid is modified atomically if @pending_rid_cv.size > 0 && @pending_rid_cv.first.first == @pending_rid next_rid, cv = @pending_rid_cv.shift cv.signal end end end ## # Do a POST request def post(body) body = body.to_s request = Net::HTTP::Post.new(@uri.path) request.content_length = body.size request.body = body request['Content-Type'] = @http_content_type Jabber::debuglog("HTTP REQUEST (#{@pending_requests}/#{@http_requests}):\n#{request.body}") net_http_args = [@uri.host, @uri.port] unless @proxy_args.empty? unless no_proxy?(@uri) net_http_args.concat @proxy_args end end http = Net::HTTP.new(*net_http_args) if @uri.kind_of? URI::HTTPS http.use_ssl = true @http_ssl_setup and @http_ssl_setup.call(http) end http.read_timeout = @http_wait * 1.1 response = http.start { |http| http.request(request) } Jabber::debuglog("HTTP RESPONSE (#{@pending_requests}/#{@http_requests}): #{response.class}\n#{response.body}") unless response.kind_of? Net::HTTPSuccess # Unfortunately, HTTPResponses aren't exceptions # TODO: rescue'ing code should be able to distinguish raise Net::HTTPBadResponse, "#{response.class}" end body = REXML::Document.new(response.body).root if body.name != 'body' and body.namespace != 'http://jabber.org/protocol/httpbind' raise REXML::ParseException.new('Malformed body') end body end ## # Check whether uri should be accessed without proxy def no_proxy?(uri) @no_proxy.each do |host_addr| return true if uri.host.match(Regexp.quote(host_addr) + '$') end return false end ## # Prepare data to POST and # handle the result def post_data(data, restart = false) req_body = nil current_rid = nil begin begin @lock.synchronize { # Do not send unneeded requests if data.size < 1 and @pending_requests > 0 and !restart @pending_requests += 1 # compensate for decrement in ensure clause Jabber::debuglog "post_data: not sending excessive poll" return end req_body = " e Jabber::debuglog("POST error (will retry): #{e.class}: #{e}") receive_elements_with_rid(current_rid, []) # It's not good to resend on *any* exception, # but there are too many cases (Timeout, 404, 502) # where resending is appropriate # TODO: recognize these conditions and act appropriate send_data(data) end end ## # Restart stream after SASL authentication def restart Jabber::debuglog("Restarting after SASL") @stream_mechanisms = [] @stream_features = {} @features_sem = Semaphore.new send_data('', true) # restart end ## # Send data, # buffered and obeying 'polling' and 'requests' limits def send_data(data, restart = false) Jabber::debuglog("send_data") while true do # exit by return @lock.synchronize do if @last_send + 0.05 >= Time.now Jabber::debuglog("send_data too fast: waiting 0.05sec") next end @send_buffer += data limited_by_polling = false if @pending_requests + 1 == @http_requests && @send_buffer.size == 0 limited_by_polling = (@last_send + @http_polling >= Time.now) end limited_by_requests = (@pending_requests + 1 > @http_requests) # Can we send? if !limited_by_polling and !limited_by_requests or !@authenticated Jabber::debuglog("send_data non_limited") data = @send_buffer @send_buffer = '' Thread.new do Thread.current.abort_on_exception = true post_data(data, restart) end return elsif limited_by_requests Jabber::debuglog("send_data limited by requests") # Do nothing. # When a pending request gets an response, it calls send_data('') return elsif # limited_by_polling && @authenticated # Wait until we get some data to send, or @http_polling expires Jabber::debuglog("send_data limited by polling: #{@http_polling}") else Jabber::errorlog("send_data: can't happen: pending_requests=#{@pending_requests} http_requests=#{@http_requests} send_buffer.size=#{@send_buffer.size} limited_by_polling=#{limited_by_polling} limited_by_requests=#{limited_by_requests}") return end end sleep(0.1) end end end end end