bunny-2.6.1/0000755000004100000410000000000013015255277012712 5ustar www-datawww-databunny-2.6.1/Rakefile0000644000004100000410000000171413015255277014362 0ustar www-datawww-datarequire 'rake' require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:integration) do |t| # excludes unit tests as those involve many iterations # and sometimes suffer from obscure interference from integration tests (!) t.pattern = ["spec/higher_level_api/integration", "spec/lower_level_api/integration", "spec/issues", "spec/stress"]. map { |dir| Dir.glob(File.join(dir, "**", "*_spec.rb")) }.reduce(&:+) - ["spec/higher_level_api/integration/tls_connection_spec.rb"] t.rspec_opts = "--format progress" end RSpec::Core::RakeTask.new(:unit) do |t| t.pattern = Dir.glob("spec/unit/**/*_spec.rb") t.rspec_opts = "--format progress --backtrace" end RSpec::Core::RakeTask.new(:recovery_integration) do |t| # otherwise all examples will be skipped ENV.delete("CI") t.pattern = Dir.glob("spec/higher_level_api/integration/connection_recovery_spec.rb") t.rspec_opts = "--format progress --backtrace" end task :default => :integration bunny-2.6.1/bin/0000755000004100000410000000000013015255277013462 5ustar www-datawww-databunny-2.6.1/bin/ci/0000755000004100000410000000000013015255277014055 5ustar www-datawww-databunny-2.6.1/bin/ci/before_build0000755000004100000410000000220713015255277016425 0ustar www-datawww-data#!/usr/bin/env ruby $ctl = ENV["BUNNY_RABBITMQCTL"] || ENV["RABBITMQCTL"] || "sudo rabbitmqctl" $plugins = ENV["BUNNY_RABBITMQ_PLUGINS"] || ENV["RABBITMQ_PLUGINS"] || "sudo rabbitmq-plugins" def rabbit_control(args) command = "#{$ctl} #{args}" system command end def rabbit_plugins(args) command = "#{$plugins} #{args}" system command end # guest:guest has full access to / rabbit_control 'add_vhost /' rabbit_control 'add_user guest guest' rabbit_control 'set_permissions -p / guest ".*" ".*" ".*"' # bunny_gem:bunny_password has full access to bunny_testbed rabbit_control 'add_vhost bunny_testbed' rabbit_control 'add_user bunny_gem bunny_password' rabbit_control 'set_permissions -p bunny_testbed bunny_gem ".*" ".*" ".*"' # guest:guest has full access to bunny_testbed rabbit_control 'set_permissions -p bunny_testbed guest ".*" ".*" ".*"' # bunny_reader:reader_password has read access to bunny_testbed rabbit_control 'add_user bunny_reader reader_password' rabbit_control 'set_permissions -p bunny_testbed bunny_reader "^---$" "^---$" ".*"' # requires RabbitMQ 3.0+ # rabbit_plugins 'enable rabbitmq_management' bunny-2.6.1/bin/ci/start_rabbitmq0000755000004100000410000000043613015255277017024 0ustar www-datawww-data#!/bin/bash if [ -z `which docker` ]; then echo 'You need to install docker to run this script. See https://docs.docker.com/engine/installation/' exit fi cd $(dirname $(readlink -f $0)) docker build -t bunny_rabbitmq ../../docker && \ exec docker run --net host -ti bunny_rabbitmq bunny-2.6.1/Gemfile0000644000004100000410000000256513015255277014215 0ustar www-datawww-data# encoding: utf-8 source "https://rubygems.org" # Use local clones if possible. # If you want to use your local copy, just symlink it to vendor. # See http://blog.101ideas.cz/posts/custom-gems-in-gemfile.html extend Module.new { def gem(name, *args) options = args.last.is_a?(Hash) ? args.last : Hash.new local_path = File.expand_path("../vendor/#{name}", __FILE__) if File.exist?(local_path) super name, options.merge(:path => local_path). delete_if { |key, _| [:git, :branch].include?(key) } else super name, *args end end } gem "rake", ">= 10.0.4" gem "effin_utf8" group :development do gem "yard" gem "redcarpet", :platform => :mri gem "ruby-prof", :platform => :mri gem "json", :platform => :ruby_18 end group :test do gem "rspec", "~> 3.4.0" gem "rabbitmq_http_api_client", "~> 1.6.0" end gemspec # Use local clones if possible. # If you want to use your local copy, just symlink it to vendor. def custom_gem(name, options = Hash.new) local_path = File.expand_path("../vendor/#{name}", __FILE__) if File.exist?(local_path) # puts "Using #{name} from #{local_path}..." gem name, options.merge(:path => local_path).delete_if { |key, _| [:git, :branch].include?(key) } else gem name, options end end custom_gem "amq-protocol", :git => "git://github.com/ruby-amqp/amq-protocol.git", :branch => "master" bunny-2.6.1/examples/0000755000004100000410000000000013015255277014530 5ustar www-datawww-databunny-2.6.1/examples/consumers/0000755000004100000410000000000013015255277016546 5ustar www-datawww-databunny-2.6.1/examples/consumers/high_and_low_priority.rb0000644000004100000410000000215213015255277023456 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "bundler" Bundler.setup $:.unshift(File.expand_path("../../../lib", __FILE__)) require 'bunny' HIGH_PRIORITY_Q = "bunny.examples.priority.hilo.high" LOW_PRIORITY_Q = "bunny.examples.priority.hilo.low" conn = Bunny.new(:heartbeat_interval => 8) conn.start ch1 = conn.create_channel ch2 = conn.create_channel hi_q = ch1.queue(HIGH_PRIORITY_Q, :durable => false) lo_q = ch2.queue(LOW_PRIORITY_Q, :durable => false) ch3 = conn.create_channel x = ch3.default_exchange # create a backlog of low priority messages 30.times do x.publish(rand.to_s, :routing_key => LOW_PRIORITY_Q) end # and a much smaller one of high priority messages 3.times do x.publish(rand.to_s, :routing_key => HIGH_PRIORITY_Q) end hi_q.subscribe do |delivery_info, metadata, payload| puts "[high] Consumed #{payload}" end lo_q.subscribe do |delivery_info, metadata, payload| puts "[low] Consumed #{payload}" end loop do sleep 0.5 data = rand.to_s rk = [HIGH_PRIORITY_Q, LOW_PRIORITY_Q].sample x.publish(data, :routing_key => rk) puts "Published #{data}, routing key: #{rk}" end bunny-2.6.1/examples/guides/0000755000004100000410000000000013015255277016010 5ustar www-datawww-databunny-2.6.1/examples/guides/getting_started/0000755000004100000410000000000013015255277021177 5ustar www-datawww-databunny-2.6.1/examples/guides/getting_started/blabbr.rb0000644000004100000410000000115413015255277022751 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" conn = Bunny.new conn.start ch = conn.create_channel x = ch.fanout("nba.scores") ch.queue("joe", :auto_delete => true).bind(x).subscribe do |delivery_info, properties, payload| puts "#{payload} => joe" end ch.queue("aaron", :auto_delete => true).bind(x).subscribe do |delivery_info, properties, payload| puts "#{payload} => aaron" end ch.queue("bob", :auto_delete => true).bind(x).subscribe do |delivery_info, properties, payload| puts "#{payload} => bob" end x.publish("BOS 101, NYK 89").publish("ORL 85, ALT 88") conn.close bunny-2.6.1/examples/guides/getting_started/weathr.rb0000644000004100000410000000474313015255277023026 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" STDOUT.sync = true connection = Bunny.new connection.start channel = connection.create_channel # topic exchange name can be any string exchange = channel.topic("weathr", :auto_delete => true) # Subscribers. channel.queue("", :exclusive => true).bind(exchange, :routing_key => "americas.north.#").subscribe do |delivery_info, properties, payload| puts "An update for North America: #{payload}, routing key is #{delivery_info.routing_key}" end channel.queue("americas.south").bind(exchange, :routing_key => "americas.south.#").subscribe do |delivery_info, properties, payload| puts "An update for South America: #{payload}, routing key is #{delivery_info.routing_key}" end channel.queue("us.california").bind(exchange, :routing_key => "americas.north.us.ca.*").subscribe do |delivery_info, properties, payload| puts "An update for US/California: #{payload}, routing key is #{delivery_info.routing_key}" end channel.queue("us.tx.austin").bind(exchange, :routing_key => "#.tx.austin").subscribe do |delivery_info, properties, payload| puts "An update for Austin, TX: #{payload}, routing key is #{delivery_info.routing_key}" end channel.queue("it.rome").bind(exchange, :routing_key => "europe.italy.rome").subscribe do |delivery_info, properties, payload| puts "An update for Rome, Italy: #{payload}, routing key is #{delivery_info.routing_key}" end channel.queue("asia.hk").bind(exchange, :routing_key => "asia.southeast.hk.#").subscribe do |delivery_info, properties, payload| puts "An update for Hong Kong: #{payload}, routing key is #{delivery_info.routing_key}" end exchange.publish("San Diego update", :routing_key => "americas.north.us.ca.sandiego"). publish("Berkeley update", :routing_key => "americas.north.us.ca.berkeley"). publish("San Francisco update", :routing_key => "americas.north.us.ca.sanfrancisco"). publish("New York update", :routing_key => "americas.north.us.ny.newyork"). publish("São Paolo update", :routing_key => "americas.south.brazil.saopaolo"). publish("Hong Kong update", :routing_key => "asia.southeast.hk.hongkong"). publish("Kyoto update", :routing_key => "asia.southeast.japan.kyoto"). publish("Shanghai update", :routing_key => "asia.southeast.prc.shanghai"). publish("Rome update", :routing_key => "europe.italy.roma"). publish("Paris update", :routing_key => "europe.france.paris") sleep 1.0 connection.close bunny-2.6.1/examples/guides/getting_started/hello_world.rb0000644000004100000410000000056013015255277024037 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" STDOUT.sync = true conn = Bunny.new conn.start ch = conn.create_channel q = ch.queue("bunny.examples.hello_world", :auto_delete => true) q.subscribe do |delivery_info, properties, payload| puts "Received #{payload}" end q.publish("Hello!", :routing_key => q.name) sleep 1.0 conn.close bunny-2.6.1/examples/guides/exchanges/0000755000004100000410000000000013015255277017755 5ustar www-datawww-databunny-2.6.1/examples/guides/exchanges/direct_exchange_routing.rb0000644000004100000410000000143213015255277025165 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" puts "=> Direct exchange routing" puts conn = Bunny.new conn.start ch = conn.create_channel x = ch.direct("examples.imaging") q1 = ch.queue("", :auto_delete => true).bind(x, :routing_key => "resize") q1.subscribe do |delivery_info, properties, payload| puts "[consumer] #{q1.name} received a 'resize' message" end q2 = ch.queue("", :auto_delete => true).bind(x, :routing_key => "watermark") q2.subscribe do |delivery_info, properties, payload| puts "[consumer] #{q2.name} received a 'watermark' message" end # just an example data = rand.to_s x.publish(data, :routing_key => "resize") x.publish(data, :routing_key => "watermark") sleep 0.5 x.delete q1.delete q2.delete puts "Disconnecting..." conn.close bunny-2.6.1/examples/guides/exchanges/fanout_exchange_routing.rb0000644000004100000410000000073113015255277025210 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" puts "=> Fanout exchange routing" puts conn = Bunny.new conn.start ch = conn.create_channel x = ch.fanout("examples.pings") 10.times do |i| q = ch.queue("", :auto_delete => true).bind(x) q.subscribe do |delivery_info, properties, payload| puts "[consumer] #{q.name} received a message: #{payload}" end end x.publish("Ping") sleep 0.5 x.delete puts "Disconnecting..." conn.close bunny-2.6.1/examples/guides/exchanges/mandatory_messages.rb0000644000004100000410000000122613015255277024170 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" puts "=> Publishing messages as mandatory" puts conn = Bunny.new conn.start ch = conn.create_channel x = ch.default_exchange x.on_return do |return_info, properties, content| puts "Got a returned message: #{content}" end q = ch.queue("", :exclusive => true) q.subscribe do |delivery_info, properties, content| puts "Consumed a message: #{content}" end x.publish("This will NOT be returned", :mandatory => true, :routing_key => q.name) x.publish("This will be returned", :mandatory => true, :routing_key => "akjhdfkjsh#{rand}") sleep 0.5 puts "Disconnecting..." conn.close bunny-2.6.1/examples/guides/exchanges/headers_exchange_routing.rb0000644000004100000410000000154213015255277025330 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" puts "=> Headers exchange routing" puts conn = Bunny.new conn.start ch = conn.create_channel x = ch.headers("headers") q1 = ch.queue("", :exclusive => true).bind(x, :arguments => {"os" => "linux", "cores" => 8, "x-match" => "all"}) q2 = ch.queue("", :exclusive => true).bind(x, :arguments => {"os" => "osx", "cores" => 4, "x-match" => "any"}) q1.subscribe do |delivery_info, properties, content| puts "#{q1.name} received #{content}" end q2.subscribe do |delivery_info, properties, content| puts "#{q2.name} received #{content}" end x.publish("8 cores/Linux", :headers => {"os" => "linux", "cores" => 8}) x.publish("8 cores/OS X", :headers => {"os" => "osx", "cores" => 8}) x.publish("4 cores/Linux", :headers => {"os" => "linux", "cores" => 4}) sleep 0.5 conn.close bunny-2.6.1/examples/guides/extensions/0000755000004100000410000000000013015255277020207 5ustar www-datawww-databunny-2.6.1/examples/guides/extensions/exchange_to_exchange_bindings.rb0000644000004100000410000000112413015255277026535 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" puts "=> Demonstrating exchange-to-exchange bindings" puts conn = Bunny.new conn.start ch = conn.create_channel x1 = ch.fanout("bunny.examples.e2e.exchange1", :auto_delete => true, :durable => false) x2 = ch.fanout("bunny.examples.e2e.exchange2", :auto_delete => true, :durable => false) # x1 will be the source x2.bind(x1) q = ch.queue("", :exclusive => true) q.bind(x2) x1.publish("") sleep 0.2 puts "Queue #{q.name} now has #{q.message_count} message in it" sleep 0.7 puts "Disconnecting..." conn.close bunny-2.6.1/examples/guides/extensions/queue_lease.rb0000644000004100000410000000075413015255277023037 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" puts "=> Demonstrating queue TTL (queue leases)" puts conn = Bunny.new conn.start ch = conn.create_channel q = ch.queue("", :exclusive => true, :arguments => {"x-expires" => 300}) sleep 0.4 begin # this will raise because the queue is already deleted q.message_count rescue Bunny::NotFound => nfe puts "Got a 404 response: the queue has already been removed" end sleep 0.7 puts "Closing..." conn.close bunny-2.6.1/examples/guides/extensions/per_message_ttl.rb0000644000004100000410000000113713015255277023713 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" puts "=> Demonstrating per-message TTL" puts conn = Bunny.new conn.start ch = conn.create_channel x = ch.fanout("amq.fanout") q = ch.queue("", :exclusive => true).bind(x) 10.times do |i| x.publish("Message #{i}", :expiration => 1000) end sleep 0.7 _, _, content1 = q.pop puts "Fetched #{content1.inspect} after 0.7 second" sleep 0.8 _, _, content2 = q.pop msg = if content2 content2.inspect else "nothing" end puts "Fetched #{msg} after 1.5 second" sleep 0.7 puts "Closing..." conn.close bunny-2.6.1/examples/guides/extensions/sender_selected_distribution.rb0000644000004100000410000000167413015255277026473 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" puts "=> Demonstrating sender-selected distribution" puts conn = Bunny.new conn.start ch = conn.create_channel x = ch.direct("bunny.examples.ssd.exchange") q1 = ch.queue("", :exclusive => true).bind(x, :routing_key => "one") q2 = ch.queue("", :exclusive => true).bind(x, :routing_key => "two") q3 = ch.queue("", :exclusive => true).bind(x, :routing_key => "three") q4 = ch.queue("", :exclusive => true).bind(x, :routing_key => "four") 10.times do |i| x.publish("Message #{i}", :routing_key => "one", :headers => {"CC" => ["two", "three"]}) end sleep 0.2 puts "Queue #{q1.name} now has #{q1.message_count} messages in it" puts "Queue #{q2.name} now has #{q2.message_count} messages in it" puts "Queue #{q3.name} now has #{q3.message_count} messages in it" puts "Queue #{q4.name} now has #{q4.message_count} messages in it" sleep 0.7 puts "Closing..." conn.close bunny-2.6.1/examples/guides/extensions/dead_letter_exchange.rb0000644000004100000410000000135013015255277024651 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" puts "=> Demonstrating dead letter exchange" puts conn = Bunny.new conn.start ch = conn.create_channel x = ch.fanout("amq.fanout") dlx = ch.fanout("bunny.examples.dlx.exchange") q = ch.queue("", :exclusive => true, :arguments => {"x-dead-letter-exchange" => dlx.name}).bind(x) # dead letter queue dlq = ch.queue("", :exclusive => true).bind(dlx) x.publish("") sleep 0.2 delivery_info, _, _ = q.pop(:manual_ack => true) puts "#{dlq.message_count} messages dead lettered so far" puts "Rejecting a message" ch.nack(delivery_info.delivery_tag) sleep 0.2 puts "#{dlq.message_count} messages dead lettered so far" dlx.delete puts "Disconnecting..." conn.close bunny-2.6.1/examples/guides/extensions/per_queue_message_ttl.rb0000644000004100000410000000117113015255277025115 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" puts "=> Demonstrating per-queue message TTL" puts conn = Bunny.new conn.start ch = conn.create_channel x = ch.fanout("amq.fanout") q = ch.queue("", :exclusive => true, :arguments => {"x-message-ttl" => 1000}).bind(x) 10.times do |i| x.publish("Message #{i}") end sleep 0.7 _, _, content1 = q.pop puts "Fetched #{content1.inspect} after 0.7 second" sleep 0.8 _, _, content2 = q.pop msg = if content2 content2.inspect else "nothing" end puts "Fetched #{msg} after 1.5 second" sleep 0.7 puts "Closing..." conn.close bunny-2.6.1/examples/guides/extensions/basic_nack.rb0000644000004100000410000000107313015255277022612 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" puts "=> Demonstrating basic.nack" puts conn = Bunny.new conn.start ch = conn.create_channel q = ch.queue("", :exclusive => true) 20.times do q.publish("") end 20.times do delivery_info, _, _ = q.pop(:manual_ack => true) if delivery_info.delivery_tag == 20 # requeue them all at once with basic.nack ch.nack(delivery_info.delivery_tag, true, true) end end puts "Queue #{q.name} still has #{q.message_count} messages in it" sleep 0.7 puts "Disconnecting..." conn.close bunny-2.6.1/examples/guides/extensions/connection_blocked.rb0000644000004100000410000000122413015255277024355 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" puts "=> Demonstrating connection.blocked" puts conn = Bunny.new conn.start ch = conn.create_channel x = ch.fanout("amq.fanout") # This example requires high memory watermark to be set # really low to demonstrate blocking. # # rabbitmqctl set_vm_memory_high_watermark 0.00000001 # # should do it. conn.on_blocked do |connection_blocked| puts "Connection is blocked. Reason: #{connection_blocked.reason}" end conn.on_unblocked do |connection_unblocked| puts "Connection is unblocked." end x.publish("z" * 1024 * 1024 * 16) sleep 120.0 puts "Disconnecting..." conn.close bunny-2.6.1/examples/guides/extensions/consumer_cancellation_notification.rb0000644000004100000410000000120513015255277027647 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" puts "=> Demonstrating consumer cancellation notification" puts conn = Bunny.new conn.start ch = conn.create_channel module Bunny module Examples class ExampleConsumer < Bunny::Consumer def cancelled? @cancelled end def handle_cancellation(basic_cancel) puts "#{@consumer_tag} was cancelled" @cancelled = true end end end end q = ch.queue("", :exclusive => true) c = Bunny::Examples::ExampleConsumer.new(ch, q) q.subscribe_with(c) sleep 0.1 q.delete sleep 0.1 puts "Disconnecting..." conn.close bunny-2.6.1/examples/guides/extensions/publisher_confirms.rb0000644000004100000410000000103213015255277024425 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" puts "=> Demonstrating publisher confirms" puts conn = Bunny.new conn.start ch = conn.create_channel x = ch.fanout("amq.fanout") q = ch.queue("", :exclusive => true).bind(x) ch.confirm_select 1000.times do x.publish("") end ch.wait_for_confirms # blocks calling thread until all acks are received sleep 0.2 puts "Received acks for all published messages. #{q.name} now has #{q.message_count} messages." sleep 0.7 puts "Disconnecting..." conn.close bunny-2.6.1/examples/guides/extensions/alternate_exchange.rb0000644000004100000410000000121613015255277024355 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" STDOUT.sync = true puts "=> Demonstrating alternate exchanges" puts conn = Bunny.new conn.start ch = conn.create_channel x1 = ch.fanout("bunny.examples.ae.exchange1", :auto_delete => true, :durable => false) x2 = ch.fanout("bunny.examples.ae.exchange2", :auto_delete => true, :durable => false, :arguments => { "alternate-exchange" => x1.name }) q = ch.queue("", :exclusive => true) q.bind(x1) x2.publish("") sleep 0.2 puts "Queue #{q.name} now has #{q.message_count} message in it" sleep 0.7 puts "Disconnecting..." conn.close bunny-2.6.1/examples/guides/queues/0000755000004100000410000000000013015255277017317 5ustar www-datawww-databunny-2.6.1/examples/guides/queues/redeliveries.rb0000644000004100000410000000373713015255277022340 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" STDOUT.sync = true puts "=> Subscribing for messages using explicit acknowledgements model" puts connection1 = Bunny.new connection1.start connection2 = Bunny.new connection2.start connection3 = Bunny.new connection3.start ch1 = connection1.create_channel ch1.prefetch(1) ch2 = connection2.create_channel ch2.prefetch(1) ch3 = connection3.create_channel ch3.prefetch(1) x = ch3.direct("amq.direct") q1 = ch1.queue("bunny.examples.acknowledgements.explicit", :auto_delete => false) q1.purge q1.bind(x).subscribe(:manual_ack => true, :block => false) do |delivery_info, properties, payload| # do some work sleep(0.2) # acknowledge some messages, they will be removed from the queue if rand > 0.5 # FYI: there is a shortcut, Bunny::Channel.ack ch1.acknowledge(delivery_info.delivery_tag, false) puts "[consumer1] Got message ##{properties.headers['i']}, redelivered?: #{delivery_info.redelivered?}, ack-ed" else # some messages are not ack-ed and will remain in the queue for redelivery # when app #1 connection is closed (either properly or due to a crash) puts "[consumer1] Got message ##{properties.headers['i']}, SKIPPED" end end q2 = ch2.queue("bunny.examples.acknowledgements.explicit", :auto_delete => false) q2.bind(x).subscribe(:manual_ack => true, :block => false) do |delivery_info, properties, payload| # do some work sleep(0.2) ch2.acknowledge(delivery_info.delivery_tag, false) puts "[consumer2] Got message ##{properties.headers['i']}, redelivered?: #{delivery_info.redelivered?}, ack-ed" end t1 = Thread.new do i = 0 loop do sleep 0.5 x.publish("Message ##{i}", :headers => { :i => i }) i += 1 end end t1.abort_on_exception = true t2 = Thread.new do sleep 4.0 connection1.close puts "----- Connection 1 is now closed (we pretend that it has crashed) -----" end t2.abort_on_exception = true sleep 7.0 connection2.close connection3.close bunny-2.6.1/examples/guides/queues/one_off_consumer.rb0000644000004100000410000000100413015255277023165 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" STDOUT.sync = true conn = Bunny.new conn.start ch = conn.create_channel q = ch.queue("bunny.examples.hello_world", :auto_delete => true) q.publish("Hello!", :routing_key => q.name) # demonstrates a blocking consumer that needs to cancel itself # in the message handler q.subscribe(:block => true) do |delivery_info, properties, payload| puts "Received #{payload}, cancelling" delivery_info.consumer.cancel end sleep 1.0 conn.close bunny-2.6.1/examples/connection/0000755000004100000410000000000013015255277016667 5ustar www-datawww-databunny-2.6.1/examples/connection/automatic_recovery_with_multiple_consumers.rb0000644000004100000410000000226513015255277030171 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "bundler" Bundler.setup $:.unshift(File.expand_path("../../../lib", __FILE__)) require 'bunny' conn = Bunny.new(:heartbeat_interval => 8) conn.start ch1 = conn.create_channel x1 = ch1.topic("bunny.examples.recovery.e1", :durable => false) q1 = ch1.queue("bunny.examples.recovery.q1", :durable => false) q1.bind(x1, :routing_key => "abc").bind(x1, :routing_key => "def") ch2 = conn.create_channel x2 = ch2.topic("bunny.examples.recovery.e2", :durable => false) q2 = ch2.queue("bunny.examples.recovery.q2", :durable => false) q2.bind(x2, :routing_key => "abc").bind(x2, :routing_key => "def") q1.subscribe do |delivery_info, metadata, payload| puts "Consumed #{payload} at stage one" x2.publish(payload, :routing_key => ["abc", "def", "xyz"].sample) end q2.subscribe do |delivery_info, metadata, payload| puts "Consumed #{payload} at stage two" end loop do sleep 2 rk = ["abc", "def", "ghi", "xyz"].sample puts "Publishing with routing key #{rk}" begin x1.publish(rand.to_s, :routing_key => rk) # happens when a message is published before the connection # is recovered rescue Bunny::ConnectionClosedError => e end end bunny-2.6.1/examples/connection/automatic_recovery_with_client_named_queues.rb0000644000004100000410000000144013015255277030243 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "bundler" Bundler.setup $:.unshift(File.expand_path("../../../lib", __FILE__)) require 'bunny' conn = Bunny.new(:heartbeat_interval => 8) conn.start ch = conn.create_channel x = ch.topic("bunny.examples.recovery.topic", :durable => false) q = ch.queue("bunny.examples.recovery.client_named_queue1", :durable => false) q.bind(x, :routing_key => "abc").bind(x, :routing_key => "def") q.subscribe do |delivery_info, metadata, payload| puts "Consumed #{payload}" end loop do sleep 2 data = rand.to_s rk = ["abc", "def"].sample begin x.publish(data, :routing_key => rk) puts "Published #{data}, routing key: #{rk}" # happens when a message is published before the connection # is recovered rescue Exception => e end end bunny-2.6.1/examples/connection/automatic_recovery_with_republishing.rb0000644000004100000410000000550313015255277026731 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "bundler" Bundler.setup $:.unshift(File.expand_path("../../../lib", __FILE__)) require 'Bunny' conn = Bunny.new(:heartbeat_interval => 8) conn.start ch0 = conn.create_channel ch1 = conn.create_channel ch2 = conn.create_channel ch3 = conn.create_channel x = ch1.topic("hb.examples.recovery.topic", :durable => false) q1 = ch1.queue("hb.examples.recovery.client_named_queue1", :durable => false) q2 = ch2.queue("hb.examples.recovery.client_named_queue2", :durable => false) q3 = ch3.queue("hb.examples.recovery.client_named_queue3", :durable => false) q1.bind(x, :routing_key => "abc") q2.bind(x, :routing_key => "def") q3.bind(x, :routing_key => "xyz") x0 = ch0.fanout("hb.examples.recovery.fanout0") x1 = ch1.fanout("hb.examples.recovery.fanout1") x2 = ch2.fanout("hb.examples.recovery.fanout2") x3 = ch3.fanout("hb.examples.recovery.fanout3") q4 = ch1.queue("", :exclusive => true) q4.bind(x0) q5 = ch2.queue("", :exclusive => true) q5.bind(x1) q6 = ch3.queue("", :exclusive => true) q6.bind(x2) q6.bind(x3) q1.subscribe do |delivery_info, metadata, payload| puts "[Q1] Consumed #{payload} on channel #{q1.channel.id}" if ch0.open? puts "Publishing a reply on channel #{ch0.id} which is open" x0.publish(Time.now.to_i.to_s) end end q2.subscribe do |delivery_info, metadata, payload| puts "[Q2] Consumed #{payload} on channel #{q2.channel.id}" if ch1.open? puts "Publishing a reply on channel #{ch1.id} which is open" x1.publish(Time.now.to_i.to_s) end end q3.subscribe do |delivery_info, metadata, payload| puts "[Q3] Consumed #{payload} (consumer 1, channel #{q3.channel.id})" if ch2.open? puts "Publishing a reply on channel #{ch1.id} which is open" x2.publish(Time.now.to_i.to_s) end end q3.subscribe do |delivery_info, metadata, payload| puts "[Q3] Consumed #{payload} (consumer 2, channel #{q3.channel.id})" if ch3.open? puts "Publishing a reply on channel #{ch3.id} which is open" x3.publish(Time.now.to_i.to_s) end end q4.subscribe do |delivery_info, metadata, payload| puts "[Q4] Consumed #{payload} on channel #{q4.channel.id}" end q5.subscribe do |delivery_info, metadata, payload| puts "[Q5] Consumed #{payload} on channel #{q5.channel.id}" end q6.subscribe do |delivery_info, metadata, payload| puts "[Q6] Consumed #{payload} on channel #{q6.channel.id}" end loop do sleep 1 data = rand.to_s rk = ["abc", "def", "xyz", Time.now.to_i.to_s].sample begin 3.times do x.publish(rand.to_s, :routing_key => rk) puts "Published #{data}, routing key: #{rk} on channel #{x.channel.id}" end # happens when a message is published before the connection # is recovered rescue Exception => e puts "Exception: #{e.message}" # e.backtrace.each do |line| # puts "\t#{line}" # end end end bunny-2.6.1/examples/connection/authentication_failure.rb0000644000004100000410000000052513015255277023744 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "bundler" Bundler.setup $:.unshift(File.expand_path("../../../lib", __FILE__)) require 'bunny' begin conn = Bunny.new("amqp://guest8we78w7e8:guest2378278@127.0.0.1") conn.start rescue Bunny::PossibleAuthenticationFailureError => e puts "Could not authenticate as #{conn.username}" end bunny-2.6.1/examples/connection/manually_reconnecting_consumer.rb0000644000004100000410000000067613015255277025520 0ustar www-datawww-data#!/usr/bin/env ruby require 'bunny' Bundler.setup begin connection = Bunny.new(:automatically_recover => false) connection.start ch = connection.channel q = ch.queue("manually_reconnecting_consumer", :exclusive => true) q.subscribe(:block => true) do |_, _, payload| puts "Consumed #{payload}" end rescue Bunny::NetworkFailure => e ch.maybe_kill_consumer_work_pool! sleep 10 puts "Recovering manually..." retry end bunny-2.6.1/examples/connection/automatic_recovery_with_basic_get.rb0000644000004100000410000000145513015255277026160 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "bundler" Bundler.setup $:.unshift(File.expand_path("../../../lib", __FILE__)) require 'bunny' conn = Bunny.new(:heartbeat_interval => 8) conn.start ch = conn.create_channel x = ch.topic("bunny.examples.recovery.topic", :durable => false) q = ch.queue("bunny.examples.recovery.client_named_queue2", :durable => true) q.purge q.bind(x, :routing_key => "abc").bind(x, :routing_key => "def") loop do sleep 8 body = rand.to_s begin x.publish(body, :routing_key => ["abc", "def"].sample) puts "Published #{body}" # happens when a message is published before the connection # is recovered rescue Exception => e end sleep 1.5 _, _, payload = q.pop if payload puts "Consumed #{payload}" else puts "Consumed nothing" end end bunny-2.6.1/examples/connection/unknown_host.rb0000644000004100000410000000050713015255277021752 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "bundler" Bundler.setup $:.unshift(File.expand_path("../../../lib", __FILE__)) require 'bunny' begin conn = Bunny.new("amqp://guest:guest@aksjhdkajshdkj.example82737.com") conn.start rescue Bunny::TCPConnectionFailed => e puts "Connection to #{conn.hostname} failed" end bunny-2.6.1/examples/connection/automatic_recovery_with_server_named_queues.rb0000644000004100000410000000133413015255277030275 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "bundler" Bundler.setup $:.unshift(File.expand_path("../../../lib", __FILE__)) require 'bunny' conn = Bunny.new(:heartbeat_interval => 8) conn.start ch = conn.create_channel x = ch.topic("bunny.examples.recovery.topic", :durable => false) q = ch.queue("", :durable => false) q.bind(x, :routing_key => "abc").bind(x, :routing_key => "def") q.subscribe do |delivery_info, metadata, payload| puts "Consumed #{payload}" end loop do sleep 2 data = rand.to_s rk = ["abc", "def"].sample begin x.publish(rand.to_s, :routing_key => rk) # happens when a message is published before the connection # is recovered rescue Bunny::ConnectionClosedError => e end end bunny-2.6.1/examples/connection/disabled_automatic_recovery.rb0000644000004100000410000000132613015255277024751 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "bundler" Bundler.setup $:.unshift(File.expand_path("../../../lib", __FILE__)) require 'bunny' conn = Bunny.new(:heartbeat_interval => 8, :automatically_recover => false) conn.start ch = conn.create_channel x = ch.topic("bunny.examples.recovery.topic", :durable => false) q = ch.queue("bunny.examples.recovery.client_named_queue2", :durable => true) q.purge q.bind(x, :routing_key => "abc").bind(x, :routing_key => "def") loop do sleep 1.5 body = rand.to_s puts "Published #{body}" x.publish(body, :routing_key => ["abc", "def"].sample) sleep 1.5 _, _, payload = q.pop if payload puts "Consumed #{payload}" else puts "Consumed nothing" end end bunny-2.6.1/examples/connection/manually_reconnecting_publisher.rb0000644000004100000410000000063413015255277025654 0ustar www-datawww-data#!/usr/bin/env ruby require 'bunny' Bundler.setup begin connection = Bunny.new(:automatically_recover => false) connection.start ch = connection.channel x = ch.default_exchange loop do 10.times do |i| print "." x.publish("") end sleep 3.0 end rescue Bunny::NetworkFailure => e ch.maybe_kill_consumer_work_pool! sleep 10 puts "Recovering manually..." retry end bunny-2.6.1/examples/connection/heartbeat.rb0000644000004100000410000000035313015255277021154 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "bundler" Bundler.setup $:.unshift(File.expand_path("../../../lib", __FILE__)) require 'bunny' conn = Bunny.new(:heartbeat_interval => 2) conn.start c = conn.create_channel sleep 10 bunny-2.6.1/examples/connection/channel_level_exception.rb0000644000004100000410000000112213015255277024065 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "bundler" Bundler.setup $:.unshift(File.expand_path("../../../lib", __FILE__)) require 'bunny' conn = Bunny.new(:heartbeat_interval => 8) conn.start begin ch2 = conn.create_channel q = "bunny.examples.recovery.q#{rand}" ch2.queue_declare(q, :durable => false) ch2.queue_declare(q, :durable => true) rescue Bunny::PreconditionFailed => e puts "Channel-level exception! Code: #{e.channel_close.reply_code}, message: #{e.channel_close.reply_text}" ensure conn.create_channel.queue_delete(q) end puts "Disconnecting..." conn.close bunny-2.6.1/ChangeLog.md0000644000004100000410000013705713015255277015100 0ustar www-datawww-data## Changes between Bunny 2.6.0 and 2.6.1 (October 23rd, 2016) ### Bunny::ConsumerWorkPool#shutdown Terminates Early When It's Safe to Do So `Bunny::ConsumerWorkPool#shutdown(true)` waited for consumer shutdown even if the pool wasn't active (there were no consumers on its channel). GitHub issue: [#438](https://github.com/ruby-amqp/bunny/issues/438). ## Changes between Bunny 2.5.0 and 2.6.0 (October 15th, 2016) ### Graceful Shutdown of Consumers Consumer work pool will now allow for a grace period before stopping pool threads so that delivery processing in progress can have a chance to finish. GitHub issue: [#437](https://github.com/ruby-amqp/bunny/pull/437) Contributed by Stefan Sedich. ### `Bunny::Channel#wait_for_confirms` Now Throws When Used on a Closed Channel GitHub issue: [#428](https://github.com/ruby-amqp/bunny/pull/428) Contributed by Dimitar Dimitrov. ### Race Condition Eliminated in `Bunny::Channel#wait_for_confirms` GitHub issue: [#424](https://github.com/ruby-amqp/bunny/issues/424) Contributed by Dimitar Dimitrov. ### More Defensive Consumer Work Pool `Bunny::ConsumerWorkPool#join` and `Bunny::ConsumerWorkPool#pause` no longer fails with a `NoMethodError` on nil when executed on a work pool that doesn't have active threads (consumers). This change is largely cosmetic and won't affect the majority of of projects in any way. ## Changes between Bunny 2.4.0 and 2.5.0 (July 20th, 2016) ### Exchange Bindings are Now Correctly Recovered GitHub issue: [#410](https://github.com/ruby-amqp/bunny/issues/410) Contributed by Andrew Bruce. ### `Bunny::Channel#wait_for_confirms` Awaits While There're Outstanding Unconfirmed Messages GitHub issue: [#424](https://github.com/ruby-amqp/bunny/issues/424) Contributed by Dimitar Dimitrov. ### Queue Recovery Respects the `:no_declare` Option Queue recovery now respects the `:no_declare` option. ### `Bunny::Channel#wait_for_confirms` Throws Early `Bunny::Channel#wait_for_confirms` now throws an exception early when invoked on a closed channel. GitHub issue: [#428](https://github.com/ruby-amqp/bunny/pull/428). Contributed by Dimitar Dimitrov. ## Changes between Bunny 2.3.0 and 2.4.0 (June 11th, 2016) **This release includes minor breaking API changes**. ### Unconfirmed Delivery Tag Set Reset on Network Recovery Channels will now reset their unconfirmed delivery tag set after recovery. GitHub issue: [#406](https://github.com/ruby-amqp/bunny/pull/406) Contributed by Bill Ruddock. ### Support (Quoted) IPv6 Addresses in Address Lists GitHub issue: [#383](https://github.com/ruby-amqp/bunny/issues/383). Contributed by Jeremy Heiler. ### Transport#read_fully Doesn't Try to Recover Since transport is replaced by a recovering connection anyway, and this produces confusing errors up the stack. GitHub issue: [#359](https://github.com/ruby-amqp/bunny/issues/359) Contributed by Donal McBreen. ### Client-Provided Session `:properties` Merged with Defaults Client-Provided Session `:properties` will now be merged with defaults instead of replacing them. This makes it much more convenient to override a single key. ### More Predictable RABBITMQ_URL Handling **This is a breaking API change**. `RABBITMQ_URL` no longer will be used if any other connection options are provided. This makes it possible to use `RABBITMQ_URL` for some connections and options for others in a single OS process. GitHub issue: [#403](https://github.com/ruby-amqp/bunny/pull/403) Contributed by Jimmy Petersen. ## Changes between Bunny 2.2.0 and 2.3.0 (Feb 26th, 2016) ### Thread#abort_on_exception Setting for Consumer Work Pool Threads `Bunny::Session#create_channel` now supports a 3rd argument that, when set to `true`, makes consumer work pool threads to have `Thread#abort_on_exception` set on them. GH issue: [#382](https://github.com/ruby-amqp/bunny/pull/382) Contributed by Seamus Abshere. ### Explicit Transport Closure on Recovery Bunny now will explicitly close previosly used transport before starting connection recovery. GitHub issue: [#377](https://github.com/ruby-amqp/bunny/pull/377). Contributed by bkanhoopla. ### No TLS Socket Double-init Makes sure that TLS sockets are not double-initialized. GH issue: [#345](https://github.com/ruby-amqp/bunny/issues/345). Contributed by Carl Hörberg. ### Lazily Evaluated Debug Log Strings GH issue: [#375](https://github.com/ruby-amqp/bunny/pull/375) Contributed by Omer Katz. ## Changes between Bunny 2.1.0 and 2.2.0 (Sep 6th, 2015) ### Add :addresses to connect options Before this the connection options only allowed multiple hosts, an address is a combination of a host and a port. This makes it possible to specify different hosts with different ports. Contributed by Bart van Zon (Tele2). ### Recover from connection.close by default Bunny will now try to reconnect also when server sent connection.close is received, e.g. when a server is restarting (but also when the connection is force closed by the server). This is in-line with how many other clients behave. The old default was `recover_from_connection_close: false`. Contributed by Carl Hörberg (CloudAMQP). ## Changes between Bunny 2.0.0 and 2.1.0 Bunny 2.1.0 has an **important breaking change**. It is highly advised that 2.1.0 is not mixed with earlier versions of Bunny in case your applications include **integers in message headers**. ### Integer Value Serialisation in Headers Integer values in headers are now serialised as signed 64-bit integers. Previously they were serialised as 32-bit unsigned integers, causing both underflows and overflows: incorrect values were observed by consumers. It is highly advised that 2.1.0 is not mixed with earlier versions of Bunny in case your applications include integers in message headers. If that's not the case, Bunny 2.1 will integeroperate with any earlier version starting with 0.9.0 just fine. Popular clients in other languages (e.g. Java and .NET) will interoperate with Bunny 2.1.0 without issues. ### Explicit Ruby 2.0 Requirement Bunny now requires Ruby 2.0 in the gemspec. Contributed by Carl Hörberg. ### JRuby Fix Bunny runs again on JRuby. Note that JRuby users are strongly advised to use March Hare instead. Contributed by Teodor Pripoae. ## Changes between Bunny 1.7.0 and 2.0.0 Bunny `2.0` doesn't have any breaking API changes but drops Ruby 1.8 and 1.9 (both EOL'ed) support, hence the version. ### Minimum Required Ruby Version is 2.0 Bunny `2.0` requires Ruby 2.0 or later. ## Non-Blocking Writes Bunny now uses non-blocking socket writes, uses a reduced number of writes for message publishing (frames are batched into a single write), and handles TCP back pressure from RabbitMQ better. Contributed by Irina Bednova and Michael Klishin. ### Reduced Timeout Use `Bunny::ContinuationQueue#poll` no longer relies on Ruby's `Timeout` which has numerous issues, including starting a new "interruptor" thread per operation, which is far from efficient. Contributed by Joe Eli McIlvain and Carl Hörberg. ### Capped Number of Connection Recovery Attempts `:recovery_attempts` is a new option that limits the number of connection recovery attempts performed by Bunny. `nil` means "no limit". Contributed by Irina Bednova. ### Bunny::Channel#basic_ack and Related Methods Improvements `Bunny::Channel#basic_ack`, `Bunny::Channel#basic_nack`, and `Bunny::Channel#basic_reject` now adjust delivery tags between connection recoveries, as well as have a default value for the second argument. Contributed by Wayne Conrad. ### Logger Output Remains Consistent Setting the `@logger.progname` attribute changes the output of the logger. This is not expected behaviour when the client provides a custom logger. Behaviour remains unchainged when the internally initialized logger is used. Contributed by Justin Carter. ### prefetch_count is Limited to 65535 Since `basic.qos`'s `prefetch_count` field is of type `short` in the protocol, Bunny must enforce its maximum allowed value to `2^16 - 1` to avoid confusing issues due to overflow. ### Per-Consumer and Per-Channel Prefetch Recent RabbitMQ versions support `basic.qos` `global` flag, controlling whether `prefetch` applies per-consumer or per-channel. Bunny `Channel#prefetch` now allows flag to be set as optional parameter, with the same default behaviour as before (per-consumer). Contributed by tiredpixel. ## Changes between Bunny 1.6.0 and 1.7.0 ### TLS Peer Verification Enabled by Default When using TLS, peer verification is now enabled by default. It is still possible to [disable verification](http://rubybunny.info/articles/tls.html), e.g. for convenient development locally. Peer verification is a means of protection against man-in-the-middle attacks and is highly recommended in production settings. However, it can be an inconvenience during local development. We believe it's time to have the default to be more secure. Contributed by Michael Klishin (Pivotal) and Andre Foeken (Nedap). ### Higher Default Connection Timeout Default connection timeout has been increased to 25 seconds. The older default of 5 seconds wasn't sufficient in some edge cases with DNS resolution (e.g. when primary DNS server is down). The value can be overriden at connection time. Contributed by Yury Batenko. ### Socket Read Timeout No Longer Set to 0 With Disabled Heartbeats GH issue: [#267](https://github.com/ruby-amqp/bunny/pull/267). ### JRuby Writes Fixes On JRuby, Bunny reverts back to using plain old `write(2)` for writes. The CRuby implementation on JRuby suffers from I/O incompatibilities. Until JRuby Bunny users who run on JRuby are highly recommended to switch to [March Hare](http://rubymarchhare.info), which has nearly identical API and is significantly more efficient. ### Bunny::Session#with_channel Synchornisation Improvements `Bunny::Session#with_channel` is now fully synchronised and won't run into `COMMAND_INVALID` errors when used from multiple threads that share a connection. ## Changes between Bunny 1.5.0 and 1.6.0 ### TLSv1 by Default TLS connections now prefer TLSv1 (or later, if available) due to the recently discovered [POODLE attack](https://www.openssl.org/~bodo/ssl-poodle.pdf) on SSLv3. Contributed by Michael Klishin (Pivotal) and Justin Powers (Desk.com). GH issues: * [#259](https://github.com/ruby-amqp/bunny/pull/259) * [#260](https://github.com/ruby-amqp/bunny/pull/260) * [#261](https://github.com/ruby-amqp/bunny/pull/261) ### Socket Read and Write Timeout Improvements Bunny now sets a read timeout on the sockets it opens, and uses `IO.select` timeouts as the most reliable option available on Ruby 1.9 and later. GH issue: [#254](https://github.com/ruby-amqp/bunny/pull/254). Contributed by Andre Foeken (Nedap). ### Inline TLS Certificates Support TLS certificate options now accept inline certificates as well as file paths. GH issues: [#255](https://github.com/ruby-amqp/bunny/pull/255), [#256](https://github.com/ruby-amqp/bunny/pull/256). Contributed by Will Barrett (Sqwiggle). ## Changes between Bunny 1.4.0 and 1.5.0 ### Improved Uncaught Exception Handler Uncaught exception handler now provides more information about the exception, including its caller (one more stack trace line). Contributed by Carl Hörberg (CloudAMQP). ### Convenience Method for Temporary (Server-named, Exclusive) Queue Declaration `Bunny::Channel#temporary_queue` is a convenience method that declares a new server-named exclusive queue: ``` ruby q = ch.temporary_queue ``` Contributed by Daniel Schierbeck (Zendesk). ### Recovery Reliability Improvements Automatic connection recovery robustness improvements. Contributed by Andre Foeken (Nedap). ### Host Lists It is now possible to pass the `:hosts` option to `Bunny.new`/`Bunny::Session#initialize`. When connection to RabbitMQ (including during connection recovery), a random host will be chosen from the list. Connection shuffling and robustness improvements. Contributed by Andre Foeken (Nedap). ### Default Channel Removed Breaks compatibility with Bunny 0.8.x. `Bunny:Session#default_channel` was removed. Please open channels explicitly now, as all the examples in the docs do. ## Changes between Bunny 1.3.0 and 1.4.0 ### Channel#wait_for_confirms Returns Immediately If All Publishes Confirmed Contributed by Matt Campbell. ### Publisher Confirms is In Sync After Recovery When a connection is recovered, the sequence counter resets on the broker, but not the client. To keep things in sync the client must store a confirmation offset after a recovery. Contributed by Devin Christensen. ### NoMethodError on Thread During Shutdown During abnormal termination, `Bunny::Session#close` no longer tries to call the non-existent `terminate_with` method on its origin thread. ## Changes between Bunny 1.2.0 and 1.3.0 ### TLS Can Be Explicitly Disabled TLS now can be explicitly disabled even when connecting (without TLS) to the default RabbitMQ TLS/amqps port (5671): ``` ruby conn = Bunny.new(:port => 5671, :tls => false) ``` Contributed by Muhan Zou. ### Single Threaded Connections Raise Shutdown Exceptions Single threaded Bunny connections will now raise exceptions that occur during shutdown as is (instead of trying to shut down I/O loop which only threaded ones have). Contributed by Carl Hörberg. ### Synchronization Improvements for Session#close `Bunny::Session#close` now better synchronizes state transitions, eliminating a few race condition scenarios with I/O reader thread. ### Bunny::Exchange.default Fix `Bunny::Exchange.default` no longer raises an exception. Note that it is a legacy compatibility method. Please use `Bunny::Channel#default_exchange` instead. Contributed by Justin Litchfield. GH issue [#211](https://github.com/ruby-amqp/bunny/pull/211). ### Bunny::Queue#pop_as_hash Removed `Bunny::Queue#pop_as_hash`, which was added to ease migration to Bunny 0.9, was removed. ### Bunny::Queue#pop Wraps Metadata `Bunny::Queue#pop` now wraps `basic.get-ok` and message properties into `Bunny::GetResponse` and `Bunny::MessageProperties`, just like `basic.consume` deliveries. GH issue: [#212](https://github.com/ruby-amqp/bunny/issues/212). ### Better Synchronization for Publisher Confirms Publisher confirms implementation now synchronizes unconfirmed set better. Contributed by Nicolas Viennot. ### Channel Allocation After Recovery Channel id allocator is no longer reset after recovery if there are channels open. Makes it possible to open channels on a recovered connection (in addition to the channels it already had). ## Changes between Bunny 1.1.0 and 1.2.0 ### :key Supported in Bunny::Channel#queue_bind It is now possible to use `:key` (which Bunny versions prior to 0.9 used) as well as `:routing_key` as an argument to `Bunny::Queue#bind`. ### System Exceptions Not Rescued by the Library Bunny now rescues `StandardError` instead of `Exception` where it automatically does so (e.g. when dispatching deliveries to consumers). Contributed by Alex Young. ### Initial Socket Connection Timeout Again Raises Bunny::TCPConnectionFailed Initial socket connection timeout again raises `Bunny::TCPConnectionFailed` on the connection origin thread. ### Thread Leaks Plugged `Bunny::Session#close` on connections that have experienced a network failure will correctly clean up I/O and heartbeat sender threads. Contributed by m-o-e. ### Bunny::Concurrent::ContinuationQueue#poll Rounding Fix `Bunny::Concurrent::ContinuationQueue#poll` no longer floors the argument to the nearest second. Contributed by Brian Abreu. ### Routing Key Limit Per AMQP 0-9-1 spec, routing keys cannot be longer than 255 characters. `Bunny::Channel#basic_publish` and `Bunny::Exchange#publish` now enforces this limit. ### Nagle's Algorithm Disabled Correctly Bunny now properly disables [Nagle's algorithm](http://boundary.com/blog/2012/05/02/know-a-delay-nagles-algorithm-and-you/) on the sockets it opens. This likely means significantly lower latency for workloads that involve sending a lot of small messages very frequently. [Contributed](https://github.com/ruby-amqp/bunny/pull/187) by Nelson Gauthier (AirBnB). ### Internal Exchanges Exchanges now can be declared as internal: ``` ruby ch = conn.create_channel x = ch.fanout("bunny.tests.exchanges.internal", :internal => true) ``` Internal exchanges cannot be published to by clients and are solely used for [Exchange-to-Exchange bindings](http://rabbitmq.com/e2e.html) and various plugins but apps may still need to bind them. Now it is possible to do so with Bunny. ### Uncaught Consumer Exceptions Uncaught consumer exceptions are now handled by uncaught exceptions handler that can be defined per channel: ``` ruby ch.on_uncaught_exception do |e, consumer| # ... end ``` ## Changes between Bunny 1.1.0.rc1 and 1.1.0 ### Synchronized Session#create_channel and Session#close_channel Full bodies of `Bunny::Session#create_channel` and `Bunny::Session#close_channel` are now synchronized, which makes sure concurrent `channel.open` and subsequent operations (e.g. `exchange.declare`) do not result in connection-level exceptions (incorrect connection state transitions). ### Corrected Recovery Log Message Bunny will now use actual recovery interval in the log. Contributed by Chad Fowler. ## Changes between Bunny 1.1.0.pre2 and 1.1.0.rc1 ### Full Channel State Recovery Channel recovery now involves recovery of publisher confirms and transaction modes. ### TLS Without Peer Verification Bunny now successfully performs TLS upgrade when peer verification is disabled. Contributed by Jordan Curzon. ### Bunny::Session#with_channel Ensures the Channel is Closed `Bunny::Session#with_channel` now makes sure the channel is closed even if provided block raises an exception Contributed by Carl Hoerberg. ### Channel Number = 0 is Rejected `Bunny::Session#create_channel` will now reject channel number 0. ### Single Threaded Mode Fixes Single threaded mode no longer fails with ``` undefined method `event_loop' ``` ## Changes between Bunny 1.1.0.pre1 and 1.1.0.pre2 ### connection.tune.channel_max No Longer Overflows `connection.tune.channel_max` could previously be configured to values greater than 2^16 - 1 (65535). This would result in a silent overflow during serialization. The issue was harmless in practice but is still a bug that can be quite confusing. Bunny now caps max number of channels to 65535. This allows it to be forward compatible with future RabbitMQ versions that may allow limiting total # of open channels via server configuration. ### amq-protocol Update Minimum `amq-protocol` version is now `1.9.0` which includes bug fixes and performance improvements for channel ID allocator. ### Thread Leaks Fixes Bunny will now correctly release heartbeat sender when allocating a new one (usually happens only when connection recovers from a network failure). ## Changes between Bunny 1.0.0 and 1.1.0.pre1 ### Versioned Delivery Tag Fix Versioned delivery tag now ensures all the arguments it operates (original delivery tag, atomic fixnum instances, etc) are coerced to `Integer` before comparison. GitHub issues: #171. ### User-Provided Loggers Bunny now can use any logger that provides the same API as Ruby standard library's `Logger`: ``` ruby require "logger" require "stringio" io = StringIO.new # will log to `io` Bunny.new(:logger => Logger.new(io)) ``` ### Default CA's Paths Are Disabled on JRuby Bunny uses OpenSSL provided CA certificate paths. This caused problems on some platforms on JRuby (see [jruby/jruby#155](https://github.com/jruby/jruby/issues/1055)). To avoid these issues, Bunny no longer uses default CA certificate paths on JRuby (there are no changes for other Rubies), so it's necessary to provide CA certificate explicitly. ### Fixes CPU Burn on JRuby Bunny now uses slightly different ways of continuously reading from the socket on CRuby and JRuby, to prevent abnormally high CPU usage on JRuby after a certain period of time (the frequency of `EWOULDBLOCK` being raised spiked sharply). ## Changes between Bunny 1.0.0.rc2 and 1.0.0.rc3 ### [Authentication Failure Notification](http://www.rabbitmq.com/auth-notification.html) Support `Bunny::AuthenticationFailureError` is a new auth failure exception that subclasses `Bunny::PossibleAuthenticationFailureError` for backwards compatibility. As such, `Bunny::PossibleAuthenticationFailureError`'s error message has changed. This extension is available in RabbitMQ 3.2+. ### Bunny::Session#exchange_exists? `Bunny::Session#exchange_exists?` is a new predicate that makes it easier to check if a exchange exists. It uses a one-off channel and `exchange.declare` with `passive` set to true under the hood. ### Bunny::Session#queue_exists? `Bunny::Session#queue_exists?` is a new predicate that makes it easier to check if a queue exists. It uses a one-off channel and `queue.declare` with `passive` set to true under the hood. ### Inline TLS Certificates and Keys It is now possible to provide inline client certificate and private key (as strings) instead of filesystem paths. The options are the same: * `:tls` which, when set to `true`, will set SSL context up and switch to TLS port (5671) * `:tls_cert` which now can be a client certificate (public key) in PEM format * `:tls_key` which now can be a client key (private key) in PEM format * `:tls_ca_certificates` which is an array of string paths to CA certificates in PEM format For example: ``` ruby conn = Bunny.new(:tls => true, :tls_cert => ENV["TLS_CERTIFICATE"], :tls_key => ENV["TLS_PRIVATE_KEY"], :tls_ca_certificates => ["./examples/tls/cacert.pem"]) ``` ## Changes between Bunny 1.0.0.rc1 and 1.0.0.rc2 ### Ruby 1.8.7 Compatibility Fixes Ruby 1.8.7 compatibility fixes around timeouts. ## Changes between Bunny 1.0.0.pre6 and 1.0.0.rc1 ### amq-protocol Update Minimum `amq-protocol` version is now `1.8.0` which includes a bug fix for messages exactly 128 Kb in size. ### Add timeout Bunny::ConsumerWorkPool#join `Bunny::ConsumerWorkPool#join` now accepts an optional timeout argument. ## Changes between Bunny 1.0.0.pre5 and 1.0.0.pre6 ### Respect RABBITMQ_URL value `RABBITMQ_URL` env variable will now have effect even if Bunny.new is invoked without arguments. ## Changes between Bunny 1.0.0.pre4 and 1.0.0.pre5 ### Ruby 1.8 Compatibility Bunny is Ruby 1.8-compatible again and no longer references `RUBY_ENGINE`. ### Bunny::Session.parse_uri `Bunny::Session.parse_uri` is a new method that parses connection URIs into hashes that `Bunny::Session#initialize` accepts. ``` ruby Bunny::Session.parse_uri("amqp://user:pwd@broker.eng.megacorp.local/myapp_qa") ``` ### Default Paths for TLS/SSL CA's on All OS'es Bunny now uses OpenSSL to detect default TLS/SSL CA's paths, extending this feature to OS'es other than Linux. Contributed by Jingwen Owen Ou. ## Changes between Bunny 1.0.0.pre3 and 1.0.0.pre4 ### Default Paths for TLS/SSL CA's on Linux Bunny now will use the following TLS/SSL CA's paths on Linux by default: * `/etc/ssl/certs/ca-certificates.crt` on Ubuntu/Debian * `/etc/ssl/certs/ca-bundle.crt` on Amazon Linux * `/etc/ssl/ca-bundle.pem` on OpenSUSE * `/etc/pki/tls/certs/ca-bundle.crt` on Fedora/RHEL and will log a warning if no CA files are available via default paths or `:tls_ca_certificates`. Contributed by Carl Hörberg. ### Consumers Can Be Re-Registered From Bunny::Consumer#handle_cancellation It is now possible to re-register a consumer (and use any other synchronous methods) from `Bunny::Consumer#handle_cancellation`, which is now invoked in the channel's thread pool. ### Bunny::Session#close Fixed for Single Threaded Connections `Bunny::Session#close` with single threaded connections no longer fails with a nil pointer exception. ## Changes between Bunny 1.0.0.pre2 and 1.0.0.pre3 This release has **breaking API changes**. ### Safe[r] basic.ack, basic.nack and basic.reject implementation Previously if a channel was recovered (reopened) by automatic connection recovery before a message was acknowledged or rejected, it would cause any operation on the channel that uses delivery tags to fail and cause the channel to be closed. To avoid this issue, every channel keeps a counter of how many times it has been reopened and marks delivery tags with them. Using a stale tag to ack or reject a message will produce no method sent to RabbitMQ. Note that unacknowledged messages will be requeued by RabbitMQ when connection goes down anyway. This involves an API change: `Bunny::DeliveryMetadata#delivery_tag` is now and instance of a class that responds to `#tag` and `#to_i` and is accepted by `Bunny::Channel#ack` and related methods. Integers are still accepted by the same methods. ## Changes between Bunny 1.0.0.pre1 and 1.0.0.pre2 ### Exclusivity Violation for Consumers Now Raises a Reasonable Exception When a second consumer is registered for the same queue on different channels, a reasonable exception (`Bunny::AccessRefused`) will be raised. ### Reentrant Mutex Implementation Bunny now allows mutex impl to be configurable, uses reentrant Monitor by default. Non-reentrant mutexes is a major PITA and may affect code that uses Bunny. Avg. publishing throughput with Monitor drops slightly from 5.73 Khz to 5.49 Khz (about 4% decrease), which is reasonable for Bunny. Apps that need these 4% can configure what mutex implementation is used on per-connection basis. ### Eliminated Race Condition in Bunny::Session#close `Bunny::Session#close` had a race condition that caused (non-deterministic) exceptions when connection transport was closed before connection reader loop was guaranteed to have stopped. ### connection.close Raises Exceptions on Connection Thread Connection-level exceptions (including when a connection is closed via management UI or `rabbitmqctl`) will now be raised on the connection thread so they * can be handled by applications * do not start connection recovery, which may be uncalled for ### Client TLS Certificates are Optional Bunny will no longer require client TLS certificates. Note that CA certificate list is still necessary. If RabbitMQ TLS configuration requires peer verification, client certificate and private key are mandatory. ## Changes between Bunny 0.9.0 and 1.0.0.pre1 ### Publishing Over Closed Connections Publishing a message over a closed connection (during a network outage, before the connection is open) will now correctly result in an exception. Contributed by Matt Campbell. ### Reliability Improvement in Automatic Network Failure Recovery Bunny now ensures a new connection transport (socket) is initialized before any recovery is attempted. ### Reliability Improvement in Bunny::Session#create_channel `Bunny::Session#create_channel` now uses two separate mutexes to avoid a (very rare) issue when the previous implementation would try to re-acquire the same mutex and fail (Ruby mutexes are non-reentrant). ## Changes between Bunny 0.9.0.rc1 and 0.9.0.rc2 ### Channel Now Properly Restarts Consumer Pool In a case when all consumers are cancelled, `Bunny::Channel` will shut down its consumer delivery thread pool. It will also now mark the pool as not running so that it can be started again successfully if new consumers are registered later. GH issue: #133. ### Bunny::Queue#pop_waiting is Removed A little bit of background: on MRI, the method raised `ThreadErrors` reliably. On JRuby, we used a different [internal] queue implementation from JDK so it wasn't an issue. `Timeout.timeout` uses `Thread#kill` and `Thread#join`, both of which eventually attempt to acquire a mutex used by Queue#pop, which Bunny currently uses for continuations. The mutex is already has an owner and so a ThreadError is raised. This is not a problem on JRuby because there we don't use Ruby's Timeout and Queue and instead rely on a JDK concurrency primitive which provides "poll with a timeout". [The issue with `Thread#kill` and `Thread#raise`](http://blog.headius.com/2008/02/ruby-threadraise-threadkill-timeoutrb.html) has been first investigated and blogged about by Ruby implementers in 2008. Finding a workaround will probably take a bit of time and may involve reimplementing standard library and core classes. We don't want this issue to block Bunny 0.9 release. Neither we want to ship a broken feature. So as a result, we will drop Bunny::Queue#pop_waiting since it cannot be reliably implemented in a reasonable amount of time on MRI. Per issue #131. ### More Flexible SSLContext Configuration Bunny will now upgrade connection to SSL in `Bunny::Session#start`, so it is possible to fine tune SSLContext and socket settings before that: ``` ruby require "bunny" conn = Bunny.new(:tls => true, :tls_cert => "examples/tls/client_cert.pem", :tls_key => "examples/tls/client_key.pem", :tls_ca_certificates => ["./examples/tls/cacert.pem"]) puts conn.transport.socket.inspect puts conn.transport.tls_context.inspect ``` This also means that `Bunny.new` will now open the socket. Previously it was only done when `Bunny::Session#start` was invoked. ## Changes between Bunny 0.9.0.pre13 and 0.9.0.rc1 ### TLS Support Bunny 0.9 finally supports TLS. There are 3 new options `Bunny.new` takes: * `:tls` which, when set to `true`, will set SSL context up and switch to TLS port (5671) * `:tls_cert` which is a string path to the client certificate (public key) in PEM format * `:tls_key` which is a string path to the client key (private key) in PEM format * `:tls_ca_certificates` which is an array of string paths to CA certificates in PEM format An example: ``` ruby conn = Bunny.new(:tls => true, :tls_cert => "examples/tls/client_cert.pem", :tls_key => "examples/tls/client_key.pem", :tls_ca_certificates => ["./examples/tls/cacert.pem"]) ``` ### Bunny::Queue#pop_waiting **This function was removed in v0.9.0.rc2** `Bunny::Queue#pop_waiting` is a new function that mimics `Bunny::Queue#pop` but will wait until a message is available. It uses a `:timeout` option and will raise an exception if the timeout is hit: ``` ruby # given 1 message in the queue, # works exactly as Bunny::Queue#get q.pop_waiting # given no messages in the queue, will wait for up to 0.5 seconds # for a message to become available. Raises an exception if the timeout # is hit q.pop_waiting(:timeout => 0.5) ``` This method only makes sense for collecting Request/Reply ("RPC") replies. ### Bunny::InvalidCommand is now Bunny::CommandInvalid `Bunny::InvalidCommand` is now `Bunny::CommandInvalid` (follows the exception class naming convention based on response status name). ## Changes between Bunny 0.9.0.pre12 and 0.9.0.pre13 ### Channels Without Consumers Now Tear Down Consumer Pools Channels without consumers left (when all consumers were cancelled) will now tear down their consumer work thread pools, thus making `HotBunnies::Queue#subscribe(:block => true)` calls unblock. This is typically the desired behavior. ### Consumer and Channel Available In Delivery Handlers Delivery handlers registered via `Bunny::Queue#subscribe` now will have access to the consumer and channel they are associated with via the `delivery_info` argument: ``` ruby q.subscribe do |delivery_info, properties, payload| delivery_info.consumer # => the consumer this delivery is for delivery_info.consumer # => the channel this delivery is on end ``` This allows using `Bunny::Queue#subscribe` for one-off consumers much easier, including when used with the `:block` option. ### Bunny::Exchange#wait_for_confirms `Bunny::Exchange#wait_for_confirms` is a convenience method on `Bunny::Exchange` that delegates to the method with the same name on exchange's channel. ## Changes between Bunny 0.9.0.pre11 and 0.9.0.pre12 ### Ruby 1.8 Compatibility Regression Fix `Bunny::Socket` no longer uses Ruby 1.9-specific constants. ### Bunny::Channel#wait_for_confirms Return Value Regression Fix `Bunny::Channel#wait_for_confirms` returns `true` or `false` again. ## Changes between Bunny 0.9.0.pre10 and 0.9.0.pre11 ### Bunny::Session#create_channel Now Accepts Consumer Work Pool Size `Bunny::Session#create_channel` now accepts consumer work pool size as the second argument: ``` ruby # nil means channel id will be allocated by Bunny. # 8 is the number of threads in the consumer work pool this channel will use. ch = conn.create_channel(nil, 8) ``` ### Heartbeat Fix For Long Running Consumers Long running consumers that don't send any data will no longer suffer from connections closed by RabbitMQ because of skipped heartbeats. Activity tracking now takes sent frames into account. ### Time-bound continuations If a network loop exception causes "main" session thread to never receive a response, methods such as `Bunny::Channel#queue` will simply time out and raise Timeout::Error now, which can be handled. It will not start automatic recovery for two reasons: * It will be started in the network activity loop anyway * It may do more damage than good Kicking off network recovery manually is a matter of calling `Bunny::Session#handle_network_failure`. The main benefit of this implementation is that it will never block the main app/session thread forever, and it is really efficient on JRuby thanks to a j.u.c. blocking queue. Fixes #112. ### Logging Support Every Bunny connection now has a logger. By default, Bunny will use STDOUT as logging device. This is configurable using the `:log_file` option: ``` ruby require "bunny" conn = Bunny.new(:log_level => :warn) ``` or the `BUNNY_LOG_LEVEL` environment variable that can take one of the following values: * `debug` (very verbose) * `info` * `warn` * `error` * `fatal` (least verbose) Severity is set to `warn` by default. To disable logging completely, set the level to `fatal`. To redirect logging to a file or any other object that can act as an I/O entity, pass it to the `:log_file` option. ## Changes between Bunny 0.9.0.pre9 and 0.9.0.pre10 This release contains a **breaking API change**. ### Concurrency Improvements On JRuby On JRuby, Bunny now will use `java.util.concurrent`-backed implementations of some of the concurrency primitives. This both improves client stability (JDK concurrency primitives has been around for 9 years and have well-defined, documented semantics) and opens the door to solving some tricky failure handling problems in the future. ### Explicitly Closed Sockets Bunny now will correctly close the socket previous connection had when recovering from network issues. ### Bunny::Exception Now Extends StandardError `Bunny::Exception` now inherits from `StandardError` and not `Exception`. Naked rescue like this ``` ruby begin # ... rescue => e # ... end ``` catches only descendents of `StandardError`. Most people don't know this and this is a very counter-intuitive practice, but apparently there is code out there that can't be changed that depends on this behavior. This is a **breaking API change**. ## Changes between Bunny 0.9.0.pre8 and 0.9.0.pre9 ### Bunny::Session#start Now Returns a Session `Bunny::Session#start` now returns a session instead of the default channel (which wasn't intentional, default channel is a backwards-compatibility implementation detail). `Bunny::Session#start` also no longer leaves dead threads behind if called multiple times on the same connection. ### More Reliable Heartbeat Sender Heartbeat sender no longer slips into an infinite loop if it encounters an exception. Instead, it will just stop (and presumably re-started when the network error recovery kicks in or the app reconnects manually). ### Network Recovery After Delay Network reconnection now kicks in after a delay to avoid aggressive reconnections in situations when we don't want to endlessly reconnect (e.g. when the connection was closed via the Management UI). The `:network_recovery_interval` option passed to `Bunny::Session#initialize` and `Bunny.new` controls the interval. Default is 5 seconds. ### Default Heartbeat Value Is Now Server-Defined Bunny will now use heartbeat value provided by RabbitMQ by default. ## Changes between Bunny 0.9.0.pre7 and 0.9.0.pre8 ### Stability Improvements Several stability improvements in the network layer, connection error handling, and concurrency hazards. ### Automatic Connection Recovery Can Be Disabled Automatic connection recovery now can be disabled by passing the `:automatically_recover => false` option to `Bunny#initialize`). When the recovery is disabled, network I/O-related exceptions will cause an exception to be raised in thee thread the connection was started on. ### No Timeout Control For Publishing `Bunny::Exchange#publish` and `Bunny::Channel#basic_publish` no longer perform timeout control (using the timeout module) which roughly increases throughput for flood publishing by 350%. Apps that need delivery guarantees should use publisher confirms. ## Changes between Bunny 0.9.0.pre6 and 0.9.0.pre7 ### Bunny::Channel#on_error `Bunny::Channel#on_error` is a new method that lets you define handlers for channel errors that are caused by methods that have no responses in the protocol (`basic.ack`, `basic.reject`, and `basic.nack`). This is rarely necessary but helps make sure no error goes unnoticed. Example: ``` ruby channel.on_error |ch, channel_close| puts channel_close.inspect end ``` ### Fixed Framing of Larger Messages With Unicode Characters Larger (over 128K) messages with non-ASCII characters are now always encoded correctly with amq-protocol `1.2.0`. ### Efficiency Improvements Publishing of large messages is now done more efficiently. Contributed by Greg Brockman. ### API Reference [Bunny API reference](http://reference.rubybunny.info) is now up online. ### Bunny::Channel#basic_publish Support For :persistent `Bunny::Channel#basic_publish` now supports both `:delivery_mode` and `:persistent` options. ### Bunny::Channel#nacked_set `Bunny::Channel#nacked_set` is a counter-part to `Bunny::Channel#unacked_set` that contains `basic.nack`-ed (rejected) delivery tags. ### Single-threaded Network Activity Mode Passing `:threaded => false` to `Bunny.new` now will use the same thread for publisher confirmations (may be useful for retry logic implementation). Contributed by Greg Brockman. ## Changes between Bunny 0.9.0.pre5 and 0.9.0.pre6 ### Automatic Network Failure Recovery Automatic Network Failure Recovery is a new Bunny feature that was earlier impemented and vetted out in [amqp gem](http://rubyamqp.info). What it does is, when a network activity loop detects an issue, it will try to periodically recover [first TCP, then] AMQP 0.9.1 connection, reopen all channels, recover all exchanges, queues, bindings and consumers on those channels (to be clear: this only includes entities and consumers added via Bunny). Publishers and consumers will continue operating shortly after the network connection recovers. Learn more in the [Error Handling and Recovery](http://rubybunny.info/articles/error_handling.html) documentation guide. ### Confirms Listeners Bunny now supports listeners (callbacks) on ``` ruby ch.confirm_select do |delivery_tag, multiple, nack| # handle confirms (e.g. perform retries) here end ``` Contributed by Greg Brockman. ### Publisher Confirms Improvements Publisher confirms implementation now uses non-strict equality (`<=`) for cases when multiple messages are confirmed by RabbitMQ at once. `Bunny::Channel#unconfirmed_set` is now part of the public API that lets developers access unconfirmed delivery tags to perform retries and such. Contributed by Greg Brockman. ### Publisher Confirms Concurrency Fix `Bunny::Channel#wait_for_confirms` will now correctly block the calling thread until all pending confirms are received. ## Changes between Bunny 0.9.0.pre4 and 0.9.0.pre5 ### Channel Errors Reset Channel error information is now properly reset when a channel is (re)opened. GH issue: #83. ### Bunny::Consumer#initial Default Change the default value of `Bunny::Consumer` noack argument changed from false to true for consistency. ### Bunny::Session#prefetch Removed Global prefetch is not implemented in RabbitMQ, so `Bunny::Session#prefetch` is gone from the API. ### Queue Redeclaration Bug Fix Fixed a problem when a queue was not declared after being deleted and redeclared GH issue: #80 ### Channel Cache Invalidation Channel queue and exchange caches are now properly invalidated when queues and exchanges are deleted. ## Changes between Bunny 0.9.0.pre3 and 0.9.0.pre4 ### Heartbeats Support Fixes Heartbeats are now correctly sent at safe intervals (half of the configured interval). In addition, setting `:heartbeat => 0` (or `nil`) will disable heartbeats, just like in Bunny 0.8 and [amqp gem](http://rubyamqp.info). Default `:heartbeat` value is now `600` (seconds), the same as RabbitMQ 3.0 default. ### Eliminate Race Conditions When Registering Consumers Fixes a potential race condition between `basic.consume-ok` handler and delivery handler when a consumer is registered for a queue that has messages in it. GH issue: #78. ### Support for Alternative Authentication Mechanisms Bunny now supports two authentication mechanisms and can be extended to support more. The supported methods are `"PLAIN"` (username and password) and `"EXTERNAL"` (typically uses TLS, UNIX sockets or another mechanism that does not rely on username/challenge pairs). To use the `"EXTERNAL"` method, pass `:auth_mechanism => "EXTERNAL"` to `Bunny.new`: ``` ruby # uses the EXTERNAL authentication mechanism conn = Bunny.new(:auth_method => "EXTERNAL") conn.start ``` ### Bunny::Consumer#cancel A new high-level API method: `Bunny::Consumer#cancel`, can be used to cancel a consumer. `Bunny::Queue#subscribe` will now return consumer instances when the `:block` option is passed in as `false`. ### Bunny::Exchange#delete Behavior Change `Bunny::Exchange#delete` will no longer delete pre-declared exchanges that cannot be declared by Bunny (`amq.*` and the default exchange). ### Bunny::DeliveryInfo#redelivered? `Bunny::DeliveryInfo#redelivered?` is a new method that is an alias to `Bunny::DeliveryInfo#redelivered` but follows the Ruby community convention about predicate method names. ### Corrected Bunny::DeliveryInfo#delivery_tag Name `Bunny::DeliveryInfo#delivery_tag` had a typo which is now fixed. ## Changes between Bunny 0.9.0.pre2 and 0.9.0.pre3 ### Client Capabilities Bunny now correctly lists RabbitMQ extensions it currently supports in client capabilities: * `basic.nack` * exchange-to-exchange bindings * consumer cancellation notifications * publisher confirms ### Publisher Confirms Support [Lightweight Publisher Confirms](http://www.rabbitmq.com/blog/2011/02/10/introducing-publisher-confirms/) is a RabbitMQ feature that lets publishers keep track of message routing without adding noticeable throughput degradation as it is the case with AMQP 0.9.1 transactions. Bunny `0.9.0.pre3` supports publisher confirms. Publisher confirms are enabled per channel, using the `Bunny::Channel#confirm_select` method. `Bunny::Channel#wait_for_confirms` is a method that blocks current thread until the client gets confirmations for all unconfirmed published messages: ``` ruby ch = connection.create_channel ch.confirm_select ch.using_publisher_confirmations? # => true q = ch.queue("", :exclusive => true) x = ch.default_exchange 5000.times do x.publish("xyzzy", :routing_key => q.name) end ch.next_publish_seq_no.should == 5001 ch.wait_for_confirms # waits until all 5000 published messages are acknowledged by RabbitMQ ``` ### Consumers as Objects It is now possible to register a consumer as an object instead of a block. Consumers that are class instances support cancellation notifications (e.g. when a queue they're registered with is deleted). To support this, Bunny introduces two new methods: `Bunny::Channel#basic_consume_with` and `Bunny::Queue#subscribe_with`, that operate on consumer objects. Objects are supposed to respond to three selectors: * `:handle_delivery` with 3 arguments * `:handle_cancellation` with 1 argument * `:consumer_tag=` with 1 argument An example: ``` ruby class ExampleConsumer < Bunny::Consumer def cancelled? @cancelled end def handle_cancellation(_) @cancelled = true end end # "high-level" API ch1 = connection.create_channel q1 = ch1.queue("", :auto_delete => true) consumer = ExampleConsumer.new(ch1, q) q1.subscribe_with(consumer) # "low-level" API ch2 = connection.create_channel q1 = ch2.queue("", :auto_delete => true) consumer = ExampleConsumer.new(ch2, q) ch2.basic_consume_with.(consumer) ``` ### RABBITMQ_URL ENV variable support If `RABBITMQ_URL` environment variable is set, Bunny will assume it contains a valid amqp URI string and will use it. This is convenient with some PaaS technologies such as Heroku. ## Changes between Bunny 0.9.0.pre1 and 0.9.0.pre2 ### Change Bunny::Queue#pop default for :ack to false It makes more sense for beginners that way. ### Bunny::Queue#subscribe now support the new :block option `Bunny::Queue#subscribe` support the new `:block` option (a boolean). It controls whether the current thread will be blocked by `Bunny::Queue#subscribe`. ### Bunny::Exchange#publish now supports :key again `Bunny::Exchange#publish` now supports `:key` as an alias for `:routing_key`. ### Bunny::Session#queue et al. `Bunny::Session#queue`, `Bunny::Session#direct`, `Bunny::Session#fanout`, `Bunny::Session#topic`, and `Bunny::Session#headers` were added to simplify migration. They all delegate to their respective `Bunny::Channel` methods on the default channel every connection has. ### Bunny::Channel#exchange, Bunny::Session#exchange `Bunny::Channel#exchange` and `Bunny::Session#exchange` were added to simplify migration: ``` ruby b = Bunny.new b.start # uses default connection channel x = b.exchange("logs.events", :topic) ``` ### Bunny::Queue#subscribe now properly takes 3 arguments ``` ruby q.subscribe(:exclusive => false, :ack => false) do |delivery_info, properties, payload| # ... end ``` ## Changes between Bunny 0.8.x and 0.9.0.pre1 ### New convenience functions: Bunny::Channel#fanout, Bunny::Channel#topic `Bunny::Channel#fanout`, `Bunny::Channel#topic`, `Bunny::Channel#direct`, `Bunny::Channel#headers`, and`Bunny::Channel#default_exchange` are new convenience methods to instantiate exchanges: ``` ruby conn = Bunny.new conn.start ch = conn.create_channel x = ch.fanout("logging.events", :durable => true) ``` ### Bunny::Queue#pop and consumer handlers (Bunny::Queue#subscribe) signatures have changed Bunny `< 0.9.x` example: ``` ruby h = queue.pop puts h[:delivery_info], h[:header], h[:payload] ``` Bunny `>= 0.9.x` example: ``` ruby delivery_info, properties, payload = queue.pop ``` The improve is both in that Ruby has positional destructuring, e.g. ``` ruby delivery_info, _, content = q.pop ``` but not hash destructuring, like, say, Clojure does. In addition we return nil for content when it should be nil (basic.get-empty) and unify these arguments betwee * Bunny::Queue#pop * Consumer (Bunny::Queue#subscribe, etc) handlers * Returned message handlers The unification moment was the driving factor. ### Bunny::Client#write now raises Bunny::ConnectionError Bunny::Client#write now raises `Bunny::ConnectionError` instead of `Bunny::ServerDownError` when network I/O operations fail. ### Bunny::Client.create_channel now uses a bitset-based allocator Instead of reusing channel instances, `Bunny::Client.create_channel` now opens new channels and uses bitset-based allocator to keep track of used channel ids. This avoids situations when channels are reused or shared without developer's explicit intent but also work well for long running applications that aggressively open and release channels. This is also how amqp gem and RabbitMQ Java client manage channel ids. ### Bunny::ServerDownError is now Bunny::TCPConnectionFailed `Bunny::ServerDownError` is now an alias for `Bunny::TCPConnectionFailed` bunny-2.6.1/docker/0000755000004100000410000000000013015255277014161 5ustar www-datawww-databunny-2.6.1/docker/Dockerfile0000644000004100000410000000071713015255277016160 0ustar www-datawww-dataFROM debian:jessie RUN apt-get -q update && \ apt-get install -yq --no-install-recommends wget ca-certificates RUN echo 'deb http://www.rabbitmq.com/debian/ testing main' > /etc/apt/sources.list.d/rabbitmq.list && \ wget -O- https://www.rabbitmq.com/rabbitmq-signing-key-public.asc | apt-key add - RUN apt-get -q update && \ apt-get install -yq --no-install-recommends rabbitmq-server COPY docker-entrypoint.sh / ENTRYPOINT /docker-entrypoint.sh bunny-2.6.1/docker/docker-entrypoint.sh0000755000004100000410000000177213015255277020207 0ustar www-datawww-data#!/bin/sh server=rabbitmq-server ctl=rabbitmqctl plugins=rabbitmq-plugins delay=3 echo '[Configuration] Starting RabbitMQ in detached mode.' $server -detached echo "[Configuration] Waiting $delay seconds for RabbitMQ to start." sleep $delay echo '*** Enabling plugins ***' $plugins enable --online rabbitmq_management $plugins enable --online rabbitmq_consistent_hash_exchange echo '*** Creating users ***' $ctl add_user bunny_gem bunny_password $ctl add_user bunny_reader reader_password echo '*** Creating virtual hosts ***' $ctl add_vhost bunny_testbed echo '*** Setting virtual host permissions ***' $ctl set_permissions -p / guest '.*' '.*' '.*' $ctl set_permissions -p bunny_testbed bunny_gem '.*' '.*' '.*' $ctl set_permissions -p bunny_testbed guest '.*' '.*' '.*' $ctl set_permissions -p bunny_testbed bunny_reader '^---$' '^---$' '.*' $ctl stop echo "[Configuration] Waiting $delay seconds for RabbitMQ to stop." sleep $delay echo 'Starting RabbitMQ in foreground (CTRL-C to exit)' exec $server bunny-2.6.1/.rspec0000644000004100000410000000000713015255277014024 0ustar www-datawww-data-c -fp bunny-2.6.1/spec/0000755000004100000410000000000013015255277013644 5ustar www-datawww-databunny-2.6.1/spec/tls/0000755000004100000410000000000013015255277014446 5ustar www-datawww-databunny-2.6.1/spec/tls/server_key.pem0000644000004100000410000000321713015255277017332 0ustar www-datawww-data-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEApL785uZwdwg6kxRGEJ3zsvA8CS5t+DL3aykAE4cw8qmt+7G+ CAtGHJQRW2X7MPPqWivkaaNlZYc3JW+us9HBrPUr8bipCeOcNH5NHLcTILb1iEkR mUeyzMObtKtputXStBUxkpfqsyaIW+Q5JW+gQKaBqCDx/nmK2lcR0dvcdsfIaswb eU6YlFvG7uyz0CW19JmBoSkA3/OpGqPrKbUUNtOOQ+7wd465D4atF2VSYkMpszkk no2EjdaGeOuA7U/naSwWBxFSaCT7uK+JGP/AO7/vdz1kGgxESgX78Fh31VOOMZ+0 f2qI0rzU+5ECC7H9gZ3yZTMKHnVkC8T3XZgdzwIDAQABAoIBABqJGLsoSt0hWi3u igqLJoGf6EuiStw7eVatV/HNSLkNU5TC4FTzW+umfdU26+rKnui/QR8yzlY4HU3O W1ljY8q0AswAblnFa5eY8Fq7sj0Guy2kd001GzuYFjBXm8QYRMP01fNCvO72Oehz LjE1dZ50M1YdWF0gvpjEhX5D55LKAOHyoYUy384YVkDu1HSrL3Ls1ZxMmHSRchrH vGunovZlMCmq1fdPtC/5pA2o7rkk4ToZHrAL2ETm/1qyWOPzKr+lkk0m8M9/4eX+ z5MaoWZ2SvPOIasNbEvKJCJbZ5XNAyymoLDPMPC2OdrrmqPaE1F0XyqRaMvakXI2 Pd6p0wECgYEA2JEhxuTcUSQMUYulS8DwCSE/xn57+0VHIq6Yjrw79GD7w4OMq4Go K7slij0K9wSBtCM+uO78fs84s5g6GzXtQzuqy2KaIbtspDpuygz0rPaBal7IS1Va FQB4ZrvyQRLlZmRTlaRxQdqPZx1gnccypnZJUx3VMgzAPyYulzIChS8CgYEAwr5S Qu3HntFpD05lcd6FaVrclGcDDF3OBkVkJC29D/lX+PROfSwWMcNoUF2s/0G6/9JG EQ2yxeVv1iKE4aKHDOBgjZubnfEXwx/yCTXlhYR/9wb4T4xbegzElltPXss+TsIZ EAbrp2VtS05LB9VoXp9F3LWmIRWab5KnizgbCWECgYEApOeGL7vu4z4uvx01hYRp gpo1xMBlScTL0OXftrgwcvoyWwLdZ274DiyX/jZTFhrxZKh9xzX7P9gsldxxSmz6 uHpDqFDxioP7yuXPnLzWxqmBi+yWWX6pdFqCU8ADTHfGA/ybBNRNhVaWYW8mNZ4L vGNPnCGYSF8bODqruWdUMfMCgYEAtJ6wzUKcksDHIFsl26b3NfAABeRE/4NBQq+5 YZTKdbZd0ZlF8vKg4ybJ6T/45Aai2gK82bRKyyExyPnbJSsI9vhklPJuMt8ZmvBW HBTQ/DUYOpVMWPtlHJ4a9xlZVCJNd96uQB8rEecQp3fGP02/r4Mp2Lrhim3u/uyW WJmA+gECgYBNncMDorlUeWVSuq/zud/d97UEAvNSkDtlg/TVQrtRYDlCM2J9lH3J UpkcbZeDJpLs4sC+edJ5IJpGwczn4j1nu8LUzy8DhjKFz0pKTJCNc48KfO0ayWwh HUm0G9JSiL+F7IgUE3nabTmtH9QJdhHOslqOwLO4VgvcZdJufj2JPg== -----END RSA PRIVATE KEY----- bunny-2.6.1/spec/tls/client_key.pem0000644000004100000410000000321313015255277017276 0ustar www-datawww-data-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAssv9JrNNAy2/CUFNDPy6llWhP8kLXfEmUBbmRDJK3LneykMf riyzyXcbupuaO9Sq3CEKpyAnjRNvfdSZiVhlpHnM829AbbzgDBLNcAyE8ozjUU16 dMPWSaU5O3OTuIBsBsxkPgp/NBn0Tdr6RTJX0xVWVgrHBHCOrYOZRa4OzDkSLqAd im5DB2VY9ULIg6ZyDfSrO9xWMrdsWC4qSPLc1NulWxOqgX/sYeyhPzlk/jCxOGDC aQy/I7XzW4wFxThjVu2fhW6wMHj/+Zze5VbZ8mgiMnDvb6ipAkqvVcxUTRtluylZ Jp+RxuLfWtt4dRFSs6qoW4mdEqAJ+TcsLzpP5wIDAQABAoIBAFWGmFiLyhnsJwaR Uv8UYViKK3DshWBxewcyfQFAIWlGC9kqlJvnTDThocT4q6VVDEnyEDtzeRfVpS5G pQ761tv/W1dS7znO8Ek2MUkY4/t2A0Kf0iTKjNV7kE3s8kt+Oq5h1M2Mjd5pT+By D+dLtpEFEuBjlWF9SfKcEZQwN2neUi3cCaz6xsp/P33EzKI9hNvPexxODlPNivYr wmwopJwP2s0VTpqJUu0K3PV6WSH/JpCC8P2Ur2equpGQQjp12EGDuCpVeFTnVx4j t48DI7IE1bJyLBHM1k59UkDbDVkEsgY7Pl569X+xg/QCdE073eYUptADLLZ1DnvF bkGaNaECgYEA2Q+cPxtMQ3L2CUUxXK3yfAfUyMSFpBY6+ZB/nGuolp+2lkEr3S/j MTyEKm2IR0wVURvkabV2RgqNMKa3BQk1iXAkn2BEPG4D5FkWf5MLLBeBMP2nce4C ZapG8cgEepnWRURtoYofOcN2INvqxnruIzfMlFsudXBkA9ANMCoTerECgYEA0t8f HWlB9mQEe2i581XFaUcqPWwZx4g9QKqz2WCVqnUZHPF7dNT3C265rSEWsIMCs3MK 5gUxzyyxBt/1cteQJNS65GFBVAf4YNbY6wWLbSMgkJL31kXChFoI4QjClOI7q4qw aRBHLlpFfg+RNMS0gCx/EwIDjxoCTFVxA2K9ahcCgYAJ6o6my2Z2hl/7GHAi3ceA JxNvgJriXimfkRxjVv/CsdUzlBKe7jsg2rTnA04Ag9dNd0q+EY4vsPe2S0d9MWiC uN7TcSBs6lG/Mtq2RU9zWjeKt2vY5pixcFABZ7Pcrf5995xSju7SQr24zpMxMhiC /1+XP3sl3laTXgPcOW/eYQKBgEmRLfnEzw52tlmXn8gVFFo2SbZEEHpcPYJiC3y7 BXwCflBqrveDlOyYJI8SDChojHiumg7FoSr2HIyZdbgrHE69KLakd3Ypka23RtY3 iPz6VFIb8/r9O2+ROafItpnak5a1O/Zrh2nUdIoWREJ0ncRZwbuP6E30dPjMljDo MczhAoGAbQ7XDKCpw2wo5AHM6brO4xS1RObPXugDM5VEGHJ6HaS9f1lFfv8vbSXr TsDTMYOoVA7oTz9kQD3exj9IjTX0f40ogmaB3b3PIPg9muNHcuNGwB75hNiRcZEj dXfbMKVvo08Eej8k8TZsVfAKRxdLm+ahaPWThSoEGxElI1mD12c= -----END RSA PRIVATE KEY----- bunny-2.6.1/spec/tls/ca_key.pem0000644000004100000410000000321313015255277016403 0ustar www-datawww-data-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAqwGrw1hf6LELt8euMTsRO7qnIsfH8yAeysK1GQgsWGuede7W cO9sXrtQOySDzb3cNkVoYKWtb6O44S+jFqCUVRwLLMbhJtUTVYuOmFbpEc6XEgJE i3GqQqfLUnU1ODHcqEuDYZ6iB0Iq2FDX+tu3AOBi8u653d7ccX5peVRF8ldZtyFo rjOJQ2M5Ya4pEW1JWX59XnY27tkw6wLHrHobwNy4laKibZGhS9KzHlNm4SKnGu/n MYEDjOk68j8CaKxoFNPZFfm9qluADql2jj5nML5Axj1E3ZO3m+mc/KtZzFZByNdV twoO04fdBK21Lxj8gPo2xCxuV2A45yNb3/Q7LQIDAQABAoIBAEHD3r69mgTvMcZ3 8LHWOYZy/hwOIQOLcswfnW4HCqbmnIZWV/oO2eqEtxJHtLjHL41fnAivLsgketF/ dIUbNHCW3GVYBvbcMxdPGIPVyMSkYsz2Ok42ZHhF95e6o8D8zBOdFuXQEBgihj+y t50uKm8X3HX5AVomfzjWmNGka1ILTNautO0QkxBRgyDaeWqj5J46G68a6xtFPj6Q 6UqdAwD94TDkxlWBvEOYKRh51+uhGX936uXV8wu0ZDQ3oJ+pvdJSNClcFcuWztlq 7qn4UEdta26uTB7bhAzTaY7fxiY4VqJFc3HMLr7lfYz5vzda1cYLT6iR1qEI0+LS K690+TECgYEA2RFcxptiEdyST+WH9LOqQ4J1vq/L4ZN9rLHKaEc38StSxheA71fZ mFfnz2SemMjTaiN5/BgU52WUfpUtKqCfZfWMzjXFTzcVgjc+rSHZQQlg6NRbWbMm LlXN0CkhL2mi6hilqQjgasyfieZ0prifHlWLZsjNIbsLtqGdMtzCN5cCgYEAya1p LrDjbEKmn5kurD/i9t3+m+RZiqOhydJwUcoyYtWPc1aKH2COSqKyaLIo/5sp2TfN Ptt+89+/1VgIkjEsW795rlQdhovY9eqoLPQ5+o/iadIDH876FMnfR7RIaxYhmXrZ FkgGxqbcAnxyizi9lYRbiYiKftbCZkg5rAXUW9sCgYBq8GnTkrOC9zbB0+PiAy2L qtcVRYhdLouIq+J68zM1RlG2M9gakh4qNoyGmRylSrMVJDsxni+WPG4m2plw2ccN tSZUZRs6xnl7OHpRU060AsnSiuQiUecqhq0ps0mdER7o05GxsCTXmM20Y2vuwx/p Et3lmmGtD3ha1k4aw7WPKwKBgHTPuxdvHxqKdEYGjHD6Q/Rm2lzi4zYY1tG10Jae /ZYfSgos+q132JLEL3/SiUqbTZ7h4kBhE+QfGdZxOe2rC51MhycA02UlxRGSy7vV 7WsCQcq2+FZJJLI7xtaaRkO4OtfRvsFYEIypAs9x00puH+jlE7vSWR1kOI7TkIvh iIF3AoGBAIpU2LHl42OhgDvWDFVx0d0FF51TzKsP0h14YeBrQJdjIyx+XRWZ6VdK ujvfORKWw+q1PE2eAzvPRz7Ec7O/A1uF2Htl3XSrY+XoD2zEQ3fg7Gp6jO1q1SO1 sHvuZuJBC/SBusKdXpKKDq2YcjtMD3uqcvDNtH1O0AAEbPen2vyb -----END RSA PRIVATE KEY----- bunny-2.6.1/spec/tls/server_certificate.pem0000644000004100000410000000207613015255277021026 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIC9TCCAd2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADAnMRUwEwYDVQQDDAxNeVRl c3RSb290Q0ExDjAMBgNVBAcMBTkxMzQzMB4XDTE2MDIyOTEwNDQwN1oXDTI2MDIy NjEwNDQwN1owJDERMA8GA1UEAxMIbWVyY3VyaW8xDzANBgNVBAoTBnNlcnZlcjCC ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKS+/ObmcHcIOpMURhCd87Lw PAkubfgy92spABOHMPKprfuxvggLRhyUEVtl+zDz6lor5GmjZWWHNyVvrrPRwaz1 K/G4qQnjnDR+TRy3EyC29YhJEZlHsszDm7SrabrV0rQVMZKX6rMmiFvkOSVvoECm gagg8f55itpXEdHb3HbHyGrMG3lOmJRbxu7ss9AltfSZgaEpAN/zqRqj6ym1FDbT jkPu8HeOuQ+GrRdlUmJDKbM5JJ6NhI3WhnjrgO1P52ksFgcRUmgk+7iviRj/wDu/ 73c9ZBoMREoF+/BYd9VTjjGftH9qiNK81PuRAgux/YGd8mUzCh51ZAvE912YHc8C AwEAAaMvMC0wCQYDVR0TBAIwADALBgNVHQ8EBAMCBSAwEwYDVR0lBAwwCgYIKwYB BQUHAwEwDQYJKoZIhvcNAQELBQADggEBAALRwwleGksytFtvqRswrv5aA1BmbcUs qLAMEOBul/JN/KthdICw78GXu8oU/kkp4QjMKDo9pobNe5klFH0m1S1r9LuqC5o7 iyxuSHDN9Y8Sy+KzZaGpZBJIdBAZnE0r1nGwEyPEFtX29IAhh/0Uh4gIobUcCMbb N+ABaNUp7FrPqFNbcOw2qR0KeoSGzoBKkCwUjx1XACixrNRvDM7T1G5JfHLXZWDu 819p71KjyR4xoIBZgkim6YcoEbgFZahBIFqpe7gmq314iiP+OgrZnOfdZ7zULvxw 6yZpSqucTzcvBFjBCTwkXzg6/pPKF5R6YGp3CXyW1TqGnZBnHfmF9Yk= -----END CERTIFICATE----- bunny-2.6.1/spec/tls/ca_certificate.pem0000644000004100000410000000206613015255277020102 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIC7jCCAdagAwIBAgIJAPo+qW/K5mHrMA0GCSqGSIb3DQEBCwUAMCcxFTATBgNV BAMMDE15VGVzdFJvb3RDQTEOMAwGA1UEBwwFOTEzNDMwHhcNMTYwMjI5MTA0NDA3 WhcNMjYwMjI2MTA0NDA3WjAnMRUwEwYDVQQDDAxNeVRlc3RSb290Q0ExDjAMBgNV BAcMBTkxMzQzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqwGrw1hf 6LELt8euMTsRO7qnIsfH8yAeysK1GQgsWGuede7WcO9sXrtQOySDzb3cNkVoYKWt b6O44S+jFqCUVRwLLMbhJtUTVYuOmFbpEc6XEgJEi3GqQqfLUnU1ODHcqEuDYZ6i B0Iq2FDX+tu3AOBi8u653d7ccX5peVRF8ldZtyForjOJQ2M5Ya4pEW1JWX59XnY2 7tkw6wLHrHobwNy4laKibZGhS9KzHlNm4SKnGu/nMYEDjOk68j8CaKxoFNPZFfm9 qluADql2jj5nML5Axj1E3ZO3m+mc/KtZzFZByNdVtwoO04fdBK21Lxj8gPo2xCxu V2A45yNb3/Q7LQIDAQABox0wGzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjAN BgkqhkiG9w0BAQsFAAOCAQEAlB1Aljx2WMfhFmfIgD5PchizCquxQG3D0IvXyZwC B2aGN9we4BkBBh+YzqD/VP0QNdqkmtAXmp+rGdzMgSzMFudtowRj534JfZ6nOFXC KgNAKEznsJw6/voRYv+f5fciFB5h/sLBI5biIiyutpZKGkxJ9/yaOjJ7tT8MMP5r WDlNEvvvgeT3ORuRma1yeg9oIVLJUamt/TuztOAeIGXW+34t87WGrt/ITGeMeJTk /OFdjLOf3onOsANfoYuBJHvsmVNy/RF6TGd16DON3BR2DNdgnUUH6mee0VfXQ5vj +ADCC7P2lHBPc7uShFtLi8EEj35/TALnHavrD6fuObKgLQ== -----END CERTIFICATE----- bunny-2.6.1/spec/tls/client_certificate.pem0000644000004100000410000000207613015255277020776 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIC9TCCAd2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAnMRUwEwYDVQQDDAxNeVRl c3RSb290Q0ExDjAMBgNVBAcMBTkxMzQzMB4XDTE2MDIyOTEwNDQwN1oXDTI2MDIy NjEwNDQwN1owJDERMA8GA1UEAxMIbWVyY3VyaW8xDzANBgNVBAoTBmNsaWVudDCC ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALLL/SazTQMtvwlBTQz8upZV oT/JC13xJlAW5kQySty53spDH64ss8l3G7qbmjvUqtwhCqcgJ40Tb33UmYlYZaR5 zPNvQG284AwSzXAMhPKM41FNenTD1kmlOTtzk7iAbAbMZD4KfzQZ9E3a+kUyV9MV VlYKxwRwjq2DmUWuDsw5Ei6gHYpuQwdlWPVCyIOmcg30qzvcVjK3bFguKkjy3NTb pVsTqoF/7GHsoT85ZP4wsThgwmkMvyO181uMBcU4Y1btn4VusDB4//mc3uVW2fJo IjJw72+oqQJKr1XMVE0bZbspWSafkcbi31rbeHURUrOqqFuJnRKgCfk3LC86T+cC AwEAAaMvMC0wCQYDVR0TBAIwADALBgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYIKwYB BQUHAwIwDQYJKoZIhvcNAQELBQADggEBAGj9EDYbrmp9+/p7lAtTxiZv5V21qiU5 8DreTTwybsdpPeGKVH7OowDydbxNiPH6gPZ0OGrMyJ9qA5zYP2ozZiYrxuoIb0xQ 5u/yQ8qCwh14dorQrtsYCXj9CqLP5Ns3siH8pg7EsHBf/lADGByn0TH45z7O9fJc hLAppaaFiS/5I8F7MvzKuwryDayX41DVwVU63zbMZ+Xloa4AI1YWwfSZ0cGrNR7R yyN1Bbzg1OK5TeEj7F/4uNuydxaPYHTtAeq1ycEppsCsjYts/J6xtG1mrjhZHhas WuZ4fmj5dkEUG1PLnVscPbmTO6F9ZD2siARq3su+PBDpdbFMpcf09+A= -----END CERTIFICATE----- bunny-2.6.1/spec/spec_helper.rb0000644000004100000410000000225413015255277016465 0ustar www-datawww-data# -*- encoding: utf-8; mode: ruby -*- $LOAD_PATH.unshift File.expand_path("../lib", __FILE__) require 'bundler' Bundler.setup(:default, :test) require "effin_utf8" require "bunny" require "rabbitmq/http/client" require "amq/protocol/version" puts "Using Ruby #{RUBY_VERSION}, amq-protocol #{AMQ::Protocol::VERSION}" module RabbitMQ module Control RABBITMQ_NODENAME = ENV['RABBITMQ_NODENAME'] || 'rabbit' def rabbitmq_pid $1.to_i if `rabbitmqctl -n #{RABBITMQ_NODENAME} status` =~ /\{pid,(\d+)\}/ end def start_rabbitmq(delay = 1.0) # this is Homebrew-specific :( `RABBITMQ_NODENAME=#{RABBITMQ_NODENAME} rabbitmq-server > /dev/null 2>&1 &`; sleep(delay) end def stop_rabbitmq(pid = rabbitmq_pid, delay = 1.0) `rabbitmqctl -n #{RABBITMQ_NODENAME} stop`; sleep(delay) end def kill_rabbitmq(pid = rabbitmq_pid, delay = 1.0) # tango is down, tango is down! Process.kill("KILL", pid); sleep(delay) end end end module PlatformDetection def mri? !defined?(RUBY_ENGINE) || (defined?(RUBY_ENGINE) && ("ruby" == RUBY_ENGINE)) end def rubinius? defined?(RUBY_ENGINE) && (RUBY_ENGINE == 'rbx') end end bunny-2.6.1/spec/higher_level_api/0000755000004100000410000000000013015255277017132 5ustar www-datawww-databunny-2.6.1/spec/higher_level_api/integration/0000755000004100000410000000000013015255277021455 5ustar www-datawww-databunny-2.6.1/spec/higher_level_api/integration/queue_delete_spec.rb0000644000004100000410000000162613015255277025467 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Queue, "#delete" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close end context "with a name of an existing queue" do it "deletes that queue" do ch = connection.create_channel q = ch.queue("") q.delete # no exception as of RabbitMQ 3.2. MK. q.delete expect(ch.queues.size).to eq 0 end end context "with a name of an existing queue" do it "DOES NOT raise an exception" do ch = connection.create_channel # no exception as of RabbitMQ 3.2. MK. ch.queue_delete("sdkhflsdjflskdjflsd#{rand}") ch.queue_delete("sdkhflsdjflskdjflsd#{rand}") ch.queue_delete("sdkhflsdjflskdjflsd#{rand}") ch.queue_delete("sdkhflsdjflskdjflsd#{rand}") end end end bunny-2.6.1/spec/higher_level_api/integration/exchange_declare_spec.rb0000644000004100000410000001240513015255277026257 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Exchange do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close end context "of default type" do it "is declared with an empty name" do ch = connection.create_channel x = Bunny::Exchange.default(ch) expect(x.name).to eq '' end end context "of type fanout" do context "with a non-predefined name" do it "is declared" do ch = connection.create_channel name = "bunny.tests.exchanges.fanout#{rand}" x = ch.fanout(name) expect(x.name).to eq name x.delete ch.close end end context "with a predefined name" do it "is NOT declared" do ch = connection.create_channel name = "amq.fanout" x = ch.fanout(name) expect(x.name).to eq name ch.close end end context "with a name prefixed with 'amq.'" do it "raises an exception" do ch = connection.create_channel expect { ch.fanout("amq.test") }.to raise_error(Bunny::AccessRefused) expect(ch).to be_closed expect { ch.fanout("amq.test") }.to raise_error(Bunny::ChannelAlreadyClosed) end end context "with the durable property" do it "is declared as durable" do ch = connection.create_channel name = "bunny.tests.exchanges.durable" x = ch.fanout(name, :durable => true) expect(x.name).to eq name expect(x).to be_durable expect(x).not_to be_auto_delete x.delete ch.close end end context "with the auto-delete property" do it "is declared as auto-delete" do ch = connection.create_channel name = "bunny.tests.exchanges.auto-delete" x = ch.fanout(name, :auto_delete => true) expect(x.name).to eq name expect(x).not_to be_durable expect(x).to be_auto_delete ch.exchange(name, :type => :fanout, :auto_delete => true) x.delete ch.close end end context "when declared with a different set of attributes" do it "raises an exception" do ch = connection.create_channel x = ch.fanout("bunny.tests.exchanges.fanout", :auto_delete => true, :durable => false) expect { # force re-declaration ch.exchange_declare("bunny.tests.exchanges.fanout", :direct, :auto_delete => false, :durable => true) }.to raise_error(Bunny::PreconditionFailed) expect(ch).to be_closed expect { ch.fanout("bunny.tests.exchanges.fanout", :auto_delete => true, :durable => false) }.to raise_error(Bunny::ChannelAlreadyClosed) end end end context "of type direct" do context "with a non-predefined name" do it "is declared" do ch = connection.create_channel name = "bunny.tests.exchanges.direct" x = ch.direct(name) expect(x.name).to eq name ch.exchange(name, :type => :direct) x.delete ch.close end end context "with a predefined name" do it "is NOT declared" do ch = connection.create_channel name = "amq.direct" x = ch.direct(name) expect(x.name).to eq name ch.close end end end context "of type topic" do context "with a non-predefined name" do it "is declared" do ch = connection.create_channel name = "bunny.tests.exchanges.topic" x = ch.topic(name) expect(x.name).to eq name ch.exchange(name, :type => :topic) x.delete ch.close end end context "with a predefined name" do it "is NOT declared" do ch = connection.create_channel name = "amq.topic" x = ch.topic(name) expect(x.name).to eq name ch.close end end end context "of type headers" do context "with a non-predefined name" do it "is declared" do ch = connection.create_channel name = "bunny.tests.exchanges.headers" x = ch.headers(name) expect(x.name).to eq name x.delete ch.close end end context "with a predefined name (amq.match)" do it "is NOT declared" do ch = connection.create_channel name = "amq.match" x = ch.headers(name) expect(x.name).to eq name ch.close end end context "with a predefined name (amq.headers)" do it "is NOT declared" do ch = connection.create_channel name = "amq.headers" x = ch.headers(name) expect(x.name).to eq name ch.close end end end context "that is internal" do it "can be declared" do ch = connection.create_channel x = ch.fanout("bunny.tests.exchanges.internal", :internal => true) expect(x).to be_internal x.delete ch.close end end context "not declared as internal" do it "is not internal" do ch = connection.create_channel x = ch.fanout("bunny.tests.exchanges.non-internal") expect(x).not_to be_internal x.delete ch.close end end end bunny-2.6.1/spec/higher_level_api/integration/heartbeat_spec.rb0000644000004100000410000000211713015255277024754 0ustar www-datawww-datarequire "spec_helper" describe "Client-defined heartbeat interval" do context "with value > 0" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :heartbeat_interval => 4) c.start c end it "can be enabled explicitly" do sleep 5.0 connection.close end end # issue 267 context "with value = 0" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :heartbeat_interval => 0, :automatically_recover => false) c.start c end it "disables heartbeats" do sleep 1.0 connection.close end end end describe "Server-defined heartbeat interval" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :heartbeat_interval => :server) c.start c end it "can be enabled explicitly" do puts "Sleeping for 5 seconds with heartbeat interval of 4" sleep 5.0 connection.close end end bunny-2.6.1/spec/higher_level_api/integration/exchange_bind_spec.rb0000644000004100000410000000131413015255277025571 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Exchange do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end it "binds two existing exchanges" do ch = connection.create_channel source = ch.fanout("bunny.exchanges.source", :auto_delete => true) destination = ch.fanout("bunny.exchanges.destination", :auto_delete => true) queue = ch.queue("", :exclusive => true) queue.bind(destination) destination.bind(source) source.publish("") sleep 0.5 expect(queue.message_count).to be > 0 ch.close end end bunny-2.6.1/spec/higher_level_api/integration/message_properties_access_spec.rb0000644000004100000410000000615113015255277030240 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Queue, "#subscribe" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end let(:queue_name) { "bunny.basic_consume#{rand}" } it "provides delivery handler access to message properties" do @now = Time.now metadata = {} envelope = {} t = Thread.new do ch = connection.create_channel q = ch.queue(queue_name, :auto_delete => true, :durable => false) q.subscribe(:exclusive => true, :manual_ack => false) do |delivery_info, properties, payload| metadata = properties envelope = delivery_info end end t.abort_on_exception = true sleep 0.5 ch = connection.create_channel x = ch.default_exchange x.publish("hello", :routing_key => queue_name, :app_id => "bunny.example", :priority => 8, :type => "kinda.checkin", # headers table keys can be anything :headers => { :coordinates => { :latitude => 59.35, :longitude => 18.066667 }, :time => @now, :participants => 11, :venue => "Stockholm", :true_field => true, :false_field => false, :nil_field => nil, :ary_field => ["one", 2.0, 3, [{"abc" => 123}]] }, :timestamp => @now.to_i, :reply_to => "a.sender", :correlation_id => "r-1", :message_id => "m-1") sleep 0.7 expect(metadata.content_type).to eq "application/octet-stream" expect(metadata.priority).to eq 8 time = metadata.headers["time"] expect(time.year).to eq @now.year expect(time.month).to eq @now.month expect(time.day).to eq @now.day expect(time.hour).to eq @now.hour expect(time.min).to eq @now.min expect(time.sec).to eq @now.sec expect(metadata.headers["coordinates"]["latitude"]).to eq 59.35 expect(metadata.headers["participants"]).to eq 11 expect(metadata.headers["venue"]).to eq "Stockholm" expect(metadata.headers["true_field"]).to eq true expect(metadata.headers["false_field"]).to eq false expect(metadata.headers["nil_field"]).to be_nil expect(metadata.headers["ary_field"]).to eq ["one", 2.0, 3, [{ "abc" => 123}]] expect(metadata.timestamp).to eq Time.at(@now.to_i) expect(metadata.type).to eq "kinda.checkin" expect(metadata.reply_to).to eq "a.sender" expect(metadata.correlation_id).to eq "r-1" expect(metadata.message_id).to eq "m-1" expect(metadata.app_id).to eq "bunny.example" expect(envelope.consumer_tag).not_to be_nil expect(envelope.consumer_tag).not_to be_empty expect(envelope).not_to be_redelivered expect(envelope.delivery_tag).to eq 1 expect(envelope.routing_key).to eq queue_name expect(envelope.exchange).to eq "" ch.close end end bunny-2.6.1/spec/higher_level_api/integration/basic_ack_spec.rb0000644000004100000410000001627213015255277024723 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Channel, "#ack" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end context "with a valid (known) delivery tag" do it "acknowledges a message" do ch = connection.create_channel q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true) x = ch.default_exchange x.publish("bunneth", :routing_key => q.name) sleep 0.5 expect(q.message_count).to eq 1 delivery_details, properties, content = q.pop(:manual_ack => true) ch.ack(delivery_details.delivery_tag, true) ch.close ch = connection.create_channel q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true) expect(q.message_count).to eq 0 ch.close end end context "with a valid (known) delivery tag (multiple = true)" do it "acknowledges a message" do ch = connection.create_channel q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true) x = ch.default_exchange x.publish("bunneth", :routing_key => q.name) x.publish("bunneth", :routing_key => q.name) sleep 0.5 expect(q.message_count).to eq 2 delivery_details_1, _properties, _content = q.pop(:manual_ack => true) delivery_details_2, _properties, _content = q.pop(:manual_ack => true) ch.ack(delivery_details_2.delivery_tag, true) ch.close ch = connection.create_channel q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true) expect(q.message_count).to eq 0 ch.close end end context "with a valid (known) delivery tag (multiple = false)" do it "acknowledges a message" do ch = connection.create_channel q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true) x = ch.default_exchange x.publish("bunneth", :routing_key => q.name) x.publish("bunneth", :routing_key => q.name) sleep 0.5 expect(q.message_count).to eq 2 delivery_details_1, _properties, _content = q.pop(:manual_ack => true) delivery_details_2, _properties, _content = q.pop(:manual_ack => true) ch.ack(delivery_details_2.delivery_tag, false) ch.close ch = connection.create_channel q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true) expect(q.message_count).to eq 1 ch.close end end context "with a valid (known) delivery tag and automatic ack mode" do it "results in a channel exception" do ch = connection.create_channel q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true) x = ch.default_exchange q.subscribe(:manual_ack => false) do |delivery_info, properties, payload| ch.ack(delivery_info.delivery_tag, false) end x.publish("bunneth", :routing_key => q.name) sleep 0.5 expect do q.message_count end.to raise_error(Bunny::ChannelAlreadyClosed) end end context "with an invalid (random) delivery tag" do it "causes a channel-level error" do ch = connection.create_channel q = ch.queue("bunny.basic.ack.unknown-delivery-tag", :exclusive => true) x = ch.default_exchange x.publish("bunneth", :routing_key => q.name) sleep 0.5 expect(q.message_count).to eq 1 _, _, content = q.pop(:manual_ack => true) ch.on_error do |ch, channel_close| @channel_close = channel_close end ch.ack(82, true) sleep 0.25 expect(@channel_close.reply_code).to eq AMQ::Protocol::PreconditionFailed::VALUE end end context "with a valid (known) delivery tag" do it "gets a depricated message warning for using :ack" do ch = connection.create_channel q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true) x = ch.default_exchange x.publish("bunneth", :routing_key => q.name) sleep 0.5 expect(q.message_count).to eq 1 orig_stderr = $stderr $stderr = StringIO.new delivery_details, properties, content = q.pop(:ack => true) $stderr.rewind expect($stderr.string.chomp).to eq("[DEPRECATION] `:ack` is deprecated. Please use `:manual_ack` instead.\n[DEPRECATION] `:ack` is deprecated. Please use `:manual_ack` instead.") $stderr = orig_stderr ch.ack(delivery_details.delivery_tag, true) ch.close ch = connection.create_channel q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true) expect(q.message_count).to eq 0 ch.close end end end describe Bunny::Channel, "#basic_ack" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end context "with a valid (known) delivery tag (multiple = true)" do it "acknowledges a message" do ch = connection.create_channel q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true) x = ch.default_exchange x.publish("bunneth", :routing_key => q.name) x.publish("bunneth", :routing_key => q.name) sleep 0.5 expect(q.message_count).to eq 2 delivery_details_1, _properties, _content = q.pop(:manual_ack => true) delivery_details_2, _properties, _content = q.pop(:manual_ack => true) ch.basic_ack(delivery_details_2.delivery_tag.to_i, true) ch.close ch = connection.create_channel q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true) expect(q.message_count).to eq 0 ch.close end end context "with a valid (known) delivery tag (multiple = false)" do it "acknowledges a message" do ch = connection.create_channel q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true) x = ch.default_exchange x.publish("bunneth", :routing_key => q.name) x.publish("bunneth", :routing_key => q.name) sleep 0.5 expect(q.message_count).to eq 2 delivery_details_1, _properties, _content = q.pop(:manual_ack => true) delivery_details_2, _properties, _content = q.pop(:manual_ack => true) ch.basic_ack(delivery_details_2.delivery_tag.to_i, false) ch.close ch = connection.create_channel q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true) expect(q.message_count).to eq 1 ch.close end end context "with a valid (known) delivery tag (multiple = default)" do it "acknowledges a message" do ch = connection.create_channel q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true) x = ch.default_exchange x.publish("bunneth", :routing_key => q.name) x.publish("bunneth", :routing_key => q.name) sleep 0.5 expect(q.message_count).to eq 2 delivery_details_1, _properties, _content = q.pop(:manual_ack => true) delivery_details_2, _properties, _content = q.pop(:manual_ack => true) ch.basic_ack(delivery_details_2.delivery_tag.to_i) ch.close ch = connection.create_channel q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true) expect(q.message_count).to eq 1 ch.close end end end bunny-2.6.1/spec/higher_level_api/integration/exclusive_queue_spec.rb0000644000004100000410000000122113015255277026223 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Queue do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end it "is closed when the connection it was declared on is closed" do ch1 = connection.create_channel ch2 = connection.create_channel q = ch1.queue("", :exclusive => true) ch1.queue_declare(q.name, :passive => true) ch2.queue_declare(q.name, :passive => true) ch1.close ch2.queue_declare(q.name, :passive => true) ch2.queue_delete(q.name) ch2.close end end bunny-2.6.1/spec/higher_level_api/integration/basic_return_spec.rb0000644000004100000410000000134713015255277025501 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Exchange, "#publish" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end context "with :mandatory => true and a bad [no routes] routing key" do it "causes a message to be returned" do ch = connection.create_channel x = ch.default_exchange returned = [] x.on_return do |basic_deliver, properties, content| returned << content end x.publish("xyzzy", :routing_key => rand.to_s, :mandatory => true) sleep 0.5 expect(returned).to include("xyzzy") ch.close end end end bunny-2.6.1/spec/higher_level_api/integration/queue_unbind_spec.rb0000644000004100000410000000205413015255277025500 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Queue, "bound to an exchange" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close end it "can be unbound from an exchange it was bound to" do ch = connection.create_channel x = ch.fanout("amq.fanout") q = ch.queue("", :exclusive => true).bind(x) x.publish("") sleep 0.3 expect(q.message_count).to eq 1 q.unbind(x) x.publish("") expect(q.message_count).to eq 1 end end describe Bunny::Queue, "NOT bound to an exchange" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close end it "is idempotent (succeeds)" do ch = connection.create_channel x = ch.fanout("amq.fanout") q = ch.queue("", :exclusive => true) # No exception as of RabbitMQ 3.2. MK. q.unbind(x) q.unbind(x) end end bunny-2.6.1/spec/higher_level_api/integration/tx_rollback_spec.rb0000644000004100000410000000061213015255277025317 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Channel, "#tx_rollback" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end it "is supported" do ch = connection.create_channel ch.tx_select ch.tx_rollback ch.close end end bunny-2.6.1/spec/higher_level_api/integration/consistent_hash_exchange_spec.rb0000644000004100000410000000312013015255277030046 0ustar www-datawww-data# -*- coding: utf-8 -*- require "spec_helper" describe "x-consistent-hash exchange" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatically_recover => true) c.start c end let(:http_client) { RabbitMQ::HTTP::Client.new("http://127.0.0.1:15672") } after :each do connection.close end let(:list) { Range.new(0, 6).to_a.map(&:to_s) } let(:m) { 1500 } let(:exchange_plugin_available) do http_client.overview.exchange_types.map(&:name).include?("x-consistent-hash") end it "distributes messages between queues bound with the same routing key" do if exchange_plugin_available ch = connection.create_channel body = "сообщение" # requires the consistent hash exchange plugin, # enable it with # # $ [sudo] rabbitmq-plugins enable rabbitmq_consistent_hash_exchange x = ch.exchange("bunny.stress.concurrent.consumers", :type => "x-consistent-hash", :durable => true) qs = [] q1 = ch.queue("", :exclusive => true).bind(x, :routing_key => "5") q2 = ch.queue("", :exclusive => true).bind(x, :routing_key => "5") sleep 1.0 5.times do |i| m.times do x.publish(body, :routing_key => list.sample) end puts "Published #{(i + 1) * m} tiny messages..." end sleep 2.0 expect(q1.message_count).to be > 100 expect(q2.message_count).to be > 100 ch.close else skip "x-consistent-hash exchange type isn't available" end # if end # it end bunny-2.6.1/spec/higher_level_api/integration/connection_spec.rb0000644000004100000410000003676513015255277025174 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Session do let(:port) { AMQ::Protocol::DEFAULT_PORT } let(:username) { "guest" } let(:tls_port) { AMQ::Protocol::TLS_PORT } context "initialized via connection URI" do after :each do subject.close if subject.open? end context "when schema is not one of [amqp, amqps]" do it "raises ArgumentError" do expect { described_class.new("http://127.0.0.1") }.to raise_error(ArgumentError, /amqp or amqps schema/) end end it "handles amqp:// URIs w/o path part" do session = described_class.new("amqp://127.0.0.1") session.start expect(session.vhost).to eq "/" expect(session.host).to eq "127.0.0.1" expect(session.port).to eq 5672 expect(session.ssl?).to eq false session.close end context "when URI ends in a slash" do it "parses vhost as an empty string" do session = described_class.new("amqp://127.0.0.1/") expect(session.hostname).to eq "127.0.0.1" expect(session.port).to eq 5672 expect(session.vhost).to eq "" end end context "when URI is amqp://dev.rabbitmq.com/a/path/with/slashes" do it "raises an ArgumentError" do expect { described_class.new("amqp://dev.rabbitmq.com/a/path/with/slashes") }.to raise_error(ArgumentError) end end end context "initialized with all defaults" do it "provides a way to fine tune socket options" do conn = Bunny.new conn.start expect(conn.transport.socket).to respond_to(:setsockopt) conn.close end it "successfully negotiates the connection" do conn = Bunny.new conn.start expect(conn).to be_connected expect(conn.server_properties).not_to be_nil expect(conn.server_capabilities).not_to be_nil props = conn.server_properties expect(props["product"]).not_to be_nil expect(props["platform"]).not_to be_nil expect(props["version"]).not_to be_nil conn.close end end unless ENV["CI"] context "initialized with TCP connection timeout = 5" do it "successfully connects" do conn = described_class.new(:connection_timeout => 5) conn.start expect(conn).to be_connected expect(conn.server_properties).not_to be_nil expect(conn.server_capabilities).not_to be_nil props = conn.server_properties expect(props["product"]).not_to be_nil expect(props["platform"]).not_to be_nil expect(props["version"]).not_to be_nil conn.close end end context "initialized with :host => 127.0.0.1" do after :each do subject.close if subject.open? end let(:host) { "127.0.0.1" } subject do described_class.new(:host => host) end it "uses hostname = 127.0.0.1" do expect(subject.host).to eq host expect(subject.hostname).to eq host end it "uses port 5672" do expect(subject.port).to eq port end it "uses username = guest" do expect(subject.username).to eq username end end context "initialized with :hostname => localhost" do after :each do subject.close if subject.open? end let(:host) { "localhost" } let(:subject) { described_class.new(:hostname => host) } it "uses hostname = localhost" do expect(subject.host).to eq host expect(subject.hostname).to eq host end it "uses port 5672" do expect(subject.port).to eq port end it "uses username = guest" do expect(subject.username).to eq username expect(subject.user).to eq username end end context "initialized with :hosts => [...]" do after :each do subject.close if subject.open? end let(:host) { "192.168.1.10" } let(:hosts) { [host] } let(:subject) { described_class.new(:hosts => hosts) } it "uses hostname = 192.168.1.10" do expect(subject.host).to eq host expect(subject.hostname).to eq host end it "uses port 5672" do expect(subject.port).to eq port end it "uses username = guest" do expect(subject.username).to eq username expect(subject.user).to eq username end end context "initialized with :addresses => [...]" do after :each do subject.close if subject.open? end let(:host) { "192.168.1.10" } let(:port) { 5673 } let(:address) { "#{host}:#{port}" } let(:addresses) { [address] } let(:subject) { described_class.new(:addresses => addresses) } it "uses hostname = 192.168.1.10" do expect(subject.host).to eq host expect(subject.hostname).to eq host end it "uses port 5673" do expect(subject.port).to eq port end it "uses username = guest" do expect(subject.username).to eq username expect(subject.user).to eq username end end context "initialized with :addresses => [...] with quoted IPv6 hostnames" do after :each do subject.close if subject.open? end let(:host) { "[2001:db8:85a3:8d3:1319:8a2e:370:7348]" } let(:port) { 5673 } let(:address) { "#{host}:#{port}" } let(:addresses) { [address] } let(:subject) { described_class.new(:addresses => addresses) } it "uses correct hostname" do expect(subject.host).to eq host expect(subject.hostname).to eq host end it "uses port 5673" do expect(subject.port).to eq port end it "uses username = guest" do expect(subject.username).to eq username expect(subject.user).to eq username end end context "initialized with :addresses => [...] with quoted IPv6 hostnames without ports" do after :each do subject.close if subject.open? end let(:host) { "[2001:db8:85a3:8d3:1319:8a2e:370:7348]" } let(:address) { host } let(:addresses) { [address] } let(:subject) { described_class.new(:addresses => addresses) } it "uses correct hostname" do expect(subject.host).to eq host expect(subject.hostname).to eq host end it "uses port 5672" do expect(subject.port).to eq 5672 end it "uses username = guest" do expect(subject.username).to eq username expect(subject.user).to eq username end end context "initialized with :addresses => [...] with an quoted IPv6 hostnames" do after :each do subject.close if subject.open? end let(:host) { "2001:db8:85a3:8d3:1319:8a2e:370:7348" } let(:port) { 5673 } let(:address) { "#{host}:#{port}" } let(:addresses) { [address] } let(:subject) { described_class.new(:addresses => addresses) } it "fails to correctly parse the host (and emits a warning)" do expect(subject.host).to eq "2001" expect(subject.hostname).to eq "2001" end it "fails to correctly parse the port (and emits a warning)" do expect(subject.port).to eq 0 end it "uses username = guest" do expect(subject.username).to eq username expect(subject.user).to eq username end end context "initialized with conflicting hosts and addresses" do let(:host) { "192.168.1.10" } let(:port) { 5673 } let(:address) { "#{host}:#{port}" } let(:io) { StringIO.new } let(:logger) { ::Logger.new(io) } it "raises an argument error when there is are hosts and an address" do expect { described_class.new(addresses: [address], hosts: [host]) }.to raise_error(ArgumentError) end it "logs a warning when there is a single host and an array" do described_class.new(addresses: [address], host: host, logger: logger) expect(io.string).to include 'WARN -- : The connection options contain '\ 'both a host and an array of hosts, the single host is ignored.' end it "converts hosts in addresses to addresses" do strategy = Proc.new { |addresses| addresses } session = described_class.new(addresses: [address,host ], hosts_shuffle_strategy: strategy) strategy = Proc.new { |addresses| addresses } expect(session.to_s).to include 'addresses=[192.168.1.10:5673,192.168.1.10:5672]' end end context "initialized with :channel_max => 4096" do after :each do subject.close if subject.open? end let(:channel_max) { 1024 } let(:subject) { described_class.new(:channel_max => channel_max) } # this assumes RabbitMQ has no lower value configured. In 3.2 # it is 0 (no limit) by default and 1024 is still a fairly low value # for future releases. MK. it "negotiates channel max to be 1024" do subject.start expect(subject.channel_max).to eq channel_max subject.close end end context "initialized with :ssl => true" do let(:subject) do described_class.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :ssl => true, :ssl_cert => "spec/tls/client_cert.pem", :ssl_key => "spec/tls/client_key.pem", :ssl_ca_certificates => ["./spec/tls/cacert.pem"]) end it "uses TLS port" do expect(subject.port).to eq tls_port end end context "initialized with :tls => true" do let(:subject) do described_class.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :tls => true, :tls_cert => "spec/tls/client_certificate.pem", :tls_key => "spec/tls/client_key.pem", :tls_ca_certificates => ["./spec/tls/ca_certificate.pem"]) end it "uses TLS port" do expect(subject.port).to eq tls_port end end end context "initialized with :host => 127.0.0.1 and non-default credentials" do after :each do subject.close if subject.open? end let(:host) { "127.0.0.1" } # see ./bin/ci/before_build let(:username) { "bunny_gem" } let(:password) { "bunny_password" } let(:vhost) { "bunny_testbed" } subject do described_class.new(:hostname => host, :username => username, :password => password, :virtual_host => vhost) end it "successfully connects" do 5.times { subject.start } expect(subject).to be_connected expect(subject.server_properties).not_to be_nil expect(subject.server_capabilities).not_to be_nil props = subject.server_properties expect(props["product"]).not_to be_nil expect(props["platform"]).not_to be_nil expect(props["version"]).not_to be_nil end it "uses hostname = 127.0.0.1" do expect(subject.host).to eq host expect(subject.hostname).to eq host end it "uses port 5672" do expect(subject.port).to eq port end it "uses provided vhost" do expect(subject.vhost).to eq vhost expect(subject.virtual_host).to eq vhost end it "uses provided username" do expect(subject.username).to eq username end it "uses provided password" do expect(subject.password).to eq password end end context "initialized with :host => 127.0.0.1 and non-default credentials (take 2)" do after :each do subject.close if subject.open? end let(:host) { "127.0.0.1" } # see ./bin/ci/before_build let(:username) { "bunny_gem" } let(:password) { "bunny_password" } let(:vhost) { "bunny_testbed" } subject do described_class.new(:hostname => host, :user => username, :pass => password, :vhost => vhost) end it "successfully connects" do subject.start expect(subject).to be_connected expect(subject.server_properties).not_to be_nil expect(subject.server_capabilities).not_to be_nil props = subject.server_properties expect(props["product"]).not_to be_nil expect(props["platform"]).not_to be_nil expect(props["version"]).not_to be_nil end it "uses hostname = 127.0.0.1" do expect(subject.host).to eq host expect(subject.hostname).to eq host end it "uses port 5672" do expect(subject.port).to eq port end it "uses provided username" do expect(subject.username).to eq username end it "uses provided password" do expect(subject.password).to eq password end end context "initialized with :host => 127.0.0.1 and non-default credentials (take 2)" do after :each do subject.close if subject.open? end let(:host) { "127.0.0.1" } # see ./bin/ci/before_build let(:username) { "bunny_gem" } let(:password) { "bunny_password" } let(:vhost) { "bunny_testbed" } let(:interval) { 1 } subject do described_class.new(:hostname => host, :user => username, :pass => password, :vhost => vhost, :heartbeat_interval => interval) end it "successfully connects" do subject.start expect(subject).to be_connected expect(subject.server_properties).not_to be_nil expect(subject.server_capabilities).not_to be_nil props = subject.server_properties expect(props["product"]).not_to be_nil expect(props["platform"]).not_to be_nil expect(props["version"]).not_to be_nil expect(props["capabilities"]).not_to be_nil # this is negotiated with RabbitMQ, so we need to # establish the connection first expect(subject.heartbeat).to eq interval end end context "initialized with :host => 127.0.0.1 and INVALID credentials" do let(:host) { "127.0.0.1" } # see ./bin/ci/before_build let(:username) { "bunny_gem#{Time.now.to_i}" } let(:password) { "sdjkfhsdf8ysd8fy8" } let(:vhost) { "___sd89aysd98789" } subject do described_class.new(:hostname => host, :user => username, :pass => password, :vhost => vhost) end it "fails to connect" do expect do subject.start end.to raise_error(Bunny::PossibleAuthenticationFailureError) end it "uses provided username" do expect(subject.username).to eq username end it "uses provided password" do expect(subject.password).to eq password end end context "initialized with unreachable host or port" do it "fails to connect" do expect do c = described_class.new(:port => 38000) c.start end.to raise_error(Bunny::TCPConnectionFailed) end it "is not connected" do begin c = described_class.new(:port => 38000) c.start rescue Bunny::TCPConnectionFailed => e true end expect(subject.status).to eq :not_connected end it "is not open" do begin c = described_class.new(:port => 38000) c.start rescue Bunny::TCPConnectionFailed => e true end expect(subject).not_to be_open end end context "initialized with a custom logger object" do let(:io) { StringIO.new } let(:logger) { ::Logger.new(io) } it "uses provided logger" do conn = described_class.new(:logger => logger) conn.start expect(io.string.length).to be > 100 conn.close end it "doesn't reassign the logger's progname attribute" do expect(logger).not_to receive(:progname=) described_class.new(:logger => logger) end end end bunny-2.6.1/spec/higher_level_api/integration/connection_stop_spec.rb0000644000004100000410000000304613015255277026223 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Session do let(:http_client) { RabbitMQ::HTTP::Client.new("http://127.0.0.1:15672") } def close_connection(client_port) c = http_client. list_connections. find { |conn_info| conn_info.peer_port.to_i == client_port } http_client.close_connection(c.name) end def wait_for_recovery sleep 0.5 end it "can be closed" do c = Bunny.new(:automatically_recover => false) c.start ch = c.create_channel expect(c).to be_connected c.stop expect(c).to be_closed end it "can be closed twice (Session#close is idempotent)" do c = Bunny.new(:automatically_recover => false) c.start ch = c.create_channel expect(c).to be_connected c.stop expect(c).to be_closed c.stop expect(c).to be_closed end describe "in a single threaded mode" do it "can be closed" do c = Bunny.new(:automatically_recover => false, :threaded => false) c.start ch = c.create_channel expect(c).to be_connected c.stop expect(c).to be_closed end end describe "that recovers from connection.close" do it "can be closed" do c = Bunny.new(:automatically_recover => true, :recover_from_connection_close => true, :network_recovery_interval => 0.2) c.start ch = c.create_channel expect(c).to be_open close_connection(c.local_port) sleep 0.2 expect(c).not_to be_open wait_for_recovery expect(c).to be_open expect(ch).to be_open c.close end end end bunny-2.6.1/spec/higher_level_api/integration/queue_purge_spec.rb0000644000004100000410000000103613015255277025342 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Queue do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close end it "can be purged" do ch = connection.create_channel q = ch.queue("", :exclusive => true) x = ch.default_exchange x.publish("xyzzy", :routing_key => q.name) sleep(0.5) expect(q.message_count).to eq 1 q.purge expect(q.message_count).to eq 0 ch.close end end bunny-2.6.1/spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb0000644000004100000410000000575613015255277032146 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Channel do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end context "with implicit consumer construction" do let(:queue_name) { "basic.consume#{rand}" } it "supports consumer cancellation notifications" do cancelled = false ch = connection.create_channel t = Thread.new do ch2 = connection.create_channel q = ch2.queue(queue_name, :auto_delete => true) q.subscribe(:on_cancellation => Proc.new { cancelled = true }) end t.abort_on_exception = true sleep 0.5 x = ch.default_exchange x.publish("abc", :routing_key => queue_name) sleep 0.5 ch.queue(queue_name, :auto_delete => true).delete sleep 0.5 expect(cancelled).to eq true ch.close end end context "with explicit consumer construction" do class ExampleConsumer < Bunny::Consumer def cancelled? @cancelled end def handle_cancellation(_) @cancelled = true end end let(:queue_name) { "basic.consume#{rand}" } it "supports consumer cancellation notifications" do consumer = nil ch = connection.create_channel t = Thread.new do ch2 = connection.create_channel q = ch2.queue(queue_name, :auto_delete => true) consumer = ExampleConsumer.new(ch2, q, "") q.subscribe_with(consumer) end t.abort_on_exception = true sleep 0.5 x = ch.default_exchange x.publish("abc", :routing_key => queue_name) sleep 0.5 ch.queue(queue_name, :auto_delete => true).delete sleep 0.5 expect(consumer).to be_cancelled ch.close end end context "with consumer re-registration" do class ExampleConsumerThatReregisters < Bunny::Consumer def handle_cancellation(_) @queue = @channel.queue("basic.consume.after_cancellation", :auto_delete => true) @channel.basic_consume_with(self) end end let(:queue_name) { "basic.consume#{rand}" } it "works correctly" do consumer = nil xs = [] ch = connection.create_channel t = Thread.new do ch2 = connection.create_channel q = ch2.queue(queue_name, :auto_delete => true) consumer = ExampleConsumerThatReregisters.new(ch2, q, "") consumer.on_delivery do |_, _, payload| xs << payload end q.subscribe_with(consumer) end t.abort_on_exception = true sleep 0.5 x = ch.default_exchange x.publish("abc", :routing_key => queue_name) sleep 0.5 ch.queue(queue_name, :auto_delete => true).delete x.publish("abc", :routing_key => queue_name) sleep 0.5 q = ch.queue("basic.consume.after_cancellation", :auto_delete => true) expect(xs).to eq ["abc"] ch.close end end end bunny-2.6.1/spec/higher_level_api/integration/basic_publish_spec.rb0000644000004100000410000000320213015255277025620 0ustar www-datawww-datarequire "spec_helper" describe "Published message" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end context "with all default delivery and a 254 character long routing key" do it "routes the messages" do ch = connection.create_channel q = ch.queue("", :exclusive => true) x = ch.fanout("amq.fanout") q.bind(x) rk = "a" * 254 x.publish("xyzzy", :routing_key => rk, :persistent => true) sleep(1) expect(q.message_count).to eq 1 _, _, payload = q.pop expect(payload).to eq "xyzzy" ch.close end end context "with all default delivery and a 255 character long routing key" do it "routes the messages" do ch = connection.create_channel q = ch.queue("", :exclusive => true) x = ch.fanout("amq.fanout") q.bind(x) rk = "a" * 255 x.publish("xyzzy", :routing_key => rk, :persistent => true) sleep(1) expect(q.message_count).to eq 1 _, _, payload = q.pop expect(payload).to eq "xyzzy" ch.close end end context "with all default delivery and a 256 character long routing key" do it "fails with a connection exception" do ch = connection.create_channel q = ch.queue("", :exclusive => true) x = ch.fanout("amq.fanout") q.bind(x) rk = "a" * 256 expect do x.publish("xyzzy", :routing_key => rk, :persistent => true) end.to raise_error(ArgumentError) ch.close end end end bunny-2.6.1/spec/higher_level_api/integration/channel_close_spec.rb0000644000004100000410000000076213015255277025616 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Channel, "when closed" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close end it "releases the id" do ch = connection.create_channel n = ch.number expect(ch).to be_open ch.close expect(ch).to be_closed # a new channel with the same id can be created connection.create_channel(n) end end bunny-2.6.1/spec/higher_level_api/integration/exchange_unbind_spec.rb0000644000004100000410000000155013015255277026136 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Exchange do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end it "unbinds two existing exchanges" do ch = connection.create_channel source = ch.fanout("bunny.exchanges.source#{rand}") destination = ch.fanout("bunny.exchanges.destination#{rand}") queue = ch.queue("", :exclusive => true) queue.bind(destination) destination.bind(source) source.publish("") sleep 0.5 expect(queue.message_count).to eq 1 queue.pop(:manual_ack => true) destination.unbind(source) source.publish("") sleep 0.5 expect(queue.message_count).to eq 0 source.delete destination.delete ch.close end end bunny-2.6.1/spec/higher_level_api/integration/basic_recover_spec.rb0000644000004100000410000000071413015255277025624 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Channel, "#recover" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end subject do connection.create_channel end it "is supported" do expect(subject.recover(true)).to be_instance_of(AMQ::Protocol::Basic::RecoverOk) expect(subject.recover(true)).to be_instance_of(AMQ::Protocol::Basic::RecoverOk) end end bunny-2.6.1/spec/higher_level_api/integration/exchange_delete_spec.rb0000644000004100000410000000514213015255277026122 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Exchange, "#delete" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end context "with a name of an existing exchange" do it "deletes that exchange" do ch = connection.create_channel x = ch.fanout("bunny.tests.exchanges.fanout#{rand}") x.delete # no exception as of RabbitMQ 3.2. MK. x.delete expect(ch.exchanges.size).to eq 0 end end context "with a name of a non-existent exchange" do it "DOES NOT rais an exception" do ch = connection.create_channel # no exception as of RabbitMQ 3.2. MK. ch.exchange_delete("sdkhflsdjflskdjflsd#{rand}") ch.exchange_delete("sdkhflsdjflskdjflsd#{rand}") ch.exchange_delete("sdkhflsdjflskdjflsd#{rand}") ch.exchange_delete("sdkhflsdjflskdjflsd#{rand}") end end context "with a name of 'amq.direct'" do it "does not delete the exchange" do ch = connection.create_channel x = ch.direct('amq.direct') expect(x.delete).to eq nil end end context "with a name of 'amq.fanout'" do it "does not delete the exchange" do ch = connection.create_channel x = ch.fanout('amq.fanout') expect(x.delete).to eq nil end end context "with a name of 'amq.topic'" do it "does not delete the exchange" do ch = connection.create_channel x = ch.topic('amq.topic') expect(x.delete).to eq nil end end context "with a name of 'amq.headers'" do it "does not delete the exchange" do ch = connection.create_channel x = ch.headers('amq.headers') expect(x.delete).to eq nil end end context "with a name of 'amq.match'" do it "does not delete the exchange" do ch = connection.create_channel x = ch.headers('amq.match') expect(x.delete).to eq nil end end describe "#exchange_exists?" do context "when a exchange exists" do it "returns true" do ch = connection.create_channel expect(connection.exchange_exists?("amq.fanout")).to eq true expect(connection.exchange_exists?("amq.direct")).to eq true expect(connection.exchange_exists?("amq.topic")).to eq true expect(connection.exchange_exists?("amq.match")).to eq true end end context "when a exchange DOES NOT exist" do it "returns false" do expect(connection.exchange_exists?("suf89u9a4jo3ndnakls##{Time.now.to_i}")).to eq false end end end end bunny-2.6.1/spec/higher_level_api/integration/basic_reject_spec.rb0000644000004100000410000001010513015255277025426 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Channel, "#reject" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end context "with requeue = true" do it "requeues a message" do ch = connection.create_channel q = ch.queue("bunny.basic.reject.manual-acks", :exclusive => true) x = ch.default_exchange x.publish("bunneth", :routing_key => q.name) sleep(0.5) expect(q.message_count).to eq 1 delivery_info, _, _ = q.pop(:manual_ack => true) ch.reject(delivery_info.delivery_tag, true) sleep(0.5) expect(q.message_count).to eq 1 ch.close end end context "with requeue = false" do it "rejects a message" do ch = connection.create_channel q = ch.queue("bunny.basic.reject.with-requeue-false", :exclusive => true) x = ch.default_exchange x.publish("bunneth", :routing_key => q.name) sleep(0.5) expect(q.message_count).to eq 1 delivery_info, _, _ = q.pop(:manual_ack => true) ch.reject(delivery_info.delivery_tag, false) sleep(0.5) ch.close ch = connection.create_channel q = ch.queue("bunny.basic.reject.with-requeue-false", :exclusive => true) expect(q.message_count).to eq 0 ch.close end end context "with an invalid (random) delivery tag" do it "causes a channel-level error" do ch = connection.create_channel q = ch.queue("bunny.basic.reject.unknown-delivery-tag", :exclusive => true) x = ch.default_exchange x.publish("bunneth", :routing_key => q.name) sleep(0.25) expect(q.message_count).to eq 1 _, _, content = q.pop(:manual_ack => true) ch.on_error do |ch, channel_close| @channel_close = channel_close end ch.reject(82, true) sleep 0.5 expect(@channel_close.reply_text).to eq "PRECONDITION_FAILED - unknown delivery tag 82" end end end describe Bunny::Channel, "#basic_reject" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end context "with requeue = true" do it "requeues a message" do ch = connection.create_channel q = ch.queue("bunny.basic.reject.manual-acks", :exclusive => true) x = ch.default_exchange x.publish("bunneth", :routing_key => q.name) sleep(0.5) expect(q.message_count).to eq 1 delivery_info, _, _ = q.pop(:manual_ack => true) ch.basic_reject(delivery_info.delivery_tag.to_i, true) sleep(0.5) expect(q.message_count).to eq 1 ch.close end end context "with requeue = false" do it "rejects a message" do ch = connection.create_channel q = ch.queue("bunny.basic.reject.with-requeue-false", :exclusive => true) x = ch.default_exchange x.publish("bunneth", :routing_key => q.name) sleep(0.5) expect(q.message_count).to eq 1 delivery_info, _, _ = q.pop(:manual_ack => true) ch.basic_reject(delivery_info.delivery_tag.to_i, false) sleep(0.5) ch.close ch = connection.create_channel q = ch.queue("bunny.basic.reject.with-requeue-false", :exclusive => true) expect(q.message_count).to eq 0 ch.close end end context "with requeue = default" do it "rejects a message" do ch = connection.create_channel q = ch.queue("bunny.basic.reject.with-requeue-false", :exclusive => true) x = ch.default_exchange x.publish("bunneth", :routing_key => q.name) sleep(0.5) expect(q.message_count).to eq 1 delivery_info, _, _ = q.pop(:manual_ack => true) ch.basic_reject(delivery_info.delivery_tag.to_i) sleep(0.5) ch.close ch = connection.create_channel q = ch.queue("bunny.basic.reject.with-requeue-false", :exclusive => true) expect(q.message_count).to eq 0 ch.close end end end bunny-2.6.1/spec/higher_level_api/integration/connection_recovery_spec.rb0000644000004100000410000002664213015255277027103 0ustar www-datawww-datarequire "spec_helper" require "rabbitmq/http/client" describe "Connection recovery" do let(:http_client) { RabbitMQ::HTTP::Client.new("http://127.0.0.1:15672") } let(:logger) { Logger.new($stderr).tap {|logger| logger.level = Logger::FATAL} } let(:recovery_interval) { 0.2 } it "reconnects after grace period" do with_open do |c| close_all_connections! wait_on_loss_and_recovery_of { connections.any? } end end it "reconnects after grace period (with multiple hosts)" do with_open_multi_host do |c| close_all_connections! wait_on_loss_and_recovery_of { connections.any? } end end it "reconnects after grace period (with multiple hosts, including a broken one)" do with_open_multi_broken_host do |c| close_all_connections! wait_on_loss_and_recovery_of { connections.any? } end end it "recovers channels" do with_open do |c| ch1 = c.create_channel ch2 = c.create_channel close_all_connections! poll_until { channels.count.zero? } poll_until { channels.count == 2 } expect(ch1).to be_open expect(ch2).to be_open end end it "recovers channels (with multiple hosts)" do with_open_multi_host do |c| ch1 = c.create_channel ch2 = c.create_channel close_all_connections! poll_until { channels.count.zero? } poll_until { channels.count == 2 } expect(ch1).to be_open expect(ch2).to be_open end end it "recovers channels (with multiple hosts, including a broken one)" do with_open_multi_broken_host do |c| ch1 = c.create_channel ch2 = c.create_channel close_all_connections! poll_until { channels.count.zero? } poll_until { channels.count == 2 } expect(ch1).to be_open expect(ch2).to be_open end end it "recovers basic.qos prefetch setting" do with_open do |c| ch = c.create_channel ch.prefetch(11) expect(ch.prefetch_count).to eq 11 expect(ch.prefetch_global).to be false close_all_connections! wait_on_loss_and_recovery_of { connections.any? } expect(ch).to be_open expect(ch.prefetch_count).to eq 11 expect(ch.prefetch_global).to be false end end it "recovers basic.qos prefetch global setting" do with_open do |c| ch = c.create_channel ch.prefetch(42, true) expect(ch.prefetch_count).to eq 42 expect(ch.prefetch_global).to be true close_all_connections! wait_on_loss_and_recovery_of { connections.any? } expect(ch).to be_open expect(ch.prefetch_count).to eq 42 expect(ch.prefetch_global).to be true end end it "recovers publisher confirms setting" do with_open do |c| ch = c.create_channel ch.confirm_select expect(ch).to be_using_publisher_confirms close_all_connections! wait_on_loss_and_recovery_of { connections.any? } expect(ch).to be_open expect(ch).to be_using_publisher_confirms end end it "recovers transactionality setting" do with_open do |c| ch = c.create_channel ch.tx_select expect(ch).to be_using_tx close_all_connections! wait_on_loss_and_recovery_of { connections.any? } expect(ch).to be_open expect(ch).to be_using_tx end end it "recovers client-named queues" do with_open do |c| ch = c.create_channel q = ch.queue("bunny.tests.recovery.client-named#{rand}") close_all_connections! wait_on_loss_and_recovery_of { connections.any? } expect(ch).to be_open ensure_queue_recovery(ch, q) q.delete end end # a very simplistic test for queues inspired by #412 it "recovers client-named queues declared with passive = true" do with_open do |c| ch = c.create_channel ch2 = c.create_channel n = rand s = "bunny.tests.recovery.client-named#{n}" q = ch.queue(s) q2 = ch2.queue(s, no_declare: true) close_all_connections! wait_on_loss_and_recovery_of { connections.any? } expect(ch).to be_open ensure_queue_recovery(ch, q) q.delete end end it "recovers server-named queues" do with_open do |c| ch = c.create_channel q = ch.queue("", :exclusive => true) close_all_connections! wait_on_loss_and_recovery_of { connections.any? } expect(ch).to be_open ensure_queue_recovery(ch, q) end end it "recovers queue bindings" do with_open do |c| ch = c.create_channel x = ch.fanout("amq.fanout") q = ch.queue("", :exclusive => true) q.bind(x) close_all_connections! wait_on_loss_and_recovery_of { connections.any? } expect(ch).to be_open ensure_queue_binding_recovery(ch, x, q) end end it "recovers exchanges and their bindings" do with_open do |c| ch = c.create_channel source = ch.fanout("source.exchange.recovery.example", auto_delete: true) destination = ch.fanout("destination.exchange.recovery.example", auto_delete: true) destination.bind(source) # Exchanges won't get auto-deleted on connection loss unless they have # had an exclusive queue bound to them. dst_queue = ch.queue("", exclusive: true) dst_queue.bind(destination, routing_key: "") src_queue = ch.queue("", exclusive: true) src_queue.bind(source, routing_key: "") close_all_connections! wait_on_loss_and_recovery_of { exchange_names_in_vhost("/").include?(source.name) } ch.confirm_select source.publish("msg", routing_key: "") ch.wait_for_confirms expect(dst_queue.message_count).to eq 1 end end # this is a simplistic test that primarily execises the code path from #412 it "recovers exchanges that were declared with passive = true" do with_open do |c| ch = c.create_channel ch2 = c.create_channel source = ch.fanout("source.exchange.recovery.example", auto_delete: true) destination = ch.fanout("destination.exchange.recovery.example", auto_delete: true) source2 = ch2.fanout("source.exchange.recovery.example", no_declare: true) destination2 = ch2.fanout("destination.exchange.recovery.example", no_declare: true) destination.bind(source) # Exchanges won't get auto-deleted on connection loss unless they have # had an exclusive queue bound to them. dst_queue = ch.queue("", exclusive: true) dst_queue.bind(destination, routing_key: "") src_queue = ch.queue("", exclusive: true) src_queue.bind(source, routing_key: "") close_all_connections! wait_on_loss_and_recovery_of { exchange_names_in_vhost("/").include?(source.name) } ch2.confirm_select source2.publish("msg", routing_key: "") ch2.wait_for_confirms expect(dst_queue.message_count).to eq 1 end end it "recovers allocated channel ids" do with_open do |c| q = "queue#{Time.now.to_i}" 10.times { c.create_channel } expect(c.queue_exists?(q)).to eq false close_all_connections! wait_on_loss_and_recovery_of { channels.any? } # make sure the connection isn't closed shortly after # due to "second 'channel.open' seen". MK. expect(c).to be_open sleep 0.1 expect(c).to be_open sleep 0.1 expect(c).to be_open end end it "recovers consumers" do with_open do |c| delivered = false ch = c.create_channel q = ch.queue("", :exclusive => true) q.subscribe do |_, _, _| delivered = true end close_all_connections! wait_on_loss_and_recovery_of { connections.any? } expect(ch).to be_open q.publish("") poll_until { delivered } end end it "recovers all consumers" do n = 1024 with_open do |c| ch = c.create_channel q = ch.queue("", :exclusive => true) n.times { q.subscribe { |_, _, _| } } close_all_connections! wait_on_loss_and_recovery_of { connections.any? } expect(ch).to be_open sleep 0.5 expect(q.consumer_count).to eq n end end it "recovers all queues" do n = 256 qs = [] with_open do |c| ch = c.create_channel n.times do qs << ch.queue("", :exclusive => true) end close_all_connections! wait_on_loss_and_recovery_of { queue_names.include?(qs.first.name) } sleep 0.5 expect(ch).to be_open qs.each do |q| ch.queue_declare(q.name, :passive => true) end end end it "tries to recover for a given number of attempts" do pending "Need a fix for https://github.com/ruby-amqp/bunny/issues/408" with_recovery_attempts_limited_to(2) do |c| close_all_connections! wait_on_loss_and_recovery_of { connections.any? } close_all_connections! wait_on_loss_and_recovery_of { connections.any? } close_all_connections! sleep(recovery_interval + 0.5) expect(connections).to be_empty end end def exchange_names_in_vhost(vhost) http_client.list_exchanges(vhost).map {|e| e["name"]} end def connections http_client.list_connections end def channels http_client.list_channels end def queue_names http_client.list_queues.map {|q| q["name"]} end def close_all_connections! connections.each do |conn_info| close_ignoring_permitted_exceptions(conn_info.name) end end def close_ignoring_permitted_exceptions(connection_name) http_client.close_connection(connection_name) rescue Bunny::ConnectionForced end def wait_on_loss_and_recovery_of(&probe) poll_while &probe poll_until &probe end def poll_while(&probe) Timeout::timeout(10) { sleep 0.1 while probe[] } end def poll_until(&probe) Timeout::timeout(10) { sleep 0.1 until probe[] } end def with_open(c = Bunny.new(network_recovery_interval: recovery_interval, recover_from_connection_close: true, logger: logger), &block) c.start block.call(c) ensure c.close end def with_open_multi_host(&block) c = Bunny.new(hosts: ["127.0.0.1", "localhost"], network_recovery_interval: recovery_interval, recover_from_connection_close: true, logger: logger) with_open(c, &block) end def with_open_multi_broken_host(&block) c = Bunny.new(hosts: ["broken", "127.0.0.1", "localhost"], hosts_shuffle_strategy: Proc.new { |hosts| hosts }, # We do not shuffle for these tests so we always hit the broken host network_recovery_interval: recovery_interval, recover_from_connection_close: true, logger: logger) with_open(c, &block) end def with_recovery_attempts_limited_to(attempts = 3, &block) c = Bunny.new(recover_from_connection_close: true, network_recovery_interval: recovery_interval, recovery_attempts: attempts, logger: logger) with_open(c, &block) end def ensure_queue_recovery(ch, q) ch.confirm_select q.purge x = ch.default_exchange x.publish("msg", routing_key: q.name) ch.wait_for_confirms expect(q.message_count).to eq 1 q.purge end def ensure_queue_binding_recovery(ch, x, q, routing_key = "") ch.confirm_select q.purge x.publish("msg", routing_key: routing_key) ch.wait_for_confirms expect(q.message_count).to eq 1 q.purge end end bunny-2.6.1/spec/higher_level_api/integration/basic_qos_spec.rb0000644000004100000410000000313713015255277024763 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Channel, "#prefetch" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close end context "with a positive integer < 65535" do it "sets that prefetch level via basic.qos" do ch = connection.create_channel expect(ch.prefetch_count).not_to eq 10 expect(ch.prefetch_global).to be_nil expect(ch.prefetch(10)).to be_instance_of(AMQ::Protocol::Basic::QosOk) expect(ch.prefetch_count).to eq 10 expect(ch.prefetch_global).to be false end it "sets that prefetch global via basic.qos" do ch = connection.create_channel expect(ch.prefetch_count).not_to eq 42 expect(ch.prefetch_global).to be_nil expect(ch.prefetch(42, true)).to be_instance_of(AMQ::Protocol::Basic::QosOk) expect(ch.prefetch_count).to eq 42 expect(ch.prefetch_global).to be true end end context "with a positive integer > 65535" do it "raises an ArgumentError" do ch = connection.create_channel expect { ch.prefetch(100_000) }.to raise_error( ArgumentError, "prefetch count must be no greater than #{Bunny::Channel::MAX_PREFETCH_COUNT}, given: 100000" ) end end context "with a negative integer" do it "raises an ArgumentError" do ch = connection.create_channel expect { ch.prefetch(-2) }.to raise_error( ArgumentError, "prefetch count must be a positive integer, given: -2" ) end end end bunny-2.6.1/spec/higher_level_api/integration/basic_get_spec.rb0000644000004100000410000000267213015255277024743 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Queue, "#pop" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatically_recover => false) c.start c end after :each do connection.close if connection.open? end context "with all defaults" do it "fetches a messages which is automatically acknowledged" do ch = connection.create_channel q = ch.queue("", :exclusive => true) x = ch.default_exchange msg = "xyzzy" x.publish(msg, :routing_key => q.name) sleep(0.5) get_ok, properties, content = q.pop expect(get_ok).to be_kind_of(Bunny::GetResponse) expect(properties).to be_kind_of(Bunny::MessageProperties) expect(properties.content_type).to eq("application/octet-stream") expect(get_ok.routing_key).to eq(q.name) expect(get_ok.delivery_tag).to be_kind_of(Bunny::VersionedDeliveryTag) expect(content).to eq(msg) expect(q.message_count).to eq 0 ch.close end end context "with an empty queue" do it "returns an empty response" do ch = connection.create_channel q = ch.queue("", :exclusive => true) q.purge get_empty, properties, content = q.pop expect(get_empty).to eq(nil) expect(properties).to eq(nil) expect(content).to eq(nil) expect(q.message_count).to eq 0 ch.close end end end bunny-2.6.1/spec/higher_level_api/integration/basic_consume_with_objects_spec.rb0000644000004100000410000000246213015255277030376 0ustar www-datawww-datarequire "spec_helper" require "set" describe Bunny::Queue, "#subscribe_with" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end context "with explicit acknowledgements mode" do class ExampleConsumer < Bunny::Consumer def cancelled? @cancelled end def handle_cancellation(_) @cancelled = true end def call(delivery_info, metadata, payload) # no-op end end # demonstrates that manual acknowledgement mode is actually # used. MK. it "requeues messages on channel closure" do ch1 = connection.create_channel ch2 = connection.create_channel q1 = ch1.queue("bunny.tests.consumer_object1", :exclusive => true) q2 = ch2.queue("bunny.tests.consumer_object1", :exclusive => true) ec = ExampleConsumer.new(ch1, q1, "", false) x = ch2.default_exchange t = Thread.new do 50.times do x.publish("hello", :routing_key => q2.name) end end t.abort_on_exception = true q1.subscribe_with(ec, :manual_ack => true) sleep 2 ch1.close expect(q2.message_count).to eq 50 end end end bunny-2.6.1/spec/higher_level_api/integration/read_only_consumer_spec.rb0000644000004100000410000000352713015255277026712 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Queue, "#subscribe" do let(:publisher_connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end let(:consumer_connection) do c = Bunny.new(:user => "bunny_reader", :password => "reader_password", :vhost => "bunny_testbed") c.start c end after :each do publisher_connection.close if publisher_connection.open? consumer_connection.close if consumer_connection.open? end context "with automatic acknowledgement mode" do let(:queue_name) { "bunny.basic_consume#{rand}" } it "registers the consumer" do delivered_keys = [] delivered_data = [] ch = publisher_connection.create_channel # declare the queue because the read-only user won't be able to issue # queue.declare q = ch.queue(queue_name, :auto_delete => true, :durable => false) t = Thread.new do # give the main thread a bit of time to declare the queue sleep 0.5 ch = consumer_connection.create_channel # this connection is read only, use passive declare to only get # a reference to the queue q = ch.queue(queue_name, :auto_delete => true, :durable => false, :passive => true) q.subscribe(:exclusive => false) do |delivery_info, properties, payload| delivered_keys << delivery_info.routing_key delivered_data << payload end end t.abort_on_exception = true sleep 0.5 x = ch.default_exchange x.publish("hello", :routing_key => queue_name) sleep 0.7 expect(delivered_keys).to include(queue_name) expect(delivered_data).to include("hello") expect(ch.queue(queue_name, :auto_delete => true, :durable => false).message_count).to eq 0 ch.close end end end # describe bunny-2.6.1/spec/higher_level_api/integration/with_channel_spec.rb0000644000004100000410000000077713015255277025472 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Channel, "#with_channel" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end it "closes if the block throws an exception" do ch = nil begin connection.with_channel do |wch| ch = wch raise Exception.new end rescue Exception end expect(ch).to be_closed end end bunny-2.6.1/spec/higher_level_api/integration/basic_consume_spec.rb0000644000004100000410000002411013015255277025624 0ustar www-datawww-datarequire "spec_helper" require "set" describe Bunny::Queue, "#subscribe" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end context "with automatic acknowledgement mode" do let(:queue_name) { "bunny.basic_consume#{rand}" } it "registers the consumer" do delivered_keys = [] delivered_data = [] t = Thread.new do ch = connection.create_channel q = ch.queue(queue_name, :auto_delete => true, :durable => false) q.subscribe(:exclusive => false, :manual_ack => false) do |delivery_info, properties, payload| delivered_keys << delivery_info.routing_key delivered_data << payload end end t.abort_on_exception = true sleep 0.5 ch = connection.create_channel x = ch.default_exchange x.publish("hello", :routing_key => queue_name) sleep 0.7 expect(delivered_keys).to include(queue_name) expect(delivered_data).to include("hello") expect(ch.queue(queue_name, :auto_delete => true, :durable => false).message_count).to eq 0 ch.close end context "with a single consumer" do let(:queue_name) { "bunny.basic_consume#{rand}" } it "provides delivery tag access" do delivery_tags = SortedSet.new cch = connection.create_channel q = cch.queue(queue_name, :auto_delete => true, :durable => false) q.subscribe(:exclusive => false, :manual_ack => false) do |delivery_info, properties, payload| delivery_tags << delivery_info.delivery_tag end sleep 0.5 ch = connection.create_channel x = ch.default_exchange 100.times do x.publish("hello", :routing_key => queue_name) end sleep 1.0 expect(delivery_tags).to eq SortedSet.new(Range.new(1, 100).to_a) expect(ch.queue(queue_name, :auto_delete => true, :durable => false).message_count).to eq 0 ch.close end end context "with multiple consumers on the same channel" do let(:queue_name) { "bunny.basic_consume#{rand}" } it "provides delivery tag access" do delivery_tags = SortedSet.new cch = connection.create_channel q = cch.queue(queue_name, :auto_delete => true, :durable => false) 7.times do q.subscribe(:exclusive => false, :manual_ack => false) do |delivery_info, properties, payload| delivery_tags << delivery_info.delivery_tag end end sleep 1.0 ch = connection.create_channel x = ch.default_exchange 100.times do x.publish("hello", :routing_key => queue_name) end sleep 1.5 expect(delivery_tags).to eq SortedSet.new(Range.new(1, 100).to_a) expect(ch.queue(queue_name, :auto_delete => true, :durable => false).message_count).to eq 0 ch.close end end end context "with manual acknowledgement mode" do let(:queue_name) { "bunny.basic_consume#{rand}" } it "register a consumer with manual acknowledgements mode" do delivered_keys = [] delivered_data = [] t = Thread.new do ch = connection.create_channel q = ch.queue(queue_name, :auto_delete => true, :durable => false) q.subscribe(:exclusive => false, :manual_ack => true) do |delivery_info, properties, payload| delivered_keys << delivery_info.routing_key delivered_data << payload ch.ack(delivery_info.delivery_tag) end end t.abort_on_exception = true sleep 0.5 ch = connection.create_channel x = ch.default_exchange x.publish("hello", :routing_key => queue_name) sleep 0.7 expect(delivered_keys).to include(queue_name) expect(delivered_data).to include("hello") expect(ch.queue(queue_name, :auto_delete => true, :durable => false).message_count).to eq 0 ch.close end end ENV.fetch("RUNS", 20).to_i.times do |i| context "with a queue that already has messages (take #{i})" do let(:queue_name) { "bunny.basic_consume#{rand}" } it "registers the consumer" do delivered_keys = [] delivered_data = [] ch = connection.create_channel q = ch.queue(queue_name, :auto_delete => true, :durable => false) x = ch.default_exchange 100.times do x.publish("hello", :routing_key => queue_name) end sleep 0.7 expect(q.message_count).to be > 50 t = Thread.new do ch = connection.create_channel q = ch.queue(queue_name, :auto_delete => true, :durable => false) q.subscribe(:exclusive => false, :manual_ack => false) do |delivery_info, properties, payload| delivered_keys << delivery_info.routing_key delivered_data << payload end end t.abort_on_exception = true sleep 0.5 expect(delivered_keys).to include(queue_name) expect(delivered_data).to include("hello") expect(ch.queue(queue_name, :auto_delete => true, :durable => false).message_count).to eq 0 ch.close end end end # 20.times context "after consumer pool has already been shut down" do let(:queue_name) { "bunny.basic_consume#{rand}" } it "registers the consumer" do delivered_keys = [] delivered_data = [] t = Thread.new do ch = connection.create_channel q = ch.queue(queue_name) c1 = q.subscribe(:exclusive => false, :manual_ack => false, :block => false) do |delivery_info, properties, payload| end c1.cancel c2 = q.subscribe(:exclusive => false, :manual_ack => false, :block => false) do |delivery_info, properties, payload| delivered_keys << delivery_info.routing_key delivered_data << payload end c2.cancel q.subscribe(:exclusive => false, :manual_ack => false, :block => true) do |delivery_info, properties, payload| delivered_keys << delivery_info.routing_key delivered_data << payload end end t.abort_on_exception = true sleep 0.5 ch = connection.create_channel x = ch.default_exchange x.publish("hello", :routing_key => queue_name) sleep 0.7 expect(delivered_keys).to include(queue_name) expect(delivered_data).to include("hello") expect(ch.queue(queue_name).message_count).to eq 0 ch.queue_delete(queue_name) ch.close end end context "with uncaught exceptions in delivery handler" do context "and defined exception handler" do let(:queue_name) { "bunny.basic_consume#{rand}" } it "uses exception handler" do caught = nil t = Thread.new do ch = connection.create_channel q = ch.queue(queue_name, :auto_delete => true, :durable => false) ch.on_uncaught_exception do |e, consumer| caught = e end q.subscribe(:exclusive => false, :manual_ack => false) do |delivery_info, properties, payload| raise RuntimeError.new(queue_name) end end t.abort_on_exception = true sleep 0.5 ch = connection.create_channel x = ch.default_exchange x.publish("hello", :routing_key => queue_name) sleep 0.5 expect(caught.message).to eq queue_name ch.close end end context "and default exception handler" do let(:queue_name) { "bunny.basic_consume#{rand}" } it "uses exception handler" do caughts = [] t = Thread.new do allow(connection.logger).to receive(:error) { |x| caughts << x } ch = connection.create_channel q = ch.queue(queue_name, :auto_delete => true, :durable => false) q.subscribe(:exclusive => false, :manual_ack => false) do |delivery_info, properties, payload| raise RuntimeError.new(queue_name) end end t.abort_on_exception = true sleep 0.5 ch = connection.create_channel x = ch.default_exchange 5.times { x.publish("hello", :routing_key => queue_name) } sleep 1.5 expect(caughts.size).to eq(5) ch.close end end context "with a single consumer" do let(:queue_name) { "bunny.basic_consume#{rand}" } it "provides delivery tag access" do delivery_tags = SortedSet.new cch = connection.create_channel q = cch.queue(queue_name, :auto_delete => true, :durable => false) q.subscribe(:exclusive => false, :manual_ack => false) do |delivery_info, properties, payload| delivery_tags << delivery_info.delivery_tag end sleep 0.5 ch = connection.create_channel x = ch.default_exchange 100.times do x.publish("hello", :routing_key => queue_name) end sleep 1.0 expect(delivery_tags).to eq SortedSet.new(Range.new(1, 100).to_a) expect(ch.queue(queue_name, :auto_delete => true, :durable => false).message_count).to eq 0 ch.close end end context "with multiple consumers on the same channel" do let(:queue_name) { "bunny.basic_consume#{rand}" } it "provides delivery tag access" do delivery_tags = SortedSet.new cch = connection.create_channel q = cch.queue(queue_name, :auto_delete => true, :durable => false) 7.times do q.subscribe(:exclusive => false, :manual_ack => false) do |delivery_info, properties, payload| delivery_tags << delivery_info.delivery_tag end end sleep 1.0 ch = connection.create_channel x = ch.default_exchange 100.times do x.publish("hello", :routing_key => queue_name) end sleep 1.5 expect(delivery_tags).to eq SortedSet.new(Range.new(1, 100).to_a) expect(ch.queue(queue_name, :auto_delete => true, :durable => false).message_count).to eq 0 ch.close end end end end # describe bunny-2.6.1/spec/higher_level_api/integration/tls_connection_spec.rb0000644000004100000410000001023313015255277026034 0ustar www-datawww-data# -*- coding: utf-8 -*- require "spec_helper" unless ENV["CI"] shared_examples_for "successful TLS connection" do it "succeeds" do expect(connection).to be_tls ch = connection.create_channel q = ch.queue("", :exclusive => true) x = ch.default_exchange x.publish("xyzzy", :routing_key => q.name). publish("xyzzy", :routing_key => q.name). publish("xyzzy", :routing_key => q.name). publish("xyzzy", :routing_key => q.name) sleep 0.5 expect(q.message_count).to eq 4 i = 0 q.subscribe do |delivery_info, _, payload| i += 1 end sleep 1.0 expect(i).to eq 4 expect(q.message_count).to eq 0 ch.close end end describe "TLS connection to RabbitMQ with client certificates" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :tls => true, :tls_cert => "spec/tls/client_certificate.pem", :tls_key => "spec/tls/client_key.pem", :tls_ca_certificates => ["./spec/tls/ca_certificate.pem"], :verify_peer => false) c.start c end after :each do connection.close end include_examples "successful TLS connection" end describe "TLS connection to RabbitMQ without client certificates" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :tls => true, :tls_ca_certificates => ["./spec/tls/ca_certificate.pem"], :verify_peer => false) c.start c end after :each do connection.close end include_examples "successful TLS connection" end describe "TLS connection to RabbitMQ with a connection string" do let(:connection) do c = Bunny.new("amqps://bunny_gem:bunny_password@127.0.0.1/bunny_testbed", :tls_cert => "spec/tls/client_certificate.pem", :tls_key => "spec/tls/client_key.pem", :tls_ca_certificates => ["./spec/tls/ca_certificate.pem"], :verify_peer => false) c.start c end after :each do connection.close end include_examples "successful TLS connection" end describe "TLS connection to RabbitMQ with a connection string and w/o client certificate and key" do let(:connection) do c = Bunny.new("amqps://bunny_gem:bunny_password@127.0.0.1/bunny_testbed", :tls_ca_certificates => ["./spec/tls/ca_certificate.pem"], :verify_peer => false) c.start c end after :each do connection.close end include_examples "successful TLS connection" end describe "TLS connection to RabbitMQ with client certificates provided inline" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :tls => true, :tls_cert => File.read("./spec/tls/client_certificate.pem"), :tls_key => File.read("./spec/tls/client_key.pem"), :tls_ca_certificates => ["./spec/tls/ca_certificate.pem"], :verify_peer => false) c.start c end after :each do connection.close end include_examples "successful TLS connection" end describe "TLS connection to RabbitMQ with tls_version TLSv1 specified" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :tls => true, :tls_protocol => :TLSv1, :tls_ca_certificates => ["./spec/tls/ca_certificate.pem"], :verify_peer => false) c.start c end after :each do connection.close end include_examples "successful TLS connection" it "connects using TLSv1" do expect(connection.transport.socket.ssl_version).to eq "TLSv1" end end end bunny-2.6.1/spec/higher_level_api/integration/channel_open_spec.rb0000644000004100000410000000246313015255277025452 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Channel, "when opened" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close end context "without explicitly provided id" do it "gets an allocated id and is successfully opened" do expect(connection).to be_connected ch = connection.create_channel expect(ch).to be_open expect(ch.id).to be > 0 end end context "with an explicitly provided id = 0" do it "raises ArgumentError" do expect(connection).to be_connected expect { connection.create_channel(0) }.to raise_error(ArgumentError) end end context "with explicitly provided id" do it "uses that id and is successfully opened" do ch = connection.create_channel(767) expect(connection).to be_connected expect(ch).to be_open expect(ch.id).to eq 767 end end context "with explicitly provided id that is already taken" do it "reuses the channel that is already opened" do ch = connection.create_channel(767) expect(connection).to be_connected expect(ch).to be_open expect(ch.id).to eq 767 expect(connection.create_channel(767)).to eq ch end end end bunny-2.6.1/spec/higher_level_api/integration/merry_go_round_spec.rb0000644000004100000410000000355213015255277026053 0ustar www-datawww-datarequire "spec_helper" describe "A message that is proxied by multiple intermediate consumers" do let(:c1) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end let(:c2) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end let(:c3) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end let(:c4) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end let(:c5) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do [c1, c2, c3, c4, c5].each do |c| c.close if c.open? end end # message flow is as follows: # # x => q4 => q3 => q2 => q1 => xs (results) it "reaches its final destination" do n = 10000 xs = [] ch1 = c1.create_channel q1 = ch1.queue("", :exclusive => true) q1.subscribe do |_, _, payload| xs << payload end ch2 = c2.create_channel q2 = ch2.queue("", :exclusive => true) q2.subscribe do |_, _, payload| q1.publish(payload) end ch3 = c3.create_channel q3 = ch2.queue("", :exclusive => true) q3.subscribe do |_, _, payload| q2.publish(payload) end ch4 = c4.create_channel q4 = ch2.queue("", :exclusive => true) q4.subscribe do |_, _, payload| q3.publish(payload) end ch5 = c5.create_channel x = ch5.default_exchange n.times do |i| x.publish("msg #{i}", :routing_key => q4.name) end t = n / 1000 * 3.0 puts "About to sleep for #{t} seconds..." sleep(t) expect(xs.size).to eq n expect(xs.last).to eq "msg #{n - 1}" end end bunny-2.6.1/spec/higher_level_api/integration/basic_cancel_spec.rb0000644000004100000410000000720013015255277025401 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Consumer, "#cancel" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end context "with a non-blocking consumer" do let(:queue_name) { "bunny.queues.#{rand}" } it "cancels the consumer" do delivered_data = [] t = Thread.new do ch = connection.create_channel q = ch.queue(queue_name, :auto_delete => true, :durable => false) consumer = q.subscribe(:block => false) do |_, _, payload| delivered_data << payload end expect(consumer.consumer_tag).not_to be_nil cancel_ok = consumer.cancel expect(cancel_ok.consumer_tag).to eq consumer.consumer_tag ch.close end t.abort_on_exception = true sleep 0.5 ch = connection.create_channel ch.default_exchange.publish("", :routing_key => queue_name) sleep 0.7 expect(delivered_data).to be_empty end end context "with a blocking consumer" do let(:queue_name) { "bunny.queues.#{rand}" } it "cancels the consumer" do delivered_data = [] consumer = nil t = Thread.new do ch = connection.create_channel q = ch.queue(queue_name, :auto_delete => true, :durable => false) consumer = Bunny::Consumer.new(ch, q) consumer.on_delivery do |_, _, payload| delivered_data << payload end q.subscribe_with(consumer, :block => false) end t.abort_on_exception = true sleep 1.0 consumer.cancel sleep 1.0 ch = connection.create_channel ch.default_exchange.publish("", :routing_key => queue_name) sleep 0.7 expect(delivered_data).to be_empty end end context "with a worker pool shutdown timeout configured" do let(:queue_name) { "bunny.queues.#{rand}" } it "processes the message if processing completes within the timeout" do delivered_data = [] consumer = nil t = Thread.new do ch = connection.create_channel(nil, 1, false, 5) q = ch.queue(queue_name, :auto_delete => true, :durable => false) consumer = Bunny::Consumer.new(ch, q) consumer.on_delivery do |_, _, payload| sleep 2 delivered_data << payload end q.subscribe_with(consumer, :block => false) end t.abort_on_exception = true sleep 1.0 ch = connection.create_channel ch.confirm_select ch.default_exchange.publish("", :routing_key => queue_name) ch.wait_for_confirms sleep 0.5 consumer.cancel sleep 1.0 expect(delivered_data).to_not be_empty end it "kills the consumer if processing takes longer than the timeout" do delivered_data = [] consumer = nil t = Thread.new do ch = connection.create_channel(nil, 1, false, 1) q = ch.queue(queue_name, :auto_delete => true, :durable => false) consumer = Bunny::Consumer.new(ch, q) consumer.on_delivery do |_, _, payload| sleep 3 delivered_data << payload end q.subscribe_with(consumer, :block => false) end t.abort_on_exception = true sleep 1.0 ch = connection.create_channel ch.confirm_select ch.default_exchange.publish("", :routing_key => queue_name) ch.wait_for_confirms sleep 0.5 consumer.cancel sleep 1.0 expect(delivered_data).to be_empty end end end bunny-2.6.1/spec/higher_level_api/integration/queue_declare_spec.rb0000644000004100000410000001174313015255277025625 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Queue do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end context "when queue name is specified" do let(:name) { "a queue declared at #{Time.now.to_i}" } it "declares a new queue with that name" do ch = connection.create_channel q = ch.queue(name) expect(q.name).to eq name q.delete ch.close end it "caches that queue" do ch = connection.create_channel q = ch.queue(name) expect(ch.queue(name).object_id).to eq q.object_id q.delete ch.close end end context "when queue name is passed on as an empty string" do it "uses server-assigned queue name" do ch = connection.create_channel q = ch.queue("") expect(q.name).not_to be_empty expect(q.name).to match /^amq.gen.+/ expect(q).to be_server_named q.delete ch.close end end context "when queue is declared as durable" do it "declares it as durable" do ch = connection.create_channel q = ch.queue("bunny.tests.queues.durable", :durable => true) expect(q).to be_durable expect(q).not_to be_auto_delete expect(q).not_to be_exclusive expect(q.arguments).to be_nil q.delete ch.close end end context "when queue is declared as exclusive" do it "declares it as exclusive" do ch = connection.create_channel q = ch.queue("bunny.tests.queues.exclusive", :exclusive => true) expect(q).to be_exclusive expect(q).not_to be_durable q.delete ch.close end end context "when queue is declared as auto-delete" do it "declares it as auto-delete" do ch = connection.create_channel q = ch.queue("bunny.tests.queues.auto-delete", :auto_delete => true) expect(q).to be_auto_delete expect(q).not_to be_exclusive expect(q).not_to be_durable q.delete ch.close end end context "when queue is declared with a different set of attributes" do it "raises an exception" do ch = connection.create_channel q = ch.queue("bunny.tests.queues.auto-delete", :auto_delete => true, :durable => false) expect { # force re-declaration ch.queue_declare("bunny.tests.queues.auto-delete", :auto_delete => false, :durable => true) }.to raise_error(Bunny::PreconditionFailed) expect(ch).to be_closed end end context "when queue is declared with message TTL" do let(:args) do # in ms {"x-message-ttl" => 1000} end it "causes all messages in it to have a TTL" do ch = connection.create_channel q = ch.queue("bunny.tests.queues.with-arguments.ttl", :arguments => args, :exclusive => true) expect(q.arguments).to eq args q.publish("xyzzy") sleep 0.1 expect(q.message_count).to eq 1 sleep 1.5 expect(q.message_count).to eq 0 ch.close end end context "when queue is declared with priorities" do let(:args) do {"x-max-priority" => 5} end it "enables priority implementation" do ch = connection.create_channel ch.confirm_select q = ch.queue("bunny.tests.queues.with-arguments.priority", :arguments => args, :exclusive => true) expect(q.arguments).to eq args q.publish("xyzzy") ch.wait_for_confirms sleep 0.1 # this test only does sanity checking, # without trying to actually test prioritisation. # # added to guard against issues such as # https://github.com/rabbitmq/rabbitmq-server/issues/488 expect(q.message_count).to eq 1 ch.close end end describe "#queue_exists?" do context "when a queue exists" do it "returns true" do ch = connection.create_channel q = ch.queue("", :exlusive => true) expect(connection.queue_exists?(q.name)).to eq true end end context "when a queue DOES NOT exist" do it "returns false" do expect(connection.queue_exists?("suf89u9a4jo3ndnakls##{Time.now.to_i}")).to eq false end end end unless ENV["CI"] # requires RabbitMQ 3.1+ context "when queue is declared with bounded length" do let(:n) { 10 } let(:args) do # in ms {"x-max-length" => n} end # see http://www.rabbitmq.com/maxlength.html for more info it "causes the queue to be bounded" do ch = connection.create_channel q = ch.queue("bunny.tests.queues.with-arguments.max-length", :arguments => args, :exclusive => true) expect(q.arguments).to eq args (n * 10).times do q.publish("xyzzy") end expect(q.message_count).to eq n (n * 5).times do q.publish("xyzzy") end expect(q.message_count).to eq n q.delete ch.close end end end end bunny-2.6.1/spec/higher_level_api/integration/dead_lettering_spec.rb0000644000004100000410000000415713015255277025775 0ustar www-datawww-datarequire "spec_helper" describe "A message" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end it "is considered to be dead-lettered when it is rejected without requeueing" do ch = connection.create_channel x = ch.fanout("amq.fanout") dlx = ch.fanout("bunny.tests.dlx.exchange") q = ch.queue("", :exclusive => true, :arguments => {"x-dead-letter-exchange" => dlx.name}).bind(x) # dead letter queue dlq = ch.queue("", :exclusive => true).bind(dlx) x.publish("") sleep 0.2 delivery_info, _, _ = q.pop(:manual_ack => true) expect(dlq.message_count).to be_zero ch.nack(delivery_info.delivery_tag) sleep 0.2 expect(q.message_count).to be_zero delivery, properties, body = dlq.pop ds = properties.headers["x-death"] expect(ds).not_to be_empty expect(ds.first["reason"]).to eq("rejected") dlx.delete end it "is considered to be dead-lettered when it expires" do ch = connection.create_channel x = ch.fanout("amq.fanout") dlx = ch.fanout("bunny.tests.dlx.exchange") q = ch.queue("", :exclusive => true, :arguments => {"x-dead-letter-exchange" => dlx.name, "x-message-ttl" => 100}).bind(x) # dead letter queue dlq = ch.queue("", :exclusive => true).bind(dlx) x.publish("") sleep 0.2 expect(q.message_count).to be_zero expect(dlq.message_count).to eq 1 dlx.delete end it "carries the x-death header" do ch = connection.create_channel x = ch.fanout("amq.fanout") dlx = ch.fanout("bunny.tests.dlx.exchange") q = ch.queue("", :exclusive => true, :arguments => {"x-dead-letter-exchange" => dlx.name, "x-message-ttl" => 100}).bind(x) # dead letter queue dlq = ch.queue("", :exclusive => true).bind(dlx) x.publish("") sleep 0.2 delivery, properties, body = dlq.pop ds = properties.headers["x-death"] expect(ds).not_to be_empty expect(ds.first["reason"]).to eq("expired") dlx.delete end end bunny-2.6.1/spec/higher_level_api/integration/queue_bind_spec.rb0000644000004100000410000000474213015255277025143 0ustar www-datawww-datarequire "spec_helper" describe "A client-named", Bunny::Queue do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end it "can be bound to a pre-declared exchange" do ch = connection.create_channel q = ch.queue("bunny.tests.queues.client-named#{rand}", :exclusive => true) expect(q).not_to be_server_named expect(q.bind("amq.fanout")).to eq q ch.close end it "can be unbound from a pre-declared exchange" do ch = connection.create_channel q = ch.queue("bunny.tests.queues.client-named#{rand}", :exclusive => true) expect(q).not_to be_server_named q.bind("amq.fanout") expect(q.unbind("amq.fanout")).to eq q ch.close end it "can be bound to a custom exchange" do ch = connection.create_channel q = ch.queue("bunny.tests.queues.client-named#{rand}", :exclusive => true) x = ch.fanout("bunny.tests.exchanges.fanout#{rand}") expect(q.bind(x)).to eq q x.delete ch.close end it "can be unbound from a custom exchange" do ch = connection.create_channel q = ch.queue("bunny.tests.queues.client-named#{rand}", :exclusive => true) expect(q).not_to be_server_named x = ch.fanout("bunny.tests.fanout", :auto_delete => true, :durable => false) q.bind(x) expect(q.unbind(x)).to eq q ch.close end end describe "A server-named", Bunny::Queue do let(:connection) do c = Bunny.new c.start c end it "can be bound to a pre-declared exchange" do ch = connection.create_channel q = ch.queue("", :exclusive => true) expect(q).to be_server_named expect(q.bind("amq.fanout")).to eq q ch.close end it "can be unbound from a pre-declared exchange" do ch = connection.create_channel q = ch.queue("", :exclusive => true) expect(q).to be_server_named q.bind("amq.fanout") expect(q.unbind("amq.fanout")).to eq q ch.close end it "can be bound to a custom exchange" do ch = connection.create_channel q = ch.queue("", :exclusive => true) x = ch.fanout("bunny.tests.exchanges.fanout#{rand}") expect(q.bind(x)).to eq q x.delete ch.close end it "can be bound from a custom exchange" do ch = connection.create_channel q = ch.queue("", :exclusive => true) name = "bunny.tests.exchanges.fanout#{rand}" x = ch.fanout(name) q.bind(x) expect(q.unbind(name)).to eq q x.delete ch.close end end bunny-2.6.1/spec/higher_level_api/integration/sender_selected_distribution_spec.rb0000644000004100000410000000206413015255277030745 0ustar www-datawww-datarequire "spec_helper" describe "Sender-selected distribution" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end it "lets publishers specify additional routing keys using CC and BCC headers" do ch = connection.create_channel x = ch.direct("bunny.tests.ssd.exchange") q1 = ch.queue("", :exclusive => true).bind(x, :routing_key => "one") q2 = ch.queue("", :exclusive => true).bind(x, :routing_key => "two") q3 = ch.queue("", :exclusive => true).bind(x, :routing_key => "three") q4 = ch.queue("", :exclusive => true).bind(x, :routing_key => "four") n = 10 n.times do |i| x.publish("Message #{i}", :routing_key => "one", :headers => {"CC" => ["two", "three"]}) end sleep 0.5 expect(q1.message_count).to eq n expect(q2.message_count).to eq n expect(q3.message_count).to eq n expect(q4.message_count).to be_zero x.delete end end bunny-2.6.1/spec/higher_level_api/integration/publisher_confirms_spec.rb0000644000004100000410000001212413015255277026711 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Channel do after :each do connection.close if connection.open? end let(:n) { 200 } shared_examples "publish confirms" do context "when publishing with confirms enabled" do it "increments delivery index" do ch = connection.create_channel expect(ch).not_to be_using_publisher_confirmations ch.confirm_select expect(ch).to be_using_publisher_confirmations q = ch.queue("", :exclusive => true) x = ch.default_exchange n.times do x.publish("xyzzy", :routing_key => q.name) end expect(ch.next_publish_seq_no).to eq n + 1 expect(ch.wait_for_confirms).to eq true sleep 0.25 expect(q.message_count).to eq n q.purge ch.close end describe "#wait_for_confirms" do it "should not hang when all the publishes are confirmed" do ch = connection.create_channel expect(ch).not_to be_using_publisher_confirmations ch.confirm_select expect(ch).to be_using_publisher_confirmations q = ch.queue("", :exclusive => true) x = ch.default_exchange n.times do x.publish("xyzzy", :routing_key => q.name) end expect(ch.next_publish_seq_no).to eq n + 1 expect(ch.wait_for_confirms).to eq true sleep 0.25 expect { Bunny::Timeout.timeout(2) do expect(ch.wait_for_confirms).to eq true end }.not_to raise_error end it "raises an error when called on a closed channel" do ch = connection.create_channel ch.confirm_select ch.close expect { ch.wait_for_confirms }.to raise_error(Bunny::ChannelAlreadyClosed) end end context "when some of the messages get nacked" do it "puts the nacks in the nacked_set" do ch = connection.create_channel expect(ch).not_to be_using_publisher_confirmations ch.confirm_select expect(ch).to be_using_publisher_confirmations q = ch.queue("", :exclusive => true) x = ch.default_exchange n.times do x.publish("xyzzy", :routing_key => q.name) end #be sneaky to simulate a nack nacked_tag = nil ch.instance_variable_get(:@unconfirmed_set_mutex).synchronize do expect(ch.unconfirmed_set).to_not be_empty nacked_tag = ch.unconfirmed_set.reduce(ch.next_publish_seq_no - 1) { |lowest, i| i < lowest ? i : lowest } ch.handle_ack_or_nack(nacked_tag, false, true) end expect(ch.nacked_set).not_to be_empty expect(ch.nacked_set).to include(nacked_tag) expect(ch.next_publish_seq_no).to eq n + 1 expect(ch.wait_for_confirms).to eq false expect(ch.nacked_set).not_to be_empty expect(ch.nacked_set).to include(nacked_tag) sleep 0.25 expect(q.message_count).to eq n q.purge ch.close end end end end context "with a multi-threaded connection" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :continuation_timeout => 10000) c.start c end include_examples "publish confirms" it "returns only when all confirmations for publishes are received" do ch = connection.create_channel operations_log = [] operations_log_mutex = Mutex.new acks_received = Queue.new log_acks = proc do |tag, _, is_nack| operations_log_mutex.synchronize do operation = "#{'n' if is_nack}ack_#{tag}" operations_log << operation unless operations_log.include?(operation) end acks_received << true end ch.confirm_select(log_acks) x = ch.default_exchange q = ch.temporary_queue x.publish('msg', routing_key: q.name) # wait for the confirmation to arrive acks_received.pop # artificially simulate a slower ack. the test should work properly even # without this patch, but it's here just to be sure we catch it. def (x.channel).handle_ack_or_nack(delivery_tag_before_offset, multiple, nack) sleep 0.1 super end x.publish('msg', routing_key: q.name) x.publish('msg', routing_key: q.name) if x.wait_for_confirms operations_log_mutex.synchronize do operations_log << 'all_confirmed' end end # wait for all the confirmations to arrive acks_received.pop acks_received.pop expect(operations_log).to eq([ 'ack_1', 'ack_2', 'ack_3', 'all_confirmed', ]) end end context "with a single-threaded connection" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :continuation_timeout => 10000, :threaded => false) c.start c end include_examples "publish confirms" end end bunny-2.6.1/spec/higher_level_api/integration/predeclared_exchanges_spec.rb0000644000004100000410000000076613015255277027324 0ustar www-datawww-datarequire "spec_helper" describe "amq.* exchanges" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end it "are predeclared" do ch = connection.create_channel ["amq.fanout", "amq.direct", "amq.topic", "amq.match", "amq.headers"].each do |e| x = ch.exchange(e) expect(x).to be_predeclared end ch.close end end bunny-2.6.1/spec/higher_level_api/integration/publishing_edge_cases_spec.rb0000644000004100000410000000412413015255277027323 0ustar www-datawww-data# -*- coding: utf-8 -*- require "spec_helper" unless ENV["CI"] describe "Message framing implementation" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :port => ENV.fetch("RABBITMQ_PORT", 5672)) c.start c end after :each do connection.close if connection.open? end context "with payload exceeding 128 Kb (max frame size)" do it "successfully frames the message" do ch = connection.create_channel q = ch.queue("", :exclusive => true) x = ch.default_exchange as = ("a" * (1024 * 1024 * 4 + 28237777)) x.publish(as, :routing_key => q.name, :persistent => true) sleep(1) expect(q.message_count).to eq 1 _, _, payload = q.pop expect(payload.bytesize).to eq as.bytesize ch.close end end context "with payload of several MBs of non-ASCII characters" do it "successfully frames the message" do ch = connection.create_channel q = ch.queue("", :exclusive => true) x = ch.default_exchange as = "кириллца, йо" * (1024 * 1024) x.publish(as, :routing_key => q.name, :persistent => true) sleep(1) expect(q.message_count).to eq 1 _, _, payload = q.pop expect(payload.bytesize).to eq as.bytesize ch.close end end context "with empty message body" do it "successfully publishes the message" do ch = connection.create_channel q = ch.queue("", :exclusive => true) x = ch.default_exchange x.publish("", :routing_key => q.name, :persistent => false, :mandatory => true) sleep(0.5) expect(q.message_count).to eq 1 envelope, headers, payload = q.pop expect(payload).to eq "" expect(headers[:content_type]).to eq "application/octet-stream" expect(headers[:delivery_mode]).to eq 1 expect(headers[:priority]).to eq 0 ch.close end end end end bunny-2.6.1/spec/higher_level_api/integration/basic_nack_spec.rb0000644000004100000410000000425213015255277025074 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Channel, "#nack" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end subject do connection.create_channel end context "with requeue = false" do it "rejects a message" do q = subject.queue("bunny.basic.nack.with-requeue-false", :exclusive => true) x = subject.default_exchange x.publish("bunneth", :routing_key => q.name) sleep(0.5) expect(q.message_count).to eq 1 delivery_info, _, content = q.pop(:manual_ack => true) subject.nack(delivery_info.delivery_tag, false, false) sleep(0.5) subject.close ch = connection.create_channel q = ch.queue("bunny.basic.nack.with-requeue-false", :exclusive => true) expect(q.message_count).to eq 0 ch.close end end context "with multiple = true" do it "rejects multiple messages" do q = subject.queue("bunny.basic.nack.with-requeue-true-multi-true", :exclusive => true) x = subject.default_exchange 3.times do x.publish("bunneth", :routing_key => q.name) end sleep(0.5) expect(q.message_count).to eq 3 _, _, _ = q.pop(:manual_ack => true) _, _, _ = q.pop(:manual_ack => true) delivery_info, _, content = q.pop(:manual_ack => true) subject.nack(delivery_info.delivery_tag, true, true) sleep(0.5) expect(q.message_count).to eq 3 subject.close end end context "with an invalid (random) delivery tag" do it "causes a channel-level error" do q = subject.queue("bunny.basic.nack.unknown-delivery-tag", :exclusive => true) x = subject.default_exchange x.publish("bunneth", :routing_key => q.name) sleep(0.25) expect(q.message_count).to eq 1 _, _, content = q.pop(:manual_ack => true) subject.on_error do |ch, channel_close| @channel_close = channel_close end subject.nack(82, false, true) sleep 0.5 expect(@channel_close.reply_text).to eq "PRECONDITION_FAILED - unknown delivery tag 82" end end end bunny-2.6.1/spec/higher_level_api/integration/tx_commit_spec.rb0000644000004100000410000000060613015255277025021 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Channel, "#tx_commit" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") c.start c end after :each do connection.close if connection.open? end it "is supported" do ch = connection.create_channel ch.tx_select ch.tx_commit ch.close end end bunny-2.6.1/spec/config/0000755000004100000410000000000013015255277015111 5ustar www-datawww-databunny-2.6.1/spec/config/rabbitmq.config0000644000004100000410000000060613015255277020103 0ustar www-datawww-data[ {rabbit, [ {ssl_listeners, [5671]}, {ssl_options, [{cacertfile,"spec/tls/cacert.pem"}, {certfile,"spec/tls/server_cert.pem"}, {keyfile,"spec/tls/server_key.pem"}, {verify,verify_none}, {fail_if_no_peer_cert,false}]} ] }, {rabbitmq_management, [{listener, [{port, 15672}] }] } ]. bunny-2.6.1/spec/config/enabled_plugins0000644000004100000410000000007213015255277020166 0ustar www-datawww-data[rabbitmq_management, rabbitmq_consistent_hash_exchange]. bunny-2.6.1/spec/stress/0000755000004100000410000000000013015255277015167 5ustar www-datawww-databunny-2.6.1/spec/stress/connection_open_close_spec.rb0000644000004100000410000000243213015255277023074 0ustar www-datawww-datarequire "spec_helper" unless defined?(JRUBY_VERSION) && !ENV["FORCE_JRUBY_RUN"] describe Bunny::Session do # creating thousands of connections means creating # twice as many threads and this won't fly with the JVM # in CI containers. MK. n = if defined?(JRUBY_VERSION) 250 else 2500 end n.times do |i| it "can be closed (automatic recovery disabled, take #{i})" do c = Bunny.new(:automatically_recover => false) c.start ch = c.create_channel expect(c).to be_connected c.stop expect(c).to be_closed end end n.times do |i| it "can be closed (automatic recovery enabled, take #{i})" do c = Bunny.new(:automatically_recover => true) c.start ch = c.create_channel expect(c).to be_connected c.stop expect(c).to be_closed end end context "in the single threaded mode" do n.times do |i| it "can be closed (single threaded mode, take #{i})" do c = Bunny.new(:automatically_recover => false, :threaded => false) c.start ch = c.create_channel expect(c).to be_connected c.stop expect(c).to be_closed end end end end end bunny-2.6.1/spec/stress/concurrent_consumers_stress_spec.rb0000644000004100000410000000335113015255277024413 0ustar www-datawww-data# -*- coding: utf-8 -*- require "spec_helper" unless ENV["CI"] describe "Concurrent consumers sharing a connection" do before :all do @connection = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatic_recovery => false) @connection.start end after :all do @connection.close end def any_not_drained?(qs) qs.any? { |q| !q.message_count.zero? } end context "when publishing thousands of messages over 128K in size" do let(:colors) { ["red", "blue", "white"] } let(:n) { 32 } let(:m) { 5000 } it "successfully drain all queues" do ch0 = @connection.create_channel ch0.confirm_select body = "абвг" x = ch0.topic("bunny.stress.concurrent.consumers.topic", :durable => true) chs = {} n.times do |i| chs[i] = @connection.create_channel end qs = [] n.times do |i| t = Thread.new do cht = chs[i] q = cht.queue("", :exclusive => true) q.bind(x.name, :routing_key => colors.sample).subscribe do |delivery_info, meta, payload| # no-op end qs << q end t.abort_on_exception = true end sleep 1.0 5.times do |i| m.times do x.publish(body, :routing_key => colors.sample) end puts "Published #{(i + 1) * m} messages..." ch0.wait_for_confirms end while any_not_drained?(qs) sleep 1.0 end puts "Drained all queues, winding down..." ch0.close chs.each { |_, ch| ch.close } end end end end bunny-2.6.1/spec/stress/channel_close_stress_spec.rb0000644000004100000410000000245113015255277022730 0ustar www-datawww-datarequire "spec_helper" describe "Rapidly closing lots of temporary channels" do before :all do @connection = Bunny.new(:automatic_recovery => false).tap do |c| c.start end end after :all do @connection.close end 100.times do |i| context "in a multi-threaded scenario A (take #{i})" do let(:n) { 20 } it "works correctly" do ts = [] n.times do t = Thread.new do @connection.with_channel do |ch1| q = ch1.queue("", :exclusive => true) q.delete ch1.close end ch2 = @connection.create_channel ch2.close end t.abort_on_exception = true ts << t end ts.each { |t| t.join } end end end 100.times do |i| context "in a multi-threaded scenario B (take #{i})" do let(:n) { 20 } it "works correctly" do ts = [] n.times do t = Thread.new do 3.times do @connection.with_channel do |ch| x = ch.topic('bunny.stress.topics.t2', :durable => false) end end end t.abort_on_exception = true ts << t end ts.each { |t| t.join } end end end end bunny-2.6.1/spec/stress/long_running_consumer_spec.rb0000644000004100000410000000371413015255277023145 0ustar www-datawww-data# -*- coding: utf-8 -*- require "spec_helper" unless ENV["CI"] require "bunny/concurrent/condition" require "bunny/test_kit" describe "Long running [relatively to heartbeat interval] consumer that never publishes" do before :all do @connection = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatic_recovery => false, :heartbeat_interval => 6) @connection.start end after :all do @connection.close end let(:target) { 512 * 1024 * 1024 } let(:queue) { "bunny.stress.long_running_consumer.#{Time.now.to_i}" } let(:rate) { 50 } let(:s) { 4.0 } it "does not skip heartbeats" do finished = Bunny::Concurrent::Condition.new ct = Thread.new do t = 0 ch = @connection.create_channel(nil, 6) begin q = ch.queue(queue, :exclusive => true) q.bind("amq.fanout").subscribe do |_, _, payload| t += payload.bytesize if t >= target puts "Hit the target, done with the test..." finished.notify_all else puts "Consumed #{(t.to_f / target.to_f).round(3) * 100}% of data" end end rescue Interrupt => e ch.maybe_kill_consumer_work_pool! ch.close end end ct.abort_on_exception = true pt = Thread.new do t = 0 ch = @connection.create_channel begin x = ch.fanout("amq.fanout") loop do break if t >= target rate.times do |i| msg = Bunny::TestKit.message_in_kb(96, 8192, i) x.publish(msg) t += msg.bytesize end sleep (s * rand) end rescue Interrupt => e ch.close end end pt.abort_on_exception = true finished.wait ct.raise Interrupt.new pt.raise Interrupt.new end end end bunny-2.6.1/spec/stress/wait_for_confirms_with_connection_loss_stress_spec.rb0000644000004100000410000000705713015255277030166 0ustar www-datawww-datarequire "spec_helper" unless ENV["CI"] describe "Publisher with wait_for_confirms suffering a lost connection" do before :all do @connection = Bunny.new( :user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :recover_from_connection_close => true, :network_recovery_interval => 0.2, :recovery_attempts => 3, :continuation_timeout => 3_000) @connection.start end after :all do @connection.close end let(:rate) { 50 } let(:inner_rate) { 5 } let(:max_retries) { 3 } let(:routing_key) { 'confirms' } let(:http_client) { RabbitMQ::HTTP::Client.new('http://127.0.0.1:15672') } let!(:ch_pub) { @connection.create_channel.tap { |ch| ch.confirm_select } } let!(:ch_sub) { @connection.create_channel } let!(:topic) { 'bunny.stress.concurrent.confirms.topic' } let!(:x) { ch_pub.topic(topic, :durable => true) } let!(:q) do ch_sub.queue('', :durable => true).tap do |q| q.bind(x.name, :routing_key => routing_key) q.purge end end def close_all_connections! http_client.list_connections.each do |conn_info| begin http_client.close_connection(conn_info.name) rescue Bunny::ConnectionForced => e # This is not a problem, but the specs intermittently believe it is. printf "Rescued forced connection: #{e.inspect}\n" end end end def wait_for_recovery sleep 1.5 end after do unless ch_sub.closed? q.delete ch_sub.close end ch_pub.close unless ch_pub.closed? end it "successfully publish and consume all messages" do begin subscriber_mutex = Mutex.new ids_received = Set.new message_count = nil sub = Thread.new do begin q.subscribe do |delivery_info, meta, payload| subscriber_mutex.synchronize do ids_received << payload.to_i message_count = q.message_count end end end end sub.abort_on_exception = true pub = Thread.new do rate.times do |i| retries = 0 begin inner_rate.times do |j| id = i * inner_rate + j x.publish(id.to_s, :routing_key => routing_key) end until ch_pub.unconfirmed_set.empty? unless ch_pub.wait_for_confirms raise "Not all messages acknowledged, nacks: #{ch_pub.nacked_set.inspect}" end end rescue => e puts "Rescued error in iteration #{i}: #{e.inspect}" retries += 1 raise if retries > max_retries puts "sleeping before retry #{retries}" sleep 0.5 retry end end puts "Published #{rate * inner_rate} messages..." end pub.abort_on_exception = true sleep 0.2 while ids_received.size < 10 close_all_connections! wait_for_recovery pub.join sleep 0.1 until message_count == 0 puts "Drained queue, winding down..." q.delete ch_pub.close ch_sub.close sub.kill expect(ch_pub.unconfirmed_set).to be_empty expected_ids = Set.new((rate * inner_rate).times) missing_ids = expected_ids - ids_received expect(missing_ids).to eq(Set.new) ensure sub.kill end end end end bunny-2.6.1/spec/stress/concurrent_publishers_stress_spec.rb0000644000004100000410000000213013015255277024547 0ustar www-datawww-data# -*- coding: utf-8 -*- require "spec_helper" unless ENV["CI"] describe "Concurrent publishers sharing a connection" do before :all do @connection = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatically_recover => false) @connection.start end after :all do @connection.close end let(:concurrency) { 24 } let(:rate) { 5_000 } it "successfully finish publishing" do body = "сообщение" chs = {} concurrency.times do |i| ch = @connection.create_channel ch.confirm_select chs[i] = ch end ts = [] concurrency.times do |i| t = Thread.new do cht = chs[i] x = cht.default_exchange rate.times do x.publish(body) end puts "Published #{rate} messages..." cht.wait_for_confirms end t.abort_on_exception = true ts << t end ts.each do |t| t.join end chs.each { |_, ch| ch.close } end end end bunny-2.6.1/spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb0000644000004100000410000000123213015255277031233 0ustar www-datawww-datarequire "spec_helper" unless ENV["CI"] describe "Rapidly opening and closing lots of channels on a non-threaded connection" do before :all do @connection = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatic_recovery => false, :threaded => false) @connection.start end after :all do @connection.close end context "in a single-threaded scenario" do let(:n) { 500 } it "works correctly" do xs = Array.new(n) { @connection.create_channel } expect(xs.size).to eq n xs.each do |ch| ch.close end end end end end bunny-2.6.1/spec/stress/channel_open_stress_spec.rb0000644000004100000410000000373613015255277022573 0ustar www-datawww-datarequire "spec_helper" describe "Rapidly opening and closing lots of channels" do before :all do @connection = Bunny.new(:automatic_recovery => false).tap do |c| c.start end end after :all do @connection.close end context "in a single-threaded scenario" do let(:n) { 500 } it "works correctly" do xs = Array.new(n) { @connection.create_channel } puts "Opened #{n} channels" expect(xs.size).to eq n xs.each do |ch| ch.close end end end 100.times do |i| context "in a multi-threaded scenario A (take #{i})" do # actually, on MRI values greater than ~100 will eventually cause write # operations to fail with a timeout (1 second is not enough) # which will cause recovery to re-acquire @channel_mutex in Session. # Because Ruby's mutexes are not re-entrant, it will raise a ThreadError. # # But this already demonstrates that within these platform constraints, # Bunny is safe to use in such scenarios. let(:n) { 20 } it "works correctly" do ts = [] n.times do t = Thread.new do ch1 = @connection.create_channel q = ch1.queue("", :exclusive => true) q.delete ch1.close ch2 = @connection.create_channel ch2.close end t.abort_on_exception = true ts << t end ts.each { |t| t.join } end end end 100.times do |i| context "in a multi-threaded scenario B (take #{i})" do let(:n) { 20 } it "works correctly" do ts = [] n.times do t = Thread.new do 3.times do ch = @connection.create_channel x = ch.topic('bunny.stress.topics.t2', :durable => false) ch.close end end t.abort_on_exception = true ts << t end ts.each { |t| t.join } end end end end bunny-2.6.1/spec/unit/0000755000004100000410000000000013015255277014623 5ustar www-datawww-databunny-2.6.1/spec/unit/bunny_spec.rb0000644000004100000410000000055313015255277017320 0ustar www-datawww-datarequire "spec_helper" describe Bunny do it "has library version" do expect(Bunny::VERSION).not_to be_nil expect(Bunny.version).not_to be_nil end it "has AMQP protocol version" do expect(Bunny::PROTOCOL_VERSION).to eq "0.9.1" expect(AMQ::Protocol::PROTOCOL_VERSION).to eq "0.9.1" expect(Bunny.protocol_version).to eq "0.9.1" end end bunny-2.6.1/spec/unit/version_delivery_tag_spec.rb0000644000004100000410000000130713015255277022406 0ustar www-datawww-datarequire "spec_helper" require "bunny/concurrent/atomic_fixnum" require "bunny/versioned_delivery_tag" describe Bunny::VersionedDeliveryTag, "#stale?" do subject { described_class.new(2, 1) } context "when delivery tag version < provided version" do it "returns true" do expect(subject.stale?(2)).to eq true end end context "when delivery tag version = provided version" do it "returns false" do expect(subject.stale?(1)).to eq false end end context "when delivery tag version > provided version" do it "returns true" do # this scenario is unrealistic but we still can # unit test it. MK. expect(subject.stale?(0)).to eq false end end end bunny-2.6.1/spec/unit/exchange_recovery_spec.rb0000644000004100000410000000277113015255277021671 0ustar www-datawww-datarequire_relative '../../lib/bunny/channel' require_relative '../../lib/bunny/exchange' module Bunny describe Exchange do context "recovery" do it "recovers exchange bindings, unless already unbound" do ch = instance_double(Bunny::Channel, exchange_declare: nil, register_exchange: nil) src1 = Exchange.new(ch, "direct", "src1") src2 = Exchange.new(ch, "direct", "src2") src3 = Exchange.new(ch, "direct", "src3") dst = Exchange.new(ch, "direct", "dst") original_binds_count = 5 expected_rebinds_count = 3 expected_total_binds = original_binds_count + expected_rebinds_count allow(ch).to receive(:exchange_bind).exactly(expected_total_binds).times dst.bind(src1, routing_key: "abc") dst.bind(src2, routing_key: "def") dst.bind(src2, routing_key: "ghi") dst.bind(src3, routing_key: "jkl") dst.bind(src3, routing_key: "jkl", arguments: {"key" => "value"}) allow(ch).to receive(:exchange_unbind).twice dst.unbind(src2, routing_key: "def") dst.unbind(src3, routing_key: "jkl", arguments: {"key" => "value"}) expect(ch).to receive(:exchange_bind).with(src1, dst, routing_key: "abc") expect(ch).to receive(:exchange_bind).with(src2, dst, routing_key: "ghi") expect(ch).to receive(:exchange_bind).with(src3, dst, routing_key: "jkl") dst.recover_from_network_failure end end end end bunny-2.6.1/spec/unit/concurrent/0000755000004100000410000000000013015255277017005 5ustar www-datawww-databunny-2.6.1/spec/unit/concurrent/linked_continuation_queue_spec.rb0000644000004100000410000000156013015255277025612 0ustar www-datawww-datarequire "spec_helper" if defined?(JRUBY_VERSION) require "bunny/concurrent/linked_continuation_queue" describe Bunny::Concurrent::LinkedContinuationQueue do describe "#poll with a timeout that is never reached" do it "blocks until the value is available, then returns it" do # force subject evaluation cq = subject t = Thread.new do cq.push(10) end t.abort_on_exception = true v = subject.poll(500) expect(v).to eq 10 end end describe "#poll with a timeout that is reached" do it "raises an exception" do # force subject evaluation cq = subject t = Thread.new do sleep 1.5 cq.push(10) end t.abort_on_exception = true expect { subject.poll(500) }.to raise_error(::Timeout::Error) end end end end bunny-2.6.1/spec/unit/concurrent/synchronized_sorted_set_spec.rb0000644000004100000410000000320213015255277025313 0ustar www-datawww-datarequire "spec_helper" require "bunny/concurrent/synchronized_sorted_set" unless ENV["CI"] describe Bunny::Concurrent::SynchronizedSortedSet do 50.times do |i| it "provides the same API as SortedSet for key operations (take #{i})" do s = described_class.new expect(s.length).to eq 0 s << 1 expect(s.length).to eq 1 s << 1 expect(s.length).to eq 1 s << 2 expect(s.length).to eq 2 s << 3 expect(s.length).to eq 3 s << 4 expect(s.length).to eq 4 s << 4 s << 4 s << 4 expect(s.length).to eq 4 s << 5 expect(s.length).to eq 5 s << 5 s << 5 s << 5 expect(s.length).to eq 5 s << 6 expect(s.length).to eq 6 s << 7 expect(s.length).to eq 7 s << 8 expect(s.length).to eq 8 s.delete 8 expect(s.length).to eq 7 s.delete_if { |i| i == 1 } expect(s.length).to eq 6 end it "synchronizes common operations needed by Bunny (take #{i})" do s = described_class.new expect(s.length).to eq 0 10.times do Thread.new do s << 1 s << 1 s << 2 s << 3 s << 4 s << 4 s << 4 s << 4 s << 5 s << 5 s << 5 s << 5 s << 6 s << 7 s << 8 s.delete 8 s.delete_if { |i| i == 1 } end end sleep 0.5 expect(s.length).to eq 6 end end end end bunny-2.6.1/spec/unit/concurrent/condition_spec.rb0000644000004100000410000000351713015255277022340 0ustar www-datawww-datarequire "spec_helper" require "bunny/concurrent/condition" describe Bunny::Concurrent::Condition do describe "#wait" do 100.times do |i| it "blocks current thread until notified (take #{i})" do condition = described_class.new xs = [] t = Thread.new do xs << :notified sleep 0.25 condition.notify end t.abort_on_exception = true condition.wait expect(xs).to eq [:notified] end end end describe "#notify" do 100.times do |i| it "notifies a single thread waiting on the latch (take #{i})" do mutex = Mutex.new condition = described_class.new xs = [] t1 = Thread.new do condition.wait mutex.synchronize { xs << :notified1 } end t1.abort_on_exception = true t2 = Thread.new do condition.wait mutex.synchronize { xs << :notified2 } end t2.abort_on_exception = true sleep 0.25 condition.notify sleep 0.5 expect(xs).to satisfy { |ys| ys.size == 1 && (ys.include?(:notified1) || ys.include?(:notified2)) } end end end describe "#notify_all" do let(:n) { 30 } 100.times do |i| it "notifies all the threads waiting on the latch (take #{i})" do mutex = Mutex.new condition = described_class.new @xs = [] n.times do |i| t = Thread.new do condition.wait mutex.synchronize { @xs << "notified#{i + 1}".to_sym } end t.abort_on_exception = true end sleep 0.5 condition.notify_all sleep 0.5 n.times do |i| item = "notified#{i + 1}".to_sym expect(@xs).to include item end end end end end bunny-2.6.1/spec/unit/concurrent/atomic_fixnum_spec.rb0000644000004100000410000000117513015255277023212 0ustar www-datawww-datarequire "spec_helper" require "bunny/concurrent/atomic_fixnum" describe Bunny::Concurrent::AtomicFixnum do it "allows retrieving the current value" do af = described_class.new(0) expect(af.get).to eq 0 expect(af).to eq 0 end it "can be updated" do af = described_class.new(0) expect(af.get).to eq 0 Thread.new do af.set(10) end sleep 0.6 expect(af.get).to eq 10 end it "can be incremented" do af = described_class.new(0) expect(af.get).to eq 0 10.times do Thread.new do af.increment end end sleep 0.6 expect(af.get).to eq 10 end end bunny-2.6.1/spec/lower_level_api/0000755000004100000410000000000013015255277017014 5ustar www-datawww-databunny-2.6.1/spec/lower_level_api/integration/0000755000004100000410000000000013015255277021337 5ustar www-datawww-databunny-2.6.1/spec/lower_level_api/integration/basic_consume_spec.rb0000644000004100000410000000545713015255277025523 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Channel, "#basic_consume" do before(:all) do @connection = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") @connection.start end after :all do @connection.close if @connection.open? end it "returns basic.consume-ok when it is received" do ch = @connection.create_channel q = ch.queue("", :exclusive => true) consume_ok = ch.basic_consume(q) expect(consume_ok).to be_instance_of AMQ::Protocol::Basic::ConsumeOk expect(consume_ok.consumer_tag).not_to be_nil ch.close end it "carries server-generated consumer tag with basic.consume-ok" do ch = @connection.create_channel q = ch.queue("", :exclusive => true) consume_ok = ch.basic_consume(q, "") expect(consume_ok.consumer_tag).to match /amq\.ctag.*/ ch.close end context "with automatic acknowledgement mode" do let(:queue_name) { "bunny.basic_consume#{rand}" } it "causes messages to be automatically removed from the queue after delivery" do delivered_keys = [] delivered_data = [] t = Thread.new do ch = @connection.create_channel q = ch.queue(queue_name, :auto_delete => true, :durable => false) ch.basic_consume(q, "", true, false) do |delivery_info, properties, payload| delivered_keys << delivery_info.routing_key delivered_data << payload end end t.abort_on_exception = true sleep 0.5 ch = @connection.create_channel x = ch.default_exchange x.publish("hello", :routing_key => queue_name) sleep 0.7 expect(delivered_keys).to include queue_name expect(delivered_data).to include "hello" expect(ch.queue(queue_name, :auto_delete => true, :durable => false).message_count).to eq 0 ch.close end end context "with manual acknowledgement mode" do let(:queue_name) { "bunny.basic_consume#{rand}" } it "waits for an explicit acknowledgement" do delivered_keys = [] delivered_data = [] t = Thread.new do ch = @connection.create_channel q = ch.queue(queue_name, :auto_delete => true, :durable => false) ch.basic_consume(q, "", false, false) do |delivery_info, properties, payload| delivered_keys << delivery_info.routing_key delivered_data << payload ch.close end end t.abort_on_exception = true sleep 0.5 ch = @connection.create_channel x = ch.default_exchange x.publish("hello", :routing_key => queue_name) sleep 0.7 expect(delivered_keys).to include queue_name expect(delivered_data).to include "hello" expect(ch.queue(queue_name, :auto_delete => true, :durable => false).message_count).to eq 0 ch.close end end end bunny-2.6.1/spec/lower_level_api/integration/basic_cancel_spec.rb0000644000004100000410000000440713015255277025271 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Channel, "#basic_cancel" do before(:all) do @connection = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") @connection.start end after :all do @connection.close if @connection.open? end let(:queue_name) { "bunny.queues.#{rand}" } it "returns basic.cancel-ok" do ch = @connection.create_channel q = ch.queue("", :exclusive => true) consume_ok = ch.basic_consume(q, "") cancel_ok = ch.basic_cancel(consume_ok.consumer_tag) expect(cancel_ok).to be_instance_of AMQ::Protocol::Basic::CancelOk expect(cancel_ok.consumer_tag).to eq consume_ok.consumer_tag ch.close end context "when the given consumer tag is valid" do let(:queue_name) { "bunny.basic.cancel.queue#{rand}" } it "cancels the consumer" do delivered_data = [] t = Thread.new do ch = @connection.create_channel q = ch.queue(queue_name, :auto_delete => true, :durable => false) consume_ok = ch.basic_consume(q, "", true, false) do |_, _, payload| delivered_data << payload end expect(consume_ok.consumer_tag).not_to be_nil cancel_ok = ch.basic_cancel(consume_ok.consumer_tag) expect(cancel_ok.consumer_tag).to eq consume_ok.consumer_tag ch.close end t.abort_on_exception = true sleep 0.5 ch = @connection.create_channel ch.default_exchange.publish("", :routing_key => queue_name) sleep 0.7 expect(delivered_data).to be_empty end end context "when the given consumer tag is invalid (was never registered)" do it "DOES NOT cause a channel error" do ch = @connection.create_channel # RabbitMQ 3.1 does not raise an exception w/ unknown consumer tag. MK. ch.basic_cancel("878798s7df89#{rand}#{Time.now.to_i}") ch.close end end context "when the given consumer tag belongs to a different channel" do it "DOES NOT cause a channel error" do ch1 = @connection.create_channel ch2 = @connection.create_channel q = ch1.queue("", :exclusive => true) cons = q.subscribe do |_, _, _| end ch2.basic_cancel(cons.consumer_tag) ch1.close ch2.close end end end bunny-2.6.1/spec/issues/0000755000004100000410000000000013015255277015157 5ustar www-datawww-databunny-2.6.1/spec/issues/issue97_spec.rb0000644000004100000410000001003013015255277020020 0ustar www-datawww-datarequire "spec_helper" describe "Message framing implementation" do before :all do @connection = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :port => ENV.fetch("RABBITMQ_PORT", 5672)) @connection.start end after :all do @connection.close if @connection.open? end unless ENV["CI"] context "with payload ~ 248K in size including non-ASCII characters" do it "successfully frames the message" do ch = @connection.create_channel q = ch.queue("", :exclusive => true) x = ch.default_exchange body = IO.read("spec/issues/issue97_attachment.json") x.publish(body, :routing_key => q.name, :persistent => true) sleep(1) expect(q.message_count).to eq 1 q.purge ch.close end end end context "with payload of several MBs in size" do it "successfully frames the message" do ch = @connection.create_channel q = ch.queue("", :exclusive => true) x = ch.default_exchange as = ("a" * (1024 * 1024 * 4 + 2823777)) x.publish(as, :routing_key => q.name, :persistent => true) sleep(1) expect(q.message_count).to eq 1 _, _, payload = q.pop expect(payload.bytesize).to eq as.bytesize ch.close end end context "with empty message body" do it "successfully publishes the message" do ch = @connection.create_channel q = ch.queue("", :exclusive => true) x = ch.default_exchange x.publish("", :routing_key => q.name, :persistent => true) sleep(1) expect(q.message_count).to eq 1 envelope, headers, payload = q.pop expect(payload).to eq "" expect(headers[:content_type]).to eq "application/octet-stream" expect(headers[:delivery_mode]).to eq 2 expect(headers[:priority]).to eq 0 ch.close end end context "with payload being 2 bytes less than 128K bytes in size" do it "successfully frames the message" do ch = @connection.create_channel q = ch.queue("", :exclusive => true) x = ch.default_exchange as = "a" * (1024 * 128 - 2) x.publish(as, :routing_key => q.name, :persistent => true) sleep(1) expect(q.message_count).to eq 1 q.purge ch.close end end context "with payload being 1 byte less than 128K bytes in size" do it "successfully frames the message" do ch = @connection.create_channel q = ch.queue("", :exclusive => true) x = ch.default_exchange as = "a" * (1024 * 128 - 1) x.publish(as, :routing_key => q.name, :persistent => true) sleep(1) expect(q.message_count).to eq 1 q.purge ch.close end end context "with payload being exactly 128K bytes in size" do it "successfully frames the message" do ch = @connection.create_channel q = ch.queue("", :exclusive => true) x = ch.default_exchange as = "a" * (1024 * 128) x.publish(as, :routing_key => q.name, :persistent => true) sleep(1) expect(q.message_count).to eq 1 q.purge ch.close end end context "with payload being 1 byte greater than 128K bytes in size" do it "successfully frames the message" do ch = @connection.create_channel q = ch.queue("", :exclusive => true) x = ch.default_exchange as = "a" * (1024 * 128 + 1) x.publish(as, :routing_key => q.name, :persistent => true) sleep(1) expect(q.message_count).to eq 1 q.purge ch.close end end context "with payload being 2 bytes greater than 128K bytes in size" do it "successfully frames the message" do ch = @connection.create_channel q = ch.queue("", :exclusive => true) x = ch.default_exchange as = "a" * (1024 * 128 + 2) x.publish(as, :routing_key => q.name, :persistent => true) sleep(1) expect(q.message_count).to eq 1 q.purge ch.close end end end bunny-2.6.1/spec/issues/issue224_spec.rb0000644000004100000410000000162713015255277020104 0ustar www-datawww-data# -*- coding: utf-8 -*- require "spec_helper" unless ENV["CI"] describe "Message framing implementation" do let(:connection) do c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :port => ENV.fetch("RABBITMQ_PORT", 5672)) c.start c end after :each do connection.close if connection.open? end context "with payload 272179 bytes in size" do it "successfully frames the message" do ch = connection.create_channel q = ch.queue("", :exclusive => true) x = ch.default_exchange as = ("a" * 272179) x.publish(as, :routing_key => q.name, :persistent => true) sleep(1) expect(q.message_count).to eq 1 _, _, payload = q.pop expect(payload.bytesize).to eq as.bytesize ch.close end end end end bunny-2.6.1/spec/issues/issue141_spec.rb0000644000004100000410000000171113015255277020074 0ustar www-datawww-datarequire "spec_helper" describe "Registering 2nd exclusive consumer on queue" do before :all do @connection = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") @connection.start end after :each do @connection.close if @connection.open? end it "raises a meaningful exception" do xs = [] ch1 = @connection.create_channel ch2 = @connection.create_channel q1 = ch1.queue("", :auto_delete => true) q2 = ch2.queue(q1.name, :auto_delete => true, :passive => true) c1 = q1.subscribe(:exclusive => true) do |_, _, payload| xs << payload end sleep 0.1 expect do q2.subscribe(:exclusive => true) do |_, _, _| end end.to raise_error(Bunny::AccessRefused) expect(ch1).to be_open expect(ch2).to be_closed q1.publish("abc") sleep 0.1 # verify that the first consumer is fine expect(xs).to eq ["abc"] q1.delete end end bunny-2.6.1/spec/issues/issue83_spec.rb0000644000004100000410000000115213015255277020020 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Channel, "#open" do before :all do @connection = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") @connection.start end after :all do @connection.close if @connection.open? end it "properly resets channel exception state" do ch = @connection.create_channel begin ch.queue("bunny.tests.does.not.exist", :passive => true) rescue Bunny::NotFound # expected end # reopen the channel ch.open # should not raise q = ch.queue("bunny.tests.my.queue") q.delete end end bunny-2.6.1/spec/issues/issue78_spec.rb0000644000004100000410000000351613015255277020032 0ustar www-datawww-datarequire "spec_helper" unless ENV["CI"] describe Bunny::Queue, "#subscribe" do before :all do @connection1 = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") @connection1.start @connection2 = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed") @connection2.start end after :all do @connection1.close if @connection1.open? @connection2.close if @connection2.open? end context "with an empty queue" do it "consumes messages" do delivered_data = [] ch1 = @connection1.create_channel ch2 = @connection1.create_channel q = ch1.queue("", :exclusive => true) q.subscribe(:manual_ack => false, :block => false) do |delivery_info, properties, payload| delivered_data << payload end sleep 0.5 x = ch2.default_exchange x.publish("abc", :routing_key => q.name) sleep 0.7 expect(delivered_data).to eq ["abc"] ch1.close ch2.close end end context "with a non-empty queue" do let(:queue_name) { "queue#{rand}" } it "consumes messages" do delivered_data = [] ch1 = @connection1.create_channel ch2 = @connection1.create_channel q = ch1.queue(queue_name, :exclusive => true) x = ch2.default_exchange 3.times do |i| x.publish("data#{i}", :routing_key => queue_name) end sleep 0.7 expect(q.message_count).to eq 3 q.subscribe(:manual_ack => false, :block => false) do |delivery_info, properties, payload| delivered_data << payload end sleep 0.7 expect(delivered_data).to eq ["data0", "data1", "data2"] ch1.close ch2.close end end end end bunny-2.6.1/spec/issues/issue97_attachment.json0000644000004100000410000074412613015255277021610 0ustar www-datawww-data[{"id":"353114740","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004489807740","authorlink":"FASky","date":"2013-01-06T13:40:17Z","topic":"Kia K9","topic_text":"Kia K9","text":"By gi l 20:35 ch cn vi tin na l bit kt w tri oy chc ti nay hok ng c w hjc c ai rnh hok 8 i cho mnh bt cng thng t hjx............cu mong mi chuyn s im p hjhjhj","language":"notdetect","sentiment":"Mixed","author":"FASky","author_text":"FASky","md5_text":"169378f23462ebff5ccc3d5e54c26af2","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757915848704},{"id":"353114054","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004385643326","authorlink":"VoianhElaNiemdau","date":"2013-01-06T13:59:36Z","topic":"Kia K9","topic_text":"Kia K9","text":"Buc eo triu dk lam ban 3 nam vi 1 kau ns di nha ngi vs a k ma ny no gen gio tn day.dung la og troi bat kong no treu ny minh thi dk minh treu thi...?tam biet van luong?tam biet ngoc kute?ka nguyen trang nua","language":"notdetect","sentiment":"Mixed","author":"VoianhElaNiemdau","author_text":"VoianhElaNiemdau","md5_text":"086ad717fbea5460656c3e50e07add08","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757917945856},{"id":"353114917","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004933687507","authorlink":"ÚaLệVìAi","date":"2013-01-06T13:33:07Z","topic":"Kia K9","topic_text":"Kia K9","text":"ak a ckac e pai dj thui di den mot noi nao no that xa va ko aj biet e la aj.e ckan cuoc song ntn lm rui bjo e ko the nao ckui dk nua rui e biet tc ak dank cko e va e kung vay e yeu ak nkiu lm .hay de cko mot ng nao do ckam lo cko ak mang den niem vui mva nu cuoi cko ak.e ngj ty cua e dank cko ak la lon rui nkug ckac con co mot ng iu ak nkiu hon bjo e ko bit pai lm tke nao ka e se ckopn cack am t5ham lang le ra dj de tham cau ckuc cko ak dk vui ve hank pkuc.dung buon vj e ak nke neu cu o alj day va mv ntn thi tha e cket dj kon hon e ko lm dk dieu do e ko du dung cam de nkin thay ng e iu o ben mot aj do ma ng do laj la....hay that hp ak nke","language":"notdetect","sentiment":"Mixed","author":"ÚaLệVìAi","author_text":"ÚaLệVìAi","md5_text":"7131beb265ff91d84c57e535b7991982","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757918994432},{"id":"353114665","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004120800210","authorlink":"HueNhu","date":"2013-01-06T13:46:17Z","topic":"Kia K9","topic_text":"Kia K9","text":"m lai dc nghi 3 ngay zui.ma lai chua co ke hoach j ca....ko pit lm j trog 3 ngay do nua...hay lai o nha ngu day..hjk","language":"notdetect","sentiment":"Mixed","author":"HueNhu","author_text":"HueNhu","md5_text":"b07b2568490c9a83716fee13ee2c2b89","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757921091584},{"id":"353114867","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004792712506","authorlink":"ToNyLinh","date":"2013-01-06T13:30:13Z","topic":"Kia K9","topic_text":"Kia K9","text":"m p khng phi khi ngi bn ng la, m l bn cnh ngi bn thng yu. m p khng phi khi bn mc mt lc hai, ba o, m l khi bn ng trc gi lnh, t pha sau n c ai khoc ln bn mt tm o. m p khng phi khi bn ni m qu, m l khi c ngi th thm vi bn: C lnh khng?. m p khng phi khi bn dng hai tay xut xoa, m l khi tay ai kia kh nm ly bn tay bn. m p khng phi khi bn i chic m len, m l khi u bn da vo mt b vai tin cy.----> Chc bn s m p bn cnh ngi bn yu thng!","language":"notdetect","sentiment":"Mixed","author":"ToNyLinh","author_text":"ToNyLinh","md5_text":"3cca6289c2e2203f2e1071d690e9d12d","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757922140160},{"id":"353114294","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=374815592610516","authorlink":"LặngthầmmộttìnhyêuX","date":"2013-01-06T13:49:01Z","topic":"Kia K9","topic_text":"Kia K9","text":"...Mt anh lc vo khu rng lc m khuya . Anh thy mt c gi\n- C em d thng , sao ng y 1 mnh vy ?\n- Em ang n li 1 k nim su sc ca em ni ny\n- K nim g vy ? K anh nghe vi.\n- Anh thy cnh cy ng kia khng?\n- Thy , ri sao na em?\n- Hi em treo c trn cnh cy =))\n\np/s: ng tim :))))\n\n--------------------------- like page Tng lai KHC hay CI ph thuc vo LI ca qu kh :\") cung oc nhng stt thuvi hn na ban nhe ------------------------------------------","language":"notdetect","sentiment":"Positive","author":"LặngthầmmộttìnhyêuX","author_text":"LặngthầmmộttìnhyêuX","md5_text":"d0c839685f789f5c9ba14d386ee61041","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757923188736},{"id":"353114462","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004526313931","authorlink":"TuyetNhuocLan","date":"2013-01-06T13:43:50Z","topic":"Kia K9","topic_text":"Kia K9","text":"Chan nh...co thi thi thi...a m...c n minh...?!","language":"notdetect","sentiment":"Mixed","author":"TuyetNhuocLan","author_text":"TuyetNhuocLan","md5_text":"bcde8c4e040000a27f0c7e5ee13a266e","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757924237312},{"id":"353114027","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002813111176","authorlink":"BiMilano","date":"2013-01-06T13:54:58Z","topic":"Kia K9","topic_text":"Kia K9","text":"~* V ma ng n, c ni vi e rng, baby I miss U.. Mt mu hoa ri ri v ngy a khng th gi, Can u feel me??..\n~* ri thi gian tri qua, ting l ri theo bc chn ca e, Tm v ni yn bnh.. V ma xun n, c ni vi em::\n..::MU VNG CA L KIA KHNG TH XANH, TR LI!!~","language":"notdetect","sentiment":"Mixed","author":"BiMilano","author_text":"BiMilano","md5_text":"ab61caea56dbe8b8319fd15d204ea468","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757925285888},{"id":"353114333","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004992632504","authorlink":"TrangKhanh","date":"2013-01-06T13:48:43Z","topic":"Kia K9","topic_text":"Kia K9","text":"L Cay khe v mt ngi za i nay khng quay tr v\nnhng nc mt ngy vn c zi hoi v sao em li khc v sao em li nh\ndu bit zng anh khng cn yu em na\nnc mt cho anh l tha\nn n ci trn mi n chm mt cuc tnh mi","language":"notdetect","sentiment":"Mixed","author":"TrangKhanh","author_text":"TrangKhanh","md5_text":"91d023ed3567598f80c504f587722d40","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757926334464},{"id":"353114578","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=175703249174546","authorlink":"BLVAnhNgọc","date":"2013-01-06T13:44:14Z","topic":"Kia K9","topic_text":"Kia K9","text":"Chun b ln sng trc tip Milan-Siena trn Th thao TV khi va bit tin Inter thua Udinese n 0-3. Khng th tin ni...","language":"notdetect","sentiment":"Mixed","author":"BLVAnhNgọc","author_text":"BLVAnhNgọc","md5_text":"014132f84c5b7d48804642ebffb12153","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757927383040},{"id":"353114519","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003647311860","authorlink":"BomNổChậm","date":"2013-01-06T13:40:52Z","topic":"Kia K9","topic_text":"Kia K9","text":"ma bun,qun t khch cng thm nn......","language":"notdetect","sentiment":"Mixed","author":"BomNổChậm","author_text":"BomNổChậm","md5_text":"48ad1ca15cccb2b0cf6a3578139454d0","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757955694592},{"id":"353114525","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003761322735","authorlink":"KannaTran","date":"2013-01-06T13:44:30Z","topic":"Kia K9","topic_text":"Kia K9","text":"trn thin ng c 1 thin s ang nhn xung trn gian thy nhng cp tnh nhn ang iu nhau v bn nhau hp, cng c nhng cp au kh v ty. Bng Cha hin ra hi v thien s :\n- Con ang nhn g? V con nhn thy g?\n- Con nhn nhng ngi ang iu nhau di nhn gian. Con thy h c nim vui ln au kh. Ti sao tnh iu nam n li khin h nh dzy vy Cha? Ty l g? Sao iu nhau lm chi phi au kh?\nV thin s vn nhn xung th gian v hi Cha. Cha p:\n- Ty k th gii thik cho con hiu c, ch c bn thn t cm nhn v tm hiu con mi thu hiu. V v sao nh th con nn t tm hiu i.\nV thin s y vn im lng ng , Cha li ni:\n- Con sinh ra l 1 thin s, con nn dnh ty ca mnh cho ca th gian, ng v qu quan tam n 1 ch iu m nh mt nhng g mnh c.\nNi ri Cha bin mt li v thin s vn 1 mnh ng lng im nhn th gian. Khng bit qua bao lu v thin s y cui cng cng dang 4 ci cnh ln mu trng ca mnh ra v bay i. V thin s y bay i gp i thin s Mikaen, ti ni v thin s y qu xung ni:\n- Hi i thin s, ta mong ngi c th gip ta 1 vic, ta mun xung th gian, ta mun cm nhn ci m con ngi gi l tnh iu, ci m h sn sng hi sinh va bung b tt c, ci m h v n m vui bun ln ln.\n- Noah! Ngi ng qu kh, ngi l t vc thin thn (4 cnh), 1 thin thn y quyn nng, ng v t m nht thi m nh mt n. ng i theo con ng m t ph loi ngi lm. Ngi cng bit 1 khi ngi xung th gian ngi s kh m quay v ni ny, ngi cng chu rt nhiu au n khi t b thn phn mnh , ngi bit ch!\n- Ta bit, nhng ta vn mun th, ta y rt lu ri! qu lu, ta cng nhn thy rt nhiu th, nhng cui cng cng ch l nhn thy, ta mun bn thn mnh t cm nhn.\nB qua mi li khuyn ca i thin thn, Noah vn mun du tin nhn gian, lm 1 con ngi cm nhn tnh iu. Cui cng vi s chp nhn ca i thin thn, Noah chu kh hnh thp t gi ca thin ng, t b thn phn thin s, b i nhng chic cnh trng m bao ngi mong c mang 1 thn xc loi ngi yu ui. Noah tha mong c, anh n th gian vi thn phn 1 con ngi ngho hn v a gp v iu 1 c gi. Anh c nhng nim vui m trc kia ni thin ng anh khng c c, a iu c ta bng tt c, nhng 1 ngy kia:\n- Mnh chia tay i a!\n- Sao vy e, a lm g sai sao? \n- K, a rt tt, ch c u a ngho qu! a k th cung phng e nhng th e cn, e tm c 1 ngi tt hn a ri, ngi ta cho e c nhng th e cn m a k th cho...thi tm bit a e i y.\nc gi ra i k h nhn li, Noah thn th ng, tri ni gi v ma ko n....Noah au kh, a t b tt c i tm 1 tnh iu v cui cng l tm c 1 ty nh th....Thi gian tri qua, a vn khng t b hi vng v 1 ty p v ch thc, a vn i tm v i tm....a quen rt nhiu ngi con gi nhng cui cng cng ch 1 kt qu... au kh khin a suy sp hon ton, cui cng anh n 1 nh th v qu xung di chn Cha:\n- Con sai ri, con khng nn i tm, nhng con khng hi hn, con khng mong Cha tha th cho con, con ch mong Cha c th chc phc v ban bnh an cho nhng ai tht s iu nhau.\n- Ta sn sng b qua mi li lm ca con, no con chin ngoan ca ta, hy cng ta quay tr v nh no.\nNoah theo Cha quay v thin ng, nhng 4 chic cnh trng ngy no ca anh gi nhum 1 mu en bun b, i mt sng ngy no hay ngm nhn th gian gi mi mi khng cn m ra na, mi tc en huyn tung bay trong gi gi cng thay bng mu trng lnh lo...Noah quay v nhng mang theo y ni tht vng v con tim tan nt,....","language":"notdetect","sentiment":"Mixed","author":"KannaTran","author_text":"KannaTran","md5_text":"6dfe062f4db408218da17d3eccfa0fa1","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757956743168},{"id":"353114107","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003093224135","authorlink":"KhiconngayNgo","date":"2013-01-06T14:01:07Z","topic":"Kia K9","topic_text":"Kia K9","text":"Uj,then gan chet! sao da mat mjh co the day the ko bjt.t nay con xjn cha.............","language":"notdetect","sentiment":"Mixed","author":"KhiconngayNgo","author_text":"KhiconngayNgo","md5_text":"df28b7681a9d4108c33db2113040c838","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757958840320},{"id":"353114476","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002726928574","authorlink":"NhocKute","date":"2013-01-06T13:41:09Z","topic":"Kia K9","topic_text":"Kia K9","text":"Nimamoto Shizuka bao gi ra Bc vy?\nV chng nh Meo Luoi b tr 1 bui ra HP cng ngy vs Nimamoto v t tp nh?\nLu lm oy chng ta hok c ng n m oy!!!\nThy c hok h G Simsimi, Bt Ti V Dng?????????\nKaraoke ch????????????????","language":"notdetect","sentiment":"Mixed","author":"NhocKute","author_text":"NhocKute","md5_text":"806f09fa5220f73e0af0c6cc4cceb7dd","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757959888896},{"id":"353114105","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003498456934","authorlink":"VângEmQuê","date":"2013-01-06T13:59:15Z","topic":"Kia K9","topic_text":"Kia K9","text":"Tui nh pa qua >.<","language":"notdetect","sentiment":"Mixed","author":"VângEmQuê","author_text":"VângEmQuê","md5_text":"b1b6eec00f399d161648f0d185a1e242","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757960937472},{"id":"353114347","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100000234857250","authorlink":"AndrewNguyễn","date":"2013-01-06T13:48:38Z","topic":"Kia K9","topic_text":"Kia K9","text":"v con tim vuii tr li ....","language":"notdetect","sentiment":"Mixed","author":"AndrewNguyễn","author_text":"AndrewNguyễn","md5_text":"902011d98d5d3db90eff05e133789e6a","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757961986048},{"id":"353114986","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003798982707","authorlink":"LoanLeThiThanh","date":"2013-01-06T13:27:33Z","topic":"Kia K9","topic_text":"Kia K9","text":"Taj sao trong tjeng vjet laj co 2 tu \"gia' nhu...\"","language":"unknown","sentiment":"Mixed","author":"LoanLeThiThanh","author_text":"LoanLeThiThanh","md5_text":"28d1d91e8fb8f18e6081cfb0b7f91f1d","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757971423232},{"id":"353114688","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002419233214","authorlink":"LeThanhhuyen","date":"2013-01-06T13:40:43Z","topic":"Kia K9","topic_text":"Kia K9","text":"\"Nu ti khc......\n.......ai s l ngi lau nc mt cho ti?\nNu ti gc ng.....\n.......ai s l ngi ko ti ng dy?\nNu ti cn mt b vai.....\n... .......ai s sn sng cho ti mn b vai ?\nNu mt ngy ti bin mt... .\n.......c ai cn nh ti tng tn ti hay ko ???","language":"notdetect","sentiment":"Mixed","author":"LeThanhhuyen","author_text":"LeThanhhuyen","md5_text":"acd9459de2659c0b91187a425c2fadfa","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757972471808},{"id":"353114779","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003810898347","authorlink":"ChúTiểuLênSàn","date":"2013-01-06T13:34:20Z","topic":"Kia K9","topic_text":"Kia K9","text":"Cho anh xin em mt ln\nNgonh mt anh thi au 1 phn\nCho 1 ln nu ko 1 tnh yu than th dang gi nh mun ngn trang v\nAnh vit ln nh mun ngn th\nTrong anh 1 ni au o o nh sng m\nBy gi / em cn ni di anh lm g vy\nSau nhng g m anh nhn thy\nLi lm l tt c anh gy\nLi ni khi xa cng tri nh nh ng my\nXin em mt ln thi\nCho anh lm trn mi, hy ta ln vai anh th tho cho thi gian dn tri\nTa ln b vai, v ng trch anh hay em sai\nAnh phi lm sao khi anh vn thit tha mt cu cho anh lm li\nAnh bit l mt iu khng phi\nAnh u phi la` ngi tng tri\nNhng em lm n ngang tri\nAnh bit l anh sai, mt ln na hy cho anh lm li\nChia xa.\nV nhng g thong qua, thong qua trong bng gi\nDi tr - Mt mnh anh lng bc ma ng khng nh thng khng vn vng\nAnh i tm em mt m trong khi sng ..\n\nHook : Kendylee (X2)\n\nMa ri trong m c ri cho thm nh ai\ni chn trong m c l hi vng 1 sm mai\n1 ngy mi n em ci chu mn v\nAnh li c em nh xa.2 ta lm li cuc tnh di cn ma\n\nLil Kiz:\n\nKhc, ni yu nhau s mi khng ri nhng qua m nay thi mnh anh li ru ng trn mi\nMt mnh anh c l trong cn phng ti,anh lc li khng c bng ngi k bn,gi bn thm li lm anh thm bun thm\nQua m nay thi bu tri s thm hiu qunh,ma ng ny anh li gi lnh mnh anh\nEm,ngi con gi anh thng gi ch cn li trong tng tng\nEm i xa\nKhng cht suy ngh nh thng\nBn nhau anh ch mong nh bnh thng\nNhng tt cCh l tng tng m thi\nB mi anh thm 1 ln na khng ai xua i gi lnh,tnh yu bn nhau vi em sao n qu mng manh ?\nAnh\nKhc na,khc na , khc na,nhng em u c v cng anh\nNi na,ni na,ni na nhng cng ni cng hiu qunh\nMu anh v em, nc mt cng ri v em tt c cng ch v em .V hm nay anh au v em !\nQuen di em, quen di cuoc doi quen di tat ca quen di ngay mai, vinh biet em vinh biet cuoc doi vinh biet ngay mai\n\nHook : Kendylee (X2)\n\nMa ri trong m c ri cho thm nh ai\ni chn trong m c l hi vng 1 sm mai\n1 ngy mi n em ci chu mn v\nAnh li c em nh xa.2 ta lm li cuc tnh di cn ma\n\nYeah ! Out love .new life\ntun hon c ngy v m m ch hoi vn chng mong n ngy mai\n1 ngy c trng v en cuc sng kia quen c 1 ngi con gi\nLun lun bn cnh chm sc v nm tay vt trng gai\nMong lm tt c gip em khng mt mi ng n tng lai\nKh khn\nri 1 ngy kia\nc 1 ngi n mang em i tht xa\nc nhn\nni 1 li chya tay\nv anh bit hon cnh . vt cht hok mang li c hnh phc cho em\ntrong anh ch c 1 tnh yu ..1 ngi con gi gip anh c gng i ln\nanh bit mt em tri tim kia au bn b cng an i\n n nc ny th my bun chi man\nN la di .. oh no ..\nKhng fi u .. tao t thy tnh yu ny khng cn v tao bung cu","language":"notdetect","sentiment":"Mixed","author":"ChúTiểuLênSàn","author_text":"ChúTiểuLênSàn","md5_text":"09a7cc122868621c742c6cbb859643a8","dimension":["Extreme Positive (autos)"],"category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757973520384},{"id":"353113995","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=534945013187112","authorlink":"NgocTrinhTran","date":"2013-01-06T14:00:32Z","topic":"Kia K9","topic_text":"Kia K9","text":"Trong chuyn tnh cm, c bao ngi ngh rng mnh khn ln, chn chn ri c mong tnh yu ca mnh phi nh th ny, nh th ka... ngi mnh yu phi lm nhng vic ny, vic kia...V ri h qun mt mt iu rng: yu khng phi ch c cm xc, m cn phi c c ci u suy ngh. Chnh ci u kt ni vi tri tim ri h phi bit c iu quan trng nht gia hai ngi yu nhau chnh l s cm thng v khng nhng chp nhn m cn l yu c nhng khuyt im ca ngi yu. Bi ngi ta c to nn t chnh tt c nhng khuyt im v u im, ch khng phi ch c duy nht nhng khuyt im. S cm thng v chp nhn cng tnh cm chn thnh, v con mt hng v pha trc s gip h i c xa hn.Ko ph nhn l yu bng tt c tnh cm l sai,nhng i khi n li l m qung.^^","language":"notdetect","sentiment":"Mixed","author":"NgocTrinhTran","author_text":"NgocTrinhTran","md5_text":"18ae1fc4d04d55dba6cd745473781da0","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757974568960},{"id":"353114778","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004331557024","authorlink":"ThonWeYenBinh","date":"2013-01-06T13:37:15Z","topic":"Kia K9","topic_text":"Kia K9","text":"Vui cho nhug ai k co n y.mua dog lah nay m n y no doi dj ro thj met lam","language":"notdetect","sentiment":"Mixed","author":"ThonWeYenBinh","author_text":"ThonWeYenBinh","md5_text":"ae3100e77c7d9c34f3fb6ecafa36d2e9","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757987151872},{"id":"353114985","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003326481446","authorlink":"KhánhHuyền","date":"2013-01-06T13:32:59Z","topic":"Kia K9","topic_text":"Kia K9","text":"Hix.. Thay chi minh theo chong bo cuoc choi ma nghi sau nay minh se lay chong that muon... 30 tro len moi lay .. Lay nhu the thi ca nha ai cung mung roi nuoc mat chu ntn thay bun lam.. Nho bme va bme cung buon va cam giac mat con roi,n la con ngkhac roi..hix... Minh se doi bjo me minh bao \" lay chong di cho toi nho\".. luc day minh se lay...:D","language":"notdetect","sentiment":"Mixed","author":"KhánhHuyền","author_text":"KhánhHuyền","md5_text":"e03b5ea3b638a764e3ac8bb90701fc6a","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757989249024},{"id":"353114833","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003683228906","authorlink":"ThảoNgọc","date":"2013-01-06T13:38:15Z","topic":"Kia K9","topic_text":"Kia K9","text":"Ngi xp qun o chun b i hc qun s................ vic g phi mang nhiu nh? xung ngi ta pht cho 3 b + 1 m m. kaka","language":"notdetect","sentiment":"Mixed","author":"ThảoNgọc","author_text":"ThảoNgọc","md5_text":"bea629f9efab89d4fea40f0fa3c7b1f0","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757990297600},{"id":"353114434","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003933994307","authorlink":"CocoMy","date":"2013-01-06T13:48:10Z","topic":"Kia K9","topic_text":"Kia K9","text":"Phong Kieu mai dat kao voi ma Jury Doan di an bach tuoc nuong coi...kao them an ^^","language":"unknown","sentiment":"Mixed","author":"CocoMy","author_text":"CocoMy","md5_text":"c3609d20ce1c429451ac36ca0f16a085","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757991346176},{"id":"353114901","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004183323170","authorlink":"RùaChjp","date":"2013-01-06T13:28:08Z","topic":"Kia K9","topic_text":"Kia K9","text":"Lanh that day .ngay nao cug ve nha oy o nka 1mik .buon chet di dc .hjx","language":"unknown","sentiment":"Mixed","author":"RùaChjp","author_text":"RùaChjp","md5_text":"f8dd1c3aa03265983523af4b3cd872e8","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757992394752},{"id":"353114252","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004737960332","authorlink":"KhanhNguyen","date":"2013-01-06T13:49:21Z","topic":"Kia K9","topic_text":"Kia K9","text":"hoang sang,minh hai ak,mjh hop nhom ti di nak,hj","language":"unknown","sentiment":"Mixed","author":"KhanhNguyen","author_text":"KhanhNguyen","md5_text":"58fe9a4b5543dc1c3749bea1736ca214","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757993443328},{"id":"353114455","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004073981559","authorlink":"ThaoPhuong","date":"2013-01-06T13:47:30Z","topic":"Kia K9","topic_text":"Kia K9","text":"Qu kh din t ht cm gic by gi...c qu nhiu tin n,qu nhiu li ni kh nghe.lm cho ngi ta suy ngh v mnh cng phi suy ngh....ming i tht ng s...h lm nh vy c li ch g cho h kg ? Ti sau h kg cho mnh c yn,mnh u c lm g c li vi h ti sau kg bun tha cho mnh ch ! gn hai nm tri qua nhng m ti sau h vn kg bung tha cho mnh.mnh chng lm g sai c,mnh sng ng ngha,v u quan trng l mnh lm trn bn phn mt ngi v,mnh lun t tin v u ...! Nhng m kg hiu sau mnh c thy mt mi khi phi chng chi vi nhng tin n,nhng li ni kh nghe ny ch!!!!!!!!","language":"notdetect","sentiment":"Mixed","author":"ThaoPhuong","author_text":"ThaoPhuong","md5_text":"be5446c19eefabed07923287923c64ef","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757994491904},{"id":"353114479","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100001022547892","authorlink":"NguyễnThuHiền","date":"2013-01-06T13:47:12Z","topic":"Kia K9","topic_text":"Kia K9","text":"Ha Ni m lanh!\n\nLuc trc tng rt thich cai cam giac m lanh but c trum chn, suy nghi min man v nhng ngi minh yu thng va mim ci.... \n\nNhng ri bng ngay kia oc 1 truyn u o noi ai y rng: ban ngay co ma ret bao nhiu cung co th chp nhn c,nhng ban m xin ng ma hay ret, ti lm nhng ai ngu ngoai hin...\n\nThy long minh ri tom... Mong tri ng ma ret m! >:D<","language":"notdetect","sentiment":"Mixed","author":"NguyễnThuHiền","author_text":"NguyễnThuHiền","md5_text":"54b7078a8b05e6a4ad3c08b08dc5638e","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757995540480},{"id":"353114738","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003166805067","authorlink":"BéBaLê","date":"2013-01-06T13:40:17Z","topic":"Kia K9","topic_text":"Kia K9","text":"Co 1 iu cht chn la khng co cai gi la cht chn...","language":"notdetect","sentiment":"Mixed","author":"BéBaLê","author_text":"BéBaLê","md5_text":"6fdae4d5e30170f2d4bf76abb7fff88e","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757996589056},{"id":"353114915","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003752180005","authorlink":"NhímTâmTrạng","date":"2013-01-06T13:33:33Z","topic":"Kia K9","topic_text":"Kia K9","text":"Nhiu lc t ra tht v tnh, ri m v nhn ra \n... mnh ang khc ... \nNhiu lc t ra tht nhn tm, ri m v nhn ra \n... mnh ang au ... \nNhiu lc t ra tht cng ci, ri m v nhn ra \n... mnh tht yu mm ... \nNhiu lc ci tht ti cng ai, ri m v nhn ra \n... mnh c c...","language":"notdetect","sentiment":"Mixed","author":"NhímTâmTrạng","author_text":"NhímTâmTrạng","md5_text":"ba6271546db9f77444c6b857353f1854","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007757997637632},{"id":"353114021","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=435678259832132","authorlink":"Colorsoflife","date":"2013-01-06T13:58:19Z","topic":"Kia K9","topic_text":"Kia K9","text":"CHUYN TNH I DP\nAnh chng mun cng em lm i dp.\nDu song hnh nhng u c bn nha\nK trc ngi sau sut qung ng di.\nTuy mt hng m chng h nhn mt.\n\nAnh no mun mi khi ln pha trc.\nLi bt em t ln mt t th.\nAnh sao n khi ngng mt nhn tri\nLi bit rng t en em ang ta.\n\nAnh u mun chia phn bao nng nhc.\nCa sc ngi ca vinh nhc bon chen.\nNhng thm nhung kia, nhng ct bi i thng.\nNo phi th bt em cng gnh chu.\n\nAnh khng th pht no ht hng.\nRi c k dm nng bn em.\ni dp kia u phi mi song hnh.\nC bao gi dp t cng mt lc?\n\nAnh sao chu ni c k no trng ging.\n nhn vo em li bo ging anh.\nRi mt mai phi minh chng hng hn.\nRng c th s bit ngay khng phi!!\n\nThi em nh bi th i dp.\nChng th l hnh dng ca hai ta.\nTuy ni nh chng km phn da dit.\nCng phi ty hon cnh v von","language":"notdetect","sentiment":"Mixed","author":"Colorsoflife","author_text":"Colorsoflife","md5_text":"ef2a45415d0b68bd1b7e544770b8596e","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758042726400},{"id":"353114558","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=427508850651984","authorlink":"Hộinhữngngườithíchđọctruyệntìnhcảm","date":"2013-01-06T13:43:05Z","topic":"Kia K9","topic_text":"Kia K9","text":"<3~~>Gi ngi em yu:\nNh mt thoi quen khng th thiu, em lai tim v vi binkhi chiu xung vao dip cui tun kimcho long minh mt chut gi o binh yn, nhe nhom.Em ngn ng ngi ngm tng t song ni nhau anh manh vao my choma tung bot trng xoa, bng dng thy minh le loi va mun gp anh hn bao gi ht! Em m mang nghi v anh, mun noi vi anh tht nhiu!Anh bit khng? Ln u tin em xa vong tay cua cha me, mt minh lac long va n c ni t khach,thc s em cha u ban linh chng nhngva vp t cuc sng y mi me nay.. Ap lc cng vic, s e nen cua xa hi lam con be con 19 tui ngy ng, v t quen sng trong s ch che cua gia inh nh em thy nget th, c c, b tc ri lng le tim ni thanh tinh va v oa nc mt, nc n ngon lanh nh mt a tre. Luc y, em cn lm b vai vng chai cua anh.Gin anh lm, anh bit khng? Lai thm mt ngay 20/10 bun. Em cm ghet nhng ngay l. Tht ich ky ung khng anh? Nhngngay o, tui ban em c cht ngt trong hanh phuc yu thng, con em lai thui thui ra hang hoa mua my bng hng v nhe nhang cm vao lo nh rng, minh cung la con gai, cung yu hoaChc sinh nht nm nay em se bun lm y! Xa cha me, ban be, se chng coai ct tng em mt canh thip, mang n cho em mtmon qua, th nhng em vn nui hi vong rng co ng But la anh xut hinva cho em c toai nguyn gic m: Nghe mt bai hat trong ngay thi nn.Sinh nht em cung la luc ma ni ni trn khp th gii, ngi ta gi tng nhau nhng cu chuc an lanh ngay Chua giang sinh; Noel m ap va hanh phuc, vy ma tai sao em vn cam thy co nhng vt gio len loi qua phi?Sao lai khng gin anh ch!ng v, hai ban chn nhobe cua em c tim ngt, au nhc vi lanh. Gia nh em c anh mua cho nhng i tt in hinh my con kitty nho nho xinh xinhva nhc em thng xuyn mang giay gi m , chn em se bt au but o anh a.Em trach anh nhiu vi chiu th Bay nao anh cung bo mc em mt minh th thn ap xe quanh thanh ph nho be nay va ngm bao ban be cungtrang la qun quyt bn ngi yu. Trng ho hanh phuc lm c! Vy ma anh n em thui thui.My bc tng phong em cng lm! Nhiu ln em cong vai cai inh c treo , nhng cang ong, vi va ri ba bai ma inh thi cong ca ln. Nu co anh o, em se khng phai t m m hi ma vic cung chng xong.Em thich ma va khat khaocung anh ap xe rong rui trong ting go nhip u u cua ma it nht mt ln, thich hai a t sung ri pha ln ci vi vui sng. Vy ma cho n luc nay, o vn la mt gic m.Anh v tm mc em lang thang trn nhng con ng ngp nc v m li bi my ngay sau o khng ai chm soc.Em to mo qua cai cam giac e ngai, then thung khi ln u tin co mt ban tay rn chc nm ly ban tay nho be, yu t cua em, dt em qua ng, m ap va binh ynEm cung khng bit minh se nh th nao nu ln u tin co mt ngi con trai la anh m em vao long? Lm luc em thc mcqua v nhng nu hn kia,sao ngi ta bao la ngot ngao, la chay bong h anh?Em con co rt nhiu c mun c cung anh thc hin, mun c hen ho, bit yu thng, hn di nh ban be cua em nh bao ngi con gai khac.Em gin anh nhiu lm anh bit khng? n by gi vn em mon moi ngong ch nh mai vang i mua xun. Sm n vi em anh nhe! Em se ch, bi em cn co anh trong cuc sng nay!","language":"notdetect","sentiment":"Positive","author":"Hộinhữngngườithíchđọctruyệntìnhcảm","author_text":"Hộinhữngngườithíchđọctruyệntìnhcảm","md5_text":"f0dc114b917b496948e89c289c9d4998","dimension":["Year 2010"],"category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758044823552},{"id":"353114726","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003472176616","authorlink":"BánhBaoHấp","date":"2013-01-06T13:40:22Z","topic":"Kia K9","topic_text":"Kia K9","text":"Ngi nha ma trong ngi khng yn c..tinh hinh nay ngay kia phai ra Ha Ni thi...hix2..","language":"notdetect","sentiment":"Mixed","author":"BánhBaoHấp","author_text":"BánhBaoHấp","md5_text":"8e1ae27400b5abcaa4a8f5ae1e1e942b","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758046920704},{"id":"353114295","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003146732128","authorlink":"MaihoaNguyen","date":"2013-01-06T13:51:26Z","topic":"Kia K9","topic_text":"Kia K9","text":"Mt ngy mt mi v lo lng\nMa ng tht lnh,lnh tht!\nSp tt ri,ti i ci ln no\nNgy tn th c xy ra u?","language":"notdetect","sentiment":"Mixed","author":"MaihoaNguyen","author_text":"MaihoaNguyen","md5_text":"0a9ddc23847524e53a9c1a5c9c6479ef","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758047969280},{"id":"353114186","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003317640538","authorlink":"NguyệtNormal","date":"2013-01-06T13:53:38Z","topic":"Kia K9","topic_text":"Kia K9","text":"Mnh ni l ko c g ri,nhng m ngi nghe ko chu hiu,c hiu cng ko chu tin...Suy ngh ra th trn i ri ni cc kiu con iu :)) Tin hay ko ty bn *Trch ca anh Hng yu qu* th thi :))\n\n cn 1 vn na. Mnh ght nht my bn gi cho mnh tin nhn g m ''nu bn ko lm theo b m bn s cht'',''nu bn ko gi cho ngi ny ngi kia th s gp xui xo,tai nn, c i'' ...=))\na y! Cm phin cc bn ng bh gi my ci mnh na nh! ^^\n Cm n rt nhiu <3","language":"notdetect","sentiment":"Mixed","author":"NguyệtNormal","author_text":"NguyệtNormal","md5_text":"ae0cad4e87dbd23d611e1e66da99b959","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758049017856},{"id":"353114296","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100001243444698","authorlink":"PhiTrầnHoàng","date":"2013-01-06T13:49:00Z","topic":"Kia K9","topic_text":"Kia K9","text":"i git no sau bao ngy n chi xa a gi l lc quay li vi hin ti...nhn ng qun o ngn ngm qu.","language":"notdetect","sentiment":"Mixed","author":"PhiTrầnHoàng","author_text":"PhiTrầnHoàng","md5_text":"c27ee9aa9d4cdfbc9bb562a6976d19cf","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758050066432},{"id":"353114197","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003912466651","authorlink":"SởKhanh","date":"2013-01-06T13:55:05Z","topic":"Kia K9","topic_text":"Kia K9","text":"584-1314-520","language":"notdetect","sentiment":"Mixed","author":"SởKhanh","author_text":"SởKhanh","md5_text":"375d2e23113b2ea3b05db5d5f95b44bf","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758051115008},{"id":"353114598","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003901562749","authorlink":"CườngUno","date":"2013-01-06T13:37:47Z","topic":"Kia K9","topic_text":"Kia K9","text":"Anh c ngh chia tay anh s qun c em........................................................................................................................................................................................ai ng..................................................................................Anh qun sch...@@!","language":"notdetect","sentiment":"Mixed","author":"CườngUno","author_text":"CườngUno","md5_text":"6762f2d953844e02b9035578020b6713","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758052163584},{"id":"353114904","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003659575742","authorlink":"ĐạiLuciFer","date":"2013-01-06T13:28:02Z","topic":"Kia K9","topic_text":"Kia K9","text":"nhiu lc ngh nhn tc mnh # ch g ci bt m tm p ngc u , lc th ch # g ci t qu , lc th nh ci t ni . hc . nhng nhiu lc cng p pht . hahaahaha/ c th ni l s ta ca mnh hahaha. ngh m bn ci","language":"notdetect","sentiment":"Positive","author":"ĐạiLuciFer","author_text":"ĐạiLuciFer","md5_text":"8690ec4392170952eab18be56bb98101","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758053212160},{"id":"353114338","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004697798633","authorlink":"NguyễnGiang","date":"2013-01-06T13:50:00Z","topic":"Kia K9","topic_text":"Kia K9","text":"lieu cuoc song nay con dog luc gi de ta ton taj va song tiep???!!!","language":"english","sentiment":"Mixed","author":"NguyễnGiang","author_text":"NguyễnGiang","md5_text":"a3716facb99650c9570fdaa457ebca48","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758054260736},{"id":"353114927","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004989846587","authorlink":"TrầnKhánhNgọc","date":"2013-01-06T13:27:55Z","topic":"Kia K9","topic_text":"Kia K9","text":"Minh Quyt Phm nha..t nhin nhc ti Hu lm j ti hm kia e nm m thy n...","language":"notdetect","sentiment":"Mixed","author":"TrầnKhánhNgọc","author_text":"TrầnKhánhNgọc","md5_text":"c0caa80f9ad175c62c8a13355a17d3b4","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758078377984},{"id":"353114704","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=130520477098482","authorlink":"Hộinhữngngườithíchtâmsựvàmuốnđượcchiasẻ","date":"2013-01-06T13:37:34Z","topic":"Kia K9","topic_text":"Kia K9","text":"Cac mem i hin tai ad rat cn`s ung h cua cac mem.that s ad khng mun cu like 1chut' nao nhng Stt nay ad mun cac mem like that nhiu cho ad....ad cn`lm ' y...1like la th hin 1s quy mn' cua moi ngi ti Ban quan tri Page mong mun 2ad Michu va Trang Kull tr lai ...c khng a....like nao...thank cac ty nhiu nhe ....<3\nAi mun c oc Truyen hay thi hay like cho Michu....Stt c thi like cho Trang Kull......ok..cung page phat trin nhe .....iu lm'....:)))\n\nLuna","language":"notdetect","sentiment":"Mixed","author":"Hộinhữngngườithíchtâmsựvàmuốnđượcchiasẻ","author_text":"Hộinhữngngườithíchtâmsựvàmuốnđượcchiasẻ","md5_text":"9422cab41a575b76556976b3d757f425","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758079426560},{"id":"353114596","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002899490366","authorlink":"DonaDang","date":"2013-01-06T13:35:10Z","topic":"Kia K9","topic_text":"Kia K9","text":"Em nh 1 ngi ko nh em .. Em nh 1 ci nm tay khi em khc ...em nh 1 ci m tht cht khi em git mnh tnh gic .... Nh ch l ci bit ci cm jiac l con gi ..... v nh bit ngy Mai ngi y lun khng thuc v mnh na .... Phi chng khi mc vy ri con gi lun Cn 1 b vai da ..... Cn 1 chic o m hn khoc ...... V ...^^*** ??","language":"notdetect","sentiment":"Mixed","author":"DonaDang","author_text":"DonaDang","md5_text":"6978a47dd05062b17429cdcd58a954fb","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758080475137},{"id":"353114727","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002640856886","authorlink":"TimQuenLang","date":"2013-01-06T13:36:13Z","topic":"Kia K9","topic_text":"Kia K9","text":"....Thy nng lng mi ma xun v !","language":"notdetect","sentiment":"Mixed","author":"TimQuenLang","author_text":"TimQuenLang","md5_text":"c02e0a45d22922f5bcd9597744d0b7ec","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758081523713},{"id":"353114425","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002828605429","authorlink":"HuongPham","date":"2013-01-06T13:41:43Z","topic":"Kia K9","topic_text":"Kia K9","text":"- Tnh hnh l. . .\n- ang nh 1 ngi\n- ang nh n ci\n- V ang li yu thng...","language":"notdetect","sentiment":"Mixed","author":"HuongPham","author_text":"HuongPham","md5_text":"61bcd8118b204ce9dfab145b0b374e0f","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758082572289},{"id":"353114457","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004948119605","authorlink":"TrangLươngMinh","date":"2013-01-06T13:45:35Z","topic":"Kia K9","topic_text":"Kia K9","text":"S rt au n khi bn yu mt ngi no m khng c p li.Nhng cn au n hn khi bn yu mt ai m khng dng cm ni cho ngi bit bn yu nh th no. \n\nC nhng khonh khc trong cuc i khin bn nh ngi ta tht nhiu , n ni bn ch mun chy n v m h tht cht . Hy cho ngi bit bn c suy ngh nh th !. \n\nTrao cho ai c con tim mnh khng bao gi l mt s m bo rng h cng yu bn, ng ch i iu \n\nngc li. Hy tnh yu ln dn trong tim h, nhng nu iu khng xy ra th hy hi lng v t ra n cng ln ln trong bn. \n\nTng lai ti sng thng da trn qu kh qun lng, bn khng th sng thanh thn nu bn khng vt b mi ni bun qua. \n\nMt iu ng bun trong cuc sng l khi bn gp mt ngi c ngha i vi bn , ri cui cng nhn ra rng h sinh ra khng phi cho bn v ch c th h i ....Nhng khi mt cnh ca ng li, mt cnh ca khc li m ra. iu bn cn lm l thi khng ch i ni cnh ca ng, hy tm mt cnh ca khc ang m ra cho mnh. \n\nng qun hy vng, s hy vng cho bn sc mnh tn ti ngay khi bn ang b b ri. \n\nng nh mt nim tin vo bn thn mnh. Ch cn tin l mnh c th lm c v bn li c l do c gng thc hin iu . \n\nng nhng kh khn nh gc bn, hy kin nhn ri bn s vt qua. \n\nng ch i nhng g bn mun m hy i tm kim chng. \n\nHy mm ci trong cuc sng. N ci ca bn mang li hnh phc cho ngi xung quanh v do cng mang li hnh phc cho chnh bn. \n\nng bao gi ni khng cn yu na nu nc mt ca ngi kia vn c th gi chn bn . \n\nng khc v mi vic qua, hy ci v mi vic ang ch pha trc. \n\nng chy theo v b ngoi ho nhong, n c th phai nht theo thi gian. \n\nng chy theo tin bc, mt ngy kia n cng s mt i. \n\nHy chy theo ngi no c th lm bn lun mm ci bi v ch c n ci xua tan mn m u ti trong bn. \n\nHy lun t mnh vo v tr ngi khc, nu iu lm tn thng bn th n cng s tn thng ngi khc. \n\nNgi hnh phc nht khng cn phi c mi th tt nht, h ch l ngi lm cho mi vic, mi chuyn u din ra theo h. \n\nHnh phc thng nh la nhng ai khc lc, nhng ai b tn thng, nhng ai tm kim v th. Nhng nh vy, h mi bit c gi tr ca nhng ngi chung quanh h. \n\nTnh yu bt u bng n ci, ln ln bng n hn v thng kt thc bng nc mt (Okies!). \n\nKhi bn c sinh ra , bn khc cn mi ngi xung quanh ci. Hy sng sao cho khi bn qua i, mi ngi khc cn bn, bn ci. \n\nHy gi nhng vt d nh nht ca ngi bn thn... bit u sau ny n s l mt k nim ca bn. \n\nHy ni nhng li yu thong nht n ngi m bn yu thng .... \n\nBn cha cn n 3 giy ni \"I love you\", cha n 3 pht gii thch cu ni y, cha n 3 ngy cm nhn c ngha ca n , nhng chng minh cu ni n gin y th c cuc i vn l cha . \n\nCng nh vy: Ch cn thi gian mt pht th bn c th cm thy thch mt ngi. Mt gi m thng mt ngi. Mt ngy m yu mt ngi. Nhng m bn s mt c i qun mt ngi. \n\nKhng ai ng gi bng nhng git nc mt ca bn. V nhng ngi ng gi s khng bao gi lm bn khc. \n\nCh khi bn tht s mong mun ai c hnh phc, thm ch hnh phc khng phi dnh cho bn, bn mi hiu rng bn yu ngi tht s mt ri. \n\nC mt s tht l bn s khng bit bn c g cho n khi nh mt n, nhng cng c mt s tht khc l bn cng s khng bit mnh ang tm kim ci g cho n khi c n. \n\nHy lm nhng g bn mun lm, m nhng g bn mun m , ti u bn mun ti , tr thnh nhng g bn mun , bi bn ch c mt cuc sng v mt c hi lm tt c nhng g bn mun . \n\nYu l mo him v c th b t chi. Nhng khng mo him th l tht bi ri v trong cuc sng iu nguy him nht l khng th thch iu g . \n\nTnh yu l con dao. N m nt con tim hay c khi n khc su vo tim ta nhng vt khc diu k v s theo ta n cui i. \n\nNgi ta v o bng kim, cn bn s v con tim bng g? \n\nTnh yu l mt mn qu m ch c th m chi ny lc khi c trao tng i. \n\nTi mong bn c hnh phc tr nn ngt ngo , c th thch tr nn vng mnh , c ni bun bn hiu cuc i , c nim tin bn bc ti v c tnh yu dng hin cho i","language":"notdetect","sentiment":"Mixed","author":"TrangLươngMinh","author_text":"TrangLươngMinh","md5_text":"393885d7c2b41bfa3c1a77961127ed4e","dimension":["Extreme Positive (autos)"],"category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758098300928},{"id":"353114659","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100001204830854","authorlink":"LyKhánhTrương","date":"2013-01-06T13:36:22Z","topic":"Kia K9","topic_text":"Kia K9","text":"Tr Tag cho NT Hiu\nAi oc status nay va nhng ai c tag stt ny mak ko tra tag hoc ko tag thm 10 ng # na o ng se phai gp en ui sut i. Ch c ngi iu lun =))\n1. Tn : trng khnh ly\n2.Mu sc yu thch: xanh da tri :x\n3.Ni di: ko ni di ko phi ngi VN :))\n4. Bi ht mi nghe: Yu anh\n5. Idols: noo~~\n6. Khi b stress: ng :\">\n7. That tinh: v ang y :((\n8.Hn ngi khc: noo~~\n9.Na m hm qa lm g: m thng em :\">\n10.Trang web hay vo: FB, mp3\n11.Nickname: ph >\"<\n12.Cung hong o : thn nng\n13.Vt nui: nooo~~\n14.Tc di hay ngn: di\n15.Chiu cao : 1m60 c m chc ch n =))\n16.Quen bn mi: nhiu\n17.Thay i h tn: never\n18.Thung ng dy lc: 8h :\">\n19.eo khuyn tai : ko~~\n20.Thun tay: phai\n21.Ung bia, ru: c 2\n22.S ma: na c na k\n23.i ra ng mt mnh vo bui ti: ko c cc tnh yu th iem ko dm i >\"<\n24.eo knh: thnh thong\n25.Thch lm bn cng nhng ngi: all\n26.Cng vic nh lm trong tng lai: mun c nh tin m\n27.Bun: i lc\n28.B m: cc t\n29.Mn th thao iu thch: ng\n31.Trng cy : noo~~\n32.Mun c bao nhiu a con: ang ngh xem ly c my thng chng :))\n33.Chn hc : often\n34.Hay tm s: ch vs BFF thi\n35. trong lp hay lm g: 1 l ng 2 l chp v 3 l chm gi :))\n36.Ght: ht tin\n37.Thch ra nc ngoi ko: c~~~\n38. nh thng lm g: ng\n39.Thch hc khng: ko~~\n40.Thch lm g: ch ng\n41.Thng i chi vs: c i u m thng vs ko\n42.Ght lm g: i ra ng vo ci tri ny. may m mnh c n.y\n43.T k: never\n44.Ght hc mn: anhhhhhhhh~~~~\n45. Nu bn tht bi: lm li t u\n46.Thch i chi : thin ng\n48.Ch nht thng lm g: nh ng\n49.Bn ang lm g: tra stt\n50.S: ht tin\n51.Nu mn g gii nht: rang tht =)))))\n52.Thch nht: chm gi\n53.Bit bi khng: no~~\n54.C nhiu tin khng: ko 1 ng xu dnh ti\n55.ang s ci g: s cht rt\n56.S trng l g: ng :))))\n58.Bui sng n g: 1 l nhn 2 l m tm\n59:Sng u: nh\n60.Trng thi ca bn by h: FA\n61. By h mn lm g: thoat khoi cai stt nay i tm >\"<\n62.nh tag nhng ai: mi ngi\n63.Hc gii khng: bt\n64.Bn gin ngi khc lu k: lu. nu c mi i n th nhanh =.=\n65.Bn c beautiful: sao n ko c ch very nh :))))\n67.Bn l ngi yu ui hay mnh m: mm yu :))))\n68.Thch i chi k: co\n69:Qu ai nht: bn thn\n70.By h bn ang t hi u j: i tm h ny chc rt lm nh :((\n71. Mun i u nht : i ng\n72. V mc in r ca bn : ci ny phi t kim chng\n73. Lc tm hay lm j : di nc :))\n74.La ngi khc: ch b\" na\n75. yu cha: cha. ch thc thi =))\n76.C thch b dnh tag k: ma n. in m thc. lu v!!!\n77.Ngi pm gn y nht: in thoi th l Phm Hng Mnh cn FB l Thang Nguyen \n78.C thch c truyn k: c. tr tnh cm th thi ri \n79.Thch hoa g: LY :))))\n80.Thng mc g: q ao\n81.Chi c nhc c g: chi chuyn :))\n82.Bn hay u: nh\n83.Bn thch ma no nht: thu\n84.Mun quay li vo thi im no trong qu kh: lc cn cha sinh ra. c g mnh l men :((\n85.Ngy bn thch nht: 28 thng 10 \n86.Mun gp ai nht: ng L Lc :))\n87.Bn thy mnh c c c ko : ko~~\n88.Nhc ch ca bn l: ch nh na\n89.Thch nghe bi ht no: Yu anh\n90.Bn c s cht k: ko th l ns di \n92.C hay nh bn k: hi b th c :))\n93.Thch n qu g: qu g cng thc\n94. lp c hay b bn b tru ko: often\n95.C chn bn chi k: c\n96.Ai t tn cho bn: bc Nng\n97.Mong mun ca bn: mai thi qua c mn anh :((\n98.Hm qua lm g: c gp ng l lc :))\n99.Cm xc hin ti: lnh v~~~\n100. Mun ni vs nhng ngi: t ti bay nn tag . xin li nha. t mun mi ng ging t by h :)))","language":"notdetect","sentiment":"Positive","author":"LyKhánhTrương","author_text":"LyKhánhTrương","md5_text":"d85b5c5ccd356d374d2e1658521e100c","dimension":["Styling","Extreme Positive (autos)","Vehicle Quality"],"category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758100398080},{"id":"353114124","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004763762576","authorlink":"ChiHieuHtv","date":"2013-01-06T13:52:11Z","topic":"Kia K9","topic_text":"Kia K9","text":"Tnh yu\nGig nh 1 Bi ton kh\nMn gii c\nth...\n.\n.\n.\n.\n.\n.\n.\n.\nth phi \"nhp\" trc vi ngi!\n=))\n... :v","language":"notdetect","sentiment":"Positive","author":"ChiHieuHtv","author_text":"ChiHieuHtv","md5_text":"19af1d2af7e9fdfe18b5383e77d97329","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758102495232},{"id":"353114199","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003899946089","authorlink":"ItachiSharigan","date":"2013-01-06T13:52:00Z","topic":"Kia K9","topic_text":"Kia K9","text":"cht ma ri ! ngy mai i hc qun s 1 thng m tt c qun o ca mjh cha kh nak ? phi lm sao y ?","language":"notdetect","sentiment":"Mixed","author":"ItachiSharigan","author_text":"ItachiSharigan","md5_text":"9267e077cac937a4ae35cbc4454d8fbc","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758103543808},{"id":"353114340","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004281160052","authorlink":"CôBéKhôngTên","date":"2013-01-06T13:49:58Z","topic":"Kia K9","topic_text":"Kia K9","text":"A a nhng gi e ha vi anh em a thc hjn ht rj cki con 1vjc na thj caj ngay y sp tj rj em se thc hjn nt lj ha cuj cung anh nk nke.....\nVa khj lj ha cuj cung c thc hjn tki em se maj maj bjn mt khoj c/s cua anh anh hay ck nke","language":"notdetect","sentiment":"Mixed","author":"CôBéKhôngTên","author_text":"CôBéKhôngTên","md5_text":"11b6d6752347ab79a68a34a468d189a3","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758104592384},{"id":"353114410","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=181636271961632","authorlink":"Anhakcolẽemyêuanhmấtrồiđừngbỏrơiemnhé","date":"2013-01-06T13:47:49Z","topic":"Kia K9","topic_text":"Kia K9","text":"Mt cu chuyn cm ng :((\n\nBt u 1 cu truyn \nLinh , Nam v Long l 3 ngi bn thn , cng hc 1 trng . Nam thch Linh lu m cha dm th l . Tnh yu ca Nam ch c giu kn trong 1 quyn nht k . nhiu ln Nam nh ni ht vs Linh nhng cha bao gi Nam dng cm ni ra chuyn c . Ln no cng th '' ln ny s c thi m '' nhng ..............\n\nMt ln th l tnh cm \nCho n 1 ngy , Nam quyt nh s ni cho bng c . Nam t nh '' nht nh s c , nht nh s c , nht nh.......''\n_ Linh , mnh...........Linh........-Nam p ng \n_G vy Nam ??? - Linh hi \n_.....mnh....mnh.....................k .....k c g c . \n1 khong lng gia 2 ngi , Nam vn t dn vt mnh , ti sao k ni nn li.....\n_Linh , tht ra th mnh ........\n_Ka Nam , v nh ri , chuyn g ni sau nha - Linh ci ti .\nBng Nam cm thy ht hng , mi chuyn ng l xong , ngy mai k bit th no y. Liu Nam cn can m ni vs Linh ??? Cu hi c dn vt Nam.\nNhng hm sau th Linh qun i , k hi li Nam na . V vy , Nam li k c c hi ...\nNgy Valentime \nHm nay l 14/2 , Nam chun b 1 thanh chocolate dnh cho Linh , v vn nh mi nm , Nam ch bit giu thanh thanh chocolate vo cp Linh. Nm no cng th , trng t chc i hi mng ngy l tnh nhn , mi bn s t chn cho mnh 1 ngi khc gii i tham d . Nam h hi mi Linh cng tham gia th \n_Nam , c ai i cng cha ??? mnh s i cng Long , vy nh , ti mnh i trc , lt Nam ti nha . - Linh vt chy mt , n bn Long .\nCuc i tht tr tru , k phi nh vy , r rng l Linh thch Long ri cn g na . Nam au n , dn vt nhn ra tnh cm ca Linh....V nh chp nhn '' chn i '' 1 bn n i cng ..........\nCui cng bui l kt thc , Nam v Linh vn cng nhau bc v nh trn con ng quen thuc.Gia 2 ngi vn l ci khong lng y , khong lng m Nam rt ght ......\n_Mnh...lm quen vi...vi...nhau nh , nh...ngi ta vn gi l ngi....y..u....y .\nNam git mnh , chuyn g th ny , Nam c tin ni k , c ng Linh va ni k vy , Nam qu sung sng , trong c hng tc cu ni '' ok '' m Nam vn mun ni , v Nam qu sung sng m k ni ni nn li..........\n_Lm g m cu suy ngh nhiu th , tt nhin l mnh phi pht cu , v sao cu c nh vy h ???? Mnh....mnh.......a.....a y.....hihi - Linh ci\n_Haha , hay tht , th ra l cu a , lm mnh c tng tht , ng th , mnh v cu th lm sao c th ........\nLinh gin gi :\n_Cu , sut i k trng thnh , ngc ......\nNi xong Ling gin gi b i , li chng Nam ngc xt ng ngy ng , k hiu chuyn .\n_G ?? Chnh cu bo l a m . Nh th , chng l , cu ni tht ?????\nNhng ngy sau \nNam v Linh vn c gp mt nhau , n gin thi , v 2 ngi hc chung lp . 2 ngi by gi c 1 khong cch rt ln . Nhiu lc Nam mun ni chuyn nhng Linh c trnh Nam hoi . Thi gian c th tri i ..... 2 ngi c th lng thm .....Nam rt au kh ....Nhng tt c nhng g Nam c th lm ch l : m thm trt ht vo cun nht k . \n'' Nam i , my tht hn h '' , Nam thng xuyn ngh mnh nh vy . \nV tip l 1 chui ngy Linh k i hc , Nam suy sp.........\nNgy ma....\nTht k may cho Nam , hm nay Nam ngh hc , Nam k c tm trng i hc na th Linh li xut hin trn lp . Tri ma nh trt , di mi hin c 2 ngi ang tr chuyn : \n_Mnh c th coi nhau nh ngi yu ??? - Long ni\n_C li g ko ??? - Linh tr li lnh bng\n_, mnh bit chuyn hai ngi , nu nh cu ng , Nam c th s ghen , v..........-Long ni.\n........................\n--------------------------------------------------------\nNgy hm sau , Nam nhn c tin nh st nh , Linh ng lm ngi yu ca Long . c th ni l ngy au kh nht ca Nam , Nam gc ng , Nam tht vng ,Nam k cn tin vo chnh bn thn , Nam gi tr thnh k '' tht bi '' \n1 iu ng ni hn , Nam k t bt c thi kh chu no : \n_Chc mng cho 2 ngi nh - Nam ci trng tht tu , mo m v gn nh chy nc mt \nng , Nam giu nc mt ca mnh vo su tn trong tim , tri tim ang r mu....\nNgy khai trng.......\nCui cng th cng kt thc nm hc cui cp , ngy chia tay bn b , thy c , Nam vn ng , trng v hng ca Linh , cn Linh ng bn Long . Nam ch mun nu ko , gi ly 1 cht g , d ch 1 cht thi , nhng giy pht c trng thy Linh . Bng , Linh tin v pha Nam \n_Nam n , sp kt thc bui chia tay ri , mnh chc Nam thi c 1 trng tht tt nh . Mnh cng phi i y , sau ny vn c th lin lc , nhng s t gp nhau , tm bit Nam nh - Linh ni , cho Nam 1 ci ri chy li bn Long\nHt tht ri, th l ht tht ri , tnh cm ca Nam by gi k c ch ng trong tim Linh , Linh mi hng n Long , cn Nam , Nam mi hng v Linh . \n'' Tm bit Nam nh '' sao c th ni ra 1 cch d dng nh th kia ch ????\nm ci bun t \nThi gian tri i , nhc Nam , Linh , Long nm no trng thnh , gi y , Nam nhn c giy mi d hn l ca Linh & Long . \nNam n d vi mt con tim cht , Nam c t v vui mng , ci ni , chc mng nhng c ai bit u tim Nam ang au tht . Tht nh 1 bi kch , Nam vn ci......\nNam ch cn 1 cht hi vng nh nhoi , mong Linh s n bn Nam , v 2 ngi s c 1 kt cc tt p , nh Nam vn thng mong mun , ri 1 iu k diu s n........\nNhng tt c kt thc thc s , l cid kt thc , gi y , Linh l ca Long.\nL tang ca ngi y....\nNhiu nm tri qua v gi th Nam ang ng trc nm m ca Linh . Nam sng 1 mnh vi tnh yu y , vn ch i 1 cch ngu ngc d Linh s k v , Nam ch c Linh .....\nKhi mi vic xong , gia nh Long mun xem li nhng k vt ca Linh li , trong c 1 cun nht k.Mi ngi m ra xem v kt thc l 1 chuyn tnh k ai ng ti....\nS tht v hi hn.......\nCho n by gi , b mt ca Linh c tit l , trong cun nht k , Ch 1 dng thi \n'' Linh yu Nam ''\nTh y , s tht bao gi cng nh th y , Nam a khc , a khc nh 1 a tr . Nam qu hi hn , qu cm ght mnh .\n'' Gi nh Nam can m hn....gi nh Nam c th ni Nam yu Linh....gi nh hm Nam tr li Linh - ok...gi nh Nam s gi Linh li khi Linh ng Long....gi nh Nam ngn li m ci trc khi qu mun...gi nh.....gi nh ........''\nNhng......tt c qu mun , ch cn li nhng '' gi nh .....'' ca Nam.","language":"notdetect","sentiment":"Mixed","author":"Anhakcolẽemyêuanhmấtrồiđừngbỏrơiemnhé","author_text":"Anhakcolẽemyêuanhmấtrồiđừngbỏrơiemnhé","md5_text":"dbf99623199e6e4dedaa6d756581e0ad","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758128709632},{"id":"353114202","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004498322069","authorlink":"KhoaRoyal","date":"2013-01-06T13:51:57Z","topic":"Kia K9","topic_text":"Kia K9","text":"Va tm xong .... phi cng nhn l khng ni lun","language":"notdetect","sentiment":"Mixed","author":"KhoaRoyal","author_text":"KhoaRoyal","md5_text":"2254d04911b6a9aa3e938c4ecb00edb1","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758131855360},{"id":"353114003","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004039278649","authorlink":"DuongMaiPhuong","date":"2013-01-06T13:58:26Z","topic":"Kia K9","topic_text":"Kia K9","text":"Bng dng mun khoc","language":"notdetect","sentiment":"Mixed","author":"DuongMaiPhuong","author_text":"DuongMaiPhuong","md5_text":"37317db1091f07e28a26c4593afad098","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758132903936},{"id":"353114307","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003205410288","authorlink":"DuyHải","date":"2013-01-06T13:53:14Z","topic":"Kia K9","topic_text":"Kia K9","text":"t nay b ngh like tc, khng like cho cc e xinh ti na =))","language":"notdetect","sentiment":"Positive","author":"DuyHải","author_text":"DuyHải","md5_text":"435594d6742f066640d3c9b8c1bc778e","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758133952512},{"id":"353114127","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004064767038","authorlink":"HàHọkGoBy","date":"2013-01-06T13:51:54Z","topic":"Kia K9","topic_text":"Kia K9","text":"Mai li phi xa con c ngy i ly hng:( nh lm:(","language":"notdetect","sentiment":"Mixed","author":"HàHọkGoBy","author_text":"HàHọkGoBy","md5_text":"243fcb9fd88881fac3c184c82eccd366","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758135001088},{"id":"353114147","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004007092739","authorlink":"MaianhNguyen","date":"2013-01-06T13:57:57Z","topic":"Kia K9","topic_text":"Kia K9","text":"Nh ai ai nh b.j nh ai y?c iiiiii","language":"notdetect","sentiment":"Mixed","author":"MaianhNguyen","author_text":"MaianhNguyen","md5_text":"684db1fc9d8b783edbd6543d2e20b1fe","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758135001089},{"id":"353114178","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=222748111159791","authorlink":"Cólẽnàomìnhxanhau","date":"2013-01-06T13:55:57Z","topic":"Kia K9","topic_text":"Kia K9","text":"Nh Nng Cnh Nh Ti\n\n[Truyn] Nh nng cnh nh ti (Cuc chin gia Nhp v Qun i Hoa)\n\nC5.\n\nSng nay mnh dy sm v mi au nhc qu. Chn cng au thu xng. Nm ngh mi v nh. T nhin mnh mun xin li, mun lm ha qu. Na v s nh, nh lm chiu nh th, bit u cn tm tr hi mnh di di. Na thy c li v em nh ra lm tr ci cho thin h.\n\n \n\nTing chung ca lm mnh git bn. Cm gic y nh ci hm b nh gi xung tt. Mnh tp tnh xung nh. Va l u ra, sut ng v nhn thy nh ng ca. Li g na y? Mnh khng dm bc thm bc no na, c ng trong nh ni vng ra:\n\n \n\n- G vy? Hm qua nh cha h ?\n\n \n\nNh th tay vo cng m then. e! M i lm khng kha cng. Chng hiu sao mnh run nh cy sy. S qui g nh ch, mnh to gp i nh, cng lm ng ca ra hp >< h. \n\n \n\nNi th nhng vn s. Nh mnh khng c ch. Nh c phm phm i vo, tay cm ng g bi nhi. Mnh lui lui vo bn trong cu thang.\n\n \n\n- Ny nha, nh na l ng ny khng nhn na u, ng ny nh li y!\n\n \n\nNh nhn mnh ci nhn mi. Mnh nh b in git, tay bung thng khng phng g na. hic.\n\n \n\n- M ti i cng tc v mua qu cho nh ng y y. Cn kia l xi sng nay m ti nu, n i cn i hc.\n\n \n\nNgi mnh mm nh bn. Nh i ra n ngoi sn ri m mnh vn l m xem ang mng du hay ang tnh. Lt t chy theo nh, n v c cu:\n\n- Mi ti th ny n sao gi? Mnh ch ch ln ci mi sng nh qu c chua\n\n- Khng n c h?\n\n \n\nNi xong nh li hm hm i vo. Mnh bit mnh li sai g ri. Li lui lui vo nh gi th th. Nh tin v pha bn, m ng va cho, ht hm vi mnh :\n\n- Ti mang v cho con Su nh ti n.\n\n- Khngggggggggg !\n\n \n\nMnh nhanh tay git ng li lun. G ch ? V l, cho ngi ta ri cn i li mang v cho ch n. C qu m khng lm g c. Nh d d ci nm m ln pha mi ti nt :\n\n- ng ti phi nh na nha. Lc y li trch ti khng yu thng sc vt.\n\ni c on nh cn quay li chc mnh :\n\n- Hm nay cha vn vai h ?\n\nCay c tht. Mnh ng gia nh, qun i ci trn, m ng bnh cu vi nm xi trng nh thng r.\n\n \n\nVo mt nm xi ri ln gc rt qun o. Mi ln nhai l mi ln mi au t ti. B nh con gi trng nh con nge m c mt pht m au nh hc u vo ct in. ang tru tro nhai, mnh sng mt khi nhn thy hai ci o ng phc trng ca nh treo pht ph ban cng. Mnh sung sng ng nghing xung quanh, cm ci gy phi qun o nh mnh khu khu hai ci o y v. Ln ny th cht nh nh. Hm nay anh cho nh ci trn i hc lun.\n\n \n\nTt xung phng, mnh treo hai ci o ca nh vo t. Mi con gi thm d man. T nhin ngi mnh cng . Kh gi! Thay qun o chun b i hc. Bng dng cht d. Khng bit sau v ny ci g s xy ra na. Nhng mnh cng ngoan c mun chin u n cng.\n\n \n\nVi vu chm chm trn ng v d g tit hai mi vo hc. C xe bus i qua lm mnh nhn mi vo tm poster dn trn thn xe. L n ng, ti khng nh ph n. ngha th c y nhng sao nghe slogan c chui th. T nhin thy bc bi. Li ngh ti nh. Mnh tr th mt a con gi th th c nh nhen qu khng?\n\n \n\n l ng c mt bng ngi quen thuc. L nh! Khng sai. Nh li hi bn l ng cnh chic xe p trng. Coi b hng xe ri. Mnh i chm chm ng r mt lc ri phng thng lun. Cho nh cht. Sng c qu nn tri pht y. i c on li thy kh chu. Vng xe li nhm nh. Nh vn lng tng, c v bt lc lm ri. Mnh nh tp xe vo l ng ch nh.\n\n \n\nNhn thy mnh nh git bn. Mt kiu hi hng hong s ch khng phi khun mt hm hm lc nh mnh u. Mnh trng xe ri bc n trc nh.\n\n \n\n- G? Th lng i. y khng nh con gi!\n\n- a! Vy ti y lm g?\n\n- Gip ng y ch lm ci g? Hi r c duyn.\n\n- H\n\n- H qu m. ng dp ra!\n\n- \n\n- qu x c, c mi ci xe sa mi khng xong. n hi!\n\n \n\nMnh va li ci tho hp xch ra. M thi bui ny ngi ta i xe p in ht ri, nh cn i ci xe mi ni xch hp ny lm chi? K quc. Va sa va qut mt hi. Mnh ang c ln mt, quay sang nh. Tri i! Mnh sut cht ng lun. Nh ng khc t bao gi. M khc khng ra ting g ht. C nhn mnh ri nc mt chy trn ra m. Khng hiu khc v gin mnh mng, hay khc v cm ng na. Run qu. Mnh m ra lng tng, ng gi u ct lc. Khng bit ni g cng khng bit lm g. May m nh bit , ci mt xung lau nc mt ri p th xe.\n\n \n\nXe quay tt l ri. Mnh sa m. Ng ng xem nh cn khc khng. Khng lm g t nhin khc chi vy m? May m khc nh ch khc to chc cng an phng ra bt mnh nht vo qu.\n\n \n\nNh lc ti sch a cho mnh ti khn giy mng. phi. Tay mnh en s du xch ri. Chm vo tay nh ang run run, thy lnh tot. Gi mi nhn nh, cui thu ri m mc ci o ng phc ma h mng dnh.\n\n \n\n- Sao tri lnh n mc th kia? B mun m ?\n\n- , qua tui phi qun khng kp o nn chc m gi to bay mt ri. Phi li o ma h ra mc .\n\n \n\nH? Mnh cng li ht ni lun. Gi thi vo t qun o mnh ch u. Nhn nh hin hin ti ti th ny m y ny n mc nga ht ngi.\n\n \n\n- Vy mc tm o ti c khng? Ngy xa cng hc cng trng y.\n\n \n\nChng ch nh tr li, mnh ra m cp xe, ly ci o ng phc phng ra a cho nh.\n\n \n\n- Chu kh mt cht nha. N hi to vi khng c thm.\n\n \n\nNh c chn ch mi, ngha ngha ci mi mnh. Chng i nh ng , mnh khoc o ln ngi nh lun ri i ra pha xe.\n\n \n\n- i hc i, mun bo v khng cho vo trng h?\n\n \n\nNh tnh ngi, li ci p chn trng xe ri tro ln phng trc ti. Nhn dng nh p xe vi vng m mnh . Trong lng mnh trng trng kiu g y, nh l nh i ri ht ht gi vi khng kh ch mnh theo.\nC6.\n\nTi ang ngi nhai bnh cu nh cho, online ln l facebook. Thy nh post ci stt mi: Cm n cu tinh qun i hoa. Mnh sng rm ran. Thm cm n ci xe p gip mnh lm ha. Ngi rung i thch ch mi. Nh tn Vi, nhn cng d thng, xinh xn gh. Ngi ln m xem ht ng nh ca nh, c note nh vit, cng thy c cm tnh. Mnh liu pm hm tin nhn ca nh.\n\n \n\n- Ny hng xm, ang lm g y?\n\n \n\n- Nh lng nch!\n\n \n\nB tay! a kiu g vy tri? M chc nh ni tht . Nh ny l m nh lng lm. Hi trc c ln mnh cn thy nh co lng chn c. Nh pm tip:\n\n \n\n- Th cn ng y?\n\n \n\n- ang un o luyn hng =))\n\n \n\nNh m i mt lc, mnh cng khng bit ni g. Lc sau nh li pm\n\n \n\n- Ny y i! y s g nht?\n\n- T ? Suy ngh 1 lc H, s rn!\n\n- Rn c, rn u ?\n\n- Khng, d ? Gin, con Gin . Con gin b lm ngm . Nhn gh cht\n\n- vy h ?\n\n- Hi chi ?\n\n- Thch th hi.\n\n \n\nRi nh li m i. Lc sau mnh thy ting nh gi ngoi ban cng. Mnh m ca i ra ban cng. Nh nhn thy mnh ci ti ri. Mnh ci li. Nh nh lm g vy tri ? Mnh ngi gh ln lan can. Mnh vi nh hi han nhau vi th, chuyn trng lp hc hnh. Thnh thong nh li ci tt mt, nhn mi. Nhn d thng khip c ngi. Ngi mt lc ngoi gi lnh, v mc mi ci o ba l nn mnh ht x lin vi ci. Nh lin chy v phng, lc ra cm ci o ng phc mnh cho nh mn hi sng, c gp gn gng, a cho mnh. Cn nh nhng dn d :\n\n \n\n- y mc i, lnh !\n\n \n\nMnh hn h n o t tay nh, khoc ln ngi lun. Nh nhn mnh ci ci. a sao nhn khng ging n ci hin hin vy ?\n\n \n\nMnh ang ng ngh xem nn ni g vi nh by gi. Tim mnh ang p thnh thch . Bng thy trong ngi nht nht nga nga. Hnh nh trong ngi bnh ang c ci g lc nhc. Rn dng tc gy. Ngi khng dm manh ng g trc mt nh, mnh c ng im, nhng cng ngy cng cm thy r rng c g trong o mnh. Nh Vi ng chng tay vo lan can chm ch nhn mnh lm mnh cng thy nng bc.\n\n \n\nM i... R rng l c ci g trong o mnh tht. Ngi mnh gai ht c ln. Vi lt o ra vt trc mt. u mai gt.\n\n \n\nCon ch.. Thi cht chi by... > < Con nh hng xm tri nh. Dm nht gin vo o ri a mnh mc. Khi phi ni, mnh nhy nhn ht m ln. Mnh khng s g trn i, ngoi gin. Hi b tu siu nc b con gin chui trong vi siu chy tt vo mm m mnh kinh hi m nh ti tn gi.\n\n \n\nSao tri t qu thn nn ra c con nh hng xm c nh con t gic kia vy ? Nh nhn mnh ci ngt ngho, trong khi mt mnh xanh nh t nhi. M mnh di nh nghe thy ting ht vi ting chn mnh dm cm cm xung nn nh, gi vi ln :\n\n \n\n- Li ln cn in h Hong ?\n\n \n\n- M i !!!!! Gin !!!!!\n\n \n\n \n\n- Tao ln b gin v mm my gi. Im i !\n\n \n\nMnh ch thiu nc khc thi. Cha bao gi thy thng con trai no khn nh mnh lc ny.\n\n \n\n- nh kia ? My chi g c vy ?\n\n \n\nNh vn m bng ci. Mnh khng th ngh ch vi pht trc tim mnh cn p tng tng v nh. Mt gi nai d s. Nhn nh ci nhe ht c hm rng nanh ra, gh gai ngi.\n\n \n\n- Thch vy ! c khng ?\n\n- \n\n- Th th ng m ng ny tr o nha.\n\n- \n\n- y mua hai ci mi ri. C gi lm ca tin trong nh i nha.\n\n \n\nMnh ! Thua tp th my ri mnh cng khng bit na. Vut vut vai vi lng xem cn con gin no khng. Hm hc bc bi. Ch mun x xc nh cho b ght.\n\n \n\n- Ny nha. Mi sng ng ny sa xe cho ng y nha. V n vy h ?\n\n- Tng ch hng tht h ? Ch la ku.\n\n \n\nNi chuyn vi nh ny lc na chc u mnh phi c ra mt. Mnh vo phng ng ca ci RM. Nm vt ra ging m vn nghe thy iu ci ca nh vang vang ngoi ca s.\nC7.\n\nNy gi m gi mnh m c ln. Mnh va l mt ra khi nh tm m qut mt chng hi mnh lm g trong nh tm lu vy. Mnh mu mo, mt nhn nh qu to tu ngm ru.\n\n \n\n- D con tm !\n\n- C nm c thy my tm u ? Hm nay d qu\n\n- Con b gin chui vo ngi.\n\n- Th th t nay ngy no tao cng bt gin nht v ngi my\n\n \n\nTrn i ny c hai ngi n b c nht th gian. L nh hng xm v m mnh. Vo ging trm chn kn u. Chn vn au but, mi vn sng tm. Hm nay li c l gin lm mn ht ngi. Mnh phi ni l thn tn ma di. Con nh ny c nha. Mnh khng tr th thin h khinh mnh. i tha nh ai mt thng con trai lm tr nh mnh b mt con nh km 2 tui troll kiu.\n\n \n\nC ngy i hc khng nghe c g lt tai. Ch mi kim cch tr th nh. C ngh n cm gic l gin b quanh mnh l c mnh li si lc bc. Nhng tr th kiu g y h tri. Nng trong ngi.\n\n \n\nChiu rong xe v. V hi sm nn ng khng tc. Ti gn nh, ang lm nhm ht lng mn t th nghe ting nh gi tru tro ng sau.\n\n \n\n- Hong i, i Vi vi !\n\n \n\nLi tr g y tri ? Li cn xng Hong vi Vi ? Nghe rn tn chn lng.\n\n \n\n- Cho Vi i cng nha. 1 on thi.\n\n \n\n- Khng cho. u thiu ng. Trnh xa tao ra\n\n \n\n- Tui ang b 2 tn d hi bm theo. Cha tay ra gip ngi ta i m !\n\n \n\nNgonh li thy hai tn u tru mt nga ga r r ngay gn mt on. Nhn cng khng nguy him lm, chc thy nh d thng bm theo lm quen xin s. Ch ng ny khng vng ngi bn ny khng dm cp ca hay hp dim gia ban ngy u. Quay ra nh, nh nhe rng ra ci. Trng pht ght. Mnh r ga i trc. B mc nh trn mt ng sau. Ln ny th cht vi anh nha nhc.\n\n \n\nV n nh, vt cp xung bn, ngi thn ra gh. Cng thy lo lo. mc nh cho hai thng mt ln, ngh cng gh. Nh u nh c mnh h g mnh cng n nn c i. Nhng nh ny cng lm tr, khng bit c thot thn c khng na. *Vng tc*. V gi m my ngy nay au ht u. Cn u nhng thng ngy yn hng th ca mnh ngy xa. Cuc sng ca mnh vn d m nh mt cun len. T nhin nh xut hin nh con mo in, v ri tung cun len y ln. Gi lm sao ngi cun li t u by gi ?\n\n \n\nRa cng hng, vn cha thy nh v. Chng nh li phng xe ra xem ? D ngi ! Nh th nh chi in. M hm nay li lm nh ln cn ri. Khng bit ti v c b nh hnh khng. i ti i lui vn cha thy nh hin hnh. Mu trong ngi mnh chun b si ln ri y.\n\n \n\nKia ri. Nh lng thng ng xa. a sao xe c khng i m dt b kia ? Mnh nu cng nhn nh. Thy c li qu. Cht nh n sng qua nhn thy nh khc lc mnh sa xe. L khc tht ch khng phi gi b g. Mt pht duy nht trong ngy thy nh b bng yu t. Gi mt pht y li quay li. Nh b nh mt con mo, con mo ny tm thi khng in. Nhn nh i chm chm di bao nhiu l l rng, tim mnh cng rng tng mng ri. Mong nh nhanh v ti cng ch tim mnh rng ht ly g m th.\n\n \n\nNh m cng i vo. Coi b b hai thng mt ln kia chn ng xe ri. Tay o bn ton t. ang ngha nh, t nhin nh quay ra lm mnh. Mnh git ngi ng thng ln, u cng v thnh tng au nt c. Nh lm mnh lu lm. Mnh lc u khng bit lm g, ng xoa u nhn nh. G ch ? Hi mnh tng y v, gi mnh p li mt ln m nhn nhau th hn th ? V l ! Nh dng xe i vo nh. B mc mnh ngoi sn, ng di bao nhiu l l dng.\n\n \n\n....\n\nC8.\n\nTi nay n cm chng ngon g ht. Tru tro nhai cm nh nhai vi. M quay sang k chuyn\n\n \n\n- Qua Hong ng say th ? Khng nghe ting g ?\n\n \n\n- Ting g m ?\n\n \n\n- Chng c Thi v gy s. Say kht. nh p hai m con n n kh.\n\n \n\n- D...\n\n \n\n- D thng mt dy n k n li hn mi khng c. m... C nh b my.. chnh ra li hay...\n\n \n\nMnh c khng nut c thm g lun. Ung xong hp nc canh, cho m i ln phng. Ngi online chng thy nh u. Mnh hi hn v chuyn hm qua qu. Mnh ng l thng n b. i chp a con gi, khng bit ngng.\n\n \n\nNg ng ra ban cng cng khng thy phng nh bt in. Mnh lang thang ln sn thng ht th.\n\n \n\nNh ang ngi trn . Ngi thu lu mt gc, tay b gi, gc mt xung. Chc ang khc ri. Mnh ng ngi ca sn thng. ng khng c m ngi cng khng xong. Thnh ra mnh va ng va ngi, c khum khum nh i nh rm.\n\n \n\nMnh chng bit lm g. Mi bc tc, k hoch tr th t nhin v ht. Con mo in khng v cun len na. Nhng n ... t ri ! Mnh mun lm g , nhng li khng bit lm g.\n\n \n\nB xung phng nm 1 lc. Thy ngoi ban cng c ting ng. Mnh bt dy d dm ra xem. M ca ra thy nh ngi ung a chn, vt vo trn lan can. Khc xong ri ? Thay i nhanh vy tri ?\n\n \n\n- , ngi ng ln c cht th sao ?\n\n \n\nNh quay sang nhn mnh. Mt hoe. Nhng ming th ci ci.\n\n \n\n- m qua s qu khng dm ly o vo h ?\n\n \n\n- Khng thm ! Vt lun !\n\n \n\n- y git cho ri. Cm vo ct i.\n\n \n\nNhn thy ci o gp gn m mnh mun khc tht. Nh ny li nh quy g na y.\n\n \n\n- ny, y khng a u nha. Chi c va thi. Trong o li c thn ln rn rt ch g ?\n\n \n\nNh c ci mnh hoi. R o ra phy phy cho mnh xem.\n\n \n\n- C cn y mc th cho lun khng ?\n\n \n\n- Thi khi, tng lun y ! Thy n l k c hi hng li a v c c. Ai dm mc.\n\n \n\n- Cho y lun y h ?\n\n \n\n- \n\n \n\n- May qu, t nhin c ci gi lau !\n\n \n\nNh ci. Ting ci vang vang trong gi. Mnh vn cnh gic ng xa xa nh. Nh ny nguy him lm. ng gn th no cng b quy nh chi. Nh hm nay cng khng ni chuyn nhiu. Ging cn ngt ngt do khc. Mun hi xem hm qua b ng xe c lm sao khng m li cng . Mun xin li mt cu m tc c. Khng ni ni.\n\n \n\nNh khng nhn mnh na. C ngi ung a chn, nhn ra xa. Mnh t nhin thnh ra tha thi. ng y hi v duyn, vo phng cng khng tin. nh gh mng ngi lan can nh mnh, tay dt my ngn tng vi pht ph trc mt.\n\n \n\n- Tng Vi sp n ri !\n\n \n\n- H ? .. ... M t sau nh ni g th ho mt ci. Git mnh t ng\n\n \n\n- Ha ha. Nht cht !\n\n \n\nLi im mt lc. Nh ny hi. Ni th ni mt chng i xem no. Thi thong tht ra cu ri im bt.\n\n- ng y c sch hc tt Ton khng ?\n\n \n\n- Khng ! y ch c sch hc tt mn Cho c thi.\n\n \n\nNh li ci. Mnh cng ci. Ln ny khng im theo nh na. Mnh t ng bt chuyn tip\n\n- Sao ? Hi sch lm g ?\n\n- Mai nhiu bi tp. Nay khng hng lm\n\n phi ri ! Tm trng khng tt ly u hng hc.\n\n- Th a y lm cho !\n\n- Lm c khng ?\n\n- Ny ! 2 nm trc y cng hc lp 11 .\n\n- By t !\n\nNh i vo phng, m ra tp v a cho mnh. Mnh cm tp v, ang nh i vo phng th nh ra, vi t tp v ln thnh lan can, ht hong hi :\n\n- Ny, trong m c nht gin l tui x v lun nha !\n\n \n\nNh li ci ri. m bng ci ngt ngho. Ti nh thng khng ng gia ban cng nhn nh. m ng v vo phng m lng mt nh c gi. Ch nh p gh. L Hong Vi. ! Tn m ca nh l tn mnh. Hong Vi ! Nghe hay qu !\n\n \n\n***\n\n \n\nB mnh v thm nh vo sng sm. Va vn lc m mnh mi ri khi nh. B vn th. Vn t ni v nh nhng nh hi cn vi hai m con.\n\n \n\n- Hong do ny hc hnh th no ?\n\n \n\n- D c !\n\n \n\n \n\n- L Hong Vi ! Ca ai y con ?\n\n \n\nB nhn tp v trn bn ung nc ri hi mnh. Mnh va nhai bnh m va tr li ly l.\n\n \n\n- Bn con b, nh con ging bi gip.\n\n \n\nB va xp vo t lnh, va hi\n\n \n\n- Con c bn gi ri h ?\n\n \n\n- B hi k vy, khng ng u !\n\n \n\nB ci !\n\n- Khng sao ! Con vui l c.\n\n \n\nB cho tin nhng mnh khng cm. B cho mnh. Mnh cng khng tin. T ngy b m ly hn, mnh mong b ng v y na. C thnh thong v, mua cho nh mt vi th, dn d mnh mt vi cu ri i. Ht kh t.\n\n \n\nNh ng i mnh cng. Mnh a tp v cho nh. Tm trng kh chu t khi b v, mnh cng chng mun ni g lun. Dt xe ra cng vn thy nh ng i. Mnh hi gt :\n\n \n\n- Cn chi na ? B mun lm cho c bi ngy mai h ?\n\n \n\n- Khng ! Cho i nh nha ! Nh nhn mnh ci nhn mi.\n\n \n\n- Khng cho! Xe u?\n\n \n\n- Ba trc b ng hng ri!\n\n \n\nNi xong nh tro ln xe mnh lun. Chong! M gh nha! Cn chun b m bo him na. R rng nh lp k hoch chn mnh t trc ri.\n\n \n\no nh nn chy chm chm. Ny nhn thy mt nh vn cn . Chc m qua vn khc. Nh ngi im re ng sau xe. C lc lng ti mc mnh phi ngonh li xem nh cn ngi khng.\n\n \n\n- Nhn g ? Tp trung li xe i\n\n \n\n- , thnh thong ng xem ri cha. C im m thy s.\n\n \n\n \n\nNh li ci. N ci nhn nht. Mnh khng bt chuyn na. mc mnh v nh qun quanh vi nhng suy ngh ca ring mi ngi. Ai ng u pha trc kia, s c nhng cn bo p ti nng ln vai ca mnh v nh.\n\n \n\nNh nng cnh nh ti\n\nCch nhau mt giu mng ti xanh rn\n\nHai ngi sng gia c n\n\nHnh nh nng c ni bun ging ti\nC9.\n\nChiu tan hc, theo li nh dn hi sng, mnh ln qua trng n nh. Trng dng nh ln cn o vy ng phc i ra cng trng xinh qu. Vy m c ti ci vi my thng bn nhn thy ght. Nh tr mt khi thy mnh ng .\n\n \n\n- n y chi vy?\n\n \n\n! Thi g th khng bit?\n\n \n\n- n thm trng c! Sao khng?\n\n \n\n- Khng sao! Thm i, tui v trc y!\n\n \n\nNh ny b khng sao vy? Hay mt tr nh ngn hn? Mi sng i nh mnh, bo mnh chiu n n. M mnh cht d, ngm ngha k nh. Sao lc sng nhn yu t nai nai, chiu gp li hin nguyn hnh con qu hay by tr. Mnh nn n ni nh ln xe hay b mc nh phng v trc? R d hi. ng v con khng rc ri khng t c. Bc c mnh.\n\n \n\nang r r ga n pha nh th nh quay pht li. Mnh p phanh t ng. Trng nh lc d nh mo in sp cn ngi.\n\n \n\n- Thi mi chn lm. o tui v!\n\n \n\nNi xong nh tro ln lun. Ln ny th mnh hi rn rn. Nhn nh nguy him khng ngn t no t xit. Mnh va i va gh. Nh ngi ng sau ng nghing, thnh thong li cn ht. Nh m c tip din nhng trng thi tm l bt thng th ny. Mnh s kin vi c Thi a nh sang Tru Qu iu tr lun.\n\n \n\nVa dng trc cng, nh nhy huch xung xe, i thng v nh, khng thm mt li cm n. Mnh ang ng ngi, nh quay li, mt li nai:\n\n \n\n- , nay li lm bi cho tui nha!\n\n \n\n- Ngh ! Tui khng rnh !\n\n \n\nNh nhn trn ng nhn mnh. Mnh cng hi bc bc ci thi ri. Nhng li nh lc nh m gi ngi khc nh thi. Nhn nh m kh kh ci hp ba cc tng t lc trng v, thy t m nn ht hm hi :\n\n \n\n- Ci hp g kia ?\n\n \n\n- Hp ny ngi nh ng y khng xem c u !\n\n \n\nXong nh b vo nh lun. C qu ! Ch nh li r ga m cng nh nh ? Thi, nh th con bc gi xi ti mnh mt. Hm hc dt xe v nh. T hi dnh ti nh n by gi, ngy no mnh cng lm vo tnh trng bc bi. Mnh th t nay nh c lm g, c khc lc hay bun b g th mnh mc k. Th con gi g u, khng khng thuc cha.\n\n \n\nTi n cm xong, m ln phng. Do ny v nh lng v luyn hng ca mnh trn facebook chm xung t bao gi. Chng c ch g chm gi c. Chn qu li m ra ngoi ban cng.\n\n \n\nCa s phng nh m, n bn ht ra sng mt gc. Chc ang lm bi tp ri. Mnh da vo lan can, dt dt my l tng vi nghch chi. Cht thy ci hp hi chiu nh m kh kh bn b lan can nh nh. Dm du khng cho mnh xem .\n\n \n\nMnh vi tay cm ci hp ba mang v. Xem bn trong c ngc ng chu bu hay b mt g m nh ni ngi nh mnh khng xem c. Tch c ming bng dnh, mnh m hp ra.\n\n \n\nM i, huhuhu. Ng t gh. Gin trong hp b lm ngm khp phng. Phi n gn chc con. Mnh m khng th kp chc ngt m n ra sn. Con qu kia b in sao m bt gin nht vo hp lm g ? Li cn em v nh t trn lan can. Troll mnh ? Con ch.\n\nMnh h ht mt hi ri chy ra ban cng, thy nh ng t bao gi, mnh c qu qut m ln:\n\n \n\n- Con nh kia, c tr c rch sao my chi hoi vy?\n\n \n\n- Tr g? Hp tui y, ng y mang v lm g cn trch tui.\n\n \n\nCng hng lun! T nhin ngu m hp gin v m ra. Thit l r ht bit. Mnh hn con nh thu tri. ang kim chi, quay sang thy nh tro sang ban cng nh mnh.\n\n \n\n- G na ? Hi tui th ny cn cha ?\n\n \n\n- Im mm i, tui bt chng n.\n\n \n\n- ng y i cha in trc i. C khng mi i nui gin.\n\n \n\n- Tui nui hi no ? Tui lm th nghim .\n\n \n\n in. Th nghim con kh. Ti ! p nhng m in. Chi vi nh ch t hi ngi. Mnh lt o vt i ri chy v ngay nh tm. M mnh t ny gi nghe ting ht vi ci c gi mi chy ln.\n\n \n\n- Ci g y Hong ? Lm g trong y y ?\n\n \n\n- Con tm !\n\n \n\n- Nay li d qu\n\n \n\n- M hi nh Vi nh c Thi y. N vt chc con gin vo phng con .\n\n \n\nM mnh ci h h vi nh Vi :\n\n- T mai c chiu chiu my vt gin vo ngi n cho bc. Thng ny c nm n khng tm. Phi th mi tr c.\n\n \n\nMnh x nc tht mnh vo ngi. Khng ch bnh nng lnh chy na. Ch tht ! Ln ny khng tr th nh mnh khng lm ngi lun.\nC10.\n\nLc t mi mi tm thy ci mn trng m gi t hi b m ci nhau. Nhng ly k vt ca m i ph th ny liu c mt dy qu khng ? Thi khi ngh. Ngh nhiu mt u lm, m gi mnh ch mun tr th nh hng xm dng ngi tm qu kia thi.\n\n \n\n- M i cho con mn cy son vi b tc gi\n\n \n\n- Lm g ?\n\n \n\n- Con mn ng kch. Lp con tp vn ngh.\n\n \n\n- Trong ngn ko bn trang im. Tp xong nh mang v khng tao git\n\n \n\nLi ci em ng ln gc. Thi son ny m bi chot m cng phi ht na, ri cn b ra pha nc lm gi mu na, hng l ci chc. Chuyn ny mnh mt vi m ri. B tc gi i xong cn gi c ch son chc nh a bn mua thi mi.\n\n \n\nVa ngi ct mn ra lm o khoc trng, va tng tng ra mt con nh lc m m b mnh da ma, sng rung i. Sau ln ny th ht troll mnh. M cn dm troll na, mnh da tr c hn.\n\nTrang im th ri ng ngm trc gng. Thy gh. Mnh trng cn pht khip. Khng bit c nn trt trt thm t nc son vo ngc lm mu cho khip thm t na khng. M tc gi ca m mt qu, mun lm x trng cho d tng m s khng chi li c th m co u mnh.\n\n \n\nL d xung cu thang, tnh bng da th m xem hiu ng, tm mi chng thy m u. Li l d i ln. Va m ca phng i vo th thy m. M tht ln che tai ri v cho mnh mt pht p mt vo cnh ca. Khi ni ch trn u ton sao l sao.\n\n \n\n- M i, con, con Hong m !\n\n \n\n- Tin s cha my, my nghch tr g mt dy th ?\n\n \n\n- Con ha trang th\n\n \n\n- Lp my nh ng v Ngha a ni dy ? Tin s b my. i ra mt i h tao ci. Lm tao t cht.\n\n \n\nMnh gi xui ngy xo thng en nm i hay sao y. Lm g cng phi b m chm vi nht mi c. Ra mt xong thy m phi sng v ln. M mnh v th th ny, b b l phi.hic. Nhc n l bun thi c rut.\n\n \n\nm m ra ging, chc ng mt gic i n m dy da nh. M ch lo m nay nh khng m ca s th mnh bit da sao gi ? i n m mai th c cht. Ci cc tc ca mnh phi nut ngay khng nght th mt. Thi nm xung , t lc b m v p mt vo tng, u c mnh chong vng, ong ong ln ri.\n\n \n\nKhng ng c, vi in thoi vo facebook. Ln l lc thy nh Vi up stt mi. Qun i hoa , xin li nha !. a xin li g vy ? Xin li v tt mnh, v ly qun i mnh, v th gin vo o mnh, hay v g ? M li nh gi tr g y ? y khng mc by ln na u. Mnh th khng tin nh, khng nng tay vi nh bao gi na. ang suy ngh min man, nh Vi gi mnh ra ban cng. Mnh lc t ly o khoc ra mc, hm nay phi phng k cng. L u ra, thy nh ngi , nhn mnh ci ci. Mnh bit iu ci gian gian ny khng cha ng ci g hay ho, nhng c tin n. Ti gn mi thy mt nh . a, li c chuyn g m khc nh na tri ? Nh a mnh vi ht mai, mnh lch s cng cm hai bin b mm ngm.\n\n \n\n- Ra y lm g ?\n\n- Bun, ra chi\n\n- Gi ngi ta ra y chi ?\n\n- Khng thch 1 mnh th gi.\n\n \n\nXong ri nh nn lun, khng ni thm g na, mnh vn cng nht chuyn, thy nh im cng im re. Lc sau n ht mai thy thm, bm bng khen :\n\n \n\n- mai ngon th !\n\n- , mi tp lm !\n\n- mai g ? V l l !\n\n- Gin, lm bng l gin hm qua bt b hp . Trn gng rang ln, nn trn trn li. Ngon gh ha.\n\n- AAAAAAAAAAAAAAAAAAAAAAA\n\n \n\nTnh dy ! M hi t mt. a ny gi mnh m. Hu hu. May qu l m ch nu tht mnh th t nay ung nc sng qua ngy ch khng n. Ng qua ng h. Mnh ng gn 2 ting ri. Hn 11h cht xu. Chun b k hoch thi.\n\n \n\nChy tt ra ban cng ng sang nh nh. H h. Hm nay ca s m. K hoch ca mnh bc u thun li ri. Mi bc v nh, bng nghe thy ting p ca rm rm, ri c ri long xong. Ting ng ny nghe quen th ? Lm mnh nh hi b vn cn cng hai m con, mi khi ci nhau m u ht ht c, ri long xong, cc chn v bn c nhng mnh thy tinh vo gm bn ch mnh hay ngi b gi np b m. Sao li c nhng ting ng lm mnh thy t ti th ny ?\n\n \n\n- Hong !!! Hong i !!!\n\n \n\nM gi mnh. M l, ting gi nghe thm thit th ? di ng cn c ting m ng gi nhau na. My m thanh qu qui c din ra ln lt lm mnh thy nng bc trong lng qu. Vi chy xung nh. M ht hi tm ly vai mnh.\n\n \n\n- My xem lm th no sang p ca gip c Thi cu con Vi ra vi. Thng Tng say ru v nht con b trong phng ri nh n mt hi. Khng cu n ra th n cht mt.\n\n \n\nCi g vy tri ? Mnh nh b ai cht ct chn tay lun. Mnh ght nh lm. Nhng sao nghe thy nh b b nh m lng mnh au vy tri. Chn tay lung cung chy qua chy li ct lc m khng bit lm g.\n\n \n\n- M i lm no gi ? Sc con o l ny sao ph c ca ?\n\n \n\n- My sang h c Thi ph i, tao i ln cng an phng gi cc bc xung x l.\n\n \n\nTrc khi i m cn ht li cho mnh cu : - Nhanh ln, li con b ra khng n b nh cht !\n\n \n\nHu hu, nh sp cht ri. Mnh mun ko m li, bt m nh lo chuyn vi mnh qu. Mnh nht t b, li cha p ph nh nhau bao gi. Gi p ca ra cng khng lm no li ng Tng ra, nht l ng cn ang say ru. ng in ln ng cn p nh Vi mt pht cht ti th sao ?\n\n \n\nc g b nh th hay bit my. B va to va khe. B s gii quyt xong chuyn t lu ri. Chuyn ca nh Vi lm mnh nh li bao nhiu mnh k c mnh c gng p v vn t lu, gi t nhin bay v chp ni li mt cch lch lc. Ngy b b i mnh mi l thng b con, ch bit ng ca khc nhn bng b khut dn. Ri hng ngy c lm li n vi hc. Sut my nm tri ch bit n trng ri v nh rc u vo sch v vi game. C l khng c b, con ng ca mnh b li cn 1 na. Va i va l c, thnh thong khng gi c thng bng b ng di di, li ng dy chi mi ri i tip. Mi khi xc xt, ch c m lau chn cho, khi nng u c m vnh o p trn mnh vo bng m. Ri dn dn mnh qun mt b. Ri dn dn mnh qun cch yu b. Ngh k mi thy b mnh con tt hn b nh Vi gp vn ln.\n\n \n\nNhc n nh Vi li thy ht hong. Chy ln phng ra ban cng, nhy sang ban cng nh nh nhn vo. Trong khung ca s c mt cnh tng m mnh c mnh khng bao gi nhn thy ln th hai. Nh Vi b dn mt gc, mt m a mu mi v nc mt. Ch Tng c lo o nhng khe nh mt con tru in, ch cm tht lng da qut vo ngi nh tng hi. Tri i, nh lm g ch m ch li nh nh th kia? Ting c Thi vi my ngi p v h ht ngoi ca. Ci ca g dy v cng th kia c p n sng mai cng khng long ra c.\n\n \n\nNhy v nh, tim mnh lc ny sp v mt ri. Cht thy ng mn trng vi tc gi trn ging. Mnh tnh phi dng cch ny thi. B mnh bo nhng thng say ru yu bng va lm. i m tc gi ln u, khoc mn bng nhng vo ngi. Ly cc nc son pha, ch kp trt trt qua mt vi vo c, khng cn kp soi gng na. Mnh phi ra ban cng, li nhy sang nh nh.\n\n \n\nHt mt hi di ly dng cm, mnh ng ca s, dang tay ra, c trn mt nhe rng, ht ht c ln\n\n \n\n- Gaoooooooooooooo!!!!!!!\n\n \n\nu tin l nh Vi nhn thy mnh, v nh ngi gc hng ra ca s. Nh tht ln kinh hi. Ch Tng nhn ra ri ng nga ch nh Vi, mt tht kinh, ct khng cn mt git mu. Nhng vt nc mnh ht ln mt c chy chy v c mnh nga nga m khng dm gi.\n\n \n\nCh Tng ht hong m ngay ca chy tho thn. Mi ngi ngoi ca ui theo ht. Ch cn nh Vi ngi . Nh vn khc, nhn mnh s lm, nhng khng chy c, chc l v au qu khng ng ni. Mnh b b tc gi xung, vut vut mt ri gi\n\n \n\n- Vi i, tui Hong n. ng ca vo i khng b Vi quay li nh tip gi.\n\n \n\nNh tr mt nhn mnh, ng dy cht ca, ri chy ra m ca ban cng cho mnh. Mnh lao vo nn nn vai vi tay nh xem b gy ch no cha. Thy mi th vn cng cng m mnh cng cng theo.hic\n\n \n\n- Sao Vi ngu vy? Khng bit chng c, khng bit nh li ? Bnh thng tt tui nh tui kinh khng lm m. Hm nay n phi thuc lit h?\n\n \n\nBP! Nh tt mnh mt ci nh tri ging. Mnh lo o da vo ca, khng hiu ti sao li b n tt v c.\n\n \n\n- in? Sao tt tui? Tui va cu ng y , khng c cm n cn tt? ng y ng l b khng.\n\n \n\nBP! Mt ci tt na. Nh qu qut lm. Mnh au v cng, nhng khng au mt u. Mnh thy nhc tim, khp mi ni trn c th. Ngy hm nay mi th i vi mnh qu nng n ri. Mnh ght nh, mnh cm th nh. Cha kp ni g. Nh y mnh bn ra ban cng ri ng ca li. Mnh chy ra ca s. nh khuyn nh vi cu cho bnh tm li ch khng phi chi nh, th m nh cng sp ca s nt. Nh tt n ti um. Cn mnh ng bn ngoi, cnh my khm tng vi ang gic nhau n.\n\n \n\nV phng, ng ca, tt in, nm vt ra ging. Mnh c th tng tng nh in cnh nh ngi gc mt khc. Thi th c khc cng c. Min l ng c lm g di dt, ng t t nha nh. D g nh cng ng thng. Mnh cha bao gi b nh au nh nh. M nhn mt nh lc hong s, m a mu vi nc mt m tim mnh li tht li.\n\n \n\nPhng ti en! Bnh thng mnh hay n ng v nht t b. Nhng hm nay mnh mun cng nh tri qua ci m en ti ny. Cn cht sc lc cui cng trong ngy, mnh ln m in thoi vo facebook, inbox cho nh vi tin nhn.\n\n \n\nThi, khc ri ng i nha. Ngy mai tri li ti sng. Tui bit nh li ci nhn mi, kim tr quy tui thui m! Khc nhiu vo nha, khc cho ht au n ti h i nha. Mai cn gp c nhau, tui nguyn cho nh nh p tui c ngy!\n\nCopy Mng X Hi Vn Hc\n\nSNow","language":"notdetect","sentiment":"Mixed","author":"Cólẽnàomìnhxanhau","author_text":"Cólẽnàomìnhxanhau","md5_text":"3cab1e75850273e7faa99e88a50710c4","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758137098240},{"id":"353114322","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100001359451427","authorlink":"PopoNguyen","date":"2013-01-06T13:48:46Z","topic":"Kia K9","topic_text":"Kia K9","text":"Sc ton tp l l ca 1 t in: ly v v gnh vc trch nhim v ngha v, ly v v tr n nh ck. ng ngha ra th v y c khc g 1osin ng ngha k c tr lng. Sao li thy coi thng v kinh tm n th k bit t in,ngh g m khn th.","language":"notdetect","sentiment":"Mixed","author":"PopoNguyen","author_text":"PopoNguyen","md5_text":"4f8c0495c7c7edc155f6a1010fe4ab38","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758150729728},{"id":"353114644","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100001834868308","authorlink":"LêThànhNhân","date":"2013-01-06T13:42:34Z","topic":"Kia K9","topic_text":"Kia K9","text":"V con gi ^^\n\nNghe v nghe ve\nNghe v con gi....\nSut ngy li nhi\nLin khc tnh yu\n ri chiu chiu\nT nm,t by\nAnh ny ht xy\nAnh kia d thng\n\nNghe v nghe ve\nTip v con gi\nT nhn xinh gi\nSut ngy Phn son\nLi th bon bon\nCi mm khng ngh\nNhng c xu x\nVn nhn l xinh\nNgi to chnh nh\nVn cho...Eo p\nNgi m dp lp\nLi bo gi eo\n\nBo i vt bo\nTh ku au c\nBo i nh c\nTh bo au tay\ni chi sut ngy\nLm g bit mt !\n\nc th c st\nVn nhn thng minh\nQua 5 mi tnh\nVn cho l t\n\nNgi nh qu mt\nQu vt khng tha\nQuen ngi la c\nHng ny qun n\nn nhiu to s\nc ch g u\nNi lm au u\nKo thm ni na !!!! \n=))","language":"notdetect","sentiment":"Mixed","author":"LêThànhNhân","author_text":"LêThànhNhân","md5_text":"44110fdee1183e2a5fef14ef8f16c103","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758151778304},{"id":"353114061","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100000211110284","authorlink":"TríTrọng","date":"2013-01-06T14:01:24Z","topic":"Kia K9","topic_text":"Kia K9","text":"Chuyn Thin : \" i Vng Sanh i !!! \"\n\nC v cao Tng, bnh nng lu ngy, coi chng thuyn gim cht no ! V lng y thm mch s, khuyn chng rng:\n- V S bnh lu ngy, sc lc cn kit lm ri ! Cc ng nn nu cho tht, hm xng cho s n vi ngy, may ra mi hi phc li c !\n chng lm theo li dn ca ngi thy thuc ! Qu nhin, vi ngy sau, v cao Tng hi phc sc khe v tnh li !!!\nS hi :\n- Cc con cho ta n ung g m ta thy ngon ming vy ? Li nghe rt khe trong mnh !!!\n chng khng dm giu chuyn, lin k cho S nghe s tht :\n- V Thy qu kit sc nn chng con nghe theo li dn ca v lng y, bng cch nu cho v canh sp tht xng cho thy dng mau lnh bnh v phc hi sc lc ! Khng th chc gi ny Thy ..... vin tch ri !!!\nV Thy nghe vy, qu au bun, than trch :\n- Sao cc con lm vy ? Ta gi gii, trng chay gn c i ngi, hm nay v tnh cc con ph i gii hnh trong sch ca ta ri ! Th l ta cht i cn hn phm ci gii m ta hng gn gi sut i !\nV cao Tng v u ut nn .... tr bnh nng ri .... tch !!!\n\nV k t ngy y, mi m, mi ngi ng cha u thy hnh bng v tng hin v, vo ra .... m hng chu .... thng thit lun !\nPht t v mn u s, mi khi hong hn bung ph v ..... hnh bng ma qui kia c i i li li, khi th trong sn cha, khi th ngoi hng nin ..... !!! H khn vi bao nhiu, hnh bng ma qui cng chng chu .... thng !!!\nChuyn n tai v Thin s ..... nn v Thin s lin tm n ngi cha ny tm thm .... hn ma ny, hi cho .... ra chuyn !!!\nTri va chng vng, v thin s ngi xung trc hin nh hnh thin ! Khong gn na m, thin s cht nghe ting than vng vng t ni cn phng m v s tch trc kia ..... !!! Li than nghe no nng, ai on, cha y mun phin ......\nThin s cht nghe vng vng ting than th no nng:\n\" Lm bnh nm lit ging\nNn phi hp cho xng ....\nV chay lt bt tnh ....\nNn nghip mi .... cn vng \"\n\nThin s chm ri dng k p li:\n\n\" Lm bnh nm ... lit ging\nC vic .... ung ... cho xng,\nTam nghip ng m Tnh\nng Pht vng Ty phng !!! \"\n\nNghe bi k i p, hn v Tng cht t u tr ti, qu xung trc mt v thin s, ci u ly t :\n\" Con rt bit n Ngi khai th cho con ! T y, con thy c ch phi i ca mnh ri ! \"\nHn v Tng ni xong lin bin mt ! V Thin s khoan thai ng dy, chm ri bc ra khi cng cha !\nCng k t , khng cn ai nghe hay thy hn ma ca v Tng hin v mi m !!!","language":"notdetect","sentiment":"Mixed","author":"TríTrọng","author_text":"TríTrọng","md5_text":"decc2b547273d8cd0f5e1b8593b89c79","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758153875456},{"id":"353114650","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100000269828945","authorlink":"HươngẾch","date":"2013-01-06T13:35:00Z","topic":"Kia K9","topic_text":"Kia K9","text":"1 ngy hi mt nhng cc k vui:xxxx rt cm n cc anh ch v cc bn qua ng h gian hng ca lp em/t 1 cch t nguyn hay hi b p buc mt cch thoi mi h h:\"> v cng cm n cc bn Bo Him thn yu cng mnh lm u bp kim bn hng kim qung co cho ko khch du lch ngy hm nay h h:dddd cng k th qun gi li cm n chn thnh nht n thy Anh Tuan ga lng ng h rt nhit tnh lp e <3 <3 <3 chc mn khng b khn c nh^^","language":"notdetect","sentiment":"Mixed","author":"HươngẾch","author_text":"HươngẾch","md5_text":"fe274f0838043a3e6f1e02d881d75682","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758298578944},{"id":"353114166","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002837937364","authorlink":"NguyễnHương","date":"2013-01-06T13:57:51Z","topic":"Kia K9","topic_text":"Kia K9","text":"Ln ny mnh s ch...........ko vi vng na!","language":"notdetect","sentiment":"Mixed","author":"NguyễnHương","author_text":"NguyễnHương","md5_text":"0964d8bdff81e0ab64b1065743e8d154","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758299627520},{"id":"353114803","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003985628988","authorlink":"NguyễnXuânHòa","date":"2013-01-06T13:34:09Z","topic":"Kia K9","topic_text":"Kia K9","text":"Phai lam th nao","language":"notdetect","sentiment":"Mixed","author":"NguyễnXuânHòa","author_text":"NguyễnXuânHòa","md5_text":"18c7a937a642788267e64bb26c4e34c0","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758300676096},{"id":"353114452","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004393961240","authorlink":"MèoMun","date":"2013-01-06T13:43:50Z","topic":"Kia K9","topic_text":"Kia K9","text":"Ngy xa c i v chng n c mt cu con trai 12 tui v mt con la nh. Mt hm h quyt nh i chu du thin h xem nhn tnh th thi.\n\nKhi i qua mt lng u tin h nghe thy nhng ngi y th thm: \"Xem thng b trn lng la ka, ng l th khng c dy bo n ni n chn Ai li ngi th khi cha m phi li b bn cnh.\"\nNghe vy ngi v lin ni vi chng: \"Khng th h ni xu v con mnh nh vy c\".\nNgi chng bn nhc cu b xung v nhy ln lng la ngi.\nKhi qua xm th hai h li nghe mi ngi y x xm: \"Xem ka, thng chng kia qu l khng bit xu h, khe mnh th m li ngi trn lng la v v con i b.\"\nAnh chng lin nhy xung khi lng la v ch v ngi ln. Hai cha con i bn cnh.\nQua xm th ba h li nghe thy ngi ta x xm: \"Ti nghip anh chng, lm lng vt v c ngy kim cm o v cho gia nh li phi i b, cn xem con v ka! C thng con na, ng l v phc mi c c b m nh vy.\"\nNghe vy c ba quyt nh tt c cng ngi ln lng la ri i tip.\nKhi i qua mt xm na h nghe thy mi ngi ni vi nhau: \"ng l l v cm, c c chng khc th vt. Ba ngi ngi trn lng co vt nh nhn th kia th n gy lng mt ch.\"\nNghe vy ba ngi lin tt khi lng la v i bn cnh con vt.\nn xm tip theo mi ngi cm thy khng th tin vo tai mnh na khi nghe thy ngi dn y ci nho bng: \"Nhn ka, ng l l ngu. C ba lch thch i b trong khi con con la chng c g trn lng.\"\n\nKt lun: ngi ta s lun lun tm cch ch trch bn, nho bng bn v hon ton khng n gin tm c mt ngi chp nhn bn nh bn vn c.\n\nCho nn: hy sng nh bn cm thy l ng n v hy i n nhng min m tri tim bn ch li\n\n\"Cuc sng nh mt mn kch khng c phn tp dt trc. Bi vy: Hy ht ca, nhy ma v yu mi mt giy pht ca cuc i bn trc khi v kch h mn khng mt ting v tay\"","language":"notdetect","sentiment":"Mixed","author":"MèoMun","author_text":"MèoMun","md5_text":"82c7f68c304fabf337ea8ab279ea6297","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758301724672},{"id":"353114907","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100001568604880","authorlink":"NguyễnHảiBình","date":"2013-01-06T13:28:01Z","topic":"Kia K9","topic_text":"Kia K9","text":"Long An Long An Long An...........................ng nm m cng thy, ln tp ri cng cn thy, khi no mi ht m t y,gruuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu","language":"notdetect","sentiment":"Mixed","author":"NguyễnHảiBình","author_text":"NguyễnHảiBình","md5_text":"64e297227463b6d5b23f7c67650924ee","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758302773248},{"id":"353114284","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002574373387","authorlink":"ShOnlyone","date":"2013-01-06T13:50:05Z","topic":"Kia K9","topic_text":"Kia K9","text":"- Thng ngu v thng u, e chn ai?\n- Ng chy theo em v ng e chy theo, em yu ai?(t y ng ma t chay theo)\n\nC L BN ANG MI CHY THEO 1 BNG HNH MI MI KHNG THUC V MNH , V ANG NH MT 1 CON NGOI HIN HU SAU LNG\nKhi nhng ht ma ma ng ang vi v ri , gieo ci but lnh n tng ni trn th gian ny , trn ng nhng i tnh nhn ang tm ni tr n . N vn lm li , lng im di theo 1 bng hnh pha trc , nhng ht ma vn l ch ri vo mt n , qun i ci but lnh ca to ha , mt n vn ang rng rng nhng git noc mt mn ng . Pha trc nh vn ang khc , nhng git nc mt nh ang v thc ri ,ha cng cn ma kia , gia khong khng bao la , gia nhng ht ma ri u , nc mt nh tr nn qu nh b , bc nhng bc chn v hn nh ang c tm v 1 ni v nh , qut ngang nhng git nc mt ang gin gia trn mi , nh kh nhch ming coi chn nn .\n\n_ anh lm n ng i theo ti na c c khng ? ( nh ni )\n\n_ xin li anh khng th !\n\n_ ti sao anh phi lm vy ?\n\n_ ti v anh yu em!\n\n_ ngu ngc anh c cm thy tic nui 2 nm qua mi theo ui 1 ngoi con gi nh ti khng ?\n\n_ khng !\n\n_ mc d bit ti c ngoi yu ???\n\n_ phi .. tnh yu m em , mnh khng th bt tri tim mnh lm khc uc !\n\nNh li tip tc buc , li ha mnh vo cn ma , nguc mt ln nhn tri nh nh ang c che i ci ni au gin x tm cang mnh t t 1 cu hi cho chnh bn thn \" ti sao ngoi ti yu li khng yu ti , cn ngoi ..\" hj, nh nh coi ngoi nhn n pha sau , nhn thng vo i mt bng t lu , noc mt nh vn khng ngng ri li tip tc bc nhng vng nc ly li vng vi khp mt ung , b nhng i chn n v nh lm v tan \n\n_ ti nh ti ri anh c th v!\n\n_ uhm , anh bit ri , em nh gi sc khe!\n\nKhng 1 li , khng 1 ng tc ngoi nhn , nh ng sm cnh ca n lng nhn , ri buc nhng boc git li , mt khng ri v pha cn nh nh , n n 1 n coi mn nguyn . Lng bc \n\n2h sng , nh vn ang dn mt vo ci mn hnh sng trng trn ci my vi tnh , di nhn theo 1 ci nick ang sng , di c theo nhng dng status ca 1 ai , khng ginh cho nh , chc chc nh li ci mt bt khc Sn , ngoi con trai nh yu mi ri xa nh chy theo 1 nim vui khc , nghe nh but ng t trong tng nhp th , nh vn cha tin v cha bao gi chp nhn ci tht ti au xt , nhn nhng dng yu thung ca Sn ginh cho ngoi con gi khc nh li khc 2 nm yu nhau gi kt thc nh vy sao ???\n\n_\" buzz\" em cha ng , em phi gi gn sc khe ch , ng ngi online sut nh vy khng tt u !\n\n_ ng lm phin ti !\n\n_ anh .. uhm anh s thc cng em vy \n\nN li di theo nh qua ci nick yahoo vn lng im ch mong lm nh vi i ci ni au hin ti , nh u bit khi nh ang ri nhng git noc mt cho 1 ngoi khc l lc n ang c gng nut tri nhng git nc mt chy nguc vo tim mnh v nh .. c l ng tri lun th .. lun thch to ra nhng bi kch cho cuc i ny .. ti sao n li yu nh ch , yu ngay ci ln u tin gp g , ti sao n li phi au kh v 1 ngoi con gi mi mi khng thuc v n.\n\nSau cn ma tri li sng , 1 m tri qua vi nh tht di , nuc mt ginh cho m hm qua nay gn nh cn , nh un o thn hnh nh mun tm 1 cht thoi mi , m toang cnh ca , nh n ly nhng tia nng u tin ca nh ban mai , bt cht nh thy 1 chic hp oc gi xinh xn t truc ca , m vi n ra , bn trong l 1 i bao tay nh nhn .. cha hiu chuyn g .. nhng nh vn em n vo nh v ct gi . Hm nay nhn nh kh hn ngy hm qua rt nhiu , nh bit coi vi nhng s vt xung quanh , d ch l nhng n coi gung . Din cho mnh 1 chic vy tht p , chn cho mnh 1 gc ngi khut trong 1 qun c ph yn tnh , nh ang c tm li cuc sng ca ring mnh \n\n_ em c cn anh ngi cnh tr chuyn khng ?\n\n_ li l anh , sao anh bit ti y .\n\n_ khng l do anh i theo em t sng n gi !\n\n_ anh ng xem ti nh 1 a con nt oc khng ? ( nh gt )\n\n_ anh\n\n_ v anh cng ng gh l ti s chn anh khi ti chia tay , n ng cc anh .. khng ai tt c khng ai n vi ph n chng ti m khng c mc ch ! Tnh dc ng khng anh m i \n\nNh ng pht dy , b i , li n vi ni au dng tro , trong mt nh n l ngoi n ng t n th sao ?\n\nK t ngy hm .. cuc sng ca nh tr nn bnh lng hn , pha sau nh khng cn n di theo na , nh ngh trong u , chc l n b nh lt ty ri , nn khng dm vc mt ti gp nh na , nh li nhch mp coi \" n ng lun th\" hng m nh vn tip tc di theo ci nick ca Sn , dung nh l 1 cng vic quen thuc vi nh , d bit l nhng ln nh th tim nh s li r mu \n\n_\" buzz\" em khe khng? .\n\nSn ang pm n ???\n\nThc hay l m y nhn vo dng status trn nick Sn , nh khng cn thy nhng li yu thung dnh cho ngoi con gi kia na , thay vo ch vn vn \" Th anh ch em !\" l tn nh .. ng ngng bng hong .. v xen ln ci nim vui sung ci cm gic m lu ri nh khng c tn hung i bn tay nh nhn .. run run \n\n_ em khe . Cn anh ?\n\n_ anh th khng c khe .. hjhj nhng by gi th anh li ang rt nh em \n\n_ anh ni vy l sao ?\n\n_ anh sp phi phu thut thay tim ri by gi anh mi tht s nui tic khong thi gian qua b ri em em c th cho anh 1 c hi c khng ?\n\n_ anh anh phi thay tim ??? anh b bnh tim ?\n\n_ h ng ri ca phu thut rt quan trng vi anh \n\n_ em s n cng anh ngay by gi !!! i em nh.\n\nMc vi 1 chic o khoc nh lt i trong m \" kinh koong\" nh bm chung nh Sn inh i , cnh ca uc m , truc mt nh l Sn , lu khng gp, nay bao yu thung li tr v , ko vi tay nh vo nh . Sn t ln mi nh 1 n hn , tay Sn nh ang c tm n nhng phn da tht ca nh h n i \n\n_ em vn nh nga no vn quyn r anh mi ln bn nhau \n\n_ by gi th anh c th ni cho em nghe uc khng bnh ca anh nh th no ?\n\n_ anh b bnh tim giai an cui ri .. may m c 1 ngoi hin tim cho anh na thng na anh s phu thut v anh tht s cn em lc ny bi anh nhn ra .. ngoi anh yu chnh l em \n\n_ nh kh coi \" em s bn anh mi mi\" h li lao vo nhau nh ang c tm kim thin ung \n\nNgy hm sau , Sn dt nh n nhng ni ngy xa thung n , nh nh cht i sng li , ci cm gic bnh yn v hnh phc ngy no gi li cht a v , n cn mn nng thm thit hn truc , Sn m nh t pha sau , hn ln m nh nh nhng , m du v dng tro , nh xoay ngoi m cht ly Sn : \" anh ng xa e na nh\".\n\n_ anh ha n , s khng bao gi xa em na , anh yu em !\n\nSn t ln mi nh 1 n hn thm thit . nh v Sn qun ly nhau nhng ngy lin tc sau , nhng g c th lm nh thch v coi Sn u c gng thc hin , cha bao gi nh cm thy hnh phc nh by gi , nh nghe tri tim mnh nh ang ha chung nhp p cng Sn \n\n_ em n , em c thch ci o khac ny khng , anh mua tng em y .\n\n_ em thch nhng sao anh li tng em o khoc \n\n_ hj, ngc th ng n ri , anh s em lnh \n\n_ khng phi ma ng ny anh bn cnh em sao?\n\n_ d nhin ri nhng c ci o ny v vng tay ca anh na ..em s khng bao gi lnh !\n\nNh tt mt coi hnh phc . Tri ng gi vn thi tng cn se lnh , ci lnh nh mun ng bng mi vt m n i qua , ngi cnh Sn , nh gh cht , ci m t 2 c th lan ta , ci m t chic o khoc Sn mua , v ci m t i bn tay sn chc ca Sn lm nh qun i rng ngoi kia ang l ma ng, ngoi tri tnh lng , nhng chic cy kh tr l nay ang c gng m chi gia ci but , khng c nhng ting chim ru rt ht vui tai , khng c nhng nh nng lung linh cho cuc sng thm tui sng nhng vi nh .. ch cn c Sn th ri cuc sng ca nh y p sc sng ,n cn mnh lit hn nhng chic l non vun mnh kia , nh thy cuc sng qu nhn nhp ri khi tri tim nh c th nghe tng nhp p ca Sn c l nh ang hnh phc .. v nh u bit ci hnh phc y uc nh i nh th no \n\n_ anh mai l anh phi i phu thut ri .\n\n_ uhm .. em yn tm anh s khng sao m \n\n_ ri anh s li v bn em ch .\n\n_ anh ha n ! ngc ca anh.\n\nRi ci ngy khng ai mun cng n , ngi trn chic xe taxi ang lm li , nh li m cht ly Sn , nh ang lo s 1 iu g Sn uc chuyn ln phng m , nh lng im , run ry , tim nh nh mun n tung ra ngoi , c ci g ang bn chn trong nh , ht ng ln ri li ngi xung , sut 6 ting ng h tri qua , m hi nh cng trn trn trn nh gc xung gh v qu mt mi \n\n_ chc mng c chng ti thay tim thnh cng cho ngui yu c \n\nNh git mnh bi 1 ging ni l , ngc nhn ln nh thy l v bc s trc tip phu thut cho Sn , nuc mt v a trong nim vui sng , nh nh 1 a tr , coi vui hn h .\n\n_ tha bc s , ti c th gp anh y c ch \n\n_ bay gi th cha th , anh y cn m man , ngy mai c c th quay li \n\nSut m hm nh khng ng , nh mong tri mau sng , mong ci thi gian chng qua nh c gp Sn , tri tim nh khng cn lo s . Mn m bung xung bao ph tt c nh ngi co ro trong phng , ht suy ngh ny li ti suy ngh khc nh iu ginh cho Sn , nh ang nh li nhng thng ny qua , nh bt coi sung sung v nh bit ci hh phc s cn theo nh ch ngy mai na thi ngy mai na thi .\n\n_anh Sn i , emn ri n!\n\nNh ht ln nh qun mt y l bnh vin , l ni cn s yn tnh, v ri ci khng kh yn tnh cng oc nh tr li . Truc mt nh Sn ang oc 1 c gi chm sc .. tng ming n tng ngm noc tay chn qu qung nh da hn thn mnh vo bc tng gn nht mt nh nhe cay ming nh lp bp\n\n_ Anh Sn .\n\n_ em n ri .\n\n_ c gi ny l ???\n\n_ l bn gi ca anh .\n\n_ cn em .\n\n_ hj em sao vy th em cng l bn gi ca anh nhng ch na thng troc thi .\n\n_ anh l \n\n_ em tht l phin phc v em m anh phi xa ngoi yu ca anh na thng xem nh na thng qua em n b cho anh . Vy l khng ai n ai .\n\n_ anh . Anh vy anh tm v ti lm g .\n\n_ lm g . thc hin li ha cho 1 thng ngu thng ngu mun anh lm cho em hnh phc nhng tic l n khng ni khong thi gian bao lu nn anh ngh na thng l qu nhiu vi em ri kaka ( Sn coi ln, em theo l ci nheo mt ca c gi ngi cnh Sn )\n\nTai nh , chn nh ng khng vng , .. gc ..\n\n_ li ha thng ngu . L sao ?\n\nNh ni trong din di .\n\n_ ny em t coi i !\n\nSn thy vi 1 sp h s v pha nh .\n\n*** ***\n\nH S BNH N\n\nNgi hin tim : Trn Cao Nhn\n\nNgoi nhn tim : Dan Nam Sn\n\nCa phu thut thnh cng .\n\n*** ***\n\nNh nh ang ri gia tng khng , ngoi hin tim cho Sn li l n ngoi con trai m thm lng l di theo nh sut hn 2 nm qua .. \"au\". Nh go khc trong v vng tay bu cht chic o khoc nh mun x toang n ra \n\n_ ny ny em ng lm rch o thng ngu n tng em ch d sao anh cng c cng chuyn gip dm em cn na i bao tay trc nh na .. ton b l n mua cho em y thng ny hay o n bit th no em cng lnh nn mua y \n\nGing Sn dt km theo ting coi khanh khch , nh ang mun tru tc nh \n\n_ anh anh l u.\n\n_ hihi i m em , anh khng u th thng khc cng u . Trn i ch c 1 thng ngu thi anh khng mun lm thng th 2 u . Kaka\n\nSn coi ln , nh vt chy khi cn phng , nuc mt nh theo tng cn gi nguc chiu thi nguc li pha sau bu cht ly ngc nh ang c kim hm nhng cn au ang dn x , nh vn chy , chy khng ai kp nhn nh khc , chy li pha sau nhng cn au m , chy tm 1 ai c ng ch nh pha truc khng v chy khi nh ngoi nhn cn c \"ai\" di theo nh khng vn v..\n\nNh ng lng cy cu cao nht ca thnh ph .. giang tht rng i cnh tay , nh m trn nhng cn gi ng vo lng , dng sng vn ang sit chy khng bao gi dng li , nhng ngi sao khng ngng nhp nhy t im cho ci bu tri m huyn du nhng i tnh nhn tay trong tay cnh nhau n ch nhng giy pht m m ca m ging sinh an lnh .. u ngoi ta li thy 1 c gi ang giang rng i cnh tay ca mnh , khe mi c ang tun tro nhng git nc mt , i b mi run run , thu i cnh tay li nh sit cht ly chic o khoc, c bung mnh xung dng sng ang chy sit , ai ny iu sng s , ngoi ta vi tay theo ci th xc c ang dn xa khi th gian ngoi ta thy nuc mt c theo nhng cn gi by ngoc ln khng trung , n nh nhng ht tuyt him hoi ca vng nhit i, \"lung linh\" ngoi ta thy mt c nhm nghin , ming c n 1 n coi chua cht , ngoi ta thy tay c sit cht ly chic o khoc nh khng mun ri xa c l l th duy nht trn ci i ny lm c m p .\n\n***\n\nNgy 22 /12\n\nVy l ngy mai anh khng cn c hi gp em na\n\nAnh khng cn c hi oc lng l di bc theo em\n\nKhng oc nhn tht sau i mt lng lnh ca em\n\nKhng cn oc nghe nhng ting em qut mng anh na\n\nAnh bit , anh sinh ra la yu em\n\nCh cn thy em ci toi trong hanh phc th anh mn nguyn ri.\n\nHy vng tri tim anh trong th xc Sn s 1 ln na li c yu em\n\nEm li bnh yn . Ngoi anh yu nht ci i ny......\n\nNhng dng tin nhn cui cng t yahoo n m nh s khng bao gi c oc..\n\nTheo bn n v nh ai l ngoi hnh phc\n\nCc chng trai c bao gi cc bn lng 1 giy suy ngh v cch mnh yu cha ???\n\nC L BN ANG MI CHY THEO 1 BNG HNH MI MI KHNG THUC V MNH , V ANG NH MT 1 CON NGOI HIN HU SAU LNG","language":"notdetect","sentiment":"Mixed","author":"ShOnlyone","author_text":"ShOnlyone","md5_text":"4f385919d555cf74dc0373c276884ea1","dimension":["Vehicle Quality"],"category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758303821824},{"id":"353114560","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004755180549","authorlink":"ChíThanhKute","date":"2013-01-06T13:46:48Z","topic":"Kia K9","topic_text":"Kia K9","text":"like gim minh nha\n\nChng : V yu i anh v ri ny.\nV : Cht ri anh i,mt ht ri.\nChng : Mt ci g c ?\nV : Mt.. mt cimt ht ri.\nChng : Nhng m l ci g c ch ? Hay l em lm mt tin.\nV : Mt tin th cn . Ci ny cn qu hn tin y.\nChng : C ci g m qu hn tin nh ?\nV : Ci m ngi ta gi l ng gi ngn vng y.\nChng : , anh hiu ri. Ch trinh ng gi ngn vng. Th ai b mt trinh?\nV : Em ch cn ai na.\nChng : (Ci) Em m ng y ? Em trao n cho anh t hi no ri,anh vn cn gi trong ti qun y ny. T nhin li m ln,lm anh tng c chuyn g nghim trng.\nV : Em nhm. Ci ny khng gi l mt trinh ,m gi lgi lhp dim.\nChng : Em b hp dim ? Bao gi ? u ? Thng no ? Gi n ra y anh cho n mt trn.\nV : N ch chun t lu ri, ngu g m ngi y i anh v.\nChng : Ci thng ch hn h. Chuyn nh th no em k ngay i. \nV : Lc ny c mt thng trm n m vo nh mnh.\nChng : Ti sao thng trm y li vo c nh mnh ?\nV : Th cng ti anh y.\nChng : sao li l tai anh ?\nV : Th ti anh v mun ch cn sao na? Em ca ri nm xem tivi i anh nhng ng qun lc no khng bit.\nChng : Sao em nh ong th ? ca m li ng qun. M anh i lm ch c i chi u.\nV : Anh th lc no cng i lm. Em chng bit ci cng ty anh th no. Cng ty a bn em, nhn ngy tn th, sp n cn cho ngh c ngy nhn vin c nh on t vi gia nh.\nChng : Thi, em ng ng n ni au ca anh na. Ci ng sp ca anh,bnh thng th qut nhn vin xi xi. Th m n ngy ny th t nhin li bo : Chng ta l mt gia nh. Chng ta phi bn nhau trong gi pht thing ling, trng i ny. Th l bt tt c nhn vin n cng ty n lin hoan vi nhau. Bon anh cng kh lm. n trong nghn ngo v nc mt ch c sung sng g. Hu.. Hu..\nV : Hu Hu Hu\nChng : Mi khc m qun mt c chuyn thng trm. Th lc n m vo nh sao em khng h hon ln ?\nV : Em ng qun c bit g u. T nhin nghe thy ting ng em cht tnh dy,ho ra n va vo gc bn. Em nghe thy n lm bm chi ra : chng c ci cc kh g ng gi li con b n mt qu r au. Th ri em nhn thy trn tay n cm mt con dao bu rt to. Lc em s qu lin ko chn che ci mt\nChng : Tt.\nV : Nhng xui xo l li h mt ci i.\nChng : i gii.\nV : N nghe thy ting st sot lin quay ra pha ging. Nhn thy em ang nm n ku ln: Cng nh an i. Th l n tin n chic ging, lt chn ra v n nhn thy em khng mc ci g trn ngi.\nChng : Th th chiu i thin h ? Tai sao li khng mc g ?\nV : Th chnh anh bo em phi ci khi i ng cho n thong, nh th mi khoa hc m.\nChng : Anh bo em, nhng ch lc no c anh bn cnh thi. Ch bnh thng ai cho php em lm nh vy ?\nV : Th ti em quen ri.\nChng : Th sau mi chuyn din ra th no?\nV : N nhn thy em thch qu nh xng vo. Em lin ht tong ln. \nChng : Tt. Th sau c ai nghe thy v n cu khng ?\nV : Khng Em va mi ht ln c mt ting th n lao ti, mt tay bt ming, tay kia d ngay con dao ang cm trn tay vo c em v do nu cn ht na l c. Cui cng em nh phi im lng.\nChng : Em nm im cho n mun lm g th lm ?\nV : Khng. Nm im l th no? Lc n lt ht v lao v pha em, em lin giy gia chng c quyt lit. Hai bn t xung hu t mt lc thi n tt em my pht. Em mt qu nh nm ph mc cho s phn quyt nh.\nChng : Cht ti ri. Th l xong tht ri. Mt ht khng cn g na ri.\nV : Anh i cha mt ht u, vn cn.\nChng : Cn ci g?\nV : Cng may l em cn thng minh y.\nChng : Thng minh th no?\nV : ng lc n ang chun bEm mi nhanh tr, em bo : khoan khoan, em c mt cch ny cc hay, m bo thch lun.\nChng : Cch g ?\nV : Em mi n dng o ma.\nChng : o ma \nV : L ci o ma bo v bn mi khi tri ma bt cht .\nChng : , hiu ri. Th n c dng khng ?\nV : Ci thng ny ngu lm. Lc em mi n cn hi y l ci g ? Th l sn c ci u ging, em li ngay ra cho n xem. Sau em cn phi mt cng gii thch cho n mi y. no l va thm va hiu quTm li l rt nhiu cng dng.\nChng : N c dng khng?\nV : Anh bit khng, cng may l n t m nn cng thch th dng th. Ch ci loi u ng x ch ny quen lang ch lung tung. N m c bnh tt t ic ri truyn sang cho mnh th coi nh l ht i.\nChng : ng ng. May m v nhanh tr. Em tht l tuyt vi. Th tip theo th no ?\nV : Lc ang d th c ting xe my ca anh. N vi vng bung em ra ri m ng qun o chy bin lun. \nChng : Tri i, vy l ht tht ri. Cn u mt ngi v nt na trong trng.\nV : Anh va khen em thng minh c m.\nChng : y l ti ni th an i mnh thi. Ch lm sao ti c th chp nhn mt ngi v nh c.\nV : Anh c phi li ti em u. \nChng : Nguyn nhn th quan trng g. Ti ch cn bit kt qu l c khng cn l ca ring ti na.\nV : Anh i, anh ng bi quan nh th. Em pht hin ra ci thng trong lc vi vng chy qun chic qun i. Nu mnh giao np n cho cng an, bit u h c th ln ra manh mi m tm c n.\nChng : Sao m ngu th? C nh thng bo cho c thin h bit mnh va b hp dim ?\n(Cm chic qun i) Ci thng ch cht. Tao cho my mt trn.\nTrm : Khoan. nguyn hin trng. Cm s vo hin vt.\nV : Anh i thng trm .\nChng : Th ra l my, thng lo tot. Dm ng vo v ng. ng quyt liu vi my.\nTrm : C gii th vo y (Chia con dao bu ra) a tr tao ci qun ngay.\nV : Anh i em s lm. Thi anh tr ci qun cho n n v i.\nChng : A. Anh bit ri.Em ng s, c bnh tnh nghe anh ni y. Ci qun ny chng ng gi g. Nhng ti sao n phi quay li xin. Chc chn ci qun ny phi rt quan trng.\nV : Ci qun c rich ny th c g m quan trng ? Chng l l hng hiu.\nChng : Khng, ci thng trm kh rch ny ly u ra rin m mua hang hiu. Em dt th. Nu khng c ci qun ny th th no v nh, thng trm ny cng b v n tut xc.\nV : i ng ri. Sao anh thng minh vy?\nChng : Cng cnh n ng vi nhau anh cn l g. My khng s thin h nhng cng bit s v y. th, tao khng lm g c my th tao s x tan ci qun ny, v nh v my s dy cho my mt trn.\nTrm : Khng c x. Nu khng tao s xin cho my mt nht.\nChng : Tao cc s. C gii th my c xin.\nTrm : Thi c. My khng bit s con dao ny th tao cng cho i my ra ng ra khoai lun. My thch v tao dy tao th tao s cho v my dy my.\nV : C chuyn g vy?\nTrm : Em i, em c bit v sao chng em li on ra ngay l do anh cn chic qun khng ? hn chng thng minh g u. Ch v hn cng va i n vng v thi. Ch lm g c ci cng ty no bt lm vic n tn gi ny.\nV : C tht th khng anh.\nChng : Em ng tin thng . N ba ra chuyn ny nhm chia r ni b . ng mc mu n.\nTrm : Anh l mt thng trm rt c lng tm. th khng bao gi ba t hay di tr. Ni c sch, mach c chng. Nu em khng tin th em hy xem ci ny i.\nV : Ci ny l th no?\nTrm : Lc ny khi anh np trong lm cy mc v pht hin thy thiu chic qun i. M y li l chic qun v anh mua tng. Anh nh np trong lm cy nh ch khi no v chng em ng th li ln vo ly qun. ng lc anh thy chng em i v. Hn ng trc cng chnh n li trang phc, lau vt son trn m. Ri anh thy hn lc ti, ly ra mt mu giy ri vo vin vt i. Tnh anh vn t m nh em bit. Vy nn\nChng : Thng trm ch cht. My dm ng vo v tao, gi cn quay li ph hoi hnh phc gia nh tao ? Tao cho my cht.\nTrm : xem ai cht trc ai.\nV : Hai ngi thi i. Anh kia, mu giy vit ci g ? \nTrm : Th tt nhin n phi l ci g khng tt,khng cn thit hoc khng mun cho em bit th hn mi vt i. Nh ? Tao cng mi suy lun ra thi. n ng chng mnh u thng minh m. ng khng ?\nChng : Khng c g u em. hn ba .\nV : Anh a cho ti xem.\nTrm : C a tr cho ti chic qun, ri ti s a c mu giy.\nChng : Khng i no.\nV : Anh a cho em chic qun.\nChng : Th cht ch quyt khng bao gi a.\nTrm : Sp cht n ni m cn kin cng gm.\nV : Cc anh im i. Ti khng cn ci g na c. Cc anh c gi ly m lm k nim. n ng cc anh l l u gi. (Chng) Ti th nh mn mi ch anh, trong khi , anh li dm i n vi gi. Vy m khi v nh, bit v b nh vy, anh chng an i ng vin ti ly na li. Li cn quay ra trch mc khinh mit ti.\nTrm : Qu t.\nV : Cn c anh na. Anh ln vo nh ti n trm, li cn d tr i bi vi ti. M anh cng c v nh y ch. Anh th ngh xem, nu v anh cng nh ti. Gi ny c y cng ang mn mi ch anh v b mt k xu ln vo lm nhc th anh ngh sao?\nTrm : Ti s cho n cht.\nV : Vy th anh hy t kt liu i mnh trc i.\nTrm : Ngu g m lm th ? N ng vo mnh th n cht,ch mnh ng vo ai l quyn ca mnh. ng khng h ng?\nChng : Qu ng. \nTrm : Bt tay nhau mt ci. Ch c n ng l hiu nhau.\nChng : Nhng my ng vo v tao.Tao cho my mt trn.\nTrm : My ng vo qun ca tao. Tao quyt khng tha cho my.\nV : Hai anh c y m nh nhau, ti i y. \nChng : Em i u.\nTrm : Mun th ny ri. \nV : i u cng c. Min l ni khng c nhng ngi n ng ch k, xu xa nh cc anh. Cng lm l cht thi. M cht cng cn sung sng hn sng nhc nh th ny.\nChng : Anh xin em, em ng b anh m cht. \nTrm : ng no hm nay cng l ngy tn th. Nu c cht th tt c s cng cht m ?\nChng : Em i anh bit li ri. Anh cng t thy xu h vi bn thn lm. \nTrm : ng y c . Ch v n ng c s din cao nn khng ni ra thi. Ti cng thy c li vi c v c v ti lm. Bi th ti mi phi xin li bng c ci qun y.\nChng : Em tha li cho anh nh. Bn anh xin th : Khng i lng nhng, khng i linh tinh, khng i b bch","language":"notdetect","sentiment":"Mixed","author":"ChíThanhKute","author_text":"ChíThanhKute","md5_text":"108cf342a07e9ce66e83f5af68093911","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758308016128},{"id":"353114551","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100000178370400","authorlink":"GiangThuHuyen","date":"2013-01-06T13:41:02Z","topic":"Kia K9","topic_text":"Kia K9","text":"hanh phuc i. tam bit nhe :(","language":"notdetect","sentiment":"Negative","author":"GiangThuHuyen","author_text":"GiangThuHuyen","md5_text":"f99e50296292eca51a5ce527a10e1549","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758311161856},{"id":"353114881","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003585004016","authorlink":"CuaBốnCàng","date":"2013-01-06T13:28:19Z","topic":"Kia K9","topic_text":"Kia K9","text":"Tri i!, va i, va mt, va rt run cm cp, ko u li cn b c 1 con airblade thng vo chn..... k gi c 1 ci bia tn l \"ngi yu du\" mnh phi my ci tiu vo mi ht bi ai th ny ko h gii?","language":"notdetect","sentiment":"Mixed","author":"CuaBốnCàng","author_text":"CuaBốnCàng","md5_text":"39763cc06c61ece835a27ad98bd01817","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758312210432},{"id":"353114076","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002966278587","authorlink":"XuanPham","date":"2013-01-06T13:58:08Z","topic":"Kia K9","topic_text":"Kia K9","text":"-''''''Hnh phc tng nh l bt tn...bng mt ngy thay th mt nim au\n(\".\")","language":"notdetect","sentiment":"Mixed","author":"XuanPham","author_text":"XuanPham","md5_text":"87969245b04a051e70e840c6eb8f7b75","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758313259008},{"id":"353114281","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003614649808","authorlink":"HoàngThu","date":"2013-01-06T13:53:08Z","topic":"Kia K9","topic_text":"Kia K9","text":"nh mi tay mi xong mt cu kim ton :( hu hu hu, c ti liu nhng khng ctrl c vi ctrl v c au tht, Suri Kem my lm my cu sau c kh nng cop py c ","language":"notdetect","sentiment":"Negative","author":"HoàngThu","author_text":"HoàngThu","md5_text":"cd9b24d426e175d83c8cb3de561e2887","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758328987648},{"id":"353114084","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100000199004806","authorlink":"ZymZyn","date":"2013-01-06T13:56:16Z","topic":"Kia K9","topic_text":"Kia K9","text":".Xin li anh khng c tin \n\n- Tng e con gu bng kia i a....\n\n. . . . . . . . . . . . . . . . . .\n- Mua cho e con gu kia i a. Con kia ka...\nVa ni con b va dng ln ch vo con gu to nht c t trong cng. Con gu rt to,phj gn bng mt ngi. Trng cng kh p...\n- A lm g c tin. To lm,chc l t. Sinh vin ngo a kim u ra...\nChng trai ni vi c b v chn chn v xu h na.\n- Mua cho e i a, a chng bgi tg e ci g to tt vy. E thch con gu y. . .\n- Nhg anh ni tht m. A xin li. Sau ny c tin ri a tg e c ko ??\n- Nhg e mun by gj m.\n- ...\n,ri a mua c ko ??\n. . . . . . . . . . . . . . . . . . .\n- A i,chiu nay i chi c ko\n- A bn ri. Khi khc c ko ?\n- . . . . .\n- A i,hm nay i cm tri,a i ko?\n- a ko i u.a c vic\n.... . . . . . . . . . . . . . . .\n- Sao k thg nay a ko i chi vi e. A trnh e h?\n\nCon b tc ti hi...\n- Ko! Anh bn tht.,\n- A bn g? Bn g m lm th? Bn ti ni ko dnh c 30p cho e ?\n- A xin li...\n- Ri a tr hn, tht ha,a ha my ln nhg a chg ti l sao. Em c phi ch i a l sao ?\nEm ko chu c u...A ko cho e c bt c th gj li cn trnh mt e...\nA qu ng lm\nV con b b i ,vi vng.\nB li chng trai ng nh tri trng gia ci ng gay gt ca buj tra gn h...\nTi con b nt : \" e mun chia tay\"\n\" sao th, a xin li e ri m.e gin a \"\n\"e cm thy ht tnh cm ri.ko y a na.vi li e cn nhiu hn nhng g a c th cho e.quyt nh vy i.chia tay\"\n\"uk\". . .\nV chia tay,con b bt u quen ri yu 1 ng khc qua s gii thiu ca bn n.thnh thog n c gp la ng yu c.\nNhg nhg g n lm l quay i v bc tip...\nC ln n nhn ng yu c ca mnh ag nh nhi m hi trog qun cf vo gj g khch nht.\nN t nh : i lm kim tjn . Chc l n tin nh...2 thng.\nN c ngi yu mi c 2 thng,cg g nga vi vic chia tay ng yu c 1 khog thi gian gn nh vy\nMy t c tn\"Em ra ngoi c ko?\"\"\n\" ang ma. A tm gp lm g\"\n\" em ra cng thi. A c th mun a cho e\"\nN vi ci ,ri lt t ra cg...\n- A tg e.Nh a ha. Mn qu cui cg\nN lg ng. L con gu y. Con gu n i mua. Ngi yu n t sg trog ma,nhg con gu th c bc kn li trog giy bg..\n- A ha tg e,khi no a tin,gi a mi c ... Tg mun 1 t. E sg hp nh\nN km kon gu to. Ng trch ko bit v nc ma hay v ki g na.\nN g lg i nhn ng yu c ca n i khut sau nhng git nc ma but t ti.\nTh ra ny n ko i chi vi n v a i lm thm. V a i lm thm l v n,v s ch k,ngu xun ca n.\nNgi yu n bug tay n th y,ko nu gj iu g,ch duy nht 1 li ha vi n.\nAnh s mua tg e khi a c tin...\nN khc...\nGja con ma cui cg m nhg git nc ma kia n bit rg ko bao gj n tm li c na...","language":"notdetect","sentiment":"Mixed","author":"ZymZyn","author_text":"ZymZyn","md5_text":"75d7ac657d2a18a11f36af8f6a022312","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758330036224},{"id":"353114973","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003726429568","authorlink":"TítGà","date":"2013-01-06T13:33:25Z","topic":"Kia K9","topic_text":"Kia K9","text":"c l no................c mng(^_*)..thi vs ch c","language":"notdetect","sentiment":"Mixed","author":"TítGà","author_text":"TítGà","md5_text":"1835a638b128e8d6bfb40da494d644b4","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758332133376},{"id":"353114216","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003940602845","authorlink":"LươngTrương","date":"2013-01-06T13:53:27Z","topic":"Kia K9","topic_text":"Kia K9","text":"V con gi ^^\nNghe v nghe ve\nNghe v con gi....\nSut ngy li nhi\nLin khc tnh yu\n ri chiu chiu\nT nm,t by\nAnh ny ht xy\nAnh kia d thng\nNghe v nghe ve\nTip v con gi\nT nhn xinh gi\nSut ngy Phn son\nLi th bon bon\nCi mm khng ngh\nNhng c xu x\nVn nhn l xinh\nNgi to chnh nh\nVn cho...Eo p\nNgi m dp lp\nLi bo gi eo\nBo i vt bo\nTh ku au c\nBo i nh c\nTh bo au tay\ni chi sut ngy\nLm g bit mt !\nc th c st\nVn nhn thng minh\nQua 5 mi tnh\nVn cho l t\nNgi nh qu mt\nQu vt khng tha\nQuen ngi la c\nHng ny qun n\nn nhiu to s\nc ch g u\nNi lm au u\nKo thm ni na !!!...","language":"notdetect","sentiment":"Mixed","author":"LươngTrương","author_text":"LươngTrương","md5_text":"5184b5f0ca07df3130c838552366ef52","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758333181952},{"id":"353114533","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002752098761","authorlink":"MaiMinhLuân","date":"2013-01-06T13:45:12Z","topic":"Kia K9","topic_text":"Kia K9","text":"Da bo nhiu thoi gian cong suc ra tap thi toi ngay dien co gang nha moi nguoi (@@)","language":"notdetect","sentiment":"Mixed","author":"MaiMinhLuân","author_text":"MaiMinhLuân","md5_text":"62dedea8a22ebab35da19b51b597ef87","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758334230528},{"id":"353114239","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003128000579","authorlink":"SúnSuSi","date":"2013-01-06T13:50:19Z","topic":"Kia K9","topic_text":"Kia K9","text":"Lnh qu lnh qu.... nh 1 vng tay :(\n Lnh nh ny m mai phi dy sm i thi ri, ch c nh b t ny ch thi t 2 cho n m...\n Nhng thi ta s c. CHC CC TNH YU CA TA NGY MAI THI TT, MAY MN NHE ! <3\np/s: Ai chc t ngy mai thi may mn iiiiiiiiiiiiiiiii >.<","language":"notdetect","sentiment":"Mixed","author":"SúnSuSi","author_text":"SúnSuSi","md5_text":"6787000a95eac740b1d56583a35ee99b","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758335279104},{"id":"353114934","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003784477135","authorlink":"MưaLuna","date":"2013-01-06T13:36:43Z","topic":"Kia K9","topic_text":"Kia K9","text":"ang pun ang ckan aj tan yu lun.hajzzz","language":"notdetect","sentiment":"Mixed","author":"MưaLuna","author_text":"MưaLuna","md5_text":"2cd670db82d435068c4852d5a64d6fd7","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758336327680},{"id":"353114207","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002596000851","authorlink":"KenvinCuong","date":"2013-01-06T13:53:27Z","topic":"Kia K9","topic_text":"Kia K9","text":"h qu ca 2 ngy banh --> 1 chn bi bng gn, chn cn li bi sng !","language":"notdetect","sentiment":"Mixed","author":"KenvinCuong","author_text":"KenvinCuong","md5_text":"15bde20498f6f48466cfaff6ed3f0200","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758337376256},{"id":"353114884","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004369124409","authorlink":"ThùyDung","date":"2013-01-06T13:28:19Z","topic":"Kia K9","topic_text":"Kia K9","text":"Ti h ri, tp trung no cc tnh yu <3","language":"notdetect","sentiment":"Positive","author":"ThùyDung","author_text":"ThùyDung","md5_text":"18b22320952fa34461234401d82f4008","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758338424832},{"id":"353114150","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004059572557","authorlink":"ThaoGa","date":"2013-01-06T13:53:57Z","topic":"Kia K9","topic_text":"Kia K9","text":"Tri lnh k ngh hc k i u ch n vs ng m cng k bo ln k t no.?Chn........","language":"notdetect","sentiment":"Mixed","author":"ThaoGa","author_text":"ThaoGa","md5_text":"2697d44514f752242fcdfda7892d3ccc","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758338424833},{"id":"353114368","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004291569248","authorlink":"NắngNhạtNhòa","date":"2013-01-06T13:48:16Z","topic":"Kia K9","topic_text":"Kia K9","text":"Maj t j thi...ca nha co aj mun ns j ko ? ... T nghe naj...0_0","language":"notdetect","sentiment":"Mixed","author":"NắngNhạtNhòa","author_text":"NắngNhạtNhòa","md5_text":"b356cd59cc0405c9f7cce086d3706e52","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758377222144},{"id":"353114811","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003760542931","authorlink":"ĐàoBằngĐược","date":"2013-01-06T13:40:04Z","topic":"Kia K9","topic_text":"Kia K9","text":"Em l ai...<3...m lm anh phi khc v yu em...<3....qu nht.!!!","language":"notdetect","sentiment":"Positive","author":"ĐàoBằngĐược","author_text":"ĐàoBằngĐược","md5_text":"63dad8adbdbc0d267eebfdcbe2bf926f","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758378270720},{"id":"353114935","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=261122294004578","authorlink":"Thơtìnhbuồnaihợptâmtrạngthìvào","date":"2013-01-06T13:27:51Z","topic":"Kia K9","topic_text":"Kia K9","text":"V th l....\n\nV th l... hai nm ri y nh\nNgy chia tay em chng ni iu g\nKhi anh hi v sao nh i ng\nNgoi mt cu rng:\"S phn vy, anh i!\"\n\nV th l... ch nh nh th thi\nEm ra i... sau nm nm cht y k nim\nC iu g em cn giu gim\nPha sau ni bun v tnh lm chng gai\n\nV th l... cng l nm th hai\nNgy em nm tay ai... thnh c du nh ngi khc\nHm em i ma ngu bun tan tc\nGi lnh la v, se tht tri tim ci\n\nV th l... khng gp na th thi\nEm tm mua nim vui gia ch i no lon\nCh ring mnh anh... ngi by bn\nnh gi... tim mnh, nh gi... mt nim au!!!\n\nNa bu ru, na ti th\nCng ngng nghnh na giang h nh ai\nThng nm cp na i trai\nTnh kia au cng phai i na phn\n\nCt bi che na ng trn\nC nh m na l thn khng nh.\nTc xanh na nht nha\nQu mnh xa qu na ra qu ngi\n\nGi ma t na cuc i\nRu no say kht na li th non.\nng i na kip cn con\nNa say h ru, na bun cu th \n\nGiang h na tnh, na m\nNa trng vi na cu th ang... chm.","language":"notdetect","sentiment":"Mixed","author":"Thơtìnhbuồnaihợptâmtrạngthìvào","author_text":"Thơtìnhbuồnaihợptâmtrạngthìvào","md5_text":"2fe5b60147ec4c81fa69eb99eb7d8aea","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758379319296},{"id":"353114325","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=112562602433","authorlink":"ArsenalFCinVietNam","date":"2013-01-06T13:55:24Z","topic":"Kia K9","topic_text":"Kia K9","text":"Arsenal va c hng pha pht , nhng Walcott st bng st khng qua c hng ro ca Swansea.","language":"notdetect","sentiment":"Mixed","author":"ArsenalFCinVietNam","author_text":"ArsenalFCinVietNam","md5_text":"0c857dc4790a4b035452aeec688ae3b0","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758380367872},{"id":"353114798","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003174079656","authorlink":"TiểuChiChi","date":"2013-01-06T13:34:15Z","topic":"Kia K9","topic_text":"Kia K9","text":"T dng c mt thng p giai(?!?),cao ku,tc vng cho,trng nh my anh Hn Xng cc k ngoan(nh cn ) mnh sai u lm y,bo i ng k dm sang ty,bo con mo l con ch cng inh ninh l con ch k dm ci :\"> *^^*\ni chi th mnh ngi trn ging nhm nhom n ch ng ch ty bo ly ci ny mc ci kia,qut nt nh m cng toe tot i lm cho mnh cng hay hay vui vui ra pht nh! =))))))~ thng th thng tht nhng cx ng m :\"3 :))\n~> hiu c cm gic ca my b n vng th trong am m ru ha :) :\"3 <3","language":"notdetect","sentiment":"Mixed","author":"TiểuChiChi","author_text":"TiểuChiChi","md5_text":"61edaf76e3451a93bf6765fd6ac87b48","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758381416448},{"id":"353114187","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003478713514","authorlink":"KietKu","date":"2013-01-06T13:50:40Z","topic":"Kia K9","topic_text":"Kia K9","text":"trj uj!, sao lj chn w j th.., .^:!::\"\"\"\"\"\"\"\"","language":"unknown","sentiment":"Mixed","author":"KietKu","author_text":"KietKu","md5_text":"537f716bdc529751d417356dc05ff161","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758382465024},{"id":"353114877","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003587397367","authorlink":"TranTuanDung","date":"2013-01-06T13:30:02Z","topic":"Kia K9","topic_text":"Kia K9","text":" Cu bit khng????\nMi ln ai nhc n tn cu\nT ch bit ci nh\nV c l i\nCi cm gic \n T mong rng !!!\nng ai nhc n cu na!\nBi v.................\nT.........................\nVn cha qun c cu !!!!!! th gii bn kia cu co nh t khng \np/s bun","language":"notdetect","sentiment":"Mixed","author":"TranTuanDung","author_text":"TranTuanDung","md5_text":"86ed771a1945b638b0ffc103405bb766","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758383513600},{"id":"353114887","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003682337944","authorlink":"ThíchChiếnKhông","date":"2013-01-06T13:36:58Z","topic":"Kia K9","topic_text":"Kia K9","text":"o aj inbox t tnh mk nh . . . .","language":"unknown","sentiment":"Mixed","author":"ThíchChiếnKhông","author_text":"ThíchChiếnKhông","md5_text":"ac9051a3f4a6ffd60dd36bb1729a51c7","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758384562176},{"id":"353114344","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002568630111","authorlink":"LeHong","date":"2013-01-06T13:53:13Z","topic":"Kia K9","topic_text":"Kia K9","text":"Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","language":"unknown","sentiment":"Mixed","author":"LeHong","author_text":"LeHong","md5_text":"3f7335b99ca68466fd9c5f5737cecca1","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758385610752},{"id":"353114262","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=1213725111","authorlink":"PhạmNguyệtThuQuỳnh","date":"2013-01-06T13:49:09Z","topic":"Kia K9","topic_text":"Kia K9","text":"i a R ct ch + tim thuc i v em n trng tc ca m v i hoi k ra :|\ni mua ht Me-O cho 2 con mi + ra ci hnh ang lm profile, photo ny n lm h s nhp hc :|\n\ni v gp c 2 em 1 en 1 trng nm thu lu gn nh Jim Anh. My nh z Tuyet Huynh v k thu hi c, ln lt gi cho ch nh v Ann L mn my, ng c r k ko tr treo vn k c chuyn g xy ra [v vn k mn c my!]. \nTi hi v n ch Tn Bnh th 1 em nhoi khi bal, buc phi tp vo ven ng ci khch sn Bel Ami g nht m vo th gp 1 thng la o/n cp/dt gi/tm gi.. gh st vo gi hi em i ng Trng Chinh ny n, mnh bo n chy ra L Thng Kit m hi n vn ko no hi mnh ang ch ai, mnh gt ' ch ai ht xong quay i n bt u gi ging chi th dn mt: \"m tao ni chuyn ng hong m my lm ci g vy? By gi tao ni ng hong c nghe k?\" [ging ngp ngng nn chc k fi cp SG]. Mnh lic n pht xong t cha kha ko ga ci o. Nhn qua gng thy n k d theo.\n\nv ti nh m thy ci gi mo, k ni nng g m nm ngay cho ci nhn: \"Con ngu ny ni mi vn k nghe thi t b tay vi m ri!\"\n\nD c em dao trong ngi, nhng vi m thc n + h s + gi mo, thm con xe lnh knh na cng nh k. Cng may thng kia n g/di tnh mi ln v i mt mnh!\n\nChuyn cng ch c g, nghe cng 1001 chuyn ri nhng my c gi cng nn nghe thm, cn thn th k tha u! y l mnh, ra ng b quy ri, b cp git, b la o [k thnh v khi chng pht hin mnh ngho v xu nn b qua cho] t hi bit i xe mt mnh ra ng ti gi ri m cn lng khng c th ai bit chuyn g s xy ra..\n\nNi chung thi bui ny i trn ng th lo m i ti ni v cng ng c tin ai ht! :|","language":"notdetect","sentiment":"Mixed","author":"PhạmNguyệtThuQuỳnh","author_text":"PhạmNguyệtThuQuỳnh","md5_text":"7cae98b7cdc581c54fd3cb9363b8afe2","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758386659328},{"id":"353114769","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=179287798876948","authorlink":"CốGượngCườiĐểNgườiTaThấyMìnhKhôngĐau","date":"2013-01-06T13:37:16Z","topic":"Kia K9","topic_text":"Kia K9","text":"Anh i ! Em Cn Lm Mt . . . !!!\n\nNgi con gi mnh m trong em bit lnh khi thiu anh, yu mm khi khng c anh bn cnh.\n\nKhi em bun, khi em mun c khc tht to, em c c anh bn. Em s trt ni bc dc, nhng kh chu m trong lng, em da vo vai anh m khc cho tha, cho cn ni bun, cho vi i nhng m c\n\nEm cn mt im ta, mi lc tm hn em trng rng, chnh vnh, c anh bn, s chia, cho em mt b vai vng chc, trao cho em cm gic an ton v em c th khc ngon lnh trong vng tay y... bnh yn...\n\nEm cn mt im ta, nhng lc tng chng nh em s gc ng trc nhng tht bi, trc sng gi ca cuc i, em c anh, nng em dy. Nm ly bn tay th rp m p y, em bit mnh khng l loi, c c. Anh mang n cho em s du dng, truyn cho em nim tin v hi m. Em bit c mt i tay dt em i bo t ca cuc i.\n\nEm cn mt im ta, sau nhng giy pht c thc hin ti, bon chen vi cuc sng gp gp, x b, c bn anh yn bnh lng ngm chiu hong hn thm, th chn trn trn bi ct mn mng, mc cho sng v v, mc cho gi ht \n\nEm cn mt im ta, nhng khonh khc yu lng, trong chc lt thi mt ai c th lm em i sai ng, em bit mnh vn c anh, , ch i v sn sng giang rng cnh tay n em vo lng. c nng nu, c anh nng bng nhng li c cnh, em thy mnh tht nh b v em bit mnh ang c ch che.\n\nEm cn mt im ta, nhng khi thy nh, lc lng th gii ca mnh, em c anh, in thoi cho anh, nghe ging ni ca anh, lm phin gic ng m anh chng h cu gin, ch ci hin ni chuyn vi em, an i v ru em gic ng m m.\n\nEm cn mt im ta, khi nhng bui nh ma chiu nay, lnh but ton thn, c chi c mt ci m tht cht lng em li m, khng cn s ma gi bo bng ngoi kia. em bit m p trong tri tim anh!\n\n th, em vn c chp, em mnh m, em chng cn ai bn mnh...\n\n th, em bit cch kim ch cm xc, em bit tm nhng th vui cuc sng ca em chng bao gi b nhm chn bi nhng lo toan tm thng nhng x b no nhit ngoi kia\n\n th, em chng phi bun nhiu u. Nu mt ngy kia anh khng cn bn em na, mc cho nhng nn n, mc cho nhng gin hn, mc cho anh nu ko, em bung tay v anh ra i...\n\nNhng ri, c mt lc no , trong khonh khc ngn ngi mt mnh, em li thy mnh c n lc lng. Em s lm g y, khi bn mnh khng c anh, em cng ch l mt c gi yu ui s st ci cm gic c c...\n\nEm tr v nh b v vi bn 4 tng im lng nh t, trng rng trong th gii ca chnh mnh v ri nc n nh mt a tr b b ri\n\nNgi con gi mnh m trong em bit lnh khi thiu anh, yu mm khi khng c anh bn cnh. Vn nh, v yu anh nhiu lm! V em bit \n\nAnh i, em cn mt im ta. Em cn lm mt ngi yu em!\n Lanh ","language":"notdetect","sentiment":"Mixed","author":"CốGượngCườiĐểNgườiTaThấyMìnhKhôngĐau","author_text":"CốGượngCườiĐểNgườiTaThấyMìnhKhôngĐau","md5_text":"429ab30f5ec809f5162e01f16cebc311","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758405533696},{"id":"353114506","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004663350570","authorlink":"NguyenvanVu","date":"2013-01-06T13:41:07Z","topic":"Kia K9","topic_text":"Kia K9","text":"Ti bt khc khi bit mnh thua cuc\n- Vng tay ny khng gi c ai kia\n- Bao ngy qua ti lm tin tng\n- V 1ngi khng phi ca ring ti","language":"notdetect","sentiment":"Mixed","author":"NguyenvanVu","author_text":"NguyenvanVu","md5_text":"b2dfb6d09d5c4a1fa92ff9a3c56e1ea3","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758407630848},{"id":"353114767","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003997290940","authorlink":"KtBuôngTay","date":"2013-01-06T13:37:19Z","topic":"Kia K9","topic_text":"Kia K9","text":"Ni au ny sao kh vt qua qu zy tri!\nNghe n0ns0p v hc th0y c ln no a e like ci ng vin tau i ngy kia tau li thi roi 1 mn tht bi nhc qu","language":"notdetect","sentiment":"Mixed","author":"KtBuôngTay","author_text":"KtBuôngTay","md5_text":"f3e759b7b60e1d7ed70392a9bc33deab","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758408679424},{"id":"353114959","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100001184643124","authorlink":"NhuTran","date":"2013-01-06T13:32:24Z","topic":"Kia K9","topic_text":"Kia K9","text":"KHNG KH SI GN LM CHO NHNG NGI CON XA S PHI NO LNG","language":"notdetect","sentiment":"Mixed","author":"NhuTran","author_text":"NhuTran","md5_text":"7e2db88f0d38b9e596e24797903d6239","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758409728000},{"id":"353114750","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=179271718793765","authorlink":"Nếutráitimtôilạnhhơnmộtchút","date":"2013-01-06T13:39:08Z","topic":"Kia K9","topic_text":"Kia K9","text":"Ny cc chng trai:\n\n- Khi mt c gi tm mi cch c ni chuyn vi bn ...\n\n- Khi mt c gi ni xin li mc d c y chng lm g qu ng c\n\n- Khi mt c gi khc rt nhiu v c y vn cn yu hoc nh bn...\n\n- Khi mt c gi vn c gng bn quay li ...\n\n- Khi mt c gi khng quan tm bn lm tn thng c y bao nhiu ln m vn yu bn ...\n\nTh ... ng bao gi bung tay v c y i ......\n\nV ... c th bn s chng bao gi tm li c mt ngi yu bn nh th ..!!! \n\nMiMi<3","language":"notdetect","sentiment":"Mixed","author":"Nếutráitimtôilạnhhơnmộtchút","author_text":"Nếutráitimtôilạnhhơnmộtchút","md5_text":"60d4fa712b58a9f2b8b4c9a375b2ea39","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758409728001},{"id":"353114273","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002731612612","authorlink":"HuyềnAnhTrần","date":"2013-01-06T13:50:07Z","topic":"Kia K9","topic_text":"Kia K9","text":"T hay ngh ngi, lo lng, v k t tin vo nhng g t c th lm, k tin tng vo kt qu t c th t c, cng nhiu ln lm ri hay trch mnh v k th ny, k th kia, hi hn v nhng quyt nh bng bt ca m, nhng ln ny th cho d c l bng bt hay bc ng, cho d n c qu kh so vi kh nng ca m th chng m cng nhau c gng hon thnh nh!!!... CHNG TA C NGH CHNG TA KHNG LM C TH S KHNG BAO GI LM C...!","language":"notdetect","sentiment":"Mixed","author":"HuyềnAnhTrần","author_text":"HuyềnAnhTrần","md5_text":"2f8470749df08d88e247a45576b687a6","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758410776576},{"id":"353114581","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100000523264494","authorlink":"NhutPham","date":"2013-01-06T13:37:58Z","topic":"Kia K9","topic_text":"Kia K9","text":"Khi minh hien wa co fai luon bi ng ta an hiep ko!? Minh luon de cao tinh nghia nhung rat de bi loi dung. Minh muon di that xa that xa chon do thi bon chen va day su loi dung nay. Ve LongAn o voi Noi la tot nhat.","language":"notdetect","sentiment":"Mixed","author":"NhutPham","author_text":"NhutPham","md5_text":"8cec6d55e4dcf1e48ebda5176802bb19","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758411825152},{"id":"353114850","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003543974450","authorlink":"KeoNgocBich","date":"2013-01-06T13:36:59Z","topic":"Kia K9","topic_text":"Kia K9","text":"ng Hi V Sao Ti Bun. H h h. Nnh","language":"notdetect","sentiment":"Mixed","author":"KeoNgocBich","author_text":"KeoNgocBich","md5_text":"0c1e65812b43f02960a5598017e26af1","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758412873728},{"id":"353114851","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003510718115","authorlink":"HoangKimGiap","date":"2013-01-06T13:35:14Z","topic":"Kia K9","topic_text":"Kia K9","text":"m p khng phi khi ngi bn ng la, m l bn cnh ngi bn thng yu. m p khng phi khi bn mc mt lc hai, ba o, m l khi bn ng trc gi lnh, t pha sau n c ai khoc ln bn mt tm o. m p khng phi khi bn ni m qu, m l khi c ngi th thm vi bn: C lnh khng?. m p khng phi khi bn dng hai tay xut xoa, m l khi tay ai kia kh nm ly bn tay bn. m p khng phi khi bn i chic m len, m l khi u bn da vo mt b vai tin cy.Nu yu ai xin hy yu bng c con tim c th s ch i ca tri tim yu chn thnh rt mong manh nhng t nht n cng khin tnh yu ca bn khng tr nn v ngha.","language":"notdetect","sentiment":"Mixed","author":"HoangKimGiap","author_text":"HoangKimGiap","md5_text":"45b8f4fd4fb0dee6c9d04ec52b646fee","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758413922304},{"id":"353114315","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003904438536","authorlink":"VìEmLàNắng","date":"2013-01-06T13:51:18Z","topic":"Kia K9","topic_text":"Kia K9","text":"M bo:\n Khi yu ng nhn sc, nhn v hay nhn xe. Hy nhn s c gng ca n. Lc trc b con chng c g trong tay c. V b bit c gng trong lm n v yu thng, m nh cc mt ln cuc i m v cng v m yu b con. V nh thy, b con khng m thua trong cuc nh cc, sau 5 nm, b con dn dn hi c mi th. Hy yu ngi v con m c gng lm mi th, ch ng yu ngi dng tin chiu con.\n\n Con gi h, i, phi dng tin ra c ny mt kia. Nhng trong tnh yu m dng tin ra th gi li lm g xc lng tin. Tnh yu cn th hin bng hnh ng ch khng phi bng li. Yu l phi hy sinh ch khng phi dng tin chim ot. Ngi n ng yu thng con l ngi dt tay con i n hnh phc ch khng phi ngi ni co con bit th no l hnh phc","language":"notdetect","sentiment":"Mixed","author":"VìEmLàNắng","author_text":"VìEmLàNắng","md5_text":"932b5a0cf22fce7265d066b84936830c","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758414970880},{"id":"353114628","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004441782964","authorlink":"QuânLừaTềnh","date":"2013-01-06T13:42:25Z","topic":"Kia K9","topic_text":"Kia K9","text":"C ng lo ang i ti mt cy cu nh\n- N ch c th dnh cho mt ngi duy nht i qua\n- Bn kia cy cu l mt cu trai\n- Va thy ng lo cu lin ht to: ti khng c thi quen nhng ng cho nhng k ngu ngc u lo gi\n- ng lo dng li v ni: ti th lun c thi quen nh vy ..!","language":"notdetect","sentiment":"Mixed","author":"QuânLừaTềnh","author_text":"QuânLừaTềnh","md5_text":"628173f8f1ea48c74b9c1e45a8023b4c","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758421262336},{"id":"353114835","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004408231301","authorlink":"NgọcTrân","date":"2013-01-06T13:38:14Z","topic":"Kia K9","topic_text":"Kia K9","text":"i ch c phi l hnh phc ko....thy iu tht s rt nhm chn...i ng thui....mai bt u 1 tun hc tp mi vs nhu tri nghim cm xc mi....g9 c nh!!!","language":"notdetect","sentiment":"Mixed","author":"NgọcTrân","author_text":"NgọcTrân","md5_text":"1fef3dea171f71bab71b4bcada3a503e","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758422310912},{"id":"353114743","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004741564448","authorlink":"QuangNam","date":"2013-01-06T13:37:26Z","topic":"Kia K9","topic_text":"Kia K9","text":"Anh Hng khng gp thi.......","language":"notdetect","sentiment":"Mixed","author":"QuangNam","author_text":"QuangNam","md5_text":"31c9ea2d670ab6ccf0bb4c360a8c3caa","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758423359488},{"id":"353114666","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004024039288","authorlink":"DuongNguyen","date":"2013-01-06T13:42:20Z","topic":"Kia K9","topic_text":"Kia K9","text":"Troj mat ma moj ng keu lah hj","language":"unknown","sentiment":"Mixed","author":"DuongNguyen","author_text":"DuongNguyen","md5_text":"ab505b029474c58c5529d8b299769d2b","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758424408064},{"id":"353114301","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003579633886","authorlink":"DanhNguyễn","date":"2013-01-06T13:53:05Z","topic":"Kia K9","topic_text":"Kia K9","text":"Q tr li =))nh A7 vs A18 w :x:x:x:x:x:xc ai nh Q k :\">","language":"unknown","sentiment":"Mixed","author":"DanhNguyễn","author_text":"DanhNguyễn","md5_text":"4de2ee7700b334eb9c925c9a88ff6923","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758425456640},{"id":"353114034","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003650645047","authorlink":"TôiLàTôi","date":"2013-01-06T13:56:40Z","topic":"Kia K9","topic_text":"Kia K9","text":"http://mp3.zing.vn/bai-hat/Dia-Nguc-Tran-Gian-Andy-Nguyen/ZWZDACBA.html","language":"unknown","sentiment":"Mixed","author":"TôiLàTôi","author_text":"TôiLàTôi","md5_text":"73d0988d3f978d13c307a3521ea4af0a","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758426505216},{"id":"353114044","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004177121391","authorlink":"LờichiatayAnhnóiHaynhưhátEmtặngAnhpháttátĐẹpnhưphim","date":"2013-01-06T13:54:49Z","topic":"Kia K9","topic_text":"Kia K9","text":"HU QU CA MT CN GIN\n( c ngm.. ai cng nn c 1 ln.. au tht y.. rt ngha )\n* * *\nHu qu ca mt cn ginTrong khi mt ngi n ng ang nh bng chic xe ca ng ta, th a con trai ln 4 tui ca ng ta nht ln mt vin si v v nhiu ng ln ln pha bn kia cnh chic xe ca ng ta. Trong lc gin d, ngi n ng nm ly bn tay ca a con v nh mnh nhiu m khng nhn rng ng ta ang dng mt ci c l vn vt nh.\nKt qu l trong bnh vin, a con trai ca ng ta mt i ht cc ngn tay ca mnh do qu nhiu ch gy. Khi a con trai nhn thy i mt b mnh biu l s au n, a b bn hi: \"B i ! Khi no cc ngn tay ca con mi c th mc tr li ?\" Ngi b cm thy rt au n v khng ni c li no; ng ta tr li chic xe ca mnh v n tht nhiu.\nTrong khi ang b lng tm dn vt v ang ngi i din pha hng ca chic xe , ng ta cht nhn thy nhng vt xc do chnh a con trai ca ng ta v rng: \"B i ! Con yu B nhiu lm !\"\nV mt ngy sau , ngi n ng quyt nh t st\nCn gin v Tnh yu khng bao gi c gii hn, nn xin hy chn Tnh Yu c mt cuc sng ti p v ng yu, v xin hy nh iu ny:\n vt th s dng, cn con ngi th yu thng.\nVn ca th gii ngy nay th ngc li: con ngi th s dng, cn vt th yu thng.\nHy lun c nh nhng ngha ny :\n- Hy cn thn vi nhng ngh ca bn, v bn s ni chng.\n- Hy cn thn vi nhng li ni ca bn, v bn s thc hin chng.\n- Hy cn thn vi nhng hnh ng ca bn, v chng s l thi quen ca bn.\n- Hy cn thn vi nhng thi quen ca bn, v chng s l c tnh ca bn.\n- Hy cn thn vi nhng c tnh ca bn, v chng s quyt nh s mnh ca bn.","language":"notdetect","sentiment":"Positive","author":"LờichiatayAnhnóiHaynhưhátEmtặngAnhpháttátĐẹpnhưphim","author_text":"LờichiatayAnhnóiHaynhưhátEmtặngAnhpháttátĐẹpnhưphim","md5_text":"27de1af92522f1786cf1627d27dceaeb","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758426505217},{"id":"353114391","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100001584148864","authorlink":"PhucNguyen","date":"2013-01-06T13:47:58Z","topic":"Kia K9","topic_text":"Kia K9","text":"\"... Tht kh khn \nLm git nc tht kh khn, gian kh \nTi bo mn cc b \nVa vo , ti t mnh x nh \n\nBng mi hi th \nBng mi t bo, ti nh \nRng ti ang lm vic ca ti \nNgha l ang lm ngi! \n\nV cng bng cch \nTi sng v vinh quang trn i \nD mt ngy kia, gi \nMang ti i khp ni...\"\n\n[ Eduardas Mieelaitis ]","language":"notdetect","sentiment":"Mixed","author":"PhucNguyen","author_text":"PhucNguyen","md5_text":"d9b0b4e4a769e5e34f741c5439da6da8","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758428602368},{"id":"353114349","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=271506896300577","authorlink":"CámơnTìnhyêutôi","date":"2013-01-06T13:48:07Z","topic":"Kia K9","topic_text":"Kia K9","text":"Lm mi hn nhn ca bn\n\n- Thay v th s gim 5 cn hay kim c nhiu tin hn trong nm ti, hy cn nhc n vic dnh thi gian nng cao cht lng tnh cm v chng, bn s thy mnh l ngi hnh phc.\n\n1. Hnh ng vui v, n gin l vui\n\n1. Hnh ng vui v, n gin l vui\n\nMi ngi thng thng ngh rng, cm xc iu khin hnh ng. Song hy ngh mt cp cao hn: Chnh hnh ng mi l k lo li tm trng bn.\n\nNu bn chn lm g cng ng yu, kin nhn, y quan tm th cui cng s to c thi quen lun c tm trng tt, kin nhn, ng yu v bit quan tm nh vy.\n\n2. Ng nhiu hn\n\nMt ng, thiu ng c th hy hoi sc khe v c mi quan h tnh cm ca bn. Gic ng c vai tr quan trng, ngha c bn lin quan n hnh vi. Nu bn khng c ng , hu qu dn n c th l nhng trn ci v v m khi tinh thn tr nn qu mt mi.\n\n3. Gii quyt vn theo cch i bn cng thng\n\nHy n lc cng vi nhau gii quyt mi vn c th xy n, v ng bao gi tranh ci v 3 ch ch c th lm hng hn nhn ca bn ch chng mang li tc dng g hn: Tin bc, Tnh dc v Con ci.\n\nCh mc kt trong vic phn nh ai ng ai sai, hy tp trung vo tm ra gii php gii quyt c vn . Khi mi ngi u cm thy ngi kia c quan tm n li ch ca mnh, th vn c gii quyt khng phi theo cch ca anh, ca ti m l theo cch i bn cng vui v.\n\n4. Bit n nhiu hn\n\nTrong i sng la i, ngha lm nu bn bit by t vi na kia rng bn thc s bit n nhng g h lm cho mnh, d l nh nht. Nu anh y ra bt gip bn, nu anh y khng tic cng t lm mt ba ti tht c bit cho hai ngi, hy by t s cm kch ca bn bng c li ni v hnh ng. Nh th na kia s cm nhn c rng, mi iu h lm, d nh nht vn rt c ngha.\n\n5. i vai\n\nLn ti, nu bn bt u thy nui tic rng mnh tht sai lm trong cuc hn nhn ny th th i vai vi na kia bnh vc anh y xem. Ph n c v s v d a ra chng minh rng ng chng chng bao gi ng chn ng tay gip mnh vic nh, song nu bn ngh li rng anh y c ch nh th no trong chuyn du lch ca nh c, t ng gi hnh l, t phng khch sn, t v v.v. th cu gin s qua i v khng cn cm thy kh chu v nhng iu vn vt trong cuc sng hng ngy na.\n\n6. t tnh cm v chng ln u tin hng u\n\nHn nhin l bn lun ngp nga trong ng danh sch nhng vic phi lm, nhng hy chc chn rng t ra, mnh c thi gian u t cho tnh cm v chng. Bi l nn tng, l ch da vng chc cho mi vn khc ca bn trong cuc sng.\n\nHy hc hi cch sng thun v thun chng, ng xem nh tnh cm thing ling y, bn s tm c ng hng t ti hn nhn cht lng mi ngy.","language":"notdetect","sentiment":"Mixed","author":"CámơnTìnhyêutôi","author_text":"CámơnTìnhyêutôi","md5_text":"1e246f3517e257e6978df89811040f93","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758429650944},{"id":"353114390","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003732283968","authorlink":"YkdnChimCút","date":"2013-01-06T13:42:06Z","topic":"Kia K9","topic_text":"Kia K9","text":"Thi xong. Mai cc em i hc bnh thng. Th mi khm ch. Ch cho ngh bui no. =((","language":"notdetect","sentiment":"Mixed","author":"YkdnChimCút","author_text":"YkdnChimCút","md5_text":"851c84074096e456c71d83d3d1e3d7d7","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758430699520},{"id":"353114819","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003273911378","authorlink":"ViênĐáNhỏ","date":"2013-01-06T13:40:00Z","topic":"Kia K9","topic_text":"Kia K9","text":"Truyn ngn: c i mem i. thy hay thid chia s nh\n\n TNG NGI N NG LM EM KHC BNG MU!\n\nMt cu chuyn rt rt hay !\n\nTrc khi tr thnh v ti , em l n b , oan nghit thay , chng phi l ngi n b ca ti , em dng hin s trong trng ca mnh cho ngi n ng u tin , sau hn phi tay , bin mt dng . theo ui em , ti phi mt mt thi gian kh lu v em l ngi nhy cm v v cng mc cm , khi em chng cn nim tin vo tnh yu . Ti phi lm rt , rt nhiu nhng iu c th em hiu , lc tht s ti yu em qu nhiu , tnh yu c l c cuc i ti chng th qun c .Th ri ti cng ci c em , vo mt ngy ma h p tri . Cuc sng lc qu vin mn , em hin lnh , m ang , lun nh nhng vi chng , lo cho m chng v c a em chng li bing . Th nhng nhng g qu m m li d mang li cm gic nhm chn . Em hin lnh qu mc , m ang qu tay v du dng qu th , i khi ti cn mt cht bng bnh c chu chung em , cn mt cht gi tnh khi ln ging tnh yu ca ti nng chy hn .Ri ti gp c c y - tnh nhn ca ti , c ta quyn r , hp dn v c bit rt bit cch ht nh nhn v s quan tm ca n ng . Th l ti lao vo cuc tnh ngoi lung , say m v in o . Ti qun mt mnh l n ng c v , thi gian dnh cho gia nh trong tun ch tnh bng pht bng giy , cn i vi tnh nhn ti gn nh dnh trn . Ri chuyn g n n , em pht hin , trong mt ln tnh c c c tin nhn trong my ti . Nhng ngi ni in chng phi em , m l ti , m ti tt em v ci ti t tin ng vo ca ti .Ti i khi nh khi tri ang m , m ti thy n o , chy ln th ch thy em khc , ngoi ra ... em chng ni g thm . Chuyn gy g gia ti v v ngy cng nhiu , m ton l do ti , ti say xn , v p ph v chi ra em , trong mt ln khng cn thn , ti ay nghin em v ci chuyn em -l-n-b trc khi ly ti , m ti nghe thy v t b chng cn thng em nh trc . M ti ngh rng l nguyn nhn ca vic v chng ti xung t , cch i x ca b thay i chng mt . Em trng gy hn trc ....Th ri tnh nhn xinh p ti bt ti li d em rt c ta v , ti khng chu , th l c ta ngng nguy , hn gin . Nhng iu cha bao gi ti thy em , ngi n b ti nghip ca ti .Sau mt thi gian di , ti dn ra ring , v nh ca lc no cng n o , em th ch m thm chu ng . T khi ra ring , ti ngy cng chng tn trng em , hn mt ln ti ni chuyn tnh t vi tnh nhn trc mt em , hn mt ln em thy ti i cng c ta , v chng phn ng g , ch cui mt v i thng . Dng nh em lun cm thy mnh l ngi c ti , th nn chng bao gi em dm phn khng li , cha mt ln ... Th ri em c thai , em ni vi ti vi ging vui mng m lu ri ti chng nghe c , ti m em vo lng , bt cht kho mt cay cay , ti chng bit v l do g , vui v ti sp c con , hay v ti cm thy thng cho tm thn em sao gy qu . Ti dt em i mua , khoc vai em gia ph ng ngi v trao em ci hn nh m p , th l em ci ...... in thoi ti reo ln tng chp , c tnh nhn ti gi .\nN ci ang trn mi em bng tt ngm , nh mt vui mng , sng long lanh cht tr v vi ni bun nh lc trc .\n- Anh ... c vic t xut .\n- Anh i i .\nEm chng ngn ti li , d em bit ti sp i u , sp bn ai , sp lm nhng g . Vy l ti b em gia ng ph ng ngi - khi em ang mang trong mnh a con ca ti ...\" Anh mun dng li \" , sau ngy hm y , ti ni v tnh nhn nh th . C ta khc lc , nu ko , th sng th cht ... th l ti khng n . V th khi v ti bu b n thng th nm , mi chuyn vn nh c .\n\" Em v nh m hai hm nh \" - Em nhn nh th khi ti ang cng ti , cng ng thi , lu lm ri em chng v thm gia nh , ti vui v ng . Ngy hm y , ti cng quyt dt vi c ta - tnh nhn xinh p ca ti .Ting chung ca bm in i , ti ng em v , vi ra m vi lng ngp trn nim vui - ti tr v vi em theo ng ngha . Th nhng trc mt ti chng phi ngi v vi n ci hin ho m l mt con n b nng nc mi ru , c ta lao vo khc v hn ti ti tp . Ti y ra v sau l ting tht , tru tro ... th l buc lng , ti phi ko c ta vo nh .Tnh nhn ca ti sau mt hi khc lc thm thit , cui cng cng bnh tnh .\n- Anh b em v sp c con ?\n- .\n- Vy em s i , khi no con anh ra i , chng ta s quay li vi nhau , phi khng ?\n- Xin li , anh ngh mnh nn dt hn.\n-...\n- Anh mun quay v vi v mnh , c y kh qu nhiu . Em s tm c ngi n ng khc yu em hn anh , cn anh , anh ngh mnh i xa qu ri .\n- Anh hi hn v yu em sao , ti sao th ? R rng em p , em gii giang hn c ta m.\n- , nhng bnh yn l cm gic c cuc i ch c c y l c th mang li cho anh.\n- Em khng tin ... EM KHNG TINNNN- Em bnh tnh i , em v c ri , anh a em v.- K ... khng .-...- C kt thc , anh cng phi cho mt ci kt nh hai k yu nhau ch , khng c ph nh vy.\n- Em mun g ?\n- Em mn li vi anh m nay , ch m nay thi.- Khng c, c th v anh s v.\n- Em xin anh, y l iu cui cng em mun .Th ri c ta li khc v ti chnh lng . Ti ngh v s khng bit u v ch m nay thi , ngy mai ... c tnh nhn ti nghip kia s khng cn bn ti na . V th l ... ti ng . m bng ma gi ... c hai k ti trong nh .\n--------------\nCHNG II : MT\n\nTing ca m .... Ti bng hong . Em cht trn , i mt em ang dn cht ly hai thn th lo l . Chng mt git nc mt no kp chy , em ng mnh xung nn nh , bch thc n cn nng hi t tay em vng ra , vung vi khp .Ci xe cp cu reo ln in i , nhi tai v dng nh bp nt c tri tim ti . Mu t chic vy hng em mc chy ra ngy mt nhiu ... Con ti !!!Cha y mt ting sau , m em v m ti u c mt ti bnh vin . Mt m em xanh mt , lo cho a con bng mang d cha , m ti th lun ming hi ti sao , ti sao , ti sao ......Ti ni c v c hai ngi u thng tht , h no bit con ngi ca ti li khn nn n vy . Th l m ti khc , c l b hi hn v tn nhn vi a con du qu i ti nghip ca mnh , v b chng thi ngng chi ra ti - thng n ng ng kinh tm .M em chng ni c g , b tht thn hng mt v pha cnh ca mu trng , cnh ca m pha sau ch c mt mnh con gi b va gnh gng ni au n qu ln , va i chi vi li hi t thn .\n- Xin li gia nh . Chng ti c gng ... nhng ch c th cu c ngi m , a tr khng qua khi.\n_ g bc s gi , nh mt va lng tng , va au xt , ngp ngng ni .Hai b m o khc , cn ti ... ti ch bit ng , ti va git i da con ca mnh ...\n- V ... d tim thuc gy m , nhng nc mt ca bnh nhn khng ngng chy ... rt nhiu . C l c y phi chu ng mt c shock qu ln m ngay bn thn mnh khng lng ni , vic y gy nh hng nghim trng trong qu trnh mang thai .... Ti hy vng gia inh hy ng vin v gip bnh nhn vt qua ni au mt con ny , c th khi tnh dy , nu bit mt a con .... ti e tm l ca c y s khng c n nh ... Ti xin chia bun cng gia nh.M ti lao vo nh , chi , ra ti ...\n- My cht i , thng chng n mt , my hi cht con my , my git tri tim v my .... my khng phi con tao ... KHNG PHI.... KHNG PHIIIIIIII...Ging m ti khn i , ri qu mt , b khuu xung ...ng vy , gi nh b ng sinh ra a con nh ti , ti s khng phi t tay p nt gia nh mnh nh th ny ....\n---------------\nNgy hm sau , em tnh !M em khng cho ti vo v s em s khng chu c . Th l ti ng ngoi , bn trong ting nc au kh ca em vang ln bn bc , nhng cu hi v a con xu s t ming em tung ra lin hi , em cn hy vng , em mun mt cu ni khng sao , nhng mi th ch l mt s tht au n ... M em v m ti ch bit khc , ch bit an i ... Khng cm lng c , ti bc vo , ti mun vut ve ni au ca em .Th nhng , va thy ti , i mt m nc ca em long ln ng s , trong cha t ni au , chu ng n ni on hn , cm th ... cha bao gi em nhn ti nh th , cha bao gi . Em go tht , ting tht x c khng gian im lng ca bnh vin , ting tht au n , ting tht hn th , ting tht i con ... Mt em ln v em giy gia , em mun tung ra khi ging , em mun git ti ...... Cc bc s a ti , ko ti ra , ti khng mun ra , th ti em git i cn hn phi i mt vi chnh mnh lc ny .Cnh ca ng sm li , au ing , ch cn ting tht ca em , th ri ngui dn ... ngui dn . H li tim cho em thuc an thn , thuc gy m.\n- N khng cn ni c na .M em ni gn ln nh th . nh mt ca b khng on khng hn nhng li cha c i dng au thng v xt con .\n- V sao ... h m?\n- Bc s ni do chn ng tm l ... c l trong tim thc , con b khng mun tip xc vi ai na .\n-...\n- Khi xut vin , n s v vi m ...\n- Vng .\n- T y cho n khi tm l n n nh , con hy chun b n li hn i .Cu ni m tung ra lnh lng , bt gic ti khng ng ni . Qu thp di chn b , ti van xin.\n- M , xin m ng bt con lm th , con mun chuc li.\n- C nhng li lm c th tha th , tuy nhin c nhng li lm c ngn i cng khng th no ra sch c , v con gy ra mt li lm vt qu s bao dung v th tha ca con gi m .\n- Con khng cn tha th , con ch xin m cho con bn c y , con ch c y t xa cng c , xin m ng bt con lm n li hn ....\n- Nu con yu n , th lc u ng i x vi n nh vy , m ngh mi chuyn mun qu ri ...Ti nghe ting tim ti t on , mt ti nc c chy , ti khng mun , khng mun .... khng mun ........\n-------------------\nNgy em ra vin , ti ch c th ng t xa nhn em , trng em nh mt cy kh sp gy , yu t , phi nh s gip ca cc c y t kia mi c th vo xe c . Em ca ti - Ngi n b ca ti ....Nhng ngy sau , ti nh mt cc xc rng khng hn , lang thang u ny y kia chng ngng ngh . Cng vic ca ti b tr tr ti cng chng quan tm , ti gn nh mt ht , th nn , thi mun mt ht ... cho trn vn ... Th ri tnh nhn khn nn ca ti li n ...\n- Anh ...\n- C i i.\n- Khng ... em s y , bn anh.\n- Cha sao.\n- ...\n- V hai k ng nguyn ra nh ti v c m mt ngi ph n phi chu ni au tt cng cuc sng , m mt a tr cha kp cho i phi ra i , th cha sao .\n- ...\n- Cha sao , chnh tay ti bp nt hnh phc gia nh ti .... CHA SAO ... CHA SAO ...Ti go khc nh mt k in , c ta chy n , m cht v tht ln mt cu ng m trm nht .\n- Ta s xy dng mt hnh phc khc . Anh s qun i ngi ph n , a tr thi ...Ci x mnh lm c ta t bt gng , ti ci ma mai cho ci cu \" mt hnh phc khc \"\n- Th n b nh c ... tn c v v lng tm .... hnh phc khc ? Ti s git nu c dm ni thm mt ln no vi ti cu na.Th ri ti quay lng siu vo bc i , b li ting khc chua cht pha sau .-----------------------------------Trong men say , c mt ngi n b gi nh nhng du ti vo nh . un nc m v lau mnh cho ti . Mt ti h m , v thy tc b y bc hn kh nhiu\n.- M ...\n- , ch ai na .\n- M ...con bit li ri....M ti au lng , ch xoa u nh khi ti cn nh ch chng bit ni g hn .\n- M ...\n- , m y.\n- Ti con khng tha th c phi khng.\n-...\n- M ... con khng mun mt thm c y ...\n- C ngn li lm trong nhn gian c tha th bng s khoan dung c trong nhng tri tim cha y tnh yu thng ca con ngi , con . M khng dm chc con du s tha th cho con , nhng m cng khng n dp tt nim hy vng cui cng ca con , con hy lm nhng iu m con ngh nn lm , nu khng c chp nhn , th du g cng ra c phn no ti li ca con .\n--------------------\nCnh ca m dn ra :\n- Con n y lm g ?\n- Con mun ... nhn c y mt cht thi m.\n- N vn vy thi , cha kh hn c , con vo s ch lm n ti t hn.\n- D m ... vy m chuyn b hoa ny dm con c khng. C y rt thch hoa hng vng ...\n- , a cho m , thi con i lm i .Ngy th nht , ti mang b 99 bng hoa hng vng n , loi hoa m em thch nht , nhng ch nhn em mt t thi cng khng th ...Tng ngy , tng ngy c tri qua mt cch lng l , em xa ti vn vn gn mt nm , trong mt nm y , ti khng bit gi cho em bit bao nhiu bng hng vng , vy m cnh ca y vn ng sm vi ti . Hm nay , khun mt quen thuc vn ra m ca :\n- C y sao ri m ?\n- N kh hn cht ri .\n- Vng , vy tt qu .\n- Con cng bn nhiu vic , khng cn ngy no cng em hoa n u .\n- Khng sao m , con i nh .\n- \nNi on , m em nhanh tay khp ca , vn nh mi hm thi . Ti bc i , chm ri nh mun c ai nu chn nh v ... tht nh thi , c ting ca bc nh ra . Em - Ti quay u li v thy em , ti vui mng n qun mnh ang u , ri dng nh em mun ni iu g , em ang chy ra .....RM !!!!!!Ch cn mt on na thi ... nhng cui cng ti vn cha chm em c.\n----------\nChic xe ti khng trnh c , tt c u bt ng qu .Bn quan ti ti , m khc thm thit , au n tt cng . Ai cng xt thng , mt ai cng ma . Ring em - vi nhng mc vt ngng ni au , em lng cm .Sau ngy y , m no ti cng c ngm em , nhng em ng rt t . Thi gian chnh , em ch nhn bc nh lc chng ti mi ci , rng r - v chm ngp trong hnh phc .Mi khi em khp mt , ti li xt , nc mt nh nhng git au vo thc, c tung mi . Kh nh ti vut m em ... bt gic em tnh dy , trong y ming hai nm cha bao gi m mp my :\n- Anh ...Ti sp tan bin , tri ma to , mt em tm kim hong lon . Th ri ... em nhn thy ti , i mt nhn ti au kh . Hnh bng ti m nht , iu cui cng ti nghe c l :\n- ng bin mt ...Gi ng mi thi .... Ti xin ngng\nng lm lnh thm - Em ang mang tang\nTang ngi n ng ... lm em khc bng mu\nTang a con th ... cha ln no gi em bng m...\nGi ng ti xin ti xin .... hy ngng i\nng go tht ... Tri tim em v ri!","language":"notdetect","sentiment":"Mixed","author":"ViênĐáNhỏ","author_text":"ViênĐáNhỏ","md5_text":"8dd2cbf872e66e37baa108d25d8847de","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758455865344},{"id":"353114966","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100000133275771","authorlink":"TâmCùiBắp","date":"2013-01-06T13:29:31Z","topic":"Kia K9","topic_text":"Kia K9","text":"T l Tm in _ Nh Tm thn. Xin hy yu ti, ti khng mun c thn. Ti mun ly chng, hy n yu ti i.","language":"notdetect","sentiment":"Mixed","author":"TâmCùiBắp","author_text":"TâmCùiBắp","md5_text":"f4aacd21a91e4c277564c2ea4e9bd312","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758466351104},{"id":"353114059","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003224990854","authorlink":"ChayKhetLet","date":"2013-01-06T13:56:33Z","topic":"Kia K9","topic_text":"Kia K9","text":"Bit u bt ng anh ln bn th n xi,\nAi cn ng di ma ngn nga cu gi hn\nV bn tay to tng m ly ci body\nTh l anh thng ri\n\nS l di lng khi em chng ngi u lo,\nLo em s mt anh trong lc ang u ym\nV tnh yu mong manh,tay em qu manly\nNgi yu i,anh c bit?\n\nChorus:\nEm yu anh hn th,nhiu hn li em vn ni.\nEm c ng m anh cht l s bnh yn\nm bung xui v c n,cn ring em vi ci xe\nAi biu ku em m chi...\n\nAnh a em theo vi, vt wa c th gii\nn bn kia em c th bn anh trn i,\nNi thng yu khng phi phai, c bn nhau mi sm mai.\nC au khng, anh i ?\nNi thng yu khng phi phai, c bn nhau mi sm mai.\nS m anh c sut i..","language":"notdetect","sentiment":"Mixed","author":"ChayKhetLet","author_text":"ChayKhetLet","md5_text":"a02f1fcb0d7b04bcd6818f29b3bc4a66","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758466351105},{"id":"353114211","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=128677373953642","authorlink":"HéLộBíMậtVề12CungHoàngĐạo","date":"2013-01-06T13:55:02Z","topic":"Kia K9","topic_text":"Kia K9","text":"B MT TNH YU - QUA NGY SINH CA BN\n\n Ngy 1, 10, 19 v 28 \n\nBn khng ch tin vo ting st i tnh, m bn tng b st nh trng. Bn c tnh c thm chng chn y nh, hm nay thy rung ng bi nh mt ny, n mai bng thy trng rng v chuyn sang tng t n ci khc. Trong tnh yu, bn th hin hai thi cc yu-ght rt r rng. \n\nYu tht s: 2 ngi \n\nTri tim tan v: 2 ln \n\n Ngy 2, 11, 20 v 29 \n\nBn thng nhm i tng t xa mi quyt nh c nn bn tn hay khng. Bn mt nhiu thi gian v cng sc trong chin dch cm ca ai . Tm li, bn thch ch ng i tm tnh yu. Chng may cho ai c lao vo bn th ch c v ch m thi. \n\nYu tht s: 3 ngi \n\nTri tim tan v: 4 ln \n\n Ngy 3, 12, 21 v 30 \n\nBn chc chn v t tin trong vic chn na kia cho mnh. Thm ch s la chn ca bn l v iu kin. Bn lun ng pha sau theo di v bo v ngi mnh yu. Bn b bn khng bao gi hiu bn bng\nngi y. \n\nYu tht s: 3 ngi \n\nTri tim tan v: 5 ln \n\n Ngy 4, 13, 22 v 31 \n\nBn khng thch yu hi ht, hoa l cnh. Theo bn tnh yu l mt phm tr lun phi nghim tc. Tuy nhin, bn c th hp vi nhiu tup ngi. Bn tm thy nhng u im hu ht mi ngi. Tnh yu hay mang li cho bn nhng bt ng m bn khng bit trc c n s xut hin khi no v u. \n\nYu tht s: 2 ngi \n\nTri tim tan v: 2 ln \n\n Ngy 5, 14 v 23 \n\nMi ngi ni bn khng c am m, tht l mt sai lm. Thc t bn rt su sc v a cm. Cng v th m bn c th yu cht m cht mt ngi ta ngay sau vi bui h hn. \n\nYu tht s: 5 ngi \n\nTri tim tan v: 5 ln \n\n Ngy 6, 15 v 24 \n\nVi bn, yu l mt h qu tt yu ca tnh bn. Trong tnh yu bn rt trung thnh, chn chn v chn tht. Bn khng phi l k la di cng khng phi tn tnh cho vui. Bn thng gi c tnh bn tt p vi ngi c, ng thi sn sng nhen nhm li ngn la tnh yu trong tng lai. \n\nYu tht s: 4 ngi \n\nTri tim tan v: 5 ln \n\n Ngy 7, 16 v 25 \n\nBnh tnh v t ch, bn tm cch by t tnh yu bng li ni. Nhiu lc, na kia phi hc nhn bit tnh cm ca bn thng qua c ch, vic lm ca bn. Bn c ti ghm cng ngi ta m khng b pht hin ra y. \n\nTnh yu thc s: 1 \n\nTri tim tan v: 2 \n\n Ngy sinh: 8, 17 v 26 \n\nBn thch cm gic ang yu, thch n ni ban khng mun mnh trong tnh trng single cht no. Tht khng may, tnh yu khng d dng chiu theo mun bn c. Nhiu ngi c m mu li dng bn y, ch c tnh yu thc s mi mang li cho bn nim vui m thi. \n\nTnh yu thc s: 2 ngi \n\nTri tim tan v: 3 ln \n\n Ngy 9, 18 v 27 \n\nVi bn, yu l th tnh cm vng vn day dt mi, d chia tay nhau. Thc t, mi tnh u lun li cm xc thing ling v mnh lit trong bn. Bn\nthng khng dt khot trong tnh cm. Chnh v th bn d l ngi b chia tay ch khng phi l ngi ch ng chia tay. \n\nTnh yu tht s: 5 \n\nTri tim tan v: 4","language":"notdetect","sentiment":"Mixed","author":"HéLộBíMậtVề12CungHoàngĐạo","author_text":"HéLộBíMậtVề12CungHoàngĐạo","md5_text":"01e231f793d3a8f2ca4dba8385c36a47","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758468448256},{"id":"353114883","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004340227805","authorlink":"HaiYenVu","date":"2013-01-06T13:32:53Z","topic":"Kia K9","topic_text":"Kia K9","text":"tinh yeu la gi ma the gioi phai khoc?","language":"notdetect","sentiment":"Mixed","author":"HaiYenVu","author_text":"HaiYenVu","md5_text":"10e54414f94392086f8d66d0d040d117","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758469496832},{"id":"353114755","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004062563828","authorlink":"ChípHô","date":"2013-01-06T13:34:33Z","topic":"Kia K9","topic_text":"Kia K9","text":"quyt inh hoc an len.... Nhng an cho ai? Hajza....","language":"notdetect","sentiment":"Mixed","author":"ChípHô","author_text":"ChípHô","md5_text":"403aff141758e93a2faf9ab6f1d4b410","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758470545408},{"id":"353114914","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002547705449","authorlink":"VijetaGupta","date":"2013-01-06T13:27:58Z","topic":"Kia K9","topic_text":"Kia K9","text":"hotho mein dabee bat h koi,aankho mein hai sapna,ajnabee sa ek dard h magr lag raha h apna...mein na janu mere humrahi kesa hai y kesa pyar hai..jaan leta jo bina bole kya dhadkan ka ikrar hai..<3<3","language":"notdetect","sentiment":"Mixed","author":"VijetaGupta","author_text":"VijetaGupta","md5_text":"6f2871274f2ac5e955cc704eff79f881","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758471593984},{"id":"353114909","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004258653188","authorlink":"GàGôChânTo","date":"2013-01-06T13:29:58Z","topic":"Kia K9","topic_text":"Kia K9","text":"xin li ,m i qua en :(((((((((((((((((((((((((((((((((","language":"notdetect","sentiment":"Mixed","author":"GàGôChânTo","author_text":"GàGôChânTo","md5_text":"fbce7bf2da5544827684b5f86bfa7f52","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758472642560},{"id":"353114839","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100001556886634","authorlink":"AnhKuBi","date":"2013-01-06T13:35:17Z","topic":"Kia K9","topic_text":"Kia K9","text":"Em i em dng li no ng ng kia c ma ri, \ntrng kia xem ng ngp bn trt chn em bit ku ai...\nl la la la la l la la...\nl la la la la l la la... \nl la la la la l la la ...\nng khc em gi b i!\n@@@@@@@@@@@@@@@@@@@@@@@@@@@","language":"notdetect","sentiment":"Mixed","author":"AnhKuBi","author_text":"AnhKuBi","md5_text":"c82bcea7f177dffc954f121512f398b5","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758473691136},{"id":"353114642","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004943352089","authorlink":"KuckithudoanHt","date":"2013-01-06T13:35:56Z","topic":"Kia K9","topic_text":"Kia K9","text":"pc mn my khm khm w. .\n. z kn m woi.wo tht ck","language":"notdetect","sentiment":"Mixed","author":"KuckithudoanHt","author_text":"KuckithudoanHt","md5_text":"7a2f78cdc2e46762d2efabaaacfd73f8","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758473691137},{"id":"353114731","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004721886293","authorlink":"ĐỗQuyên","date":"2013-01-06T13:40:22Z","topic":"Kia K9","topic_text":"Kia K9","text":"hung nen dj cat toc .pjo hoj han. . .huhu","language":"unknown","sentiment":"Mixed","author":"ĐỗQuyên","author_text":"ĐỗQuyên","md5_text":"0e772ca12e36aa2e2be893e40ead3912","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758483128320},{"id":"353114062","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=279538412081342","authorlink":"KhámPháBíẨn12ChòmSao","date":"2013-01-06T13:58:11Z","topic":"Kia K9","topic_text":"Kia K9","text":"[chm sao no sng quy c nguyn tc]\n\nKim Ngu\n\nThn Nng\n\nThin Bnh\n\nBo Bnh\n\nMa Kt","language":"notdetect","sentiment":"Mixed","author":"KhámPháBíẨn12ChòmSao","author_text":"KhámPháBíẨn12ChòmSao","md5_text":"87590322872734b2d3cd8f46672f4db5","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758484176896},{"id":"353114448","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100000130474382","authorlink":"LươngNgọcDiệp","date":"2013-01-06T13:41:26Z","topic":"Kia K9","topic_text":"Kia K9","text":"cai s iem no kh! Dinh vao nha co 3 a nn n! Con ca con ut no thik film kinh di! Iem-a gia thi s ma n ai ca ra qun, h iem ang ngi trong chn c gng hoc t mi mai kim tra con 2 con mu kia lai ngi xem film kinh di (2 mu y co cai thoi quen xem film kinh di vao cui tun) iem hoi moi ngi! Lam th nao hoc y?","language":"notdetect","sentiment":"Mixed","author":"LươngNgọcDiệp","author_text":"LươngNgọcDiệp","md5_text":"cc6647afb3cc9a4e95ef45d000bad7d4","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758485225472},{"id":"353114080","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003737403437","authorlink":"ThiênThầnNhỏ","date":"2013-01-06T13:58:08Z","topic":"Kia K9","topic_text":"Kia K9","text":"Chng ai hiu minh bng 9 pan thm minh. ung nhi","language":"notdetect","sentiment":"Mixed","author":"ThiênThầnNhỏ","author_text":"ThiênThầnNhỏ","md5_text":"9df874455fdd2d5475b40083bd560041","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758486274048},{"id":"353114728","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003956412467","authorlink":"TrungNguyen","date":"2013-01-06T13:39:17Z","topic":"Kia K9","topic_text":"Kia K9","text":"Cac ban ne, neu co 3.900.000 d. Ban se mua may anh hieu j (Canon, Sony,....) hjhj","language":"unknown","sentiment":"Mixed","author":"TrungNguyen","author_text":"TrungNguyen","md5_text":"6c5447e591ba022ffb7011fb0894cc97","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758487322624},{"id":"353114634","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004337319527","authorlink":"LuyenPhung","date":"2013-01-06T13:40:55Z","topic":"Kia K9","topic_text":"Kia K9","text":"Ti.\n* Khng trnh ua vi\ni.\n* V khng mun ph li vi loi\nngi khn nn !\n* Cuc i ti khng phi dng\nsng cn. . .\n* Ai cng khng lm hn\nhn cuc i ca ti.!\n*ng gi v lm bn ri khn\nnn sau lng.\n* ng quan tm ht mnh ri bt\nthnh lnh b mc.\n*ng ni yu n pht in ri\nvn c hn nhin phn bi.\n*ng ua theo x hi sng bng\nb mt v ti gi nai.\n*ng hoi vi sao ti ac!\n* i no bac,sng nhat cho bt\nau.\nVy thi.","language":"notdetect","sentiment":"Mixed","author":"LuyenPhung","author_text":"LuyenPhung","md5_text":"3e12923502b8da3896fafe8f6475cf91","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758488371200},{"id":"353114069","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004600962712","authorlink":"PhamtheVinh","date":"2013-01-06T14:01:22Z","topic":"Kia K9","topic_text":"Kia K9","text":"Mt b xng ngi ri ngha a lang thang ngoi ng th gp mt b xng khc bn hi:\n- Cu cht nm no vy?\n- T cht i nm t Du.\nang ni chuyn th c b xng na i ti. \n\n- Cu cht nm no vy? \n\n- T cht v thin tai. \n\nBa b xng cng nhau i tip, c mt lc th gp b xng th t.\n\n- Tri t! Cu cht nm no m b dng t ti vy? \n\n- ng c tr o ti - B xng kia cu - ti ang sng s ra y.\n\n- Vy cu lm ngh g?\n\n- Sinh vin nm cui, ti va i gia s v!","language":"notdetect","sentiment":"Mixed","author":"PhamtheVinh","author_text":"PhamtheVinh","md5_text":"d5e3238459533c96216f7d30a9043031","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758489419776},{"id":"353114616","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003658073388","authorlink":"NgôViếtKhôi","date":"2013-01-06T13:43:55Z","topic":"Kia K9","topic_text":"Kia K9","text":"Cu ton hc 2\nCng l ti tin nhng ch c 8 ti....ging nhau v hnh dng bn ngoi....trong c 1 ti nng hn 7 ti cn li...\n????? : Dc php cn 2 ln tm ra c ti ??\n\nCu ny d hn nhiu!!!!!!!!!!!!!!!!!!!","language":"notdetect","sentiment":"Mixed","author":"NgôViếtKhôi","author_text":"NgôViếtKhôi","md5_text":"15ca4f8875861e914df624f912e218b6","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758490468352},{"id":"353114658","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003884559137","authorlink":"NốiThoátChoNgười","date":"2013-01-06T13:46:26Z","topic":"Kia K9","topic_text":"Kia K9","text":"nu ngy sa bc i nhanh qua con ng ma th anh khng gp ngi.nu trc kia anh khng ? em l ai th gi y u phi nh th ny","language":"notdetect","sentiment":"Mixed","author":"NốiThoátChoNgười","author_text":"NốiThoátChoNgười","md5_text":"1994e4137b48217389a88844fc44318f","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758490468353},{"id":"353114435","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003837226895","authorlink":"BaoKhangNguyenHua","date":"2013-01-06T13:52:15Z","topic":"Kia K9","topic_text":"Kia K9","text":"ch mun z SG nhng m vn phi i o.","language":"notdetect","sentiment":"Mixed","author":"BaoKhangNguyenHua","author_text":"BaoKhangNguyenHua","md5_text":"c22234da5391ebe78ed1f56f7424caad","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758491516928},{"id":"353114940","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100000327706184","authorlink":"LiênTrần","date":"2013-01-06T13:27:49Z","topic":"Kia K9","topic_text":"Kia K9","text":"Mt khi mu th ng hi b chu l ai. i xem phim 1 mnh nh! \n12 con gip. Keke","language":"notdetect","sentiment":"Mixed","author":"LiênTrần","author_text":"LiênTrần","md5_text":"3f33284e46b0bd9f06b21c7411628e75","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758534508544},{"id":"353114210","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004101645210","authorlink":"BillyTran","date":"2013-01-06T13:51:54Z","topic":"Kia K9","topic_text":"Kia K9","text":"Co ba chj an choj wa dj.","language":"unknown","sentiment":"Mixed","author":"BillyTran","author_text":"BillyTran","md5_text":"b80249e00b93df0f12ed6e4590c5b8a8","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758535557120},{"id":"353114900","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002983531547","authorlink":"XecoKute","date":"2013-01-06T13:33:13Z","topic":"Kia K9","topic_text":"Kia K9","text":"Hai ngi n ng ang trong hnh lang phng khm. ng gi hn trng c v bn chn. Ngi kia hi thm:\n\n- Trng bc rt lo lng, chc b bnh nng lm?\n\n- Ti n th mu.\n\n- Th ? Khng bit ngi ta s lm g khi th mu nh?\n\n- th mu, h s ct tay ti. i, mi ngh n rng mnh ri.\n\nNghe n vy, anh chng kia mt mi ti mt, c thc tay vo ti qun ri run bn ln. ng th mu bn hi:\n\n- Anh lm sao th? Sao t nhin li hong s vy? \n\n- Ti n y th nc tiu...","language":"notdetect","sentiment":"Mixed","author":"XecoKute","author_text":"XecoKute","md5_text":"bff4cd9b94e80c5f77083a69aa41da6a","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758535557121},{"id":"353114447","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100001102514164","authorlink":"DoLa","date":"2013-01-06T13:45:36Z","topic":"Kia K9","topic_text":"Kia K9","text":"- BA TIC M TRONG NH V SINH : Ch l Oshin ngi gip vic nh cho mt ng ch ngoi ng tun, rt giu c. m xung, xong vic, vi vng v vi a con trai nh 5 tui sut ngy ngng i trong cn nh ti tn ...\n\nHm y, ch nh c l ln, mi rt nhiu bn b quan khch n d tic m. ng ch bo : Hm nay vic nhiu, ch c th v mun hn khng? Tha c , c iu a con trai nh qu, nh ti mt mnh lu s s hi. ng ch n cn: Vy ch hy mang chu n cng nh.\nCh mang theo con trai n. i ng ni vi n rng : M s cho con i d tic m. Thng b rt ho hc. N u bit l m lm Oshin l nh th no kia ch! V li, ch cng khng mun cho tr tu non nt ca n phi sm hiu s khc bit gia ngi giu k ngho. Ch m thm mua 2 chic xc xch.\nKhch kha n mi lc mi ng. Ai cng lch s. Ngi nh rng v trng l Nhiu ngi tham quan, i li, tr chuyn. Ch rt bn khng thng xuyn mt c n a con nhch nhc ca mnh. Ch s hnh nh n lm hng bui l ca mi ngi. Cui cng ch cng tm ra c cch : a n vo ngi trong phng v sinh ca ch c v nh l ni yn tnh v khng ai dng ti trong bui tic m nay.\nt 2 ming xc xch va mua vo chic a s, ch c ly ging vui v ni vi Con : y l phng dnh ring cho con y, no tic m bt u! Ch dn con c ngi yn trong i ch n v. Thng b nhn cn phng dnh cho n tht sch s thm tho, p qu mc m cha tng c bit. N thch th v cng, ngi xung sn, bt u n xc xch c t trn bn c gng, v m ht t mng cho mnh.\nTic m bt u. Ngi ch nh nh n con trai ch, gp ch ang trong bp hi. Ch tr li p ng: Khng bit n chy i ng no ng ch nhn ch lm thu nh c v giu dim kh ni. ng lng l i tm Qua phng v sinh thy ting tr con ht vng ra, ng m ca, ngy ngi: Chu np y lm g ? Chu bit y l ch no khng ? Thng b h hi : y l phng ng ch nh dnh ring cho chu d tic m, m chu bo th, nhng chu mun c ai cng vi chu ngi y cng n c!\nng ch nh thy sng mi mnh cay x, c km nc mt chy ra, ng r tt c, nh nhng ngi xung ni m p: Con hy i ta nh. Ri ng quay li bn tic ni vi mi ngi hy t nhin vui v, cn ng s bn tip mt ngi khc c bit ca bui ti hm nay. ng mt cht thc n trn ci a to, v mang xung phng v sinh. ng g ca phng lch s Thng b m ca ng bc vo: No chng ta cng n tic trong cn phng tuyt vi ny nh.\nThng b vui sng lm. Hai ngi ngi xung sn va n ngon lnh va chuyn tr r rch, li cn cng nhau nghu ngao ht na ch Mi ngi cng bit. Lin tc c khch n n cn g ca phng v sinh, cho hi hai ngi rt lch s v chc h ngon ming, thm ch nhiu ngi cng ngi xung sn ht nhng bi ht vui ca tr nh Tt c u tht chn thnh, m p!\nNhiu nm thng qua i Cu b rt thnh t, tr nn giu c, vn ln tng lp thng lu trong x hi. Nhng khng bao gi qun gip nhng ngi ngho kh chm ch. Mt iu quan trng hnh thnh trong nhn cch ca anh: ng ch nh nm xa v cng nhn i v cn trng bo v tnh cm v s t tn ca mt a b 5 tui nh th no ","language":"notdetect","sentiment":"Mixed","author":"DoLa","author_text":"DoLa","md5_text":"cb6481980d1e46f62afb27e431c6d71b","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758537654272},{"id":"353114932","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004923124387","authorlink":"NiNiLe","date":"2013-01-06T13:36:44Z","topic":"Kia K9","topic_text":"Kia K9","text":" Khng nhn tin, khng t khng phi l \nqun!\n Khng hi han, khng quan tm cng cha\nchc l khng nh...\n M ch l...\n\" S...\"\n S cch ngi ta tr li 1 cch min\ncng...\n S s xut hin ca mnh lm phin cuc\nsng ca ai kia...\n...nho...","language":"notdetect","sentiment":"Mixed","author":"NiNiLe","author_text":"NiNiLe","md5_text":"13fce432c32e85a1477459051a94b1c3","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758538702848},{"id":"353114274","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100001263778163","authorlink":"VickMin","date":"2013-01-06T13:49:07Z","topic":"Kia K9","topic_text":"Kia K9","text":"10. Em bnh thng. (n ng phi lm g v nn lm ngay i. ng mi chuyn bt bnh thng l cho i lun ).\n9. Anh thy c b kia xinh khng? (Em ang mun th xem anh c phi loi con trai ho sc hay i tn tnh cc c gi xinh p khng y!).\n8a. em ngi mt mnh c khng, em ang bun! (Hy ngi bn cnh, v v em v l mt b vai vng chc).\n8b. Cui tun em bn ri anh i, khng i c u! (Khng bit anh c thc lng mi khng, c c gng r em bng c hay khng, ch cui tun em bn xem TV, bn nh ngi khng, bn xem dng ngi qua li trc nh c ng khng...)\n7. Anh l mt ngi con trai tt. (Tht ra: em khng th yu anh).\nCc cu ng ngha v u t du chm ht cho hy vng ca chng: \"Em coi anh l ngi anh/ngi bn\".\n6. em tr cho, anh tr nhiu ln ri. (Mc d khng ng vi tt c ph n, nhng phn ln h vn ch i nam gii \"cover\" mi th, c bit nu l ln hn h u tin).\n5. Em khng bn tm chuyn anh vn lm bn vi ngi yu c u. (Em chng thch iu t no).\n4. Em khng bn tm n thu nhp ca anh u. (Nhng anh khng khai bo thnh khn th kh c hng \"khoan hng ca cch mng\").\n3. Em s khng thay i iu g anh c. (ng y, ch thay i mt cht v n mc, mt cht xu v tnh cch, vi mt cht v th ny, cht na v th kia. n gin phi khng no?)\n2. Em lun ng h anh giao lu vi bn b. (Anh coi trng bn b hn th dn qua sng vi my ng bn vng lun i. Gi lut php cn cho ng gii ci nhau ri ).\n1. Em khng phi l ngi sn u. (H, my con bn em, bn trai n lng mn qu tri. Hoa qu u, n ni nh nhng, lt tai, u ra y c. Anh tng em n ng chc, sinh nht cng ch ku i n, Valentine cng n, n n n).","language":"notdetect","sentiment":"Mixed","author":"VickMin","author_text":"VickMin","md5_text":"86babc6f0387ddd6aabeb7a4876882c7","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758539751424},{"id":"353114377","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100000302681621","authorlink":"ZôThánhThiện","date":"2013-01-06T13:48:29Z","topic":"Kia K9","topic_text":"Kia K9","text":"Cn 1 ngi lm tim mnh rung ng =))\nChc phi tm ngi ta trong tiu thuyt q :v","language":"notdetect","sentiment":"Positive","author":"ZôThánhThiện","author_text":"ZôThánhThiện","md5_text":"706400724815f62397ac8eb487af278a","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758540800000},{"id":"353114408","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100001612712448","authorlink":"ĐặngHoàiDuyên","date":"2013-01-06T13:48:25Z","topic":"Kia K9","topic_text":"Kia K9","text":"\"\"gi th t ty cu\"\"","language":"notdetect","sentiment":"Mixed","author":"ĐặngHoàiDuyên","author_text":"ĐặngHoàiDuyên","md5_text":"903fcbfdc082d59a05e5c634807ad06d","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758541848576},{"id":"353114492","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004874616124","authorlink":"ThiênNguyễn","date":"2013-01-06T13:47:05Z","topic":"Kia K9","topic_text":"Kia K9","text":"Trong nh giam n c i ca ni ting hung hng. Phm nhn mi no lm mch lng hn u b \"iu tr\" ra tr. \nMt hm ngi nh phm nhn mi mang vo mt con g bo thm ngon. i ca thm lm nhng chng l li ra cp g n th mt th din qu, lin gi tn n em ra.\n- My xem thng kia n lm g vi con g u tin th my cng lm y nh th vi n. N b cnh my b tay, n b i g my cng b i n... Nu my khng lm th vi n th tao s lm th vi my - i ca gn ging.\n- Vng, tha i ca.\nCh phm nhn mi tot ht m hi ht... Nhng mt lc sau... tn n em quay v bm bo i ca, va i va khc:\n- Em xin chu ti vi i ca em khng lm c.\n- Thng ny nhu nhc qu, th n lm g vi con g u tin?\n- N lim phao cu con g.","language":"notdetect","sentiment":"Mixed","author":"ThiênNguyễn","author_text":"ThiênNguyễn","md5_text":"2ba96d8dc4bdbfa512c34cacb085c311","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758542897152},{"id":"353114683","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004635822652","authorlink":"DoTanTai","date":"2013-01-06T13:34:57Z","topic":"Kia K9","topic_text":"Kia K9","text":"cb9 v ich rj ha ha","language":"english","sentiment":"Mixed","author":"DoTanTai","author_text":"DoTanTai","md5_text":"f89ce0a5bf3310f70f6d80beeda3066c","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758543945728},{"id":"353114431","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003929565212","authorlink":"CuTun","date":"2013-01-06T13:48:13Z","topic":"Kia K9","topic_text":"Kia K9","text":"tng cc nng...^_^\n\nNghe v nghe ve\nNghe v con gi....\nSut ngy li nhi\nLin khc tnh yu\n ri chiu chiu\nT nm,t by\nAnh ny ht xy\nAnh kia d thng\n\nNghe v nghe ve\nTip v con gi\nT nhn xinh gi\nSut ngy Phn son\nLi th bon bon\nCi mm khng ngh\nNhng c xu x\nVn nhn l xinh\nNgi to chnh nh\nVn cho...Eo p\nNgi m dp lp\nLi bo gi eo\n\nBo i vt bo\nTh ku au c\nBo i nh c\nTh bo au tay\ni chi sut ngy\nLm g bit mt !\n\nc th c st\nVn nhn thng minh\nQua 5 mi tnh\nVn cho l t\n\nNgi nh qu mt\nQu vt khng tha\nQuen ngi la c\nHng ny qun n\nn nhiu to s\nc ch g u\nNi lm au u\nKo thm ni na !!!...","language":"notdetect","sentiment":"Mixed","author":"CuTun","author_text":"CuTun","md5_text":"1e3cc3c71e45f16bf6a11c53cff5ae01","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758727446528},{"id":"353114031","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003823741460","authorlink":"BiênBuônBoom","date":"2013-01-06T14:01:39Z","topic":"Kia K9","topic_text":"Kia K9","text":"***Con ngi l th m , ai ch c lng cm ght k , ai ch c khuyt im , ai ch c s din... Quan trng l n mc no m thi....!!! \n~> t v va phi th s khng gy ra nhng chuyn ngi khc phi khinh b cn nhiu th ng nhin l ngc li ri\n***Bc no dm ni mnh khng c khuyt im...???\n***V th ng li nhng ci quy tc , nhng ci thit ch , nhng ci gi l l tng ch c trong l thuyt hoc tn ti mt vi ngi ( m nhng ngi li mc cc khuyt im khc ) ra p dng cho ngi khc trong khi mnh cng khng chc l s thc hin c...!!! n tht nc ci v l bch ng khng cc bc.\n***V cng ng c t ra th ny th kia hay lng m ngi khc , n khng nhng s khng lm p thm hnh nh ca bn m cn lm ngi khc nhn bn vi con mt khinh thng m thi....!!!\n~> V th em xin mt s ngi ng nh gi em khi cha nh gi c c bn thn mnh :\">","language":"notdetect","sentiment":"Mixed","author":"BiênBuônBoom","author_text":"BiênBuônBoom","md5_text":"4824c2e84bb5aa6e4de608c83fbdc07b","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758728495104},{"id":"353114793","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003024868483","authorlink":"ThanhHuyền","date":"2013-01-06T13:37:10Z","topic":"Kia K9","topic_text":"Kia K9","text":"khi chiu chp c ht cng sut khng cc tnh yu Huyen Nhung Nguyen, LaZy Nguyn, Tr Cn, Thu Trn, K Su, Ngn Nguyn, Nhung Cc,,,","language":"notdetect","sentiment":"Negative","author":"ThanhHuyền","author_text":"ThanhHuyền","md5_text":"a7bd7b50c43fba894fb95ea1717b48a3","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758729543680},{"id":"353114777","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=401692139866690","authorlink":"Truyệncườicấmtrẻemdưới18T","date":"2013-01-06T13:36:02Z","topic":"Kia K9","topic_text":"Kia K9","text":"C gi kia dn b v nh ng. H\nleo ln tng trn ca ci ging\ntng. Anh chng sa son leo ln\nngi c gi th c ta ngn li ni:\n- Ch em ng pha di ! Em\nkhng mun cho ch y bit l\nmnh ang lm g! Cho nn khi em\nni \"Em mun n nem c\" th anh\n\"y\" mnh hn, cn khi em ni\n\"Em mun n c rem c\" th anh\n\"y\" t t nhe!\nTh l h bt u \"nhp cuc\".\nNgi con gi rn ln \"Em mun\nn nem c! \"Em mun n nem c!\n\"Em mun n nem c!\" V khi\nnghe ci ging ku ct kt\nnhiu qu c vi th tho \"Em\nmun n c rem c! Em mun n\nc rem c! Em mun n c rem\nc!\"\nBng c ting ni ca c ch \ndi vng ln:\n- Ti my c n nem hay n c\nrem g th k ti my, nhng phi\ncn thn ch \"c rem\" n chy\nxung y ngi tao ri y\nn!!!!! =))))\n-4-","language":"notdetect","sentiment":"Mixed","author":"Truyệncườicấmtrẻemdưới18T","author_text":"Truyệncườicấmtrẻemdưới18T","md5_text":"49a9e99a181670e96bb3a2f00498e61a","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758730592256},{"id":"353114077","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004979554581","authorlink":"HạtCátVôDanh","date":"2013-01-06T13:56:31Z","topic":"Kia K9","topic_text":"Kia K9","text":"HU QU CA MT CN GIN\n( c ngm.. ai cng nn c 1 ln.. au tht y.. rt ngha )\n* * *\nHu qu ca mt cn ginTrong khi mt ngi n ng ang nh bng chic xe ca ng ta, th a con trai ln 4 tui ca ng ta nht ln mt vin si v v nhiu ng ln ln pha bn kia cnh chic xe ca ng ta. Trong lc gin d, ngi n ng nm ly bn tay ca a con v nh mnh nhiu m khng nhn rng ng ta ang dng mt ci c l vn vt nh.\nKt qu l trong bnh vin, a con trai ca ng ta mt i ht cc ngn tay ca mnh do qu nhiu ch gy. Khi a con trai nhn thy i mt b mnh biu l s au n, a b bn hi: \"B i ! Khi no cc ngn tay ca con mi c th mc tr li ?\" Ngi b cm thy rt au n v khng ni c li no; ng ta tr li chic xe ca mnh v n tht nhiu.\nTrong khi ang b lng tm dn vt v ang ngi i din pha hng ca chic xe , ng ta cht nhn thy nhng vt xc do chnh a con trai ca ng ta v rng: \"B i ! Con yu B nhiu lm !\"\nV mt ngy sau , ngi n ng quyt nh t st\nCn gin v Tnh yu khng bao gi c gii hn, nn xin hy chn Tnh Yu c mt cuc sng ti p v ng yu, v xin hy nh iu ny:\n vt th s dng, cn con ngi th yu thng.\nVn ca th gii ngy nay th ngc li: con ngi th s dng, cn vt th yu thng.\nHy lun c nh nhng ngha ny :\n- Hy cn thn vi nhng ngh ca bn, v bn s ni chng.\n- Hy cn thn vi nhng li ni ca bn, v bn s thc hin chng.\n- Hy cn thn vi nhng hnh ng ca bn, v chng s l thi quen ca bn.\n- Hy cn thn vi nhng thi quen ca bn, v chng s l c tnh ca bn.\n- Hy cn thn vi nhng c tnh ca bn, v chng s quyt nh s mnh ca bn.","language":"notdetect","sentiment":"Positive","author":"HạtCátVôDanh","author_text":"HạtCátVôDanh","md5_text":"27de1af92522f1786cf1627d27dceaeb","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758731640832},{"id":"353114784","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004920481393","authorlink":"XinlộiVìAnhnghèo","date":"2013-01-06T13:35:56Z","topic":"Kia K9","topic_text":"Kia K9","text":"Git l kh ri, t b mi xinh\nong m chi tnh mnh\nRi ngy bun s qua, bit tnh yu n ni xa vi \nCn li k nim th thi.\nNhn li pht giy, i mnh bn nhau\nAi ng u ngy sau\nT hi lng th sao, tri tim mt cm gic yu\nXin li anh khng l m\nNc mt anh ri, chc khng ni ln li\nCh cu xin em ng nu ko\nXa ht i em, nh cha tng c bao gi \nng qua nhng ni ta hn h\n\nNhn li pht giy, i mnh bn nhau\nai ng u ngy sau\nT hi lng th sao, tri tim mt cm gic yu\nXin li anh khng l m\nNc mt anh ri, chc khng ni ln li\nCh cu xin em ng khc na\nHy nn i em, xin em qun n au ny\n ri xa tn anh i ngi i!\n \nBng Ti Ly Caf","language":"notdetect","sentiment":"Mixed","author":"XinlộiVìAnhnghèo","author_text":"XinlộiVìAnhnghèo","md5_text":"e89efad61d991ad050064a3e149da1b8","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758732689408},{"id":"353114005","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003892451136","authorlink":"QuangThanh","date":"2013-01-06T13:56:58Z","topic":"Kia K9","topic_text":"Kia K9","text":"chng c ai nc chn qu...........","language":"notdetect","sentiment":"Mixed","author":"QuangThanh","author_text":"QuangThanh","md5_text":"14dabea287feac079cc6814c40403812","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758733737984},{"id":"353114271","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002905367578","authorlink":"TranHuy","date":"2013-01-06T13:55:47Z","topic":"Kia K9","topic_text":"Kia K9","text":"Chay xe bang 1 con mat.","language":"english","sentiment":"Mixed","author":"TranHuy","author_text":"TranHuy","md5_text":"30105b65ab063f31d12a74346967c404","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758733737985},{"id":"353114547","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003229282587","authorlink":"ToànMinhTrần","date":"2013-01-06T13:45:00Z","topic":"Kia K9","topic_text":"Kia K9","text":"Tht nghip lai sp tt ri.ai bit ch nao tuyn nv k?pao minh vi.","language":"notdetect","sentiment":"Mixed","author":"ToànMinhTrần","author_text":"ToànMinhTrần","md5_text":"38213b0750582ab2adfb69fddfe4d74b","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758734786560},{"id":"353114686","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003075821893","authorlink":"AdilLotangLatogZa","date":"2013-01-06T13:34:56Z","topic":"Kia K9","topic_text":"Kia K9","text":"Aducc digin bagedc ujann","language":"unknown","sentiment":"Mixed","author":"AdilLotangLatogZa","author_text":"AdilLotangLatogZa","md5_text":"281bd98a7027577bae57c8463e66a667","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007758735835136},{"id":"353115077","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003242037368","authorlink":"KhánhNguyễn","date":"2013-01-06T13:31:16Z","topic":"Kia K9","topic_text":"Kia K9","text":"sau 7 nm mi c ba cm gia nh y ","language":"notdetect","sentiment":"Mixed","author":"KhánhNguyễn","author_text":"KhánhNguyễn","md5_text":"abfd41443620d07726f9c828b0082482","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768286265344},{"id":"353115753","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004562795610","authorlink":"DungDung","date":"2013-01-06T13:02:36Z","topic":"Kia K9","topic_text":"Kia K9","text":"t hng cui cng trc Tt v n nh...... chm bi nhung, da bo lt bng cc cc dy, leg dc ren thm mu mi, c bit l qun ha tit Houndstooth hot nht nm nay ln u tin v shop vs kiu dng mi :x\nHn cc gi mai chp hnh r ngy kia up hnh naz :*\nCh c 1 tin bun l qun tt lt n ht sch mu m, n da chn cng k cn 1c... ch vt vt c 2c lng chut vs 2c mu nu, 1c mu tm thi :((.............. gi no qua nhanh th cn naz :x","language":"notdetect","sentiment":"Mixed","author":"DungDung","author_text":"DungDung","md5_text":"8bb2a051467aad3847e7487fc97c726f","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768288362496},{"id":"353115179","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002925488379","authorlink":"TrinhNgocDuy","date":"2013-01-06T13:28:36Z","topic":"Kia K9","topic_text":"Kia K9","text":"M SUY T\nKHI MN M BUNG XUNG L LC NGI TA SNG THT VI LNG MNH NHT ,NHM NHI MT TCH TR THY NG NGT U MI ,NHN RA BU TRI RNG LN BAO LA NGM NHNG V SAO V T HI KHNG BIT MNH L NGI SAO NO NH ?MNH KHNG L NGI SAO NO TRN BU TRI KIA V MNH C N V L LOI ,MNH CNG M NHT CH U C SNG LP LNH TH KIA.\nKHI MN M BUNG XUNG MNH TR V L NG MNH KHNG S VIN NG THNG ,KHNG QUN L O LT ,KHNG PHI GNG MNH LN SNG ,KHNG PHI CI CI NI NI KHI M TM HN LNG TRU NI BUN \nM L LC YN TNH NHT TRONG MT NGY KHNG N O VI V ,KHNG TP LP CH C TING GI THI VI VU V TING NHNG L KH RI LNG L \nM L LC TI C L CHNH TI ,C BUN ,C LM NHNG G MNH THCH M KHNG PHI NHN NHNG NGI XUNG QUANH XEM H NGH G \nM CNG L LC TI THY C N NHT ,TI QUEN VI BNG M V S YN TNH CA N NHNG TI LI S NI C N ANG M GIT CHT TI TNG NGY","language":"notdetect","sentiment":"Mixed","author":"TrinhNgocDuy","author_text":"TrinhNgocDuy","md5_text":"1659efd0873a3f42ad12be30e22b491c","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768289411072},{"id":"353115464","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=326788774078382","authorlink":"NamThanhNữTúThanhHàHảiDương","date":"2013-01-06T13:16:17Z","topic":"Kia K9","topic_text":"Kia K9","text":"hnay ad sang ha bc :) nhiu ban vui tinh lm nhe mem nao ha bc kg :)\n-bi<3-","language":"notdetect","sentiment":"Positive","author":"NamThanhNữTúThanhHàHảiDương","author_text":"NamThanhNữTúThanhHàHảiDương","md5_text":"9293a9c38022a4c51eb33b22d32cd4c5","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768297799680},{"id":"353115918","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003111704258","authorlink":"VênhVênhTiểuThư","date":"2013-01-06T13:03:43Z","topic":"Kia K9","topic_text":"Kia K9","text":"Ai chi Garena vi minh khng? :x","language":"notdetect","sentiment":"Mixed","author":"VênhVênhTiểuThư","author_text":"VênhVênhTiểuThư","md5_text":"a4b402cf94e3bebee5d54ba59d46f38a","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768297799681},{"id":"353115418","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003192500471","authorlink":"NyNyNguyễn","date":"2013-01-06T13:18:19Z","topic":"Kia K9","topic_text":"Kia K9","text":"cuc sng vui tr li sau nhng ngy lng i........... :D","language":"notdetect","sentiment":"Positive","author":"NyNyNguyễn","author_text":"NyNyNguyễn","md5_text":"01d16441e050593c8160a866edec094d","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768298848256},{"id":"353115979","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004419942705","authorlink":"HoaBồCôngAnh","date":"2013-01-06T13:06:10Z","topic":"Kia K9","topic_text":"Kia K9","text":"Tr tag cho Zt Ln nek\nNhng Ai Lit Qua u Phi Lm Ht Nghen :\"> Hem Lm Xui C Nm (2013) =))\nTui b tag nn hem bt g ht thng cm dm tui :)) ng cho gch - nh nhu qu i =))\n-----------------------------------------------------------------------------\n1/ Tr li thnh tht 100 cu hi di y\n2/ Send list cho nhng ngi m bn yu qu ^^\n~~~START~~~\n1. Tn:TT ^^\n2. Mu sc yu thch : xanh,do,tim,vang oi tat ca cac mau :\">\n3. Ni di: luon luon nen coi chung:))\n4. Bi ht mi nghe: qu cho anh -miu le\n5. Idols: nobody\n6. Khi b stress: tu ki :))\n7. Tht tnh: chac roi :))\n8. Hn ngi khc: roi ak :\">\n9. Na m hm qua lm g: ngu chu lam gi choy >.<\n10. Trang web hay vo: Faceboo,mp3,zingme :\")\n11. Nickname: heo.,mat hi\n12. Cung hong o : xu nu\n13. Vt nui: hk thix con j hjt >.<\n14. Tc di hay ngn: tat nhien la` dai` roi....:\")*chop' chop'*\n15. Chiu cao:1m60\n16. Quen bn mi: thy gap nhau nc vs nhau hop thy mjnh toi vs nhau zay ak :\">\n17. Thay i h tn : khong:(\n18. Thng ng dy lc : con ga` no' bat' dau` gay' o` o' ooooooooooooooo >.<\n19. Thch n cm : khong an com thj an j :(( oaoaoaoa\n20. Thun tay: phai O.o\n21. Ung bia, ru: khong :((\n22. S ma : con phai hoy :\"(\n23. i ra ng mt mnh vo bui ti : it khi:)\n24. eo knh: it khi deo kinh,dj duong moi deo\n25. Thch lm bn cng nhng ngi: zui tinh,hoa dong\n26. Cng vic nh lm trong tng lai: hk bjt nua,chac co the la 1 osin cao cap\n27. Bun: hien dang rat buon,vay day':((\n28. B m: khong\n29. Mn th thao iu thch: hui nao gio rat get the thao,thjx danh cau long\n30. Ngi bn thn nht: ai cung than hjk ak:))\n31. Trng cy: khong co>.<\n32. Mun c bao nhiu a con: 1 dua chac cug dc roy,s kug dc ,nhug dug nhju wa,chjt xm\n33. Chn hc: dag rat chan\n34. Hay tm s: nhju nguoi\n35. trong lp hay lm g: an,ngu\n36. Ght: ghet j la ghet j?:((\n37. Thch ra nc ngoi ko: thjk chu...thjk ms zo kai lop 12CLH01 nek :\">*iu lam co*\n38. nh thng lm g:an,ngu,phu nau kum:((\n39. Thch hc mn: thix mon nao ma khong co gi lien wan toi mon toan do\n40. Thch lm gi : lam nhung gi mjnh thjk,nhu di choi zoi bb,ng iu,dac bjet la an uong\n41. Thng i chi vs: ban tui a.:\")\n42. Ght lm g: lam nhung gi tui hk tjk\n43. T k:khi bun:(\n44. Ght hc mn:mon ton,get nhu dien lun >.<\n45. Nu bn tht bi: co len,lam laj tu dau\n46. Lm chc g trong lp:...hk bjt\n47. Hay nt vs: ban tui :))\n48. Ch nht thng lm g:an,ngu,phu nau kum,cu nzay ma phat huy\n49. Bn ang lm g: tra tag nak>.<\n50. S: ma,ran,chuot\n51. Nu mn g gii nht :hk bjk nua,\n52. Thch nht: tien vang kim cuong va cac' laoi chau bau' khac'...v...v\n53. Bit bi khng: khong,so chit duoi lam>.<\n54. C nhiu tin khng :khong ,dag ngheo,khong 1 xu dnh ti\n55. ang s ci g: thi hok du diem:(( so hoc lai:((\n56. S trng l g: an,va ngu:))\n57. Ti m gi ng: tuy bua...\n58. Bui sng n g: tuy bua,aj nau gj thi an do:))\n59. Sng u: bjh trung tay ,q2\n60.Trng thi ca bn by h : met moi...\n61. By h mn lm: can' ai kia\n62. nh tag nhng ai: cac' t.y cua tui\n63. Hc gii khng: hk bjt nua,chac la rat do:))\n64. Bn gin ngi khc lu ko : vai phut' la het:))\n65. Bn c beautiful/handsome ko: khong:))\n66. Chu au c ko: dau long thi dc...con co the thi hk\n67. Bn l ngi yu ui hay mnh m: tuy m.n nghi thui\n68. Thch i chi ko: thich^^\n69. Qu ai nht: ai cung qui\n70. By h bn ang t hi iu g: rat nhju:((\n71. Mun i u nht:di dau ma tui co the wn tat ca:((\n72. V mc in r ca bn: bay h,hien tai la 100%\n73. Lc tm hay lm g:hat,khog hju tai s,tam la lai thix hat:))\n74. La ngi khc: rat' nhiu roi:))nhung hok y' xau'\n75. yu cha: luc trc co,pay gio thi chua:(\n76. C thch b dnh tag k: 50/100\n77. Ngi pm gn y nht: ban trong lop CLH01\n78. C thch c truyn ko:khong,cam no la mun ngu lun:))\n79. Thch hoa g: hong,phong lan,...\n80. Thng mc g : bat cu cai' gi thay' hop:))\n81. Chi c nhc c g:khong bjt choi ji hjt\n82. Bn hay u: phong tui\n83. Bn thch ma no nht: thu - ng - xuan\n84. Mun quay li vo thi im no trong qa kh:thoi hoc sinh,kai thoi ao trang mong mo:(\n85. Ngy bn thch nht: 5\n86. Mun gp ai nht: nobody\n87. Bn thy mnh c c c k : doi khi:))\n88. Nhc ch ca bn l: tri ky,khong the khong thay nho,yeu anh lam\n89. Thch nghe bi ht no: tuy tam trang:))\n90. Bn c s cht k: so chu:((\n91. Thch l hi no nht: tet\n92. C hay nh bn : co',hui cap ba hay an hjp ngkhac:))kakak\n95. C chn bn chi k : khong,thich thi mjh den voi nhau:))\n96. Ai t tn cho bn:hui nho lam s bjt dc aj da dat ten cho mjh:\">\n97. Mong mun ca bn: co' 1 cuoc song' tot' sau nay:\"> >.<\n98. Hm qua lm g: di dam cuoi o nha Ngoc,di cung Zt Ln,Zino Hung Ozawa,Phm Quang Thang, Phuong Thao Nguyen Thi, Huy Doan\n99. Cm xc hin ti: buon` nat' long`:((\n100. Mun ni vs nhng ngi dnh tag: lam on tra tag dum,co' moi cai' tag de tra cho zui ma cung luoi>.<","language":"notdetect","sentiment":"Positive","author":"HoaBồCôngAnh","author_text":"HoaBồCôngAnh","md5_text":"2fc8f98d64a026e62ffa8bfd90cb425d","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768299896832},{"id":"353115112","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002894563703","authorlink":"ÚtThủyNgôNguyễn","date":"2013-01-06T13:30:54Z","topic":"Kia K9","topic_text":"Kia K9","text":"Tra nay chng kin cnh 2 ngi ph n cu x ln nhau, ph bng nhc m nhau ginh cm tha ca cng ty, tht s rt xt xa...\nC th ngi ta ang nh mt i nhn phm ca chnh mnh ch v cuc sng cn tt qu kh khn, c th ...ni ngn gn trong 2 t CM - O...\nCm thy cuc sng ca mnh nh vy l d chu ri, thi th c gi gn Nhn phm chng phi chu h ly ca cuc sng qu i x b i ....\nThng thay nhng kip ngi ngoi tri ging bo kia...\nTic thay tay mnh mm yu qu...","language":"notdetect","sentiment":"Mixed","author":"ÚtThủyNgôNguyễn","author_text":"ÚtThủyNgôNguyễn","md5_text":"4d45b0aa6d9a0802eda40e919bf56a2d","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768303042560},{"id":"353115329","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004013799752","authorlink":"VũTiếnNam","date":"2013-01-06T13:23:28Z","topic":"Kia K9","topic_text":"Kia K9","text":"Hihi gap duoc Hoai Thuong ngoai doi rui. Hihi xinh ghe. Hiiii","language":"unknown","sentiment":"Mixed","author":"VũTiếnNam","author_text":"VũTiếnNam","md5_text":"639c4dd84cc10458687d66280286c926","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768303042561},{"id":"353115845","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004136529644","authorlink":"ĐạiTiểuThư","date":"2013-01-06T13:03:59Z","topic":"Kia K9","topic_text":"Kia K9","text":"Sao anh khng la di em\nKhng cho em mt A CON\nA CON ca anh v em m thi\n.\n.\n.\nAnh, lm em cung in v yu\nLm em \"tt\" v nh\nLm em \"tt\" v khc\nLm em \"tt\" v...\n( Ngi in yu-MH) \np.s: \"tt\" l j y=]]]","language":"notdetect","sentiment":"Mixed","author":"ĐạiTiểuThư","author_text":"ĐạiTiểuThư","md5_text":"fb1e3f46897b574996777dd4d42b4964","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768304091136},{"id":"353115359","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100000993945327","authorlink":"HoaThienTam","date":"2013-01-06T13:23:14Z","topic":"Kia K9","topic_text":"Kia K9","text":"thoi cong nghe co khac. minh di chua ve toi nha giang ho da post hinh cuoi day mang roi hjhj","language":"notdetect","sentiment":"Mixed","author":"HoaThienTam","author_text":"HoaThienTam","md5_text":"ef1b8046d0aee0538b086996232e65a6","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768321916928},{"id":"353115916","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002928744466","authorlink":"HoaHoahy","date":"2013-01-06T13:04:34Z","topic":"Kia K9","topic_text":"Kia K9","text":"in mt!","language":"notdetect","sentiment":"Mixed","author":"HoaHoahy","author_text":"HoaHoahy","md5_text":"79f3297b71eb1a2ef13b2daf7b8df159","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768322965504},{"id":"353115759","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100001899398143","authorlink":"TrungNguyễn","date":"2013-01-06T13:12:06Z","topic":"Kia K9","topic_text":"Kia K9","text":"Lnh! T dng mun cm gic t t u li.. m lng m d xua i ci lnh gi ^^","language":"notdetect","sentiment":"Mixed","author":"TrungNguyễn","author_text":"TrungNguyễn","md5_text":"29709c87d51f41c152cfbb625da90e4d","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768322965505},{"id":"353115099","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004632631866","authorlink":"NguyễnTrúcLy","date":"2013-01-06T13:31:28Z","topic":"Kia K9","topic_text":"Kia K9","text":"th l ngy cui cng trong chui ngy chn tri qua..................ni chung hm nay cng tm c khng nh 2 ngy kia cng an i..................v ngy mai l ta c i hc ri......................mong rng mi chuyn s tt p..................i hc ng ngha l i hc","language":"notdetect","sentiment":"Mixed","author":"NguyễnTrúcLy","author_text":"NguyễnTrúcLy","md5_text":"0cac5cf87f7544830c4f45b089d948bb","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768324014080},{"id":"353115236","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100000014765184","authorlink":"TuyếnĐỗ","date":"2013-01-06T13:30:16Z","topic":"Kia K9","topic_text":"Kia K9","text":"ZZZZ. Gi i cn mt in thoi ch.:(.\nMi ngi cho mnh xin li s nh Lan Anh, Bluewave Pham, Lin Trn, Phm Trung Hu, Nhn Hong, Giang Pham, Anh Khoa, Nguyn Khi, Bi i, Linh Hn","language":"notdetect","sentiment":"Negative","author":"TuyếnĐỗ","author_text":"TuyếnĐỗ","md5_text":"2b7e4924a44ff87308f321590bf4fde6","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768325062656},{"id":"353115896","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100000193383583","authorlink":"ThànhCông","date":"2013-01-06T13:04:50Z","topic":"Kia K9","topic_text":"Kia K9","text":"Ngi ta thng thch ngh v nhng k nim v ch c nhng k nim y l khng thay i theo thi gian...\n( copied by Thnh Cng)","language":"notdetect","sentiment":"Mixed","author":"ThànhCông","author_text":"ThànhCông","md5_text":"f17592631321ccffad835a247f9065b2","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768325062657},{"id":"353115980","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004299339873","authorlink":"LuacojNguyen","date":"2013-01-06T13:03:03Z","topic":"Kia K9","topic_text":"Kia K9","text":"Chung tjnh la faj chap nhan tjnh chung.\nTo vua dj h0c m0t' do.ba c0n thay co dung ko.hjhj.","language":"unknown","sentiment":"Mixed","author":"LuacojNguyen","author_text":"LuacojNguyen","md5_text":"39821e4d04ce6bd3c7908c204d7dc3b2","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768326111232},{"id":"353115338","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003224657283","authorlink":"LongNguyen","date":"2013-01-06T13:24:47Z","topic":"Kia K9","topic_text":"Kia K9","text":"c e no cha c ngi yu cn c ngi yu trong ma ng lnh ny khng :v ?","language":"notdetect","sentiment":"Mixed","author":"LongNguyen","author_text":"LongNguyen","md5_text":"64aacf5be84d65cbf0a7a10a43705171","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768327159808},{"id":"353115183","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002138301139","authorlink":"HạtĐỗ","date":"2013-01-06T13:21:31Z","topic":"Kia K9","topic_text":"Kia K9","text":"Bng dng thy nh b v nhn nht...bng dng mun khc...ri bng dng thy chn..i khi chnh bn thn cng khng hiu ni triu chng \"bng dng\" ny...C l mi khi mnh ln ln thm mt cht, cm xc ca mnh vn khng nhiu thay i, vn l phn khch, xao xuyn, bi hi, nhng trong cm xc y c t nhiu nhng trn tr, suy t...i khi thy mnh gi trc tui , i khi li thy mnh nh mt a con nt mi khng thnh ngi ln c...","language":"notdetect","sentiment":"Mixed","author":"HạtĐỗ","author_text":"HạtĐỗ","md5_text":"ae34d1cd4d91fefaf3a6ffd9b2f6e640","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768328208384},{"id":"353115702","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100001964883001","authorlink":"DuyThông","date":"2013-01-06T13:10:25Z","topic":"Kia K9","topic_text":"Kia K9","text":"Tm t tn t tnh tan tc\nMan mc mnh mng mi mng m\n n u a y i n c\nThao thc thn thng thy thm thng\n..........................$.......................................","language":"notdetect","sentiment":"Mixed","author":"DuyThông","author_text":"DuyThông","md5_text":"ee232eea0b7191b1e479a2c59c2ab60e","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768329256960},{"id":"353115842","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004094078764","authorlink":"ChuanKim","date":"2013-01-06T13:07:30Z","topic":"Kia K9","topic_text":"Kia K9","text":"Hey, chn, chn, chn hc m ch hnh, khng bit th no na y mai thi ri m :((\nTm li l my c chu hc khng th bo x(","language":"notdetect","sentiment":"Mixed","author":"ChuanKim","author_text":"ChuanKim","md5_text":"bde196c30fdf5a34adfe9b77ef8a83f1","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768492834816},{"id":"353115885","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003966534377","authorlink":"ThanhLanPhan","date":"2013-01-06T13:00:03Z","topic":"Kia K9","topic_text":"Kia K9","text":"1 ngi chn 1 cuc sng t nht, khng n o, khng vi v, khng nhiu bn b...ch cn 1 ngi nhng l bn thn thit, cng nhau vt qua nhng vui bun l ri...nhng n 1 lc no khi khng cn ngi bn na th li tr nn c n, l loi v cm thy tht nng n v hnh nh quen vi s c mt ca ngi bn mi khi vui bun ri.............","language":"notdetect","sentiment":"Mixed","author":"ThanhLanPhan","author_text":"ThanhLanPhan","md5_text":"a62b5e0b3e6758c64aab5e9ba3edcad6","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768493883392},{"id":"353115035","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003639574319","authorlink":"HanghipLuong","date":"2013-01-06T13:32:30Z","topic":"Kia K9","topic_text":"Kia K9","text":"t nhin nh ci gi l qu kh...................lm t au :((((((((","language":"notdetect","sentiment":"Mixed","author":"HanghipLuong","author_text":"HanghipLuong","md5_text":"8af7a51f20f1e1b258a1bd3d1b496b9d","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768494931968},{"id":"353115971","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004556945271","authorlink":"MaxĐặng","date":"2013-01-06T13:03:18Z","topic":"Kia K9","topic_text":"Kia K9","text":"Va i ni chuyn vi thy v chuyn ngi\nLiu ngi l c tht ?\nTht u ko thy b ng kia chy xe qut 1 ci z chn by h au qu\nHix t nhin thy cuc i xui xo qu ~\nMn khc gh","language":"notdetect","sentiment":"Mixed","author":"MaxĐặng","author_text":"MaxĐặng","md5_text":"23f30f7d8b2dde5c9ea919c0121e17ac","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768494931969},{"id":"353115342","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003297717175","authorlink":"BồcônganhTrongGió","date":"2013-01-06T13:18:40Z","topic":"Kia K9","topic_text":"Kia K9","text":"m ngc time v nh thui. nng chn wa ri. \nhaizzzzz","language":"notdetect","sentiment":"Mixed","author":"BồcônganhTrongGió","author_text":"BồcônganhTrongGió","md5_text":"2141740c33a99142e8f29f2a545a5b40","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768495980544},{"id":"353115937","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003919573500","authorlink":"LinhTrần","date":"2013-01-06T13:04:18Z","topic":"Kia K9","topic_text":"Kia K9","text":"Ta noi dao nay tich cc ln face chi ...thy ban mt cua 1 ngi. Haizzz yu ri chng? c ma hng mun vy u nha! huhuhu....","language":"notdetect","sentiment":"Mixed","author":"LinhTrần","author_text":"LinhTrần","md5_text":"5af6a5f4dbb260b41a6a3153d8b3f852","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768497029120},{"id":"353115717","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004467967493","authorlink":"PhuongNguyen","date":"2013-01-06T13:08:35Z","topic":"Kia K9","topic_text":"Kia K9","text":"i gn 2 thng ri hay sao, s qu, tgian i chm chm thi, ko mun phi i na cht no ht","language":"notdetect","sentiment":"Mixed","author":"PhuongNguyen","author_text":"PhuongNguyen","md5_text":"e7ee7665aa271caa4c1adf479f7e87ad","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768502272000},{"id":"353115147","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002848488315","authorlink":"CopedangiuSokkon","date":"2013-01-06T13:21:42Z","topic":"Kia K9","topic_text":"Kia K9","text":"Cuc i nay bt cng qua y. Co nhiu ngi giau n chi, mua sm k pit tin la gi. Con qua nhiu ngi ngheo ang c gng vn ln..., vi tin...vi kai n...\nVi gia inh. Sau nay minh nht inh se giau co. Nhng k bao gi minh ging nhng con ngi giau kia.","language":"notdetect","sentiment":"Mixed","author":"CopedangiuSokkon","author_text":"CopedangiuSokkon","md5_text":"4cf8ed1e80e06478f54938309ac57d6c","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768502272001},{"id":"353115271","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100001658615342","authorlink":"XuânLiêmNgô","date":"2013-01-06T13:25:07Z","topic":"Kia K9","topic_text":"Kia K9","text":"Chiu nay , em bit khng , anh li mt mnh lang thang trn ph , do qua nhng con ng mt thi gn vi k nim ca hai chng ta Anh t nh vi lng, con trai phi mnh m Vy m bao k c xa hin v, nc mt li ri Anh t hi, c phi tnh yu ca chng mnh khng th vt qua ci quy lut nghit ng tnh ch p khi cn dang d hay l s yu ui , bt lc ca hai chng ta ? D th no th gi y mnh cng xa nhau ri . Em nh ng my v tnh , mang ma n ti mt tm hn anh , ri gi li mang em i , li cho anh mt khong tri c n , vng lng \n\nAnh khng trch em , ch trch chng mnh c duyn m khng n","language":"notdetect","sentiment":"Mixed","author":"XuânLiêmNgô","author_text":"XuânLiêmNgô","md5_text":"9f49827ceb28066426d3a4b161dadb52","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768503320576},{"id":"353115653","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003902638805","authorlink":"PekeoHit","date":"2013-01-06T13:14:29Z","topic":"Kia K9","topic_text":"Kia K9","text":"bun lam rat bun bun mun khoc lam","language":"english","sentiment":"Mixed","author":"PekeoHit","author_text":"PekeoHit","md5_text":"3afe5a058dac62396a6ecb9ffa0a2252","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768504369152},{"id":"353115019","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004265680853","authorlink":"NeymarVàTôi","date":"2013-01-06T13:32:41Z","topic":"Kia K9","topic_text":"Kia K9","text":"Cn ma ngang qua khin em nht nha.\nChng mt li cho ngi vi ri b i khng chia li cho con tim anh thm bao yu mm!\nCn ma ngang qua cun i bao yu thng.\nCn ma ngang qua khin con tim mt phng hng\nCn ma Kia nng ht,i ma thm nng ht..\nEm ri xa i bn tay trong con tim ca anh.","language":"notdetect","sentiment":"Mixed","author":"NeymarVàTôi","author_text":"NeymarVàTôi","md5_text":"327158191735c23b8207ebca73f2f19b","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768519049216},{"id":"353115241","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004584600174","authorlink":"HanHuy","date":"2013-01-06T13:26:04Z","topic":"Kia K9","topic_text":"Kia K9","text":"Dkm. kaj nay lm tao ngj vl. tao chuj ng tao ju thuog nhat. nhug den bjo doj voj tao thj kaj l0n j kung x0ng het. chung may thjh the l0n nao kug dc.","language":"unknown","sentiment":"Mixed","author":"HanHuy","author_text":"HanHuy","md5_text":"d257cb8097b47d24b4b77435ed5298b0","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768520097792},{"id":"353115502","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004581546789","authorlink":"CôngTửThànhVinh","date":"2013-01-06T13:17:36Z","topic":"Kia K9","topic_text":"Kia K9","text":"Nc mt ca...con trai ???\n \nL khi h bun rt nhiu, bao tm trng dn nn ri mt lc no o khc nh a tr! Con ngi ai cng c tri tim, ai cng c suy ngh v cm nhn ca ring mnh nn h c th khc cho tho mn bn thn khng cn bit mnh l ai!\n \nu ch ring con gi mi c khc, d cho c mnh m v cng rn n mc no cng s c lc tan chy v git l h tun ri\n \nBn thn con trai sinh ra mang tnh cht l tr ct, l bn lnhs mnh m trong con ngi h l mt ci mc nhng trong tm h cng yu ui nh con gi vy thi.\n \nH khc khi yu ai mt cch m su m khng c p tr, khc khi khng c bn cnh ngi m mnh yu thng, khc khi khng th lm g cho h, v khi ngi thn h ri xa tht xa ni ny.\n \nSuy cho cng th ai cng c tm trng nhng che y cm xc th con trai l ngi gii nht...V h cng l con ngi,m l ngi th ai chng c...nc mt....","language":"notdetect","sentiment":"Mixed","author":"CôngTửThànhVinh","author_text":"CôngTửThànhVinh","md5_text":"8c9d199bd0ef1f8b8bb050e4dcac0f4c","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768520097793},{"id":"353115334","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100000290186182","authorlink":"RosMagma","date":"2013-01-06T13:17:32Z","topic":"Kia K9","topic_text":"Kia K9","text":"her her t nhin t xi li c vui g :))","language":"notdetect","sentiment":"Mixed","author":"RosMagma","author_text":"RosMagma","md5_text":"3f4a7aa784ceb1e020d7fb51f967ad0b","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768521146368},{"id":"353115767","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=736215222","authorlink":"AnhHai","date":"2013-01-06T13:02:28Z","topic":"Kia K9","topic_text":"Kia K9","text":"Mt du khch i v mt vng qu n thy c mt nng tri. Du khch n gn v hi bc nng dn:\n- Ny bc hai con b n mt ngy bao nhiu kg c?\nBc nng dn hi:\n- ng hi con trng hay con en?\nDu khch:\n- V d con en i.\n- Ht mt ngy 10kg c\n- Th cn con trng?\n- , con trng cng ht 10kg c\nDu khch hi tip\n- Th mt ngy bc vt c bao nhiu sa?\n- Con trng hay con en?\n- Con trng chng hn.\n- con trng 1 ngy 5 lt sa\n- Th con en?\n- Cng 5 lt.\nDu khch tn ngn v hi li\nNy bc, sao ny gi hai con ging nhau m bc c hi con trng hay en, sao khng tr li gp chung li cho khe.\nBc nng dn ni.\n- ng ko bit thi, ti v con trng l ca ti.\nDu khch ln \n- ra th. Vy th con en l ca ai?\n- Con en cng ca ti =))))))))))","language":"notdetect","sentiment":"Mixed","author":"AnhHai","author_text":"AnhHai","md5_text":"b44e805e16c5be05284633ae8c4587fa","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768522194944},{"id":"353115844","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003111969456","authorlink":"TiếnLợi","date":"2013-01-06T13:07:07Z","topic":"Kia K9","topic_text":"Kia K9","text":"Cht tht sp n tt ri... tt 2013 vn cha c ngi yu khng bit tt 2014 s th no y... ang ri vo tnh trng ri ;)) =))","language":"notdetect","sentiment":"Positive","author":"TiếnLợi","author_text":"TiếnLợi","md5_text":"4c61db8d47588adbc44cbd2d997bc4ee","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768523243520},{"id":"353115941","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004742893702","authorlink":"HeoTocxoan","date":"2013-01-06T13:06:25Z","topic":"Kia K9","topic_text":"Kia K9","text":"Thank you for leaving me.themselves as never before what happened.I will walk on my feet.I will go my way.go somewhere can find your pictures just like the old days.just stay with your happiness.goodbye.","language":"english","sentiment":"Positive","author":"HeoTocxoan","author_text":"HeoTocxoan","md5_text":"b53caaf77ad0b2ec1c043053f2565de0","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768524292096},{"id":"353115086","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004645720680","authorlink":"ĐặngNgọcNhưPhước","date":"2013-01-06T13:31:06Z","topic":"Kia K9","topic_text":"Kia K9","text":"Tm Li\nng gp: \nChng mun nghe g\nChng mun nhn khc\ni mt lun tm...tm v\nNgi nht mi ci\nNgi nh mt n hng\nNgi khc trong lng\nHt ri...\nTm li i hy tm li trong mi ngi \n ta khng thy ta nh lc ny\nng xa qu di ri ta mt nhoi v ta khng c bn nhau na ri.\n\nNgi m sao tn\nNgy y ng n\nng n vi vng...ht ri\nNgi xung y bn\nCn chn ru kia\nCn ht m ngy...ht ri\nTm li i hy tm li trong mi ngi ta khng thy ta nh lc ny \nng xa qu di ri ta mt nhoi v ta khng c bn nhau na ri...\n \n\nTm li i hy tm li trong mi ngi ta khng thy ta nh lc ny\nng xa qu di ri ta mt nhoi v ta khng c bn nhau na...\nTm li i hy tm li trong mi ngi ta khng thy ta nh lc ny\nng xa qu di ri ta mt nhoi v ta khng c bn nhau na...\nTm li i hy tm li trong mi ngi ta khng thy ta nh lc ny \nng xa qu di ri ta mt nhoi v ta khng c bn nhau na ri...na ri...na ri i i i...","language":"notdetect","sentiment":"Mixed","author":"ĐặngNgọcNhưPhước","author_text":"ĐặngNgọcNhưPhước","md5_text":"3b904893822e521f4875a731ef050680","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768525340672},{"id":"353115635","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=258124047544025","authorlink":"HộiDotAViệtNamAntiHackmap","date":"2013-01-06T13:12:30Z","topic":"Kia K9","topic_text":"Kia K9","text":"Test cc tnh hung cho mem ci nh :3\n1.Bn cm thy c ch nht l khi?\nA) ang dota m bng dng cp in\nB) Combat d th b b m bt i sai lm vic nh\nC) ang late th bng nhin anh ch e nhy ra ginh my c vic\n\n2. Th loi game th no m bn ght nht ? \nA) nh theo phong cch \"siu nhn\" ( Thch i 1 mnh lm siu nhn nhng n khi team combat th ch thy u ! n khi b team n p do i l th li hi sao team k chu ln cu )\nB) Th loi hay chi team :-j thua cng chi thng th ni l do mnh nn mi thng\nC) HM\n\n3. Bn thch chi dota u nht\nA) nh qun cng m bn :3\nB) Reg gold member ri chi nh ( li i lm )\nC) Mang lap qua nh thng bn nh dota vi n :\">\n\nNh test ad check li kt qu nh :3\n-Zu-","language":"notdetect","sentiment":"Positive","author":"HộiDotAViệtNamAntiHackmap","author_text":"HộiDotAViệtNamAntiHackmap","md5_text":"8f733837c776cbdb8d4c7b45478266c8","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768526389248},{"id":"353115436","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100000471782174","authorlink":"ThanhHaiTrieu","date":"2013-01-06T13:16:29Z","topic":"Kia K9","topic_text":"Kia K9","text":"v nh bnh an ri hi cc chin hu Phan Thanh Ton Nguyt D Tran Duc Linh bla..bla... Noi chung l Ton bnh ni xng xo wa', \" e ch c cho\", ra m i c on b i x lui ngi sau gt g gt g ch m s rt khn ik =)) Noi chung l bun ng ri y :))","language":"notdetect","sentiment":"Positive","author":"ThanhHaiTrieu","author_text":"ThanhHaiTrieu","md5_text":"a2ef9217af4bd62f4112a6f0e853e2c6","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768526389249},{"id":"353115770","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003809841354","authorlink":"VũTrần","date":"2013-01-06T13:04:58Z","topic":"Kia K9","topic_text":"Kia K9","text":"2 ngy na l cho x yu v n tt sm ri ........ 25 fiive club tp hp........!","language":"notdetect","sentiment":"Mixed","author":"VũTrần","author_text":"VũTrần","md5_text":"2bbd77b09738a99de5ad1be9f5150aff","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768547360768},{"id":"353115154","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003996409355","authorlink":"SulyDoll","date":"2013-01-06T13:28:49Z","topic":"Kia K9","topic_text":"Kia K9","text":"Chg nao c ngi tt y ha tri ??! Chan qa =((","language":"unknown","sentiment":"Mixed","author":"SulyDoll","author_text":"SulyDoll","md5_text":"5b960dcc7528f75f4e75a9d10a41c895","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768548409344},{"id":"353115401","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003697947658","authorlink":"BíchThủyHoàng","date":"2013-01-06T13:16:54Z","topic":"Kia K9","topic_text":"Kia K9","text":"Hm ni ngy chi m en i ra....","language":"notdetect","sentiment":"Mixed","author":"BíchThủyHoàng","author_text":"BíchThủyHoàng","md5_text":"9eddee27e55f96cc8064f74f5d3f856d","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768548409345},{"id":"353115659","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100004528651915","authorlink":"PhamAn","date":"2013-01-06T13:10:45Z","topic":"Kia K9","topic_text":"Kia K9","text":"tnh hnh l mnh khu B i my a i...\nTrn Nguyn Phng Tr Pu Kina H L Nga Luong Kem Kem ng Th ThaoVi Tran Tran Thao Jade Nguyen Zenny Le,,,, tag thm gip An vs....","language":"notdetect","sentiment":"Mixed","author":"PhamAn","author_text":"PhamAn","md5_text":"6ac38b64ac5d0a271cc176791e6801ff","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768550506496},{"id":"353115862","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100000867159535","authorlink":"TùngDokusin","date":"2013-01-06T13:00:19Z","topic":"Kia K9","topic_text":"Kia K9","text":"nh mt ngoi qu :(","language":"notdetect","sentiment":"Negative","author":"TùngDokusin","author_text":"TùngDokusin","md5_text":"758ba937d5dc9f2dcc150db12945647c","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768550506497},{"id":"353115120","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100002847330491","authorlink":"LongVu","date":"2013-01-06T13:22:06Z","topic":"Kia K9","topic_text":"Kia K9","text":"Xe a con vo trng n Ka 3\nc 3 thng con nhn lnh ra ta \nng quan ta tht ln 1 ting\nn t hnh git cht i con\nXin cha m tha th cho con \nPhn lm con ch hiu cha trn\nCha m gi nay ai phng dng\nCon du hin sm chiu qunh hiu\nThng cho n em b th ngy\nVnh khn tang chng qun ln u\na t nht cm tay m hi\n\"M i m bao gi anh v?\"\n\"Anh khng v c na u con\nV ta kia bn anh ri\nVo ngy ny nm sau khi lm gi\nAnh li v qua ln khi hng bay\"\nDi sui vng con quyt ch n nn\np tn my xanh con quyt ch tr v\nCon tr v t trn cao con nhn xung\nDng m gy lng tht con au\nEm! C phi em khng em?\nang gc u khc thng cho ngi tnh\nNgi tnh gi y khng cn na \nn tnh xin tr li cho em....\nXin Vnh Bit Th Gii Mu Xanh........!","language":"notdetect","sentiment":"Mixed","author":"LongVu","author_text":"LongVu","md5_text":"d33c12397913d688a2f07a9ba5ac8a2f","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768551555072},{"id":"353115713","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=187955667946831","authorlink":"ĐạiHọcKinhTếKỹThuậtCôngNghiệp","date":"2013-01-06T13:08:20Z","topic":"Kia K9","topic_text":"Kia K9","text":"Hu qu ca mt cn gin\n\nTrong\nkhi mt ngi n ng ang\nnh bng chic xe ca ng ta,\nth a con trai ln 4 tui ca ng\nta nht ln mt vin si v v\nnhiu ng ln ln pha bn kia\ncnh chic xe ca ng ta. Trong\nlc gin d, ngi n ng \nnm ly bn tay ca a con v\nnh mnh nhiu m khng nhn\nrng ng ta ang dng mt ci c\nl vn vt nh.\nKt qu l trong bnh vin, a\ncon trai ca ng ta mt i ht\ncc ngn tay ca mnh do qu\nnhiu ch gy. Khi a con trai\nnhn thy i mt b mnh biu l\ns au n, a b bn hi: \"B\ni ! Khi no cc ngn tay ca con\nmi c th mc tr li ?\" Ngi\nb cm thy rt au n v\nkhng ni c li no; ng ta\ntr li chic xe ca mnh v n\ntht nhiu.\nTrong khi ang b lng tm dn\nvt v ang ngi i din pha\nhng ca chic xe , ng ta cht\nnhn thy nhng vt xc do\nchnh a con trai ca ng ta \nv rng: \"B i ! Con yu B nhiu\nlm !\"\nV mt ngy sau , ngi n\nng quyt nh t st\nCn gin v Tnh yu khng bao\ngi c gii hn, nn xin hy chn\nTnh Yu c mt cuc sng\nti p v ng yu, v xin hy\nnh iu ny:\n vt th s dng, cn con\nngi th yu thng.\nVn ca th gii ngy nay th\nngc li: con ngi th s\ndng, cn vt th yu\nthng.\nHy lun c nh nhng ngha\nny :\n- Hy cn thn vi nhng ngh\nca bn, v bn s ni chng.\n- Hy cn thn vi nhng li ni\nca bn, v bn s thc hin\nchng.\n- Hy cn thn vi nhng hnh\nng ca bn, v chng s l thi\nquen ca bn.\n- Hy cn thn vi nhng thi\nquen ca bn, v chng s l c\ntnh ca bn.\n- Hy cn thn vi nhng c tnh\nca bn, v chng s quyt nh\ns mnh ca bn.","language":"notdetect","sentiment":"Positive","author":"ĐạiHọcKinhTếKỹThuậtCôngNghiệp","author_text":"ĐạiHọcKinhTếKỹThuậtCôngNghiệp","md5_text":"57a8410818280a75a7df06dd17271c45","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768552603648},{"id":"353115505","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=1633577234","authorlink":"NhưTrang","date":"2013-01-06T13:15:18Z","topic":"Kia K9","topic_text":"Kia K9","text":"Trc khi noi xu ngi khac thi nn nhin lai ban thn minh co ep e hay khng a :))\nSng th ngi ta bit thi kha la nhuc y <3 =))","language":"notdetect","sentiment":"Positive","author":"NhưTrang","author_text":"NhưTrang","md5_text":"07247045e2812f3ac92ceb5f5d95e679","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768553652224},{"id":"353115240","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100000308699269","authorlink":"MaMútSiêuKool","date":"2013-01-06T13:23:43Z","topic":"Kia K9","topic_text":"Kia K9","text":"ci ngy g m xui vi c linh hn>.<\nSng th ng qun, ti chu i thi tr\nV gp ci thi th b trt t ( lm c ti n nh ci my)\nThi xong th b ng kia chi\" sao b thy tui cha i thi m hok kiu tui\"(mo con cng thi tr cha i_._)\nMi rn xe ra khi cng trng thn iu l b 1 chic xe ti d sau t xe=.=\nBn roi, quyt tm i mua tr sa n, ti qun \" ht tr sa r em\"=.=mi 3h m ht....\nHok mn v nh, i lng vng 4h n 1 t m qung, chy lng xng 6h n h tu, v ti nh li mn n thy l 8h 1 t cho lng vs 1 ly tr sa( tng thit hi l 49k)...n xong ci bn n nh cng lun\nV ti nh ng m cng cn gp 1 thng bn thi, ni g m\" em gi p p g \" (m i!!!) vt v nh cp tc...\nTm li 1 cu\" i khng nh l m\"","language":"notdetect","sentiment":"Positive","author":"MaMútSiêuKool","author_text":"MaMútSiêuKool","md5_text":"9e8491184daca93c83f13c46d37329cb","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768554700800},{"id":"353115463","title":"Kia K9","search_source":"Facebook","source_domain":"www.facebook.com","source_domain_text":"www.facebook.com","url":"http://www.facebook.com/profile.php?id=100003214484845","authorlink":"TinTỸTỞn","date":"2013-01-06T13:19:17Z","topic":"Kia K9","topic_text":"Kia K9","text":"tui hag tui vi da~ wa iu e \nj phai? de e roi xa tui the 1 lan nua~","language":"notdetect","sentiment":"Mixed","author":"TinTỸTỞn","author_text":"TinTỸTỞn","md5_text":"a83fd75bfdcae3bded8c6807787ac411","category":["Kia","Car"],"query_id":[60528109],"_version_":1425007768555749376}] bunny-2.6.1/spec/issues/issue100_spec.rb0000644000004100000410000000177413015255277020100 0ustar www-datawww-datarequire "spec_helper" unless ENV["CI"] describe Bunny::Channel, "#basic_publish" do before :all do @connection = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :write_timeout => 0, :read_timeout => 0) @connection.start end after :all do @connection.close if @connection.open? end context "when publishing thousands of messages" do let(:n) { 2_000 } let(:m) { 10 } it "successfully publishers them all" do ch = @connection.create_channel q = ch.queue("", :exclusive => true) x = ch.default_exchange body = "x" * 1024 m.times do |i| n.times do x.publish(body, :routing_key => q.name) end puts "Published #{i * n} 1K messages..." end q.purge ch.close end end end end bunny-2.6.1/spec/issues/issue202_spec.rb0000644000004100000410000000054113015255277020072 0ustar www-datawww-datarequire "spec_helper" describe Bunny::Session do context "with unreachable host" do it "raises Bunny::TCPConnectionFailed" do begin conn = Bunny.new(:hostname => "192.192.192.192") conn.start fail "expected 192.192.192.192 to be unreachable" rescue Bunny::TCPConnectionFailed => e end end end end bunny-2.6.1/.travis.yml0000644000004100000410000000043413015255277015024 0ustar www-datawww-datalanguage: ruby bundler_args: --without development cache: bundler before_script: "./bin/ci/before_build" script: "bundle exec rspec -cf documentation spec" rvm: - "2.3.0" - "2.2.2" notifications: email: michael@rabbitmq.com services: - rabbitmq branches: only: - master bunny-2.6.1/lib/0000755000004100000410000000000013015255277013460 5ustar www-datawww-databunny-2.6.1/lib/amq/0000755000004100000410000000000013015255277014236 5ustar www-datawww-databunny-2.6.1/lib/amq/protocol/0000755000004100000410000000000013015255277016077 5ustar www-datawww-databunny-2.6.1/lib/amq/protocol/extensions.rb0000644000004100000410000000041313015255277020621 0ustar www-datawww-data# @private module AMQ # @private module Protocol # @private class Basic # Extended to allow wrapping delivery tag into # a versioned one. # # @private class GetOk attr_writer :delivery_tag end end end end bunny-2.6.1/lib/bunny/0000755000004100000410000000000013015255277014613 5ustar www-datawww-databunny-2.6.1/lib/bunny/ssl_socket.rb0000644000004100000410000000035113015255277017310 0ustar www-datawww-data# See #165. MK. if defined?(JRUBY_VERSION) require "bunny/jruby/ssl_socket" module Bunny SSLSocketImpl = JRuby::SSLSocket end else require "bunny/cruby/ssl_socket" module Bunny SSLSocketImpl = SSLSocket end end bunny-2.6.1/lib/bunny/message_properties.rb0000644000004100000410000000512213015255277021040 0ustar www-datawww-datamodule Bunny # Wraps basic properties hash as returned by amq-protocol to # provide access to the delivery properties as immutable hash as # well as methods. class MessageProperties # # Behaviors # include Enumerable # # API # # @private def initialize(properties) @properties = properties end # Iterates over the message properties # @see Enumerable#each def each(*args, &block) @properties.each(*args, &block) end # Accesses message properties by key # @see Hash#[] def [](k) @properties[k] end # @return [Hash] Hash representation of this delivery info def to_hash @properties end # @private def to_s to_hash.to_s end # @private def inspect to_hash.inspect end # @return [String] (Optional) content type of the message, as set by the publisher def content_type @properties[:content_type] end # @return [String] (Optional) content encoding of the message, as set by the publisher def content_encoding @properties[:content_encoding] end # @return [String] Message headers def headers @properties[:headers] end # @return [Integer] Delivery mode (persistent or transient) def delivery_mode @properties[:delivery_mode] end # @return [Integer] Message priority, as set by the publisher def priority @properties[:priority] end # @return [String] What message this message is a reply to (or corresponds to), as set by the publisher def correlation_id @properties[:correlation_id] end # @return [String] (Optional) How to reply to the publisher (usually a reply queue name) def reply_to @properties[:reply_to] end # @return [String] Message expiration, as set by the publisher def expiration @properties[:expiration] end # @return [String] Message ID, as set by the publisher def message_id @properties[:message_id] end # @return [Time] Message timestamp, as set by the publisher def timestamp @properties[:timestamp] end # @return [String] Message type, as set by the publisher def type @properties[:type] end # @return [String] Publishing user, as set by the publisher def user_id @properties[:user_id] end # @return [String] Publishing application, as set by the publisher def app_id @properties[:app_id] end # @return [String] Cluster ID, as set by the publisher def cluster_id @properties[:cluster_id] end end end bunny-2.6.1/lib/bunny/consumer_tag_generator.rb0000644000004100000410000000102413015255277021671 0ustar www-datawww-datamodule Bunny # Used to generate consumer tags in the client class ConsumerTagGenerator # # API # # @return [String] Generated consumer tag def generate "#{Kernel.rand}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}" end # generate # Unique string supposed to be used as a consumer tag. # # @return [String] Unique string. # @api public def generate_prefixed(name = "bunny") "#{name}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}" end end end bunny-2.6.1/lib/bunny/exchange.rb0000644000004100000410000002364713015255277016736 0ustar www-datawww-datarequire 'amq/protocol' module Bunny # Represents AMQP 0.9.1 exchanges. # # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide class Exchange # # API # # @return [Bunny::Channel] attr_reader :channel # @return [String] attr_reader :name # Type of this exchange (one of: :direct, :fanout, :topic, :headers). # @return [Symbol] attr_reader :type # @return [Symbol] # @api plugin attr_reader :status # Options hash this exchange instance was instantiated with # @return [Hash] attr_accessor :opts # The default exchange. This exchange is a direct exchange that is predefined by the broker # and that cannot be removed. Every queue is bound to this exchange by default with # the following routing semantics: messages will be routed to the queue with the same # name as the message's routing key. In other words, if a message is published with # a routing key of "weather.usa.ca.sandiego" and there is a queue with this name, # the message will be routed to the queue. # # @param [Bunny::Channel] channel_or_connection Channel to use. {Bunny::Session} instances # are only supported for backwards compatibility. # # @example Publishing a messages to the tasks queue # channel = Bunny::Channel.new(connection) # tasks_queue = channel.queue("tasks") # Bunny::Exchange.default(channel).publish("make clean", :routing_key => "tasks") # # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide # @see http://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf AMQP 0.9.1 specification (Section 2.1.2.4) # @note Do not confuse the default exchange with amq.direct: amq.direct is a pre-defined direct # exchange that doesn't have any special routing semantics. # @return [Exchange] An instance that corresponds to the default exchange (of type direct). # @api public def self.default(channel_or_connection) self.new(channel_or_connection, :direct, AMQ::Protocol::EMPTY_STRING, :no_declare => true) end # @param [Bunny::Channel] channel Channel this exchange will use. # @param [Symbol,String] type Exchange type # @param [String] name Exchange name # @param [Hash] opts Exchange properties # # @option opts [Boolean] :durable (false) Should this exchange be durable? # @option opts [Boolean] :auto_delete (false) Should this exchange be automatically deleted when it is no longer used? # @option opts [Boolean] :arguments ({}) Additional optional arguments (typically used by RabbitMQ extensions and plugins) # # @see Bunny::Channel#topic # @see Bunny::Channel#fanout # @see Bunny::Channel#direct # @see Bunny::Channel#headers # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide # @api public def initialize(channel, type, name, opts = {}) @channel = channel @name = name @type = type @options = self.class.add_default_options(name, opts) @durable = @options[:durable] @auto_delete = @options[:auto_delete] @internal = @options[:internal] @arguments = @options[:arguments] @bindings = Set.new declare! unless opts[:no_declare] || predeclared? || (@name == AMQ::Protocol::EMPTY_STRING) @channel.register_exchange(self) end # @return [Boolean] true if this exchange was declared as durable (will survive broker restart). # @api public def durable? @durable end # durable? # @return [Boolean] true if this exchange was declared as automatically deleted (deleted as soon as last consumer unbinds). # @api public def auto_delete? @auto_delete end # auto_delete? # @return [Boolean] true if this exchange is internal (used solely for exchange-to-exchange # bindings and cannot be published to by clients) def internal? @internal end # @return [Hash] Additional optional arguments (typically used by RabbitMQ extensions and plugins) # @api public def arguments @arguments end # Publishes a message # # @param [String] payload Message payload. It will never be modified by Bunny or RabbitMQ in any way. # @param [Hash] opts Message properties (metadata) and delivery settings # # @option opts [String] :routing_key Routing key # @option opts [Boolean] :persistent Should the message be persisted to disk? # @option opts [Boolean] :mandatory Should the message be returned if it cannot be routed to any queue? # @option opts [Integer] :timestamp A timestamp associated with this message # @option opts [Integer] :expiration Expiration time after which the message will be deleted # @option opts [String] :type Message type, e.g. what type of event or command this message represents. Can be any string # @option opts [String] :reply_to Queue name other apps should send the response to # @option opts [String] :content_type Message content type (e.g. application/json) # @option opts [String] :content_encoding Message content encoding (e.g. gzip) # @option opts [String] :correlation_id Message correlated to this one, e.g. what request this message is a reply for # @option opts [Integer] :priority Message priority, 0 to 9. Not used by RabbitMQ, only applications # @option opts [String] :message_id Any message identifier # @option opts [String] :user_id Optional user ID. Verified by RabbitMQ against the actual connection username # @option opts [String] :app_id Optional application ID # # @return [Bunny::Exchange] Self # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide # @api public def publish(payload, opts = {}) @channel.basic_publish(payload, self.name, (opts.delete(:routing_key) || opts.delete(:key)), opts) self end # Deletes the exchange unless it is predeclared # # @param [Hash] opts Options # # @option opts [Boolean] if_unused (false) Should this exchange be deleted only if it is no longer used # # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide # @api public def delete(opts = {}) @channel.deregister_exchange(self) @channel.exchange_delete(@name, opts) unless predeclared? end # Binds an exchange to another (source) exchange using exchange.bind AMQP 0.9.1 extension # that RabbitMQ provides. # # @param [String] source Source exchange name # @param [Hash] opts Options # # @option opts [String] routing_key (nil) Routing key used for binding # @option opts [Hash] arguments ({}) Optional arguments # # @return [Bunny::Exchange] Self # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide # @see http://rubybunny.info/articles/bindings.html Bindings guide # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide # @api public def bind(source, opts = {}) @channel.exchange_bind(source, self, opts) @bindings.add(source: source, opts: opts) self end # Unbinds an exchange from another (source) exchange using exchange.unbind AMQP 0.9.1 extension # that RabbitMQ provides. # # @param [String] source Source exchange name # @param [Hash] opts Options # # @option opts [String] routing_key (nil) Routing key used for binding # @option opts [Hash] arguments ({}) Optional arguments # # @return [Bunny::Exchange] Self # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide # @see http://rubybunny.info/articles/bindings.html Bindings guide # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide # @api public def unbind(source, opts = {}) @channel.exchange_unbind(source, self, opts) @bindings.delete(source: source, opts: opts) self end # Defines a block that will handle returned messages # @see http://rubybunny.info/articles/exchanges.html # @api public def on_return(&block) @on_return = block self end # Waits until all outstanding publisher confirms on the channel # arrive. # # This is a convenience method that delegates to {Bunny::Channel#wait_for_confirms} # # @api public def wait_for_confirms @channel.wait_for_confirms end # @private def recover_from_network_failure declare! unless @options[:no_declare] ||predefined? @bindings.each do |b| bind(b[:source], b[:opts]) end end # # Implementation # # @private def handle_return(basic_return, properties, content) if @on_return @on_return.call(basic_return, properties, content) else # TODO: log a warning end end # @return [Boolean] true if this exchange is a pre-defined one (amq.direct, amq.fanout, amq.match and so on) def predefined? (@name == AMQ::Protocol::EMPTY_STRING) || !!(@name =~ /^amq\.(direct|fanout|topic|headers|match)/i) end # predefined? alias predeclared? predefined? protected # @private def declare! @channel.exchange_declare(@name, @type, @options) end # @private def self.add_default_options(name, opts) # :nowait is always false for Bunny h = { :queue => name, :nowait => false }.merge(opts) if name.empty? { :passive => false, :durable => false, :auto_delete => false, :internal => false, :arguments => nil }.merge(h) else h end end end end bunny-2.6.1/lib/bunny/framing.rb0000644000004100000410000000340513015255277016565 0ustar www-datawww-datamodule Bunny # @private module Framing ENCODINGS_SUPPORTED = defined? Encoding HEADER_SLICE = (0..6).freeze DATA_SLICE = (7..-1).freeze PAYLOAD_SLICE = (0..-2).freeze # @private module String class Frame < AMQ::Protocol::Frame def self.decode(string) header = string[HEADER_SLICE] type, channel, size = self.decode_header(header) data = string[DATA_SLICE] payload = data[PAYLOAD_SLICE] frame_end = data[-1, 1] frame_end.force_encoding(AMQ::Protocol::Frame::FINAL_OCTET.encoding) if ENCODINGS_SUPPORTED # 1) the size is miscalculated if payload.bytesize != size raise BadLengthError.new(size, payload.bytesize) end # 2) the size is OK, but the string doesn't end with FINAL_OCTET raise NoFinalOctetError.new if frame_end != AMQ::Protocol::Frame::FINAL_OCTET self.new(type, payload, channel) end end end # String # @private module IO class Frame < AMQ::Protocol::Frame def self.decode(io) header = io.read(7) type, channel, size = self.decode_header(header) data = io.read_fully(size + 1) payload, frame_end = data[PAYLOAD_SLICE], data[-1, 1] # 1) the size is miscalculated if payload.bytesize != size raise BadLengthError.new(size, payload.bytesize) end # 2) the size is OK, but the string doesn't end with FINAL_OCTET raise NoFinalOctetError.new if frame_end != AMQ::Protocol::Frame::FINAL_OCTET self.new(type, payload, channel) end # self.from end # Frame end # IO end # Framing end # Bunny bunny-2.6.1/lib/bunny/jruby/0000755000004100000410000000000013015255277015746 5ustar www-datawww-databunny-2.6.1/lib/bunny/jruby/ssl_socket.rb0000644000004100000410000000303113015255277020441 0ustar www-datawww-datamodule Bunny module JRuby begin require "bunny/cruby/ssl_socket" require "openssl" # TLS-enabled TCP socket that implements convenience # methods found in Bunny::Socket. class SSLSocket < Bunny::SSLSocket # Reads given number of bytes with an optional timeout # # @param [Integer] count How many bytes to read # @param [Integer] timeout Timeout # # @return [String] Data read from the socket # @api public def read_fully(count, timeout = nil) return nil if @__bunny_socket_eof_flag__ value = '' begin loop do value << read_nonblock(count - value.bytesize) break if value.bytesize >= count end rescue EOFError => e @__bunny_socket_eof_flag__ = true rescue OpenSSL::SSL::SSLError => e if e.message == "read would block" if IO.select([self], nil, nil, timeout) retry else raise Timeout::Error, "IO timeout when reading #{count} bytes" end else raise e end rescue *READ_RETRY_EXCEPTION_CLASSES => e if IO.select([self], nil, nil, timeout) retry else raise Timeout::Error, "IO timeout when reading #{count} bytes" end end value end end rescue LoadError => le puts "Could not load OpenSSL" end end end bunny-2.6.1/lib/bunny/jruby/socket.rb0000644000004100000410000000221613015255277017564 0ustar www-datawww-datarequire "bunny/cruby/socket" module Bunny module JRuby # TCP socket extension that uses Socket#readpartial to avoid excessive CPU # burn after some time. See issue #165. # @private module Socket include Bunny::Socket # Reads given number of bytes with an optional timeout # # @param [Integer] count How many bytes to read # @param [Integer] timeout Timeout # # @return [String] Data read from the socket # @api public def read_fully(count, timeout = nil) return nil if @__bunny_socket_eof_flag__ value = '' begin loop do value << readpartial(count - value.bytesize) break if value.bytesize >= count end rescue EOFError # @eof will break Rubinius' TCPSocket implementation. MK. @__bunny_socket_eof_flag__ = true rescue *READ_RETRY_EXCEPTION_CLASSES if IO.select([self], nil, nil, timeout) retry else raise Timeout::Error, "IO timeout when reading #{count} bytes" end end value end # read_fully end end end bunny-2.6.1/lib/bunny/test_kit.rb0000644000004100000410000000135713015255277016774 0ustar www-datawww-data# -*- coding: utf-8 -*- module Bunny # Unit, integration and stress testing toolkit class TestKit class << self # @return [Integer] Random integer in the range of [a, b] # @api private def random_in_range(a, b) Range.new(a, b).to_a.sample end # @param [Integer] a Lower bound of message size, in KB # @param [Integer] b Upper bound of message size, in KB # @param [Integer] i Random number to use in message generation # @return [String] Message payload of length in the given range, with non-ASCII characters # @api public def message_in_kb(a, b, i) s = "Ю#{i}" n = random_in_range(a, b) / s.bytesize s * n * 1024 end end end end bunny-2.6.1/lib/bunny/versioned_delivery_tag.rb0000644000004100000410000000120013015255277021665 0ustar www-datawww-datamodule Bunny # Wraps a delivery tag (which is an integer) so that {Bunny::Channel} could # detect stale tags after connection recovery. # # @private class VersionedDeliveryTag attr_reader :tag attr_reader :version def initialize(tag, version) raise ArgumentError.new("tag cannot be nil") unless tag raise ArgumentError.new("version cannot be nil") unless version @tag = tag.to_i @version = version.to_i end def to_i @tag end def stale?(version) raise ArgumentError.new("version cannot be nil") unless version @version < version.to_i end end end bunny-2.6.1/lib/bunny/channel_id_allocator.rb0000644000004100000410000000405613015255277021271 0ustar www-datawww-datarequire "thread" require "monitor" require "amq/int_allocator" module Bunny # Bitset-based channel id allocator. When channels are closed, # ids are released back to the pool. # # Every connection has its own allocator. # # Allocating and releasing ids is synchronized and can be performed # from multiple threads. class ChannelIdAllocator # # API # # @param [Integer] max_channel Max allowed channel id def initialize(max_channel = ((1 << 16) - 1)) @allocator = AMQ::IntAllocator.new(1, max_channel) @mutex = Monitor.new end # Returns next available channel id. This method is thread safe. # # @return [Fixnum] # @api public # @see ChannelManager#release_channel_id # @see ChannelManager#reset_channel_id_allocator def next_channel_id @mutex.synchronize do @allocator.allocate end end # Releases previously allocated channel id. This method is thread safe. # # @param [Fixnum] i Channel id to release # @api public # @see ChannelManager#next_channel_id # @see ChannelManager#reset_channel_id_allocator def release_channel_id(i) @mutex.synchronize do @allocator.release(i) end end # Returns true if given channel id has been previously allocated and not yet released. # This method is thread safe. # # @param [Fixnum] i Channel id to check # @return [Boolean] true if given channel id has been previously allocated and not yet released # @api public # @see ChannelManager#next_channel_id # @see ChannelManager#release_channel_id def allocated_channel_id?(i) @mutex.synchronize do @allocator.allocated?(i) end end # Resets channel allocator. This method is thread safe. # @api public # @see Channel.next_channel_id # @see Channel.release_channel_id def reset_channel_id_allocator @mutex.synchronize do @allocator.reset end end # @private def synchronize(&block) @mutex.synchronize(&block) end end end bunny-2.6.1/lib/bunny/delivery_info.rb0000644000004100000410000000432113015255277017776 0ustar www-datawww-datarequire "bunny/versioned_delivery_tag" module Bunny # Wraps {AMQ::Protocol::Basic::Deliver} to # provide access to the delivery properties as immutable hash as # well as methods. class DeliveryInfo # # Behaviors # include Enumerable # # API # # @return [Bunny::Consumer] Consumer this delivery is for attr_reader :consumer # @return [Bunny::Channel] Channel this delivery is on attr_reader :channel # @private def initialize(basic_deliver, consumer, channel) @basic_deliver = basic_deliver @hash = { :consumer_tag => basic_deliver.consumer_tag, :delivery_tag => VersionedDeliveryTag.new(basic_deliver.delivery_tag, channel.recoveries_counter), :redelivered => basic_deliver.redelivered, :exchange => basic_deliver.exchange, :routing_key => basic_deliver.routing_key, :consumer => consumer, :channel => channel } @consumer = consumer @channel = channel end # Iterates over the delivery properties # @see Enumerable#each def each(*args, &block) @hash.each(*args, &block) end # Accesses delivery properties by key # @see Hash#[] def [](k) @hash[k] end # @return [Hash] Hash representation of this delivery info def to_hash @hash end # @private def to_s to_hash.to_s end # @private def inspect to_hash.inspect end # @return [String] Consumer tag this delivery is for def consumer_tag @basic_deliver.consumer_tag end # @return [String] Delivery identifier that is used to acknowledge, reject and nack deliveries def delivery_tag @basic_deliver.delivery_tag end # @return [Boolean] true if this delivery is a redelivery (the message was requeued at least once) def redelivered @basic_deliver.redelivered end alias redelivered? redelivered # @return [String] Name of the exchange this message was published to def exchange @basic_deliver.exchange end # @return [String] Routing key this message was published with def routing_key @basic_deliver.routing_key end end end bunny-2.6.1/lib/bunny/get_response.rb0000644000004100000410000000341113015255277017634 0ustar www-datawww-datarequire "bunny/versioned_delivery_tag" module Bunny # Wraps {AMQ::Protocol::Basic::GetOk} to # provide access to the delivery properties as immutable hash as # well as methods. class GetResponse # # Behaviors # include Enumerable # # API # # @return [Bunny::Channel] Channel this basic.get-ok response is on attr_reader :channel # @private def initialize(get_ok, channel) @get_ok = get_ok @hash = { :delivery_tag => @get_ok.delivery_tag, :redelivered => @get_ok.redelivered, :exchange => @get_ok.exchange, :routing_key => @get_ok.routing_key, :channel => channel } @channel = channel end # Iterates over the delivery properties # @see Enumerable#each def each(*args, &block) @hash.each(*args, &block) end # Accesses delivery properties by key # @see Hash#[] def [](k) @hash[k] end # @return [Hash] Hash representation of this delivery info def to_hash @hash end # @private def to_s to_hash.to_s end # @private def inspect to_hash.inspect end # @return [String] Delivery identifier that is used to acknowledge, reject and nack deliveries def delivery_tag @get_ok.delivery_tag end # @return [Boolean] true if this delivery is a redelivery (the message was requeued at least once) def redelivered @get_ok.redelivered end alias redelivered? redelivered # @return [String] Name of the exchange this message was published to def exchange @get_ok.exchange end # @return [String] Routing key this message was published with def routing_key @get_ok.routing_key end end end bunny-2.6.1/lib/bunny/version.rb0000644000004100000410000000014413015255277016624 0ustar www-datawww-data# encoding: utf-8 module Bunny # @return [String] Version of the library VERSION = "2.6.1" end bunny-2.6.1/lib/bunny/consumer.rb0000644000004100000410000001040013015255277016766 0ustar www-datawww-datamodule Bunny # Base class that represents consumer interface. Subclasses of this class implement # specific logic of handling consumer life cycle events. Note that when the only event # you are interested in is message deliveries, it is recommended to just use # {Bunny::Queue#subscribe} instead of subclassing this class. # # @see Bunny::Queue#subscribe # @see Bunny::Queue#subscribe_with # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public class Consumer # # API # attr_reader :channel attr_reader :queue attr_accessor :consumer_tag attr_reader :arguments attr_reader :no_ack attr_reader :exclusive # @param [Bunny::Channel] channel Channel this consumer will use # @param [Bunny::Queue,String] queue Queue messages will be consumed from # @param [String] consumer_tag Consumer tag (unique identifier). Generally it is better to let Bunny generate one. # Empty string means RabbitMQ will generate consumer tag. # @param [Boolean] no_ack (true) If true, delivered messages will be automatically acknowledged. # If false, manual acknowledgements will be necessary. # @param [Boolean] exclusive (false) Should this consumer be exclusive? # @param [Hash] arguments (nil) Optional arguments that may be used by RabbitMQ extensions, etc # @api public def initialize(channel, queue, consumer_tag = channel.generate_consumer_tag, no_ack = true, exclusive = false, arguments = {}) @channel = channel || raise(ArgumentError, "channel is nil") @queue = queue || raise(ArgumentError, "queue is nil") @consumer_tag = consumer_tag @exclusive = exclusive @arguments = arguments # no_ack set to true = no manual ack = automatic ack. MK. @no_ack = no_ack @on_cancellation = [] end # Defines message delivery handler # @api public def on_delivery(&block) @on_delivery = block self end # Invokes message delivery handler # @private def call(*args) @on_delivery.call(*args) if @on_delivery end alias handle_delivery call # Defines consumer cancellation notification handler # # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide # @api public def on_cancellation(&block) @on_cancellation << block self end # Invokes consumer cancellation notification handler # @private def handle_cancellation(basic_cancel) @on_cancellation.each do |fn| fn.call(basic_cancel) end end # Cancels this consumer. Messages for this consumer will no longer be delivered. If the queue # it was on is auto-deleted and this consumer was the last one, the queue will be deleted. # # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def cancel @channel.basic_cancel(@consumer_tag) end # @return [String] More detailed human-readable string representation of this consumer def inspect "#<#{self.class.name}:#{object_id} @channel_id=#{@channel.number} @queue=#{self.queue_name}> @consumer_tag=#{@consumer_tag} @exclusive=#{@exclusive} @no_ack=#{@no_ack}>" end # @return [String] Brief human-readable string representation of this consumer def to_s "#<#{self.class.name}:#{object_id} @channel_id=#{@channel.number} @queue=#{self.queue_name}> @consumer_tag=#{@consumer_tag}>" end # @return [Boolean] true if this consumer uses automatic acknowledgement mode # @api public def automatic_acknowledgement? @no_ack == true end # @return [Boolean] true if this consumer uses manual (explicit) acknowledgement mode # @api public def manual_acknowledgement? @no_ack == false end # @return [String] Name of the queue this consumer is on # @api public def queue_name if @queue.respond_to?(:name) @queue.name else @queue end end # # Recovery # # @private def recover_from_network_failure @channel.basic_consume_with(self) end end end bunny-2.6.1/lib/bunny/channel.rb0000644000004100000410000021133313015255277016553 0ustar www-datawww-data# -*- coding: utf-8 -*- require "thread" require "monitor" require "set" require "bunny/concurrent/atomic_fixnum" require "bunny/consumer_work_pool" require "bunny/exchange" require "bunny/queue" require "bunny/delivery_info" require "bunny/return_info" require "bunny/message_properties" if defined?(JRUBY_VERSION) require "bunny/concurrent/linked_continuation_queue" else require "bunny/concurrent/continuation_queue" end module Bunny # ## Channels in RabbitMQ # # To quote {http://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf AMQP 0.9.1 specification}: # # AMQP 0.9.1 is a multi-channelled protocol. Channels provide a way to multiplex # a heavyweight TCP/IP connection into several light weight connections. # This makes the protocol more “firewall friendly” since port usage is predictable. # It also means that traffic shaping and other network QoS features can be easily employed. # Channels are independent of each other and can perform different functions simultaneously # with other channels, the available bandwidth being shared between the concurrent activities. # # # ## Opening Channels # # Channels can be opened either via `Bunny::Session#create_channel` (sufficient in the majority # of cases) or by instantiating `Bunny::Channel` directly: # # conn = Bunny.new # conn.start # # ch = conn.create_channel # # This will automatically allocate a channel id. # # ## Closing Channels # # Channels are closed via {Bunny::Channel#close}. Channels that get a channel-level exception are # closed, too. Closed channels can no longer be used. Attempts to use them will raise # {Bunny::ChannelAlreadyClosed}. # # ch = conn.create_channel # ch.close # # ## Higher-level API # # Bunny offers two sets of methods on {Bunny::Channel}: known as higher-level and lower-level # APIs, respectively. Higher-level API mimics {http://rubyamqp.info amqp gem} API where # exchanges and queues are objects (instance of {Bunny::Exchange} and {Bunny::Queue}, respectively). # Lower-level API is built around AMQP 0.9.1 methods (commands), where queues and exchanges are # passed as strings (à la RabbitMQ Java client, {http://clojurerabbitmq.info Langohr} and Pika). # # ### Queue Operations In Higher-level API # # * {Bunny::Channel#queue} is used to declare queues. The rest of the API is in {Bunny::Queue}. # # # ### Exchange Operations In Higher-level API # # * {Bunny::Channel#topic} declares a topic exchange. The rest of the API is in {Bunny::Exchange}. # * {Bunny::Channel#direct} declares a direct exchange. # * {Bunny::Channel#fanout} declares a fanout exchange. # * {Bunny::Channel#headers} declares a headers exchange. # * {Bunny::Channel#default_exchange} # * {Bunny::Channel#exchange} is used to declare exchanges with type specified as a symbol or string. # # # ## Channel Qos (Prefetch Level) # # It is possible to control how many messages at most a consumer will be given (before it acknowledges # or rejects previously consumed ones). This setting is per channel and controlled via {Bunny::Channel#prefetch}. # # # ## Channel IDs # # Channels are identified by their ids which are integers. Bunny takes care of allocating and # releasing them as channels are opened and closed. It is almost never necessary to specify # channel ids explicitly. # # There is a limit on the maximum number of channels per connection, usually 65536. Note # that allocating channels is very cheap on both client and server so having tens, hundreds # or even thousands of channels is not a problem. # # ## Channels and Error Handling # # Channel-level exceptions are more common than connection-level ones and often indicate # issues applications can recover from (such as consuming from or trying to delete # a queue that does not exist). # # With Bunny, channel-level exceptions are raised as Ruby exceptions, for example, # {Bunny::NotFound}, that provide access to the underlying `channel.close` method # information. # # @example Handling 404 NOT_FOUND # begin # ch.queue_delete("queue_that_should_not_exist#{rand}") # rescue Bunny::NotFound => e # puts "Channel-level exception! Code: #{e.channel_close.reply_code}, message: #{e.channel_close.reply_text}" # end # # @example Handling 406 PRECONDITION_FAILED # begin # ch2 = conn.create_channel # q = "bunny.examples.recovery.q#{rand}" # # ch2.queue_declare(q, :durable => false) # ch2.queue_declare(q, :durable => true) # rescue Bunny::PreconditionFailed => e # puts "Channel-level exception! Code: #{e.channel_close.reply_code}, message: #{e.channel_close.reply_text}" # ensure # conn.create_channel.queue_delete(q) # end # # @see http://www.rabbitmq.com/tutorials/amqp-concepts.html AMQP 0.9.1 Model Concepts Guide # @see http://rubybunny.info/articles/getting_started.html Getting Started with RabbitMQ Using Bunny # @see http://rubybunny.info/articles/queues.html Queues and Consumers # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing # @see http://rubybunny.info/articles/error_handling.html Error Handling and Recovery Guide class Channel # # API # # @return [Integer] Channel id attr_accessor :id # @return [Bunny::Session] AMQP connection this channel was opened on attr_reader :connection # @return [Symbol] Channel status (:opening, :open, :closed) attr_reader :status # @return [Bunny::ConsumerWorkPool] Thread pool delivered messages are dispatched to. attr_reader :work_pool # @return [Integer] Next publisher confirmations sequence index attr_reader :next_publish_seq_no # @return [Hash] Queue instances declared on this channel attr_reader :queues # @return [Hash] Exchange instances declared on this channel attr_reader :exchanges # @return [Set] Set of published message indexes that are currently unconfirmed attr_reader :unconfirmed_set # @return [Set] Set of nacked message indexes that have been nacked attr_reader :nacked_set # @return [Hash] Consumer instances declared on this channel attr_reader :consumers # @return [Integer] active basic.qos prefetch value attr_reader :prefetch_count # @return [Integer] active basic.qos prefetch global mode attr_reader :prefetch_global DEFAULT_CONTENT_TYPE = "application/octet-stream".freeze SHORTSTR_LIMIT = 255 # @param [Bunny::Session] connection AMQP 0.9.1 connection # @param [Integer] id Channel id, pass nil to make Bunny automatically allocate it # @param [Bunny::ConsumerWorkPool] work_pool Thread pool for delivery processing, by default of size 1 def initialize(connection = nil, id = nil, work_pool = ConsumerWorkPool.new(1)) @connection = connection @logger = connection.logger @id = id || @connection.next_channel_id @status = :opening @connection.register_channel(self) @queues = Hash.new @exchanges = Hash.new @consumers = Hash.new @work_pool = work_pool # synchronizes frameset delivery. MK. @publishing_mutex = @connection.mutex_impl.new @consumer_mutex = @connection.mutex_impl.new @unconfirmed_set_mutex = @connection.mutex_impl.new self.reset_continuations # threads awaiting on continuations. Used to unblock # them when network connection goes down so that busy loops # that perform synchronous operations can work. MK. @threads_waiting_on_continuations = Set.new @threads_waiting_on_confirms_continuations = Set.new @threads_waiting_on_basic_get_continuations = Set.new @next_publish_seq_no = 0 @delivery_tag_offset = 0 @recoveries_counter = Bunny::Concurrent::AtomicFixnum.new(0) @uncaught_exception_handler = Proc.new do |e, consumer| @logger.error "Uncaught exception from consumer #{consumer.to_s}: #{e.inspect} @ #{e.backtrace[0]}" end end attr_reader :recoveries_counter # @private def wait_on_continuations_timeout @connection.transport_write_timeout end # Opens the channel and resets its internal state # @return [Bunny::Channel] Self # @api public def open @threads_waiting_on_continuations = Set.new @threads_waiting_on_confirms_continuations = Set.new @threads_waiting_on_basic_get_continuations = Set.new @connection.open_channel(self) # clear last channel error @last_channel_error = nil @status = :open self end # Closes the channel. Closed channels can no longer be used (this includes associated # {Bunny::Queue}, {Bunny::Exchange} and {Bunny::Consumer} instances. # @api public def close @connection.close_channel(self) @status = :closed @work_pool.shutdown maybe_kill_consumer_work_pool! end # @return [Boolean] true if this channel is open, false otherwise # @api public def open? @status == :open end # @return [Boolean] true if this channel is closed (manually or because of an exception), false otherwise # @api public def closed? @status == :closed end def to_s oid = ("0x%x" % (self.object_id << 1)) "<#{self.class.name}:#{oid} number=#{@channel.id} @open=#{open?} connection=#{@connection.to_s}>" end def inspect to_s end # # @group Backwards compatibility with 0.8.0 # # @return [Integer] Channel id def number self.id end # @return [Boolean] true if this channel is open def active open? end # @return [Bunny::Session] Connection this channel was opened on def client @connection end # @private def frame_size @connection.frame_max end # @endgroup # # Higher-level API, similar to amqp gem # # @group Higher-level API for exchange operations # Declares a fanout exchange or looks it up in the cache of previously # declared exchanges. # # @param [String] name Exchange name # @param [Hash] opts Exchange parameters # # @option opts [Boolean] :durable (false) Should the exchange be durable? # @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use? # @option opts [Hash] :arguments ({}) Optional exchange arguments (used by RabbitMQ extensions) # # @return [Bunny::Exchange] Exchange instance # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide # @api public def fanout(name, opts = {}) Exchange.new(self, :fanout, name, opts) end # Declares a direct exchange or looks it up in the cache of previously # declared exchanges. # # @param [String] name Exchange name # @param [Hash] opts Exchange parameters # # @option opts [Boolean] :durable (false) Should the exchange be durable? # @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use? # @option opts [Hash] :arguments ({}) Optional exchange arguments (used by RabbitMQ extensions) # # @return [Bunny::Exchange] Exchange instance # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide # @api public def direct(name, opts = {}) Exchange.new(self, :direct, name, opts) end # Declares a topic exchange or looks it up in the cache of previously # declared exchanges. # # @param [String] name Exchange name # @param [Hash] opts Exchange parameters # # @option opts [Boolean] :durable (false) Should the exchange be durable? # @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use? # @option opts [Hash] :arguments ({}) Optional exchange arguments (used by RabbitMQ extensions) # # @return [Bunny::Exchange] Exchange instance # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide # @api public def topic(name, opts = {}) Exchange.new(self, :topic, name, opts) end # Declares a headers exchange or looks it up in the cache of previously # declared exchanges. # # @param [String] name Exchange name # @param [Hash] opts Exchange parameters # # @option opts [Boolean] :durable (false) Should the exchange be durable? # @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use? # @option opts [Hash] :arguments ({}) Optional exchange arguments # # @return [Bunny::Exchange] Exchange instance # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide # @api public def headers(name, opts = {}) Exchange.new(self, :headers, name, opts) end # Provides access to the default exchange # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide # @api public def default_exchange Exchange.default(self) end # Declares a headers exchange or looks it up in the cache of previously # declared exchanges. # # @param [String] name Exchange name # @param [Hash] opts Exchange parameters # # @option opts [String,Symbol] :type (:direct) Exchange type, e.g. :fanout or "x-consistent-hash" # @option opts [Boolean] :durable (false) Should the exchange be durable? # @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use? # @option opts [Hash] :arguments ({}) Optional exchange arguments # # @return [Bunny::Exchange] Exchange instance # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide def exchange(name, opts = {}) Exchange.new(self, opts.fetch(:type, :direct), name, opts) end # @endgroup # @group Higher-level API for queue operations # Declares a queue or looks it up in the per-channel cache. # # @param [String] name Queue name. Pass an empty string to declare a server-named queue (make RabbitMQ generate a unique name). # @param [Hash] opts Queue properties and other options # # @option opts [Boolean] :durable (false) Should this queue be durable? # @option opts [Boolean] :auto-delete (false) Should this queue be automatically deleted when the last consumer disconnects? # @option opts [Boolean] :exclusive (false) Should this queue be exclusive (only can be used by this connection, removed when the connection is closed)? # @option opts [Boolean] :arguments ({}) Additional optional arguments (typically used by RabbitMQ extensions and plugins) # # @return [Bunny::Queue] Queue that was declared or looked up in the cache # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide # @api public def queue(name = AMQ::Protocol::EMPTY_STRING, opts = {}) q = find_queue(name) || Bunny::Queue.new(self, name, opts) register_queue(q) end # Declares a new server-named queue that is automatically deleted when the # connection is closed. # # @return [Bunny::Queue] Queue that was declared # @see #queue # @api public def temporary_queue(opts = {}) queue("", opts.merge(:exclusive => true)) end # @endgroup # @group QoS and Flow Control # Flow control. When set to false, RabbitMQ will stop delivering messages on this # channel. # # @param [Boolean] active Should messages to consumers on this channel be delivered? # @api public def flow(active) channel_flow(active) end # Tells RabbitMQ to redeliver unacknowledged messages # @api public def recover(ignored = true) # RabbitMQ only supports basic.recover with requeue = true basic_recover(true) end # @endgroup # @group Message acknowledgements # Rejects a message. A rejected message can be requeued or # dropped by RabbitMQ. # # @param [Integer] delivery_tag Delivery tag to reject # @param [Boolean] requeue Should this message be requeued instead of dropping it? # @see Bunny::Channel#ack # @see Bunny::Channel#nack # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def reject(delivery_tag, requeue = false) basic_reject(delivery_tag.to_i, requeue) end # Acknowledges a message. Acknowledged messages are completely removed from the queue. # # @param [Integer] delivery_tag Delivery tag to acknowledge # @param [Boolean] multiple (false) Should all unacknowledged messages up to this be acknowledged as well? # @see Bunny::Channel#nack # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def ack(delivery_tag, multiple = false) basic_ack(delivery_tag.to_i, multiple) end alias acknowledge ack # Rejects a message. A rejected message can be requeued or # dropped by RabbitMQ. This method is similar to {Bunny::Channel#reject} but # supports rejecting multiple messages at once, and is usually preferred. # # @param [Integer] delivery_tag Delivery tag to reject # @param [Boolean] multiple (false) Should all unacknowledged messages up to this be rejected as well? # @param [Boolean] requeue (false) Should this message be requeued instead of dropping it? # @see Bunny::Channel#ack # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def nack(delivery_tag, multiple = false, requeue = false) basic_nack(delivery_tag.to_i, multiple, requeue) end # @endgroup # # Lower-level API, exposes protocol operations as they are defined in the protocol, # without any OO sugar on top, by design. # # @group Consumer and Message operations (basic.*) # Publishes a message using basic.publish AMQP 0.9.1 method. # # @param [String] payload Message payload. It will never be modified by Bunny or RabbitMQ in any way. # @param [String] exchange Exchange to publish to # @param [String] routing_key Routing key # @param [Hash] opts Publishing options # # @option opts [Boolean] :persistent Should the message be persisted to disk? # @option opts [Boolean] :mandatory Should the message be returned if it cannot be routed to any queue? # @option opts [Integer] :timestamp A timestamp associated with this message # @option opts [Integer] :expiration Expiration time after which the message will be deleted # @option opts [String] :type Message type, e.g. what type of event or command this message represents. Can be any string # @option opts [String] :reply_to Queue name other apps should send the response to # @option opts [String] :content_type Message content type (e.g. application/json) # @option opts [String] :content_encoding Message content encoding (e.g. gzip) # @option opts [String] :correlation_id Message correlated to this one, e.g. what request this message is a reply for # @option opts [Integer] :priority Message priority, 0 to 9. Not used by RabbitMQ, only applications # @option opts [String] :message_id Any message identifier # @option opts [String] :user_id Optional user ID. Verified by RabbitMQ against the actual connection username # @option opts [String] :app_id Optional application ID # # @return [Bunny::Channel] Self # @api public def basic_publish(payload, exchange, routing_key, opts = {}) raise_if_no_longer_open! raise ArgumentError, "routing key cannot be longer than #{SHORTSTR_LIMIT} characters" if routing_key && routing_key.size > SHORTSTR_LIMIT exchange_name = if exchange.respond_to?(:name) exchange.name else exchange end mode = if opts.fetch(:persistent, true) 2 else 1 end opts[:delivery_mode] ||= mode opts[:content_type] ||= DEFAULT_CONTENT_TYPE opts[:priority] ||= 0 if @next_publish_seq_no > 0 @unconfirmed_set_mutex.synchronize do @unconfirmed_set.add(@next_publish_seq_no) @next_publish_seq_no += 1 end end frames = AMQ::Protocol::Basic::Publish.encode(@id, payload, opts, exchange_name, routing_key, opts[:mandatory], false, @connection.frame_max) @connection.send_frameset(frames, self) self end # Synchronously fetches a message from the queue, if there are any. This method is # for cases when the convenience of synchronous operations is more important than # throughput. # # @param [String] queue Queue name # @param [Hash] opts Options # # @option opts [Boolean] :ack (true) [DEPRECATED] Use :manual_ack instead # @option opts [Boolean] :manual_ack (true) Will this message be acknowledged manually? # # @return [Array] A triple of delivery info, message properties and message content # # @example Using Bunny::Channel#basic_get with manual acknowledgements # conn = Bunny.new # conn.start # ch = conn.create_channel # # here we assume the queue already exists and has messages # delivery_info, properties, payload = ch.basic_get("bunny.examples.queue1", :manual_ack => true) # ch.acknowledge(delivery_info.delivery_tag) # @see Bunny::Queue#pop # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def basic_get(queue, opts = {:manual_ack => true}) raise_if_no_longer_open! unless opts[:ack].nil? warn "[DEPRECATION] `:ack` is deprecated. Please use `:manual_ack` instead." opts[:manual_ack] = opts[:ack] end @connection.send_frame(AMQ::Protocol::Basic::Get.encode(@id, queue, !(opts[:manual_ack]))) # this is a workaround for the edge case when basic_get is called in a tight loop # and network goes down we need to perform recovery. The problem is, basic_get will # keep blocking the thread that calls it without clear way to constantly unblock it # from the network activity loop (where recovery happens) with the current continuations # implementation (and even more correct and convenient ones, such as wait/notify, should # we implement them). So we return a triple of nils immediately which apps should be # able to handle anyway as "got no message, no need to act". MK. last_basic_get_response = if @connection.open? wait_on_basic_get_continuations else [nil, nil, nil] end raise_if_continuation_resulted_in_a_channel_error! last_basic_get_response end # prefetch_count is of type short in the protocol. MK. MAX_PREFETCH_COUNT = (2 ** 16) - 1 # Controls message delivery rate using basic.qos AMQP 0.9.1 method. # # @param [Integer] prefetch_count How many messages can consumers on this channel be given at a time # (before they have to acknowledge or reject one of the earlier received messages) # @param [Boolean] global # Whether to use global mode for prefetch: # - +false+: per-consumer # - +true+: per-channel # Note that the default value (+false+) hasn't actually changed, but # previous documentation described that as meaning per-channel and # unsupported in RabbitMQ, whereas it now actually appears to mean # per-consumer and supported # (https://www.rabbitmq.com/consumer-prefetch.html). # @return [AMQ::Protocol::Basic::QosOk] RabbitMQ response # @see Bunny::Channel#prefetch # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def basic_qos(count, global = false) raise ArgumentError.new("prefetch count must be a positive integer, given: #{count}") if count < 0 raise ArgumentError.new("prefetch count must be no greater than #{MAX_PREFETCH_COUNT}, given: #{count}") if count > MAX_PREFETCH_COUNT raise_if_no_longer_open! @connection.send_frame(AMQ::Protocol::Basic::Qos.encode(@id, 0, count, global)) Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_basic_qos_ok = wait_on_continuations end raise_if_continuation_resulted_in_a_channel_error! @prefetch_count = count @prefetch_global = global @last_basic_qos_ok end alias prefetch basic_qos # Redeliver unacknowledged messages # # @param [Boolean] requeue Should messages be requeued? # @return [AMQ::Protocol::Basic::RecoverOk] RabbitMQ response # @api public def basic_recover(requeue) raise_if_no_longer_open! @connection.send_frame(AMQ::Protocol::Basic::Recover.encode(@id, requeue)) Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_basic_recover_ok = wait_on_continuations end raise_if_continuation_resulted_in_a_channel_error! @last_basic_recover_ok end # Rejects or requeues a message. # # @param [Integer] delivery_tag Delivery tag obtained from delivery info # @param [Boolean] requeue Should the message be requeued? # @return [NilClass] nil # # @example Requeue a message # conn = Bunny.new # conn.start # # ch = conn.create_channel # q.subscribe do |delivery_info, properties, payload| # # requeue the message # ch.basic_reject(delivery_info.delivery_tag, true) # end # # @example Reject a message # conn = Bunny.new # conn.start # # ch = conn.create_channel # q.subscribe do |delivery_info, properties, payload| # # reject the message # ch.basic_reject(delivery_info.delivery_tag, false) # end # # @example Requeue a message fetched via basic.get # conn = Bunny.new # conn.start # # ch = conn.create_channel # # we assume the queue exists and has messages # delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :manual_ack => true) # ch.basic_reject(delivery_info.delivery_tag, true) # # @see Bunny::Channel#basic_nack # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def basic_reject(delivery_tag, requeue = false) guarding_against_stale_delivery_tags(delivery_tag) do raise_if_no_longer_open! @connection.send_frame(AMQ::Protocol::Basic::Reject.encode(@id, delivery_tag, requeue)) nil end end # Acknowledges a delivery (message). # # @param [Integer] delivery_tag Delivery tag obtained from delivery info # @param [Boolean] multiple Should all deliveries up to this one be acknowledged? # @return [NilClass] nil # # @example Ack a message # conn = Bunny.new # conn.start # # ch = conn.create_channel # q.subscribe do |delivery_info, properties, payload| # # requeue the message # ch.basic_ack(delivery_info.delivery_tag.to_i) # end # # @example Ack a message fetched via basic.get # conn = Bunny.new # conn.start # # ch = conn.create_channel # # we assume the queue exists and has messages # delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :manual_ack => true) # ch.basic_ack(delivery_info.delivery_tag.to_i) # # @example Ack multiple messages fetched via basic.get # conn = Bunny.new # conn.start # # ch = conn.create_channel # # we assume the queue exists and has messages # _, _, payload1 = ch.basic_get("bunny.examples.queue3", :manual_ack => true) # _, _, payload2 = ch.basic_get("bunny.examples.queue3", :manual_ack => true) # delivery_info, properties, payload3 = ch.basic_get("bunny.examples.queue3", :manual_ack => true) # # ack all fetched messages up to payload3 # ch.basic_ack(delivery_info.delivery_tag.to_i, true) # # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def basic_ack(delivery_tag, multiple = false) guarding_against_stale_delivery_tags(delivery_tag) do raise_if_no_longer_open! @connection.send_frame(AMQ::Protocol::Basic::Ack.encode(@id, delivery_tag, multiple)) nil end end # Rejects or requeues messages just like {Bunny::Channel#basic_reject} but can do so # with multiple messages at once. # # @param [Integer] delivery_tag Delivery tag obtained from delivery info # @param [Boolean] requeue Should the message be requeued? # @param [Boolean] multiple Should all deliveries up to this one be rejected/requeued? # @return [NilClass] nil # # @example Requeue a message # conn = Bunny.new # conn.start # # ch = conn.create_channel # q.subscribe do |delivery_info, properties, payload| # # requeue the message # ch.basic_nack(delivery_info.delivery_tag, false, true) # end # # @example Reject a message # conn = Bunny.new # conn.start # # ch = conn.create_channel # q.subscribe do |delivery_info, properties, payload| # # requeue the message # ch.basic_nack(delivery_info.delivery_tag) # end # # @example Requeue a message fetched via basic.get # conn = Bunny.new # conn.start # # ch = conn.create_channel # # we assume the queue exists and has messages # delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :manual_ack => true) # ch.basic_nack(delivery_info.delivery_tag, false, true) # # # @example Requeue multiple messages fetched via basic.get # conn = Bunny.new # conn.start # # ch = conn.create_channel # # we assume the queue exists and has messages # _, _, payload1 = ch.basic_get("bunny.examples.queue3", :manual_ack => true) # _, _, payload2 = ch.basic_get("bunny.examples.queue3", :manual_ack => true) # delivery_info, properties, payload3 = ch.basic_get("bunny.examples.queue3", :manual_ack => true) # # requeue all fetched messages up to payload3 # ch.basic_nack(delivery_info.delivery_tag, true, true) # # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide # @api public def basic_nack(delivery_tag, multiple = false, requeue = false) guarding_against_stale_delivery_tags(delivery_tag) do raise_if_no_longer_open! @connection.send_frame(AMQ::Protocol::Basic::Nack.encode(@id, delivery_tag, multiple, requeue)) nil end end # Registers a consumer for queue. Delivered messages will be handled with the block # provided to this method. # # @param [String, Bunny::Queue] queue Queue to consume from # @param [String] consumer_tag Consumer tag (unique identifier), generated by Bunny by default # @param [Boolean] no_ack (false) If true, delivered messages will be automatically acknowledged. # If false, manual acknowledgements will be necessary. # @param [Boolean] exclusive (false) Should this consumer be exclusive? # @param [Hash] arguments (nil) Optional arguments that may be used by RabbitMQ extensions, etc # # @return [AMQ::Protocol::Basic::ConsumeOk] RabbitMQ response # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def basic_consume(queue, consumer_tag = generate_consumer_tag, no_ack = false, exclusive = false, arguments = nil, &block) raise_if_no_longer_open! maybe_start_consumer_work_pool! queue_name = if queue.respond_to?(:name) queue.name else queue end # helps avoid race condition between basic.consume-ok and basic.deliver if there are messages # in the queue already. MK. if consumer_tag && consumer_tag.strip != AMQ::Protocol::EMPTY_STRING add_consumer(queue_name, consumer_tag, no_ack, exclusive, arguments, &block) end @connection.send_frame(AMQ::Protocol::Basic::Consume.encode(@id, queue_name, consumer_tag, false, no_ack, exclusive, false, arguments)) begin Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_basic_consume_ok = wait_on_continuations end rescue Exception => e # if basic.consume-ok never arrives, unregister the proactively # registered consumer. MK. unregister_consumer(@last_basic_consume_ok.consumer_tag) raise e end # in case there is another exclusive consumer and we get a channel.close # response here. MK. raise_if_channel_close!(@last_basic_consume_ok) # covers server-generated consumer tags add_consumer(queue_name, @last_basic_consume_ok.consumer_tag, no_ack, exclusive, arguments, &block) @last_basic_consume_ok end alias consume basic_consume # Registers a consumer for queue as {Bunny::Consumer} instance. # # @param [Bunny::Consumer] consumer Consumer to register. It should already have queue name, consumer tag # and other attributes set. # # @return [AMQ::Protocol::Basic::ConsumeOk] RabbitMQ response # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def basic_consume_with(consumer) raise_if_no_longer_open! maybe_start_consumer_work_pool! # helps avoid race condition between basic.consume-ok and basic.deliver if there are messages # in the queue already. MK. if consumer.consumer_tag && consumer.consumer_tag.strip != AMQ::Protocol::EMPTY_STRING register_consumer(consumer.consumer_tag, consumer) end @connection.send_frame(AMQ::Protocol::Basic::Consume.encode(@id, consumer.queue_name, consumer.consumer_tag, false, consumer.no_ack, consumer.exclusive, false, consumer.arguments)) begin Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_basic_consume_ok = wait_on_continuations end rescue Exception => e # if basic.consume-ok never arrives, unregister the proactively # registered consumer. MK. unregister_consumer(@last_basic_consume_ok.consumer_tag) raise e end # in case there is another exclusive consumer and we get a channel.close # response here. MK. raise_if_channel_close!(@last_basic_consume_ok) # covers server-generated consumer tags register_consumer(@last_basic_consume_ok.consumer_tag, consumer) raise_if_continuation_resulted_in_a_channel_error! @last_basic_consume_ok end alias consume_with basic_consume_with # Removes a consumer. Messages for this consumer will no longer be delivered. If the queue # it was on is auto-deleted and this consumer was the last one, the queue will be deleted. # # @param [String] consumer_tag Consumer tag (unique identifier) to cancel # # @return [AMQ::Protocol::Basic::CancelOk] RabbitMQ response # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def basic_cancel(consumer_tag) @connection.send_frame(AMQ::Protocol::Basic::Cancel.encode(@id, consumer_tag, false)) Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_basic_cancel_ok = wait_on_continuations end # reduces thread usage for channels that don't have any # consumers @work_pool.shutdown(true) unless self.any_consumers? @last_basic_cancel_ok end # @return [Boolean] true if there are consumers on this channel # @api public def any_consumers? @consumer_mutex.synchronize { @consumers.any? } end # @endgroup # @group Queue operations (queue.*) # Declares a queue using queue.declare AMQP 0.9.1 method. # # @param [String] name Queue name # @param [Hash] opts Queue properties # # @option opts [Boolean] durable (false) Should information about this queue be persisted to disk so that it # can survive broker restarts? Typically set to true for long-lived queues. # @option opts [Boolean] auto_delete (false) Should this queue be deleted when the last consumer is cancelled? # @option opts [Boolean] exclusive (false) Should only this connection be able to use this queue? # If true, the queue will be automatically deleted when this # connection is closed # @option opts [Boolean] passive (false) If true, queue will be checked for existence. If it does not # exist, {Bunny::NotFound} will be raised. # # @return [AMQ::Protocol::Queue::DeclareOk] RabbitMQ response # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def queue_declare(name, opts = {}) raise_if_no_longer_open! @connection.send_frame(AMQ::Protocol::Queue::Declare.encode(@id, name, opts.fetch(:passive, false), opts.fetch(:durable, false), opts.fetch(:exclusive, false), opts.fetch(:auto_delete, false), false, opts[:arguments])) @last_queue_declare_ok = wait_on_continuations raise_if_continuation_resulted_in_a_channel_error! @last_queue_declare_ok end # Deletes a queue using queue.delete AMQP 0.9.1 method # # @param [String] name Queue name # @param [Hash] opts Options # # @option opts [Boolean] if_unused (false) Should this queue be deleted only if it has no consumers? # @option opts [Boolean] if_empty (false) Should this queue be deleted only if it has no messages? # # @return [AMQ::Protocol::Queue::DeleteOk] RabbitMQ response # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def queue_delete(name, opts = {}) raise_if_no_longer_open! @connection.send_frame(AMQ::Protocol::Queue::Delete.encode(@id, name, opts[:if_unused], opts[:if_empty], false)) Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_queue_delete_ok = wait_on_continuations end raise_if_continuation_resulted_in_a_channel_error! @last_queue_delete_ok end # Purges a queue (removes all messages from it) using queue.purge AMQP 0.9.1 method. # # @param [String] name Queue name # # @return [AMQ::Protocol::Queue::PurgeOk] RabbitMQ response # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def queue_purge(name, opts = {}) raise_if_no_longer_open! @connection.send_frame(AMQ::Protocol::Queue::Purge.encode(@id, name, false)) Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_queue_purge_ok = wait_on_continuations end raise_if_continuation_resulted_in_a_channel_error! @last_queue_purge_ok end # Binds a queue to an exchange using queue.bind AMQP 0.9.1 method # # @param [String] name Queue name # @param [String] exchange Exchange name # @param [Hash] opts Options # # @option opts [String] routing_key (nil) Routing key used for binding # @option opts [Hash] arguments ({}) Optional arguments # # @return [AMQ::Protocol::Queue::BindOk] RabbitMQ response # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @see http://rubybunny.info/articles/bindings.html Bindings guide # @api public def queue_bind(name, exchange, opts = {}) raise_if_no_longer_open! exchange_name = if exchange.respond_to?(:name) exchange.name else exchange end @connection.send_frame(AMQ::Protocol::Queue::Bind.encode(@id, name, exchange_name, (opts[:routing_key] || opts[:key]), false, opts[:arguments])) Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_queue_bind_ok = wait_on_continuations end raise_if_continuation_resulted_in_a_channel_error! @last_queue_bind_ok end # Unbinds a queue from an exchange using queue.unbind AMQP 0.9.1 method # # @param [String] name Queue name # @param [String] exchange Exchange name # @param [Hash] opts Options # # @option opts [String] routing_key (nil) Routing key used for binding # @option opts [Hash] arguments ({}) Optional arguments # # @return [AMQ::Protocol::Queue::UnbindOk] RabbitMQ response # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @see http://rubybunny.info/articles/bindings.html Bindings guide # @api public def queue_unbind(name, exchange, opts = {}) raise_if_no_longer_open! exchange_name = if exchange.respond_to?(:name) exchange.name else exchange end @connection.send_frame(AMQ::Protocol::Queue::Unbind.encode(@id, name, exchange_name, opts[:routing_key], opts[:arguments])) Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_queue_unbind_ok = wait_on_continuations end raise_if_continuation_resulted_in_a_channel_error! @last_queue_unbind_ok end # @endgroup # @group Exchange operations (exchange.*) # Declares a echange using echange.declare AMQP 0.9.1 method. # # @param [String] name Exchange name # @param [String,Symbol] type Exchange type, e.g. :fanout or :topic # @param [Hash] opts Exchange properties # # @option opts [Boolean] durable (false) Should information about this echange be persisted to disk so that it # can survive broker restarts? Typically set to true for long-lived exchanges. # @option opts [Boolean] auto_delete (false) Should this echange be deleted when it is no longer used? # @option opts [Boolean] passive (false) If true, exchange will be checked for existence. If it does not # exist, {Bunny::NotFound} will be raised. # # @return [AMQ::Protocol::Exchange::DeclareOk] RabbitMQ response # @see http://rubybunny.info/articles/echanges.html Exchanges and Publishing guide # @api public def exchange_declare(name, type, opts = {}) raise_if_no_longer_open! @connection.send_frame(AMQ::Protocol::Exchange::Declare.encode(@id, name, type.to_s, opts.fetch(:passive, false), opts.fetch(:durable, false), opts.fetch(:auto_delete, false), opts.fetch(:internal, false), false, # nowait opts[:arguments])) Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_exchange_declare_ok = wait_on_continuations end raise_if_continuation_resulted_in_a_channel_error! @last_exchange_declare_ok end # Deletes a exchange using exchange.delete AMQP 0.9.1 method # # @param [String] name Exchange name # @param [Hash] opts Options # # @option opts [Boolean] if_unused (false) Should this exchange be deleted only if it is no longer used # # @return [AMQ::Protocol::Exchange::DeleteOk] RabbitMQ response # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide # @api public def exchange_delete(name, opts = {}) raise_if_no_longer_open! @connection.send_frame(AMQ::Protocol::Exchange::Delete.encode(@id, name, opts[:if_unused], false)) Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_exchange_delete_ok = wait_on_continuations end raise_if_continuation_resulted_in_a_channel_error! @last_exchange_delete_ok end # Binds an exchange to another exchange using exchange.bind AMQP 0.9.1 extension # that RabbitMQ provides. # # @param [String] source Source exchange name # @param [String] destination Destination exchange name # @param [Hash] opts Options # # @option opts [String] routing_key (nil) Routing key used for binding # @option opts [Hash] arguments ({}) Optional arguments # # @return [AMQ::Protocol::Exchange::BindOk] RabbitMQ response # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide # @see http://rubybunny.info/articles/bindings.html Bindings guide # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide # @api public def exchange_bind(source, destination, opts = {}) raise_if_no_longer_open! source_name = if source.respond_to?(:name) source.name else source end destination_name = if destination.respond_to?(:name) destination.name else destination end @connection.send_frame(AMQ::Protocol::Exchange::Bind.encode(@id, destination_name, source_name, opts[:routing_key], false, opts[:arguments])) Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_exchange_bind_ok = wait_on_continuations end raise_if_continuation_resulted_in_a_channel_error! @last_exchange_bind_ok end # Unbinds an exchange from another exchange using exchange.unbind AMQP 0.9.1 extension # that RabbitMQ provides. # # @param [String] source Source exchange name # @param [String] destination Destination exchange name # @param [Hash] opts Options # # @option opts [String] routing_key (nil) Routing key used for binding # @option opts [Hash] arguments ({}) Optional arguments # # @return [AMQ::Protocol::Exchange::UnbindOk] RabbitMQ response # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide # @see http://rubybunny.info/articles/bindings.html Bindings guide # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide # @api public def exchange_unbind(source, destination, opts = {}) raise_if_no_longer_open! source_name = if source.respond_to?(:name) source.name else source end destination_name = if destination.respond_to?(:name) destination.name else destination end @connection.send_frame(AMQ::Protocol::Exchange::Unbind.encode(@id, destination_name, source_name, opts[:routing_key], false, opts[:arguments])) Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_exchange_unbind_ok = wait_on_continuations end raise_if_continuation_resulted_in_a_channel_error! @last_exchange_unbind_ok end # @endgroup # @group Flow control (channel.*) # Enables or disables message flow for the channel. When message flow is disabled, # no new messages will be delivered to consumers on this channel. This is typically # used by consumers that cannot keep up with the influx of messages. # # @note Recent (e.g. 2.8.x., 3.x) RabbitMQ will employ TCP/IP-level back pressure on publishers if it detects # that consumers do not keep up with them. # # @return [AMQ::Protocol::Channel::FlowOk] RabbitMQ response # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def channel_flow(active) raise_if_no_longer_open! @connection.send_frame(AMQ::Protocol::Channel::Flow.encode(@id, active)) Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_channel_flow_ok = wait_on_continuations end raise_if_continuation_resulted_in_a_channel_error! @last_channel_flow_ok end # @endgroup # @group Transactions (tx.*) # Puts the channel into transaction mode (starts a transaction) # @return [AMQ::Protocol::Tx::SelectOk] RabbitMQ response # @api public def tx_select raise_if_no_longer_open! @connection.send_frame(AMQ::Protocol::Tx::Select.encode(@id)) Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_tx_select_ok = wait_on_continuations end raise_if_continuation_resulted_in_a_channel_error! @tx_mode = true @last_tx_select_ok end # Commits current transaction # @return [AMQ::Protocol::Tx::CommitOk] RabbitMQ response # @api public def tx_commit raise_if_no_longer_open! @connection.send_frame(AMQ::Protocol::Tx::Commit.encode(@id)) Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_tx_commit_ok = wait_on_continuations end raise_if_continuation_resulted_in_a_channel_error! @last_tx_commit_ok end # Rolls back current transaction # @return [AMQ::Protocol::Tx::RollbackOk] RabbitMQ response # @api public def tx_rollback raise_if_no_longer_open! @connection.send_frame(AMQ::Protocol::Tx::Rollback.encode(@id)) Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_tx_rollback_ok = wait_on_continuations end raise_if_continuation_resulted_in_a_channel_error! @last_tx_rollback_ok end # @return [Boolean] true if this channel has transactions enabled def using_tx? !!@tx_mode end # @endgroup # @group Publisher Confirms (confirm.*) # @return [Boolean] true if this channel has Publisher Confirms enabled, false otherwise # @api public def using_publisher_confirmations? @next_publish_seq_no > 0 end alias using_publisher_confirms? using_publisher_confirmations? # Enables publisher confirms for the channel. # @return [AMQ::Protocol::Confirm::SelectOk] RabbitMQ response # @see #wait_for_confirms # @see #unconfirmed_set # @see #nacked_set # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide # @api public def confirm_select(callback=nil) raise_if_no_longer_open! if @next_publish_seq_no == 0 @confirms_continuations = new_continuation @unconfirmed_set = Set.new @nacked_set = Set.new @next_publish_seq_no = 1 @only_acks_received = true end @confirms_callback = callback @connection.send_frame(AMQ::Protocol::Confirm::Select.encode(@id, false)) Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout) do @last_confirm_select_ok = wait_on_continuations end raise_if_continuation_resulted_in_a_channel_error! @last_confirm_select_ok end # Blocks calling thread until confirms are received for all # currently unacknowledged published messages. Returns immediately # if there are no outstanding confirms. # # @return [Boolean] true if all messages were acknowledged positively since the last time this method was called, false otherwise # @see #confirm_select # @see #unconfirmed_set # @see #nacked_set # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide # @api public def wait_for_confirms wait_on_confirms_continuations read_and_reset_only_acks_received end # @endgroup # @group Misc # Synchronizes given block using this channel's mutex. # @api public def synchronize(&block) @publishing_mutex.synchronize(&block) end # Unique string supposed to be used as a consumer tag. # # @return [String] Unique string. # @api plugin def generate_consumer_tag(name = "bunny") "#{name}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}" end # @endgroup # # Error Handilng # # Defines a handler for errors that are not responses to a particular # operations (e.g. basic.ack, basic.reject, basic.nack). # # @api public def on_error(&block) @on_error = block end # Defines a handler for uncaught exceptions in consumers # (e.g. delivered message handlers). # # @api public def on_uncaught_exception(&block) @uncaught_exception_handler = block end # # Recovery # # @group Network Failure Recovery # Recovers basic.qos setting, exchanges, queues and consumers. Used by the Automatic Network Failure # Recovery feature. # # @api plugin def recover_from_network_failure @logger.debug { "Recovering channel #{@id} after network failure" } release_all_continuations recover_prefetch_setting recover_confirm_mode recover_tx_mode recover_exchanges # this includes recovering bindings recover_queues recover_consumers increment_recoveries_counter end # Recovers basic.qos setting. Used by the Automatic Network Failure # Recovery feature. # # @api plugin def recover_prefetch_setting basic_qos(@prefetch_count, @prefetch_global) if @prefetch_count end # Recovers publisher confirms mode. Used by the Automatic Network Failure # Recovery feature. # # @api plugin def recover_confirm_mode if using_publisher_confirmations? @unconfirmed_set.clear @delivery_tag_offset = @next_publish_seq_no - 1 confirm_select(@confirms_callback) end end # Recovers transaction mode. Used by the Automatic Network Failure # Recovery feature. # # @api plugin def recover_tx_mode tx_select if @tx_mode end # Recovers exchanges. Used by the Automatic Network Failure # Recovery feature. # # @api plugin def recover_exchanges @exchanges.values.dup.each do |x| x.recover_from_network_failure end end # Recovers queues and bindings. Used by the Automatic Network Failure # Recovery feature. # # @api plugin def recover_queues @queues.values.dup.each do |q| @logger.debug { "Recovering queue #{q.name}" } q.recover_from_network_failure end end # Recovers consumers. Used by the Automatic Network Failure # Recovery feature. # # @api plugin def recover_consumers unless @consumers.empty? @work_pool = ConsumerWorkPool.new(@work_pool.size, @work_pool.abort_on_exception) @work_pool.start end @consumers.values.dup.each do |c| c.recover_from_network_failure end end # @private def increment_recoveries_counter @recoveries_counter.increment end # @api public def recover_cancelled_consumers! @recover_cancelled_consumers = true end # @api public def recovers_cancelled_consumers? !!@recover_cancelled_consumers end # @endgroup # @return [String] Brief human-readable representation of the channel def to_s "#<#{self.class.name}:#{object_id} @id=#{self.number} @connection=#{@connection.to_s}>" end # # Implementation # # @private def register_consumer(consumer_tag, consumer) @consumer_mutex.synchronize do @consumers[consumer_tag] = consumer end end # @private def unregister_consumer(consumer_tag) @consumer_mutex.synchronize do @consumers.delete(consumer_tag) end end # @private def add_consumer(queue, consumer_tag, no_ack, exclusive, arguments, &block) @consumer_mutex.synchronize do c = Consumer.new(self, queue, consumer_tag, no_ack, exclusive, arguments) c.on_delivery(&block) if block @consumers[consumer_tag] = c end end # @private def handle_method(method) @logger.debug { "Channel#handle_frame on channel #{@id}: #{method.inspect}" } case method when AMQ::Protocol::Queue::DeclareOk then @continuations.push(method) when AMQ::Protocol::Queue::DeleteOk then @continuations.push(method) when AMQ::Protocol::Queue::PurgeOk then @continuations.push(method) when AMQ::Protocol::Queue::BindOk then @continuations.push(method) when AMQ::Protocol::Queue::UnbindOk then @continuations.push(method) when AMQ::Protocol::Exchange::BindOk then @continuations.push(method) when AMQ::Protocol::Exchange::UnbindOk then @continuations.push(method) when AMQ::Protocol::Exchange::DeclareOk then @continuations.push(method) when AMQ::Protocol::Exchange::DeleteOk then @continuations.push(method) when AMQ::Protocol::Basic::QosOk then @continuations.push(method) when AMQ::Protocol::Basic::RecoverOk then @continuations.push(method) when AMQ::Protocol::Channel::FlowOk then @continuations.push(method) when AMQ::Protocol::Basic::ConsumeOk then @continuations.push(method) when AMQ::Protocol::Basic::Cancel then if consumer = @consumers[method.consumer_tag] @work_pool.submit do begin if recovers_cancelled_consumers? consumer.handle_cancellation(method) @logger.info "Automatically recovering cancelled consumer #{consumer.consumer_tag} on queue #{consumer.queue_name}" consume_with(consumer) else @consumers.delete(method.consumer_tag) consumer.handle_cancellation(method) end rescue Exception => e @logger.error "Got exception when notifying consumer #{method.consumer_tag} about cancellation!" @uncaught_exception_handler.call(e, consumer) if @uncaught_exception_handler end end else @logger.warn "No consumer for tag #{method.consumer_tag} on channel #{@id}!" end when AMQ::Protocol::Basic::CancelOk then @continuations.push(method) unregister_consumer(method.consumer_tag) when AMQ::Protocol::Tx::SelectOk, AMQ::Protocol::Tx::CommitOk, AMQ::Protocol::Tx::RollbackOk then @continuations.push(method) when AMQ::Protocol::Tx::SelectOk then @continuations.push(method) when AMQ::Protocol::Confirm::SelectOk then @continuations.push(method) when AMQ::Protocol::Basic::Ack then handle_ack_or_nack(method.delivery_tag, method.multiple, false) when AMQ::Protocol::Basic::Nack then handle_ack_or_nack(method.delivery_tag, method.multiple, true) when AMQ::Protocol::Channel::Close then closed! @connection.send_frame(AMQ::Protocol::Channel::CloseOk.encode(@id)) # basic.ack, basic.reject, basic.nack. MK. if channel_level_exception_after_operation_that_has_no_response?(method) @on_error.call(self, method) if @on_error else @last_channel_error = instantiate_channel_level_exception(method) @continuations.push(method) end when AMQ::Protocol::Channel::CloseOk then @continuations.push(method) else raise "Do not know how to handle #{method.inspect} in Bunny::Channel#handle_method" end end # @private def channel_level_exception_after_operation_that_has_no_response?(method) method.reply_code == 406 && method.reply_text =~ /unknown delivery tag/ end # @private def handle_basic_get_ok(basic_get_ok, properties, content) basic_get_ok.delivery_tag = VersionedDeliveryTag.new(basic_get_ok.delivery_tag, @recoveries_counter.get) @basic_get_continuations.push([basic_get_ok, properties, content]) end # @private def handle_basic_get_empty(basic_get_empty) @basic_get_continuations.push([nil, nil, nil]) end # @private def handle_frameset(basic_deliver, properties, content) consumer = @consumers[basic_deliver.consumer_tag] if consumer @work_pool.submit do begin consumer.call(DeliveryInfo.new(basic_deliver, consumer, self), MessageProperties.new(properties), content) rescue StandardError => e @uncaught_exception_handler.call(e, consumer) if @uncaught_exception_handler end end else @logger.warn "No consumer for tag #{basic_deliver.consumer_tag} on channel #{@id}!" end end # @private def handle_basic_return(basic_return, properties, content) x = find_exchange(basic_return.exchange) if x x.handle_return(ReturnInfo.new(basic_return), MessageProperties.new(properties), content) else @logger.warn "Exchange #{basic_return.exchange} is not in channel #{@id}'s cache! Dropping returned message!" end end # @private def handle_ack_or_nack(delivery_tag_before_offset, multiple, nack) delivery_tag = delivery_tag_before_offset + @delivery_tag_offset confirmed_range_start = multiple ? @delivery_tag_offset + 1 : delivery_tag confirmed_range_end = delivery_tag confirmed_range = (confirmed_range_start..confirmed_range_end) @unconfirmed_set_mutex.synchronize do if nack @nacked_set.merge(@unconfirmed_set & confirmed_range) end @unconfirmed_set.subtract(confirmed_range) @only_acks_received = (@only_acks_received && !nack) @confirms_continuations.push(true) if @unconfirmed_set.empty? if @confirms_callback confirmed_range.each { |tag| @confirms_callback.call(tag, false, nack) } end end end # @private def wait_on_continuations if @connection.threaded t = Thread.current @threads_waiting_on_continuations << t begin @continuations.poll(@connection.continuation_timeout) ensure @threads_waiting_on_continuations.delete(t) end else connection.reader_loop.run_once until @continuations.length > 0 @continuations.pop end end # @private def wait_on_basic_get_continuations if @connection.threaded t = Thread.current @threads_waiting_on_basic_get_continuations << t begin @basic_get_continuations.poll(@connection.continuation_timeout) ensure @threads_waiting_on_basic_get_continuations.delete(t) end else connection.reader_loop.run_once until @basic_get_continuations.length > 0 @basic_get_continuations.pop end end # @private def wait_on_confirms_continuations raise_if_no_longer_open! if @connection.threaded t = Thread.current @threads_waiting_on_confirms_continuations << t begin while @unconfirmed_set_mutex.synchronize { !@unconfirmed_set.empty? } @confirms_continuations.poll(@connection.continuation_timeout) end ensure @threads_waiting_on_confirms_continuations.delete(t) end else unless @unconfirmed_set.empty? connection.reader_loop.run_once until @confirms_continuations.length > 0 @confirms_continuations.pop end end end # @private def read_and_reset_only_acks_received @unconfirmed_set_mutex.synchronize do result = @only_acks_received @only_acks_received = true result end end # Releases all continuations. Used by automatic network recovery. # @private def release_all_continuations @threads_waiting_on_confirms_continuations.each do |t| t.run end @threads_waiting_on_continuations.each do |t| t.run end @threads_waiting_on_basic_get_continuations.each do |t| t.run end self.reset_continuations end # Starts consumer work pool. Lazily called by #basic_consume to avoid creating new threads # that won't do any real work for channels that do not register consumers (e.g. only used for # publishing). MK. # @private def maybe_start_consumer_work_pool! if @work_pool && !@work_pool.running? @work_pool.start end end # @private def maybe_pause_consumer_work_pool! @work_pool.pause if @work_pool && @work_pool.running? end # @private def maybe_kill_consumer_work_pool! if @work_pool && @work_pool.running? @work_pool.kill end end # @private def read_next_frame(options = {}) @connection.read_next_frame(options = {}) end # @private def deregister_queue(queue) @queues.delete(queue.name) end # @private def deregister_queue_named(name) @queues.delete(name) end # @private def register_queue(queue) @queues[queue.name] = queue end # @private def find_queue(name) @queues[name] end # @private def deregister_exchange(exchange) @exchanges.delete(exchange.name) end # @private def register_exchange(exchange) @exchanges[exchange.name] = exchange end # @private def find_exchange(name) @exchanges[name] end protected # @private def closed! @status = :closed @work_pool.shutdown @connection.release_channel_id(@id) end # @private def instantiate_channel_level_exception(frame) case frame when AMQ::Protocol::Channel::Close then klass = case frame.reply_code when 403 then AccessRefused when 404 then NotFound when 405 then ResourceLocked when 406 then PreconditionFailed else ChannelLevelException end klass.new(frame.reply_text, self, frame) end end # @private def raise_if_continuation_resulted_in_a_channel_error! raise @last_channel_error if @last_channel_error end # @private def raise_if_no_longer_open! raise ChannelAlreadyClosed.new("cannot use a channel that was already closed! Channel id: #{@id}", self) if closed? end # @private def raise_if_channel_close!(method) if method && method.is_a?(AMQ::Protocol::Channel::Close) # basic.ack, basic.reject, basic.nack. MK. if channel_level_exception_after_operation_that_has_no_response?(method) @on_error.call(self, method) if @on_error else @last_channel_error = instantiate_channel_level_exception(method) raise @last_channel_error end end end # @private def reset_continuations @continuations = new_continuation @confirms_continuations = new_continuation @basic_get_continuations = new_continuation end if defined?(JRUBY_VERSION) # @private def new_continuation Concurrent::LinkedContinuationQueue.new end else # @private def new_continuation Concurrent::ContinuationQueue.new end end # if defined? # @private def guarding_against_stale_delivery_tags(tag, &block) case tag # if a fixnum was passed, execute unconditionally. MK. when Fixnum then block.call # versioned delivery tags should be checked to avoid # sending out stale (invalid) tags after channel was reopened # during network failure recovery. MK. when VersionedDeliveryTag then if !tag.stale?(@recoveries_counter.get) block.call end end end end # Channel end # Bunny bunny-2.6.1/lib/bunny/return_info.rb0000644000004100000410000000300513015255277017470 0ustar www-datawww-datamodule Bunny # Wraps AMQ::Protocol::Basic::Return to # provide access to the delivery properties as immutable hash as # well as methods. class ReturnInfo # # Behaviors # include Enumerable # # API # def initialize(basic_return) @basic_return = basic_return @hash = { :reply_code => basic_return.reply_code, :reply_text => basic_return.reply_text, :exchange => basic_return.exchange, :routing_key => basic_return.routing_key } end # Iterates over the returned delivery properties # @see Enumerable#each def each(*args, &block) @hash.each(*args, &block) end # Accesses returned delivery properties by key # @see Hash#[] def [](k) @hash[k] end # @return [Hash] Hash representation of this returned delivery info def to_hash @hash end # @private def to_s to_hash.to_s end # @private def inspect to_hash.inspect end # @return [Integer] Reply (status) code of the cause def reply_code @basic_return.reply_code end # @return [Integer] Reply (status) text of the cause, explaining why the message was returned def reply_text @basic_return.reply_text end # @return [String] Exchange the message was published to def exchange @basic_return.exchange end # @return [String] Routing key the message has def routing_key @basic_return.routing_key end end end bunny-2.6.1/lib/bunny/reader_loop.rb0000644000004100000410000000730113015255277017434 0ustar www-datawww-datarequire "thread" module Bunny # Network activity loop that reads and passes incoming AMQP 0.9.1 methods for # processing. They are dispatched further down the line in Bunny::Session and Bunny::Channel. # This loop uses a separate thread internally. # # This mimics the way RabbitMQ Java is designed quite closely. # @private class ReaderLoop def initialize(transport, session, session_thread) @transport = transport @session = session @session_thread = session_thread @logger = @session.logger @mutex = Mutex.new end def start @thread = Thread.new(&method(:run_loop)) end def resume start end def run_loop loop do begin break if @mutex.synchronize { @stopping || @stopped || @network_is_down } run_once rescue AMQ::Protocol::EmptyResponseError, IOError, SystemCallError, Timeout::Error => e break if terminate? || @session.closing? || @session.closed? log_exception(e) @network_is_down = true if @session.automatically_recover? @session.handle_network_failure(e) else @session_thread.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e)) end rescue ShutdownSignal => _ @mutex.synchronize { @stopping = true } break rescue Exception => e break if terminate? if !(@session.closing? || @session.closed?) log_exception(e) @network_is_down = true @session_thread.raise(Bunny::NetworkFailure.new("caught an unexpected exception in the network loop: #{e.message}", e)) end rescue Errno::EBADF => ebadf break if terminate? # ignored, happens when we loop after the transport has already been closed @mutex.synchronize { @stopping = true } end end @mutex.synchronize { @stopped = true } end def run_once frame = @transport.read_next_frame return if frame.is_a?(AMQ::Protocol::HeartbeatFrame) if !frame.final? || frame.method_class.has_content? header = @transport.read_next_frame content = '' if header.body_size > 0 loop do body_frame = @transport.read_next_frame content << body_frame.decode_payload break if content.bytesize >= header.body_size end end @session.handle_frameset(frame.channel, [frame.decode_payload, header.decode_payload, content]) else @session.handle_frame(frame.channel, frame.decode_payload) end end def stop @mutex.synchronize { @stopping = true } end def stopped? @mutex.synchronize { @stopped } end def stopping? @mutex.synchronize { @stopping } end def terminate_with(e) @mutex.synchronize { @stopping = true } self.raise(e) end def raise(e) @thread.raise(e) if @thread end def join @thread.join if @thread end def kill if @thread @thread.kill @thread.join end end protected def log_exception(e) if !(io_error?(e) && (@session.closing? || @session.closed?)) @logger.error "Exception in the reader loop: #{e.class.name}: #{e.message}" @logger.error "Backtrace: " e.backtrace.each do |line| @logger.error "\t#{line}" end end end def io_error?(e) [AMQ::Protocol::EmptyResponseError, IOError, SystemCallError].any? do |klazz| e.is_a?(klazz) end end def terminate? @mutex.synchronize { @stopping || @stopped } end end end bunny-2.6.1/lib/bunny/session.rb0000644000004100000410000013011113015255277016620 0ustar www-datawww-datarequire "socket" require "thread" require "monitor" require "bunny/transport" require "bunny/channel_id_allocator" require "bunny/heartbeat_sender" require "bunny/reader_loop" require "bunny/authentication/credentials_encoder" require "bunny/authentication/plain_mechanism_encoder" require "bunny/authentication/external_mechanism_encoder" if defined?(JRUBY_VERSION) require "bunny/concurrent/linked_continuation_queue" else require "bunny/concurrent/continuation_queue" end require "amq/protocol/client" require "amq/settings" module Bunny # Represents AMQP 0.9.1 connection (to a RabbitMQ node). # @see http://rubybunny.info/articles/connecting.html Connecting to RabbitMQ guide class Session # Default host used for connection DEFAULT_HOST = "127.0.0.1" # Default virtual host used for connection DEFAULT_VHOST = "/" # Default username used for connection DEFAULT_USER = "guest" # Default password used for connection DEFAULT_PASSWORD = "guest" # Default heartbeat interval, the same value as RabbitMQ 3.0 uses. DEFAULT_HEARTBEAT = :server # @private DEFAULT_FRAME_MAX = 131072 # 2^16 - 1, maximum representable signed 16 bit integer. # @private CHANNEL_MAX_LIMIT = 65535 DEFAULT_CHANNEL_MAX = CHANNEL_MAX_LIMIT # backwards compatibility # @private CONNECT_TIMEOUT = Transport::DEFAULT_CONNECTION_TIMEOUT # @private DEFAULT_CONTINUATION_TIMEOUT = 15000 # RabbitMQ client metadata DEFAULT_CLIENT_PROPERTIES = { :capabilities => { :publisher_confirms => true, :consumer_cancel_notify => true, :exchange_exchange_bindings => true, :"basic.nack" => true, :"connection.blocked" => true, # See http://www.rabbitmq.com/auth-notification.html :authentication_failure_close => true }, :product => "Bunny", :platform => ::RUBY_DESCRIPTION, :version => Bunny::VERSION, :information => "http://rubybunny.info", } # @private DEFAULT_LOCALE = "en_GB" # Default reconnection interval for TCP connection failures DEFAULT_NETWORK_RECOVERY_INTERVAL = 5.0 # # API # # @return [Bunny::Transport] attr_reader :transport attr_reader :status, :port, :heartbeat, :user, :pass, :vhost, :frame_max, :channel_max, :threaded attr_reader :server_capabilities, :server_properties, :server_authentication_mechanisms, :server_locales attr_reader :channel_id_allocator # Authentication mechanism, e.g. "PLAIN" or "EXTERNAL" # @return [String] attr_reader :mechanism # @return [Logger] attr_reader :logger # @return [Integer] Timeout for blocking protocol operations (queue.declare, queue.bind, etc), in milliseconds. Default is 15000. attr_reader :continuation_timeout attr_reader :network_recovery_interval # @param [String, Hash] connection_string_or_opts Connection string or a hash of connection options # @param [Hash] optz Extra options not related to connection # # @option connection_string_or_opts [String] :host ("127.0.0.1") Hostname or IP address to connect to # @option connection_string_or_opts [Array] :hosts (["127.0.0.1"]) list of hostname or IP addresses to select hostname from when connecting # @option connection_string_or_opts [Array] :addresses (["127.0.0.1:5672"]) list of addresses to select hostname and port from when connecting # @option connection_string_or_opts [Integer] :port (5672) Port RabbitMQ listens on # @option connection_string_or_opts [String] :username ("guest") Username # @option connection_string_or_opts [String] :password ("guest") Password # @option connection_string_or_opts [String] :vhost ("/") Virtual host to use # @option connection_string_or_opts [Integer] :heartbeat (600) Heartbeat interval. 0 means no heartbeat. # @option connection_string_or_opts [Integer] :network_recovery_interval (4) Recovery interval periodic network recovery will use. This includes initial pause after network failure. # @option connection_string_or_opts [Boolean] :tls (false) Should TLS/SSL be used? # @option connection_string_or_opts [String] :tls_cert (nil) Path to client TLS/SSL certificate file (.pem) # @option connection_string_or_opts [String] :tls_key (nil) Path to client TLS/SSL private key file (.pem) # @option connection_string_or_opts [Array] :tls_ca_certificates Array of paths to TLS/SSL CA files (.pem), by default detected from OpenSSL configuration # @option connection_string_or_opts [String] :verify_peer (true) Whether TLS peer verification should be performed # @option connection_string_or_opts [Keyword] :tls_version (negotiated) What TLS version should be used (:TLSv1, :TLSv1_1, or :TLSv1_2) # @option connection_string_or_opts [Integer] :continuation_timeout (15000) Timeout for client operations that expect a response (e.g. {Bunny::Queue#get}), in milliseconds. # @option connection_string_or_opts [Integer] :connection_timeout (5) Timeout in seconds for connecting to the server. # @option connection_string_or_opts [Proc] :hosts_shuffle_strategy A Proc that reorders a list of host strings, defaults to Array#shuffle # @option connection_string_or_opts [Logger] :logger The logger. If missing, one is created using :log_file and :log_level. # @option connection_string_or_opts [IO, String] :log_file The file or path to use when creating a logger. Defaults to STDOUT. # @option connection_string_or_opts [IO, String] :logfile DEPRECATED: use :log_file instead. The file or path to use when creating a logger. Defaults to STDOUT. # @option connection_string_or_opts [Integer] :log_level The log level to use when creating a logger. Defaults to LOGGER::WARN # @option connection_string_or_opts [Boolean] :automatically_recover (true) Should automatically recover from network failures? # @option connection_string_or_opts [Integer] :recovery_attempts (nil) Max number of recovery attempts, nil means forever, 0 means never # @option connection_string_or_opts [Boolean] :recover_from_connection_close (true) Recover from server-sent connection.close # # @option optz [String] :auth_mechanism ("PLAIN") Authentication mechanism, PLAIN or EXTERNAL # @option optz [String] :locale ("PLAIN") Locale RabbitMQ should use # # @see http://rubybunny.info/articles/connecting.html Connecting to RabbitMQ guide # @see http://rubybunny.info/articles/tls.html TLS/SSL guide # @api public def initialize(connection_string_or_opts = ENV['RABBITMQ_URL'], optz = Hash.new) opts = case (connection_string_or_opts) when nil then Hash.new when String then self.class.parse_uri(connection_string_or_opts) when Hash then connection_string_or_opts end.merge(optz) @default_hosts_shuffle_strategy = Proc.new { |hosts| hosts.shuffle } @opts = opts log_file = opts[:log_file] || opts[:logfile] || STDOUT log_level = opts[:log_level] || ENV["BUNNY_LOG_LEVEL"] || Logger::WARN # we might need to log a warning about ill-formatted IPv6 address but # progname includes hostname, so init like this first @logger = opts.fetch(:logger, init_default_logger_without_progname(log_file, log_level)) @addresses = self.addresses_from(opts) @address_index = 0 # re-init, see above @logger = opts.fetch(:logger, init_default_logger(log_file, log_level)) @user = self.username_from(opts) @pass = self.password_from(opts) @vhost = self.vhost_from(opts) @threaded = opts.fetch(:threaded, true) validate_connection_options(opts) # should automatic recovery from network failures be used? @automatically_recover = if opts[:automatically_recover].nil? && opts[:automatic_recovery].nil? true else opts[:automatically_recover] || opts[:automatic_recovery] end @recovery_attempts = opts[:recovery_attempts] @network_recovery_interval = opts.fetch(:network_recovery_interval, DEFAULT_NETWORK_RECOVERY_INTERVAL) @recover_from_connection_close = opts.fetch(:recover_from_connection_close, true) # in ms @continuation_timeout = opts.fetch(:continuation_timeout, DEFAULT_CONTINUATION_TIMEOUT) @status = :not_connected @blocked = false # these are negotiated with the broker during the connection tuning phase @client_frame_max = opts.fetch(:frame_max, DEFAULT_FRAME_MAX) @client_channel_max = normalize_client_channel_max(opts.fetch(:channel_max, DEFAULT_CHANNEL_MAX)) # will be-renegotiated during connection tuning steps. MK. @channel_max = @client_channel_max @client_heartbeat = self.heartbeat_from(opts) @client_properties = DEFAULT_CLIENT_PROPERTIES.merge(opts.fetch(:properties, {})) @mechanism = opts.fetch(:auth_mechanism, "PLAIN") @credentials_encoder = credentials_encoder_for(@mechanism) @locale = @opts.fetch(:locale, DEFAULT_LOCALE) @mutex_impl = @opts.fetch(:mutex_impl, Monitor) # mutex for the channel id => channel hash @channel_mutex = @mutex_impl.new # transport operations/continuations mutex. A workaround for # the non-reentrant Ruby mutexes. MK. @transport_mutex = @mutex_impl.new @status_mutex = @mutex_impl.new @address_index_mutex = @mutex_impl.new @channels = Hash.new @origin_thread = Thread.current self.reset_continuations self.initialize_transport end def validate_connection_options(options) if options[:hosts] && options[:addresses] raise ArgumentError, "Connection options can't contain hosts and addresses at the same time" end if (options[:host] || options[:hostname]) && (options[:hosts] || options[:addresses]) @logger.warn "The connection options contain both a host and an array of hosts, the single host is ignored." end end # @return [String] RabbitMQ hostname (or IP address) used def hostname; self.host; end # @return [String] Username used def username; self.user; end # @return [String] Password used def password; self.pass; end # @return [String] Virtual host used def virtual_host; self.vhost; end # @return [Integer] Heartbeat interval used def heartbeat_interval; self.heartbeat; end # @return [Boolean] true if this connection uses TLS (SSL) def uses_tls? @transport.uses_tls? end alias tls? uses_tls? # @return [Boolean] true if this connection uses TLS (SSL) def uses_ssl? @transport.uses_ssl? end alias ssl? uses_ssl? # @return [Boolean] true if this connection uses a separate thread for I/O activity def threaded? @threaded end def host @transport ? @transport.host : host_from_address(@addresses[@address_index]) end def port @transport ? @transport.port : port_from_address(@addresses[@address_index]) end def reset_address_index @address_index_mutex.synchronize { @address_index = 0 } end # @private attr_reader :mutex_impl # Provides a way to fine tune the socket used by connection. # Accepts a block that the socket will be yielded to. def configure_socket(&block) raise ArgumentError, "No block provided!" if block.nil? @transport.configure_socket(&block) end # @return [Integer] Client socket port def local_port @transport.local_address.ip_port end # Starts the connection process. # # @see http://rubybunny.info/articles/getting_started.html # @see http://rubybunny.info/articles/connecting.html # @api public def start return self if connected? @status_mutex.synchronize { @status = :connecting } # reset here for cases when automatic network recovery kicks in # when we were blocked. MK. @blocked = false self.reset_continuations begin begin # close existing transport if we have one, # to not leak sockets @transport.maybe_initialize_socket @transport.post_initialize_socket @transport.connect if @socket_configurator @transport.configure_socket(&@socket_configurator) end self.init_connection self.open_connection @reader_loop = nil self.start_reader_loop if threaded? rescue TCPConnectionFailed => e @logger.warn e.message self.initialize_transport @logger.warn "Will try to connect to the next endpoint in line: #{@transport.host}:#{@transport.port}" return self.start rescue @status_mutex.synchronize { @status = :not_connected } raise end rescue HostListDepleted self.reset_address_index @status_mutex.synchronize { @status = :not_connected } raise TCPConnectionFailedForAllHosts end self end # Socket operation write timeout used by this connection # @return [Integer] # @private def transport_write_timeout @transport.write_timeout end # Opens a new channel and returns it. This method will block the calling # thread until the response is received and the channel is guaranteed to be # opened (this operation is very fast and inexpensive). # # @return [Bunny::Channel] Newly opened channel def create_channel(n = nil, consumer_pool_size = 1, consumer_pool_abort_on_exception = false, consumer_pool_shutdown_timeout = 60) raise ArgumentError, "channel number 0 is reserved in the protocol and cannot be used" if 0 == n @channel_mutex.synchronize do if n && (ch = @channels[n]) ch else ch = Bunny::Channel.new(self, n, ConsumerWorkPool.new(consumer_pool_size || 1, consumer_pool_abort_on_exception, consumer_pool_shutdown_timeout)) ch.open ch end end end alias channel create_channel # Closes the connection. This involves closing all of its channels. def close @status_mutex.synchronize { @status = :closing } ignoring_io_errors do if @transport.open? close_all_channels self.close_connection(true) end clean_up_on_shutdown end @status_mutex.synchronize { @status = :closed } end alias stop close # Creates a temporary channel, yields it to the block given to this # method and closes it. # # @return [Bunny::Session] self def with_channel(n = nil) ch = create_channel(n) begin yield ch ensure ch.close if ch.open? end self end # @return [Boolean] true if this connection is still not fully open def connecting? status == :connecting end # @return [Boolean] true if this AMQP 0.9.1 connection is closing # @api private def closing? @status_mutex.synchronize { @status == :closing } end # @return [Boolean] true if this AMQP 0.9.1 connection is closed def closed? @status_mutex.synchronize { @status == :closed } end # @return [Boolean] true if this AMQP 0.9.1 connection is open def open? @status_mutex.synchronize do (status == :open || status == :connected || status == :connecting) && @transport.open? end end alias connected? open? # @return [Boolean] true if this connection has automatic recovery from network failure enabled def automatically_recover? @automatically_recover end # Defines a callback that will be executed when RabbitMQ blocks the connection # because it is running low on memory or disk space (as configured via config file # and/or rabbitmqctl). # # @yield [AMQ::Protocol::Connection::Blocked] connection.blocked method which provides a reason for blocking # # @api public def on_blocked(&block) @block_callback = block end # Defines a callback that will be executed when RabbitMQ unblocks the connection # that was previously blocked, e.g. because the memory or disk space alarm has cleared. # # @see #on_blocked # @api public def on_unblocked(&block) @unblock_callback = block end # @return [Boolean] true if the connection is currently blocked by RabbitMQ because it's running low on # RAM, disk space, or other resource; false otherwise # @see #on_blocked # @see #on_unblocked def blocked? @blocked end # Parses an amqp[s] URI into a hash that {Bunny::Session#initialize} accepts. # # @param [String] uri amqp or amqps URI to parse # @return [Hash] Parsed URI as a hash def self.parse_uri(uri) AMQ::Settings.parse_amqp_url(uri) end # Checks if a queue with given name exists. # # Implemented using queue.declare # with passive set to true and a one-off (short lived) channel # under the hood. # # @param [String] name Queue name # @return [Boolean] true if queue exists def queue_exists?(name) ch = create_channel begin ch.queue(name, :passive => true) true rescue Bunny::NotFound => _ false ensure ch.close if ch.open? end end # Checks if a exchange with given name exists. # # Implemented using exchange.declare # with passive set to true and a one-off (short lived) channel # under the hood. # # @param [String] name Exchange name # @return [Boolean] true if exchange exists def exchange_exists?(name) ch = create_channel begin ch.exchange(name, :passive => true) true rescue Bunny::NotFound => _ false ensure ch.close if ch.open? end end # # Implementation # # @private def open_channel(ch) n = ch.number self.register_channel(ch) @transport_mutex.synchronize do @transport.send_frame(AMQ::Protocol::Channel::Open.encode(n, AMQ::Protocol::EMPTY_STRING)) end @last_channel_open_ok = wait_on_continuations raise_if_continuation_resulted_in_a_connection_error! @last_channel_open_ok end # @private def close_channel(ch) @channel_mutex.synchronize do n = ch.number @transport.send_frame(AMQ::Protocol::Channel::Close.encode(n, 200, "Goodbye", 0, 0)) @last_channel_close_ok = wait_on_continuations raise_if_continuation_resulted_in_a_connection_error! self.unregister_channel(ch) self.release_channel_id(ch.id) @last_channel_close_ok end end # @private def close_all_channels @channels.reject {|n, ch| n == 0 || !ch.open? }.each do |_, ch| Bunny::Timeout.timeout(@transport.disconnect_timeout, ClientTimeout) { ch.close } end end # @private def close_connection(sync = true) if @transport.open? @transport.send_frame(AMQ::Protocol::Connection::Close.encode(200, "Goodbye", 0, 0)) if sync @last_connection_close_ok = wait_on_continuations end end shut_down_all_consumer_work_pools! maybe_shutdown_heartbeat_sender @status_mutex.synchronize { @status = :not_connected } end # Handles incoming frames and dispatches them. # # Channel methods (`channel.open-ok`, `channel.close-ok`) are # handled by the session itself. # Connection level errors result in exceptions being raised. # Deliveries and other methods are passed on to channels to dispatch. # # @private def handle_frame(ch_number, method) @logger.debug { "Session#handle_frame on #{ch_number}: #{method.inspect}" } case method when AMQ::Protocol::Channel::OpenOk then @continuations.push(method) when AMQ::Protocol::Channel::CloseOk then @continuations.push(method) when AMQ::Protocol::Connection::Close then if recover_from_connection_close? @logger.warn "Recovering from connection.close (#{method.reply_text})" clean_up_on_shutdown handle_network_failure(instantiate_connection_level_exception(method)) else clean_up_and_fail_on_connection_close!(method) end when AMQ::Protocol::Connection::CloseOk then @last_connection_close_ok = method begin @continuations.clear rescue StandardError => e @logger.error e.class.name @logger.error e.message @logger.error e.backtrace ensure @continuations.push(:__unblock__) end when AMQ::Protocol::Connection::Blocked then @blocked = true @block_callback.call(method) if @block_callback when AMQ::Protocol::Connection::Unblocked then @blocked = false @unblock_callback.call(method) if @unblock_callback when AMQ::Protocol::Channel::Close then begin ch = @channels[ch_number] ch.handle_method(method) ensure self.unregister_channel(ch) end when AMQ::Protocol::Basic::GetEmpty then @channels[ch_number].handle_basic_get_empty(method) else if ch = @channels[ch_number] ch.handle_method(method) else @logger.warn "Channel #{ch_number} is not open on this connection!" end end end # @private def raise_if_continuation_resulted_in_a_connection_error! raise @last_connection_error if @last_connection_error end # @private def handle_frameset(ch_number, frames) method = frames.first case method when AMQ::Protocol::Basic::GetOk then @channels[ch_number].handle_basic_get_ok(*frames) when AMQ::Protocol::Basic::GetEmpty then @channels[ch_number].handle_basic_get_empty(*frames) when AMQ::Protocol::Basic::Return then @channels[ch_number].handle_basic_return(*frames) else @channels[ch_number].handle_frameset(*frames) end end # @private def recover_from_connection_close? @recover_from_connection_close end # @private def handle_network_failure(exception) raise NetworkErrorWrapper.new(exception) unless @threaded @status_mutex.synchronize { @status = :disconnected } if !recovering_from_network_failure? begin @recovering_from_network_failure = true if recoverable_network_failure?(exception) @logger.warn "Recovering from a network failure..." @channels.each do |n, ch| ch.maybe_kill_consumer_work_pool! end @reader_loop.stop if @reader_loop maybe_shutdown_heartbeat_sender recover_from_network_failure else # TODO: investigate if we can be a bit smarter here. MK. end ensure @recovering_from_network_failure = false end end end # @private def recoverable_network_failure?(exception) # TODO: investigate if we can be a bit smarter here. MK. true end # @private def recovering_from_network_failure? @recovering_from_network_failure end # @private def recover_from_network_failure sleep @network_recovery_interval @logger.debug "About to start connection recovery..." self.initialize_transport @logger.warn "Retrying connection on next host in line: #{@transport.host}:#{@transport.port}" self.start if open? @recovering_from_network_failure = false recover_channels end rescue HostListDepleted reset_address_index retry rescue TCPConnectionFailedForAllHosts, TCPConnectionFailed, AMQ::Protocol::EmptyResponseError => e @logger.warn "TCP connection failed, reconnecting in #{@network_recovery_interval} seconds" sleep @network_recovery_interval if should_retry_recovery? @recovery_attempts -= 1 if @recovery_attempts retry if recoverable_network_failure?(e) end end # @private def should_retry_recovery? @recovery_attempts.nil? || @recovery_attempts > 1 end # @private def recover_channels @channels.each do |n, ch| ch.open ch.recover_from_network_failure end end # @private def instantiate_connection_level_exception(frame) case frame when AMQ::Protocol::Connection::Close then klass = case frame.reply_code when 320 then ConnectionForced when 501 then FrameError when 503 then CommandInvalid when 504 then ChannelError when 505 then UnexpectedFrame when 506 then ResourceError when 530 then NotAllowedError when 541 then InternalError else raise "Unknown reply code: #{frame.reply_code}, text: #{frame.reply_text}" end klass.new("Connection-level error: #{frame.reply_text}", self, frame) end end def clean_up_and_fail_on_connection_close!(method) @last_connection_error = instantiate_connection_level_exception(method) @continuations.push(method) clean_up_on_shutdown if threaded? @origin_thread.raise(@last_connection_error) else raise @last_connection_error end end def clean_up_on_shutdown begin shut_down_all_consumer_work_pools! maybe_shutdown_reader_loop maybe_shutdown_heartbeat_sender rescue ShutdownSignal => sse # no-op rescue Exception => e @logger.warn "Caught an exception when cleaning up after receiving connection.close: #{e.message}" ensure close_transport end end # @private def addresses_from(options) shuffle_strategy = options.fetch(:hosts_shuffle_strategy, @default_hosts_shuffle_strategy) addresses = options[:host] || options[:hostname] || options[:addresses] || options[:hosts] || ["#{DEFAULT_HOST}:#{port_from(options)}"] addresses = [addresses] unless addresses.is_a? Array addresses.map! do |address| host_with_port?(address) ? address : "#{address}:#{port_from(@opts)}" end shuffle_strategy.call addresses end # @private def port_from(options) fallback = if options[:tls] || options[:ssl] AMQ::Protocol::TLS_PORT else AMQ::Protocol::DEFAULT_PORT end options.fetch(:port, fallback) end # @private def host_with_port?(address) # we need to handle cases such as [2001:db8:85a3:8d3:1319:8a2e:370:7348]:5671 last_colon = address.rindex(":") last_closing_square_bracket = address.rindex("]") if last_closing_square_bracket.nil? address.include?(":") else last_closing_square_bracket < last_colon end end # @private def host_from_address(address) # we need to handle cases such as [2001:db8:85a3:8d3:1319:8a2e:370:7348]:5671 last_colon = address.rindex(":") last_closing_square_bracket = address.rindex("]") if last_closing_square_bracket.nil? parts = address.split(":") # this looks like an unquoted IPv6 address, so emit a warning if parts.size > 2 @logger.warn "Address #{address} looks like an unquoted IPv6 address. Make sure you quote IPv6 addresses like so: [2001:db8:85a3:8d3:1319:8a2e:370:7348]" end return parts[0] end if last_closing_square_bracket < last_colon # there is a port address[0, last_colon] elsif last_closing_square_bracket > last_colon address end end # @private def port_from_address(address) # we need to handle cases such as [2001:db8:85a3:8d3:1319:8a2e:370:7348]:5671 last_colon = address.rindex(":") last_closing_square_bracket = address.rindex("]") if last_closing_square_bracket.nil? parts = address.split(":") # this looks like an unquoted IPv6 address, so emit a warning if parts.size > 2 @logger.warn "Address #{address} looks like an unquoted IPv6 address. Make sure you quote IPv6 addresses like so: [2001:db8:85a3:8d3:1319:8a2e:370:7348]" end return parts[1].to_i end if last_closing_square_bracket < last_colon # there is a port address[(last_colon + 1)..-1].to_i end end # @private def vhost_from(options) options[:virtual_host] || options[:vhost] || DEFAULT_VHOST end # @private def username_from(options) options[:username] || options[:user] || DEFAULT_USER end # @private def password_from(options) options[:password] || options[:pass] || options[:pwd] || DEFAULT_PASSWORD end # @private def heartbeat_from(options) options[:heartbeat] || options[:heartbeat_interval] || options[:requested_heartbeat] || DEFAULT_HEARTBEAT end # @private def next_channel_id @channel_id_allocator.next_channel_id end # @private def release_channel_id(i) @channel_id_allocator.release_channel_id(i) end # @private def register_channel(ch) @channel_mutex.synchronize do @channels[ch.number] = ch end end # @private def unregister_channel(ch) @channel_mutex.synchronize do n = ch.number self.release_channel_id(n) @channels.delete(ch.number) end end # @private def start_reader_loop reader_loop.start end # @private def reader_loop @reader_loop ||= ReaderLoop.new(@transport, self, Thread.current) end # @private def maybe_shutdown_reader_loop if @reader_loop @reader_loop.stop if threaded? # this is the easiest way to wait until the loop # is guaranteed to have terminated @reader_loop.terminate_with(ShutdownSignal) # joining the thread here may take forever # on JRuby because sun.nio.ch.KQueueArrayWrapper#kevent0 is # a native method that cannot be (easily) interrupted. # So we use this ugly hack or else our test suite takes forever # to run on JRuby (a new connection is opened/closed per example). MK. if defined?(JRUBY_VERSION) sleep 0.075 else @reader_loop.join end else # single threaded mode, nothing to do. MK. end end @reader_loop = nil end # @private def close_transport begin @transport.close rescue StandardError => e @logger.error "Exception when closing transport:" @logger.error e.class.name @logger.error e.message @logger.error e.backtrace end end # @private def signal_activity! @heartbeat_sender.signal_activity! if @heartbeat_sender end # Sends frame to the peer, checking that connection is open. # Exposed primarily for Bunny::Channel # # @raise [ConnectionClosedError] # @private def send_frame(frame, signal_activity = true) if open? # @transport_mutex.synchronize do # @transport.write(frame.encode) # end @transport.write(frame.encode) signal_activity! if signal_activity else raise ConnectionClosedError.new(frame) end end # Sends frame to the peer, checking that connection is open. # Uses transport implementation that does not perform # timeout control. Exposed primarily for Bunny::Channel. # # @raise [ConnectionClosedError] # @private def send_frame_without_timeout(frame, signal_activity = true) if open? @transport.write_without_timeout(frame.encode) signal_activity! if signal_activity else raise ConnectionClosedError.new(frame) end end # Sends multiple frames, in one go. For thread safety this method takes a channel # object and synchronizes on it. # # @private def send_frameset(frames, channel) # some developers end up sharing channels between threads and when multiple # threads publish on the same channel aggressively, at some point frames will be # delivered out of order and broker will raise 505 UNEXPECTED_FRAME exception. # If we synchronize on the channel, however, this is both thread safe and pretty fine-grained # locking. Note that "single frame" methods do not need this kind of synchronization. MK. channel.synchronize do # see rabbitmq/rabbitmq-server#156 data = frames.reduce("") { |acc, frame| acc << frame.encode } @transport.write(data) signal_activity! end end # send_frameset(frames) # Sends multiple frames, one by one. For thread safety this method takes a channel # object and synchronizes on it. Uses transport implementation that does not perform # timeout control. # # @private def send_frameset_without_timeout(frames, channel) # some developers end up sharing channels between threads and when multiple # threads publish on the same channel aggressively, at some point frames will be # delivered out of order and broker will raise 505 UNEXPECTED_FRAME exception. # If we synchronize on the channel, however, this is both thread safe and pretty fine-grained # locking. Note that "single frame" methods do not need this kind of synchronization. MK. channel.synchronize do frames.each { |frame| self.send_frame_without_timeout(frame, false) } signal_activity! end end # send_frameset_without_timeout(frames) # @private def send_raw_without_timeout(data, channel) # some developers end up sharing channels between threads and when multiple # threads publish on the same channel aggressively, at some point frames will be # delivered out of order and broker will raise 505 UNEXPECTED_FRAME exception. # If we synchronize on the channel, however, this is both thread safe and pretty fine-grained # locking. Note that "single frame" methods do not need this kind of synchronization. MK. channel.synchronize do @transport.write(data) signal_activity! end end # send_frameset_without_timeout(frames) # @return [String] # @api public def to_s oid = ("0x%x" % (self.object_id << 1)) "#<#{self.class.name}:#{oid} #{@user}@#{host}:#{port}, vhost=#{@vhost}, addresses=[#{@addresses.join(',')}]>" end def inspect to_s end protected # @private def init_connection self.send_preamble connection_start = @transport.read_next_frame.decode_payload @server_properties = connection_start.server_properties @server_capabilities = @server_properties["capabilities"] @server_authentication_mechanisms = (connection_start.mechanisms || "").split(" ") @server_locales = Array(connection_start.locales) @status_mutex.synchronize { @status = :connected } end # @private def open_connection @transport.send_frame(AMQ::Protocol::Connection::StartOk.encode(@client_properties, @mechanism, self.encode_credentials(username, password), @locale)) @logger.debug "Sent connection.start-ok" frame = begin fr = @transport.read_next_frame while fr.is_a?(AMQ::Protocol::HeartbeatFrame) fr = @transport.read_next_frame end fr # frame timeout means the broker has closed the TCP connection, which it # does per 0.9.1 spec. rescue nil end if frame.nil? raise TCPConnectionFailed.new('An empty frame was received while opening the connection. In RabbitMQ <= 3.1 this could mean an authentication issue.') end response = frame.decode_payload if response.is_a?(AMQ::Protocol::Connection::Close) @state = :closed @logger.error "Authentication with RabbitMQ failed: #{response.reply_code} #{response.reply_text}" raise Bunny::AuthenticationFailureError.new(self.user, self.vhost, self.password.size) end connection_tune = response @frame_max = negotiate_value(@client_frame_max, connection_tune.frame_max) @channel_max = negotiate_value(@client_channel_max, connection_tune.channel_max) # this allows for disabled heartbeats. MK. @heartbeat = if heartbeat_disabled?(@client_heartbeat) 0 else negotiate_value(@client_heartbeat, connection_tune.heartbeat) end @logger.debug { "Heartbeat interval negotiation: client = #{@client_heartbeat}, server = #{connection_tune.heartbeat}, result = #{@heartbeat}" } @logger.info "Heartbeat interval used (in seconds): #{@heartbeat}" # We set the read_write_timeout to twice the heartbeat value # This allows us to miss a single heartbeat before we time out the socket. @transport.read_timeout = if heartbeat_disabled?(@client_heartbeat) Transport::DEFAULT_READ_TIMEOUT else # pad to account for edge cases. MK. @heartbeat * 2.2 end # if there are existing channels we've just recovered from # a network failure and need to fix the allocated set. See issue 205. MK. if @channels.empty? @channel_id_allocator = ChannelIdAllocator.new(@channel_max) end @transport.send_frame(AMQ::Protocol::Connection::TuneOk.encode(@channel_max, @frame_max, @heartbeat)) @logger.debug { "Sent connection.tune-ok with heartbeat interval = #{@heartbeat}, frame_max = #{@frame_max}, channel_max = #{@channel_max}" } @transport.send_frame(AMQ::Protocol::Connection::Open.encode(self.vhost)) @logger.debug { "Sent connection.open with vhost = #{self.vhost}" } frame2 = begin fr = @transport.read_next_frame while fr.is_a?(AMQ::Protocol::HeartbeatFrame) fr = @transport.read_next_frame end fr # frame timeout means the broker has closed the TCP connection, which it # does per 0.9.1 spec. rescue nil end if frame2.nil? raise TCPConnectionFailed.new('An empty frame was received while opening the connection. In RabbitMQ <= 3.1 this could mean an authentication issue.') end connection_open_ok = frame2.decode_payload @status_mutex.synchronize { @status = :open } if @heartbeat && @heartbeat > 0 initialize_heartbeat_sender end unless connection_open_ok.is_a?(AMQ::Protocol::Connection::OpenOk) if connection_open_ok.is_a?(AMQ::Protocol::Connection::Close) e = instantiate_connection_level_exception(connection_open_ok) begin shut_down_all_consumer_work_pools! maybe_shutdown_reader_loop rescue ShutdownSignal => sse # no-op rescue Exception => e @logger.warn "Caught an exception when cleaning up after receiving connection.close: #{e.message}" ensure close_transport end if threaded? @origin_thread.raise(e) else raise e end else raise "could not open connection: server did not respond with connection.open-ok but #{connection_open_ok.inspect} instead" end end end def heartbeat_disabled?(val) 0 == val || val.nil? end # @private def negotiate_value(client_value, server_value) return server_value if client_value == :server if client_value == 0 || server_value == 0 [client_value, server_value].max else [client_value, server_value].min end end # @private def initialize_heartbeat_sender maybe_shutdown_heartbeat_sender @logger.debug "Initializing heartbeat sender..." @heartbeat_sender = HeartbeatSender.new(@transport, @logger) @heartbeat_sender.start(@heartbeat) end # @private def maybe_shutdown_heartbeat_sender @heartbeat_sender.stop if @heartbeat_sender end # @private def initialize_transport if address = @addresses[ @address_index ] @address_index_mutex.synchronize { @address_index += 1 } @transport.close rescue nil # Let's make sure the previous transport socket is closed @transport = Transport.new(self, host_from_address(address), port_from_address(address), @opts.merge(:session_thread => @origin_thread) ) # Reset the cached progname for the logger only when no logger was provided @default_logger.progname = self.to_s @transport else raise HostListDepleted end end # @private def maybe_close_transport @transport.close if @transport end # Sends AMQ protocol header (also known as preamble). # @private def send_preamble @transport.write(AMQ::Protocol::PREAMBLE) @logger.debug "Sent protocol preamble" end # @private def encode_credentials(username, password) @credentials_encoder.encode_credentials(username, password) end # encode_credentials(username, password) # @private def credentials_encoder_for(mechanism) Authentication::CredentialsEncoder.for_session(self) end if defined?(JRUBY_VERSION) # @private def reset_continuations @continuations = Concurrent::LinkedContinuationQueue.new end else # @private def reset_continuations @continuations = Concurrent::ContinuationQueue.new end end # @private def wait_on_continuations unless @threaded reader_loop.run_once until @continuations.length > 0 end @continuations.poll(@continuation_timeout) end # @private def init_default_logger(logfile, level) @default_logger = begin lgr = ::Logger.new(logfile) lgr.level = normalize_log_level(level) lgr.progname = self.to_s lgr end end # @private def init_default_logger_without_progname(logfile, level) @default_logger = begin lgr = ::Logger.new(logfile) lgr.level = normalize_log_level(level) lgr end end # @private def normalize_log_level(level) case level when :debug, Logger::DEBUG, "debug" then Logger::DEBUG when :info, Logger::INFO, "info" then Logger::INFO when :warn, Logger::WARN, "warn" then Logger::WARN when :error, Logger::ERROR, "error" then Logger::ERROR when :fatal, Logger::FATAL, "fatal" then Logger::FATAL else Logger::WARN end end # @private def shut_down_all_consumer_work_pools! @channels.each do |_, ch| ch.maybe_kill_consumer_work_pool! end end def normalize_client_channel_max(n) return CHANNEL_MAX_LIMIT if n > CHANNEL_MAX_LIMIT case n when 0 then CHANNEL_MAX_LIMIT else n end end def ignoring_io_errors(&block) begin block.call rescue AMQ::Protocol::EmptyResponseError, IOError, SystemCallError, Bunny::NetworkFailure => _ # ignore end end end # Session # backwards compatibility Client = Session end bunny-2.6.1/lib/bunny/transport.rb0000644000004100000410000003345413015255277017205 0ustar www-datawww-datarequire "socket" require "thread" require "monitor" begin require "openssl" rescue LoadError => le $stderr.puts "Could not load OpenSSL" end require "bunny/exceptions" require "bunny/socket" module Bunny # @private class Transport # # API # # Default TCP connection timeout DEFAULT_CONNECTION_TIMEOUT = 30.0 DEFAULT_READ_TIMEOUT = 30.0 DEFAULT_WRITE_TIMEOUT = 30.0 attr_reader :session, :host, :port, :socket, :connect_timeout, :read_timeout, :write_timeout, :disconnect_timeout attr_reader :tls_context attr_writer :read_timeout def initialize(session, host, port, opts) @session = session @session_thread = opts[:session_thread] @host = host @port = port @opts = opts @logger = session.logger @tls_enabled = tls_enabled?(opts) @read_timeout = opts[:read_timeout] || DEFAULT_READ_TIMEOUT @read_timeout = nil if @read_timeout == 0 @write_timeout = opts[:socket_timeout] # Backwards compatability @write_timeout ||= opts[:write_timeout] || DEFAULT_WRITE_TIMEOUT @write_timeout = nil if @write_timeout == 0 @connect_timeout = self.timeout_from(opts) @connect_timeout = nil if @connect_timeout == 0 @disconnect_timeout = @write_timeout || @read_timeout || @connect_timeout @writes_mutex = @session.mutex_impl.new prepare_tls_context(opts) if @tls_enabled end def hostname @host end def local_address @socket.local_address end def uses_tls? @tls_enabled end alias tls? uses_tls? def uses_ssl? @tls_enabled end alias ssl? uses_ssl? def connect if uses_ssl? @socket.connect if uses_tls? && @verify_peer @socket.post_connection_check(host) end @status = :connected @socket else # no-op end end def connected? :not_connected == @status && open? end def configure_socket(&block) block.call(@socket) if @socket end def configure_tls_context(&block) block.call(@tls_context) if @tls_context end if defined?(JRUBY_VERSION) # Writes data to the socket. def write(data) return write_without_timeout(data) unless @write_timeout begin if open? @writes_mutex.synchronize do @socket.write(data) end end rescue SystemCallError, Timeout::Error, Bunny::ConnectionError, IOError => e @logger.error "Got an exception when sending data: #{e.message} (#{e.class.name})" close @status = :not_connected if @session.automatically_recover? @session.handle_network_failure(e) else @session_thread.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e)) end end end else # Writes data to the socket. If read/write timeout was specified the operation will return after that # amount of time has elapsed waiting for the socket. def write(data) return write_without_timeout(data) unless @write_timeout begin if open? @writes_mutex.synchronize do @socket.write_nonblock_fully(data, @write_timeout) end end rescue SystemCallError, Timeout::Error, Bunny::ConnectionError, IOError => e @logger.error "Got an exception when sending data: #{e.message} (#{e.class.name})" close @status = :not_connected if @session.automatically_recover? @session.handle_network_failure(e) else @session_thread.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e)) end end end end # Writes data to the socket without timeout checks def write_without_timeout(data) begin @writes_mutex.synchronize { @socket.write(data) } @socket.flush rescue SystemCallError, Bunny::ConnectionError, IOError => e close if @session.automatically_recover? @session.handle_network_failure(e) else @session_thread.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e)) end end end # Sends frame to the peer. # # @raise [ConnectionClosedError] # @private def send_frame(frame) if closed? @session.handle_network_failure(ConnectionClosedError.new(frame)) else write(frame.encode) end end # Sends frame to the peer without timeout control. # # @raise [ConnectionClosedError] # @private def send_frame_without_timeout(frame) if closed? @session.handle_network_failure(ConnectionClosedError.new(frame)) else write_without_timeout(frame.encode) end end def close(reason = nil) @socket.close if open? end def open? @socket && !@socket.closed? end def closed? !open? end def flush @socket.flush if @socket end def read_fully(count) begin @socket.read_fully(count, @read_timeout) rescue SystemCallError, Timeout::Error, Bunny::ConnectionError, IOError => e @logger.error "Got an exception when receiving data: #{e.message} (#{e.class.name})" close @status = :not_connected if @session.automatically_recover? raise else @session_thread.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e)) end end end def read_ready?(timeout = nil) io = IO.select([@socket].compact, nil, nil, timeout) io && io[0].include?(@socket) end # Exposed primarily for Bunny::Channel # @private def read_next_frame(opts = {}) header = read_fully(7) # TODO: network issues here will sometimes cause # the socket method return an empty string. We need to log # and handle this better. # type, channel, size = begin # AMQ::Protocol::Frame.decode_header(header) # rescue AMQ::Protocol::EmptyResponseError => e # puts "Got AMQ::Protocol::EmptyResponseError, header is #{header.inspect}" # end type, channel, size = AMQ::Protocol::Frame.decode_header(header) payload = read_fully(size) frame_end = read_fully(1) # 1) the size is miscalculated if payload.bytesize != size raise BadLengthError.new(size, payload.bytesize) end # 2) the size is OK, but the string doesn't end with FINAL_OCTET raise NoFinalOctetError.new if frame_end != AMQ::Protocol::Frame::FINAL_OCTET AMQ::Protocol::Frame.new(type, payload, channel) end def self.reacheable?(host, port, timeout) begin s = Bunny::SocketImpl.open(host, port, :connect_timeout => timeout) true rescue SocketError, Timeout::Error => e false ensure s.close if s end end def self.ping!(host, port, timeout) raise ConnectionTimeout.new("#{host}:#{port} is unreachable") if !reacheable?(host, port, timeout) end def initialize_socket begin @socket = Bunny::SocketImpl.open(@host, @port, :keepalive => @opts[:keepalive], :connect_timeout => @connect_timeout) rescue StandardError, ClientTimeout => e @status = :not_connected raise Bunny::TCPConnectionFailed.new(e, self.hostname, self.port) end @socket end def maybe_initialize_socket initialize_socket if !@socket || closed? end def post_initialize_socket @socket = if uses_tls? and !@socket.is_a?(Bunny::SSLSocketImpl) wrap_in_tls_socket(@socket) else @socket end end protected def tls_enabled?(opts) return opts[:tls] unless opts[:tls].nil? return opts[:ssl] unless opts[:ssl].nil? (opts[:port] == AMQ::Protocol::TLS_PORT) || false end def tls_certificate_path_from(opts) opts[:tls_cert] || opts[:ssl_cert] || opts[:tls_cert_path] || opts[:ssl_cert_path] || opts[:tls_certificate_path] || opts[:ssl_certificate_path] end def tls_key_path_from(opts) opts[:tls_key] || opts[:ssl_key] || opts[:tls_key_path] || opts[:ssl_key_path] end def tls_certificate_from(opts) begin read_client_certificate! rescue MissingTLSCertificateFile => e inline_client_certificate_from(opts) end end def tls_key_from(opts) begin read_client_key! rescue MissingTLSKeyFile => e inline_client_key_from(opts) end end def inline_client_certificate_from(opts) opts[:tls_certificate] || opts[:ssl_cert_string] || opts[:tls_cert] end def inline_client_key_from(opts) opts[:tls_key] || opts[:ssl_key_string] end def prepare_tls_context(opts) if (opts[:verify_ssl] || opts[:verify_peer]).nil? opts[:verify_peer] = true end # client cert/key paths @tls_certificate_path = tls_certificate_path_from(opts) @tls_key_path = tls_key_path_from(opts) # client cert/key @tls_certificate = tls_certificate_from(opts) @tls_key = tls_key_from(opts) @tls_certificate_store = opts[:tls_certificate_store] @tls_ca_certificates = opts.fetch(:tls_ca_certificates, default_tls_certificates) @verify_peer = (opts[:verify_ssl] || opts[:verify_peer]) @tls_context = initialize_tls_context(OpenSSL::SSL::SSLContext.new, opts) end def wrap_in_tls_socket(socket) raise ArgumentError, "cannot wrap nil into TLS socket, @tls_context is nil. This is a Bunny bug." unless socket raise "cannot wrap a socket into TLS socket, @tls_context is nil. This is a Bunny bug." unless @tls_context s = Bunny::SSLSocketImpl.new(socket, @tls_context) s.sync_close = true s end def check_local_certificate_path!(s) raise MissingTLSCertificateFile, "cannot read client TLS certificate from #{s}" unless File.file?(s) && File.readable?(s) end def check_local_key_path!(s) raise MissingTLSKeyFile, "cannot read client TLS private key from #{s}" unless File.file?(s) && File.readable?(s) end def read_client_certificate! if @tls_certificate_path check_local_certificate_path!(@tls_certificate_path) @tls_certificate = File.read(@tls_certificate_path) end end def read_client_key! if @tls_key_path check_local_key_path!(@tls_key_path) @tls_key = File.read(@tls_key_path) end end def initialize_tls_context(ctx, opts={}) ctx.cert = OpenSSL::X509::Certificate.new(@tls_certificate) if @tls_certificate ctx.key = OpenSSL::PKey::RSA.new(@tls_key) if @tls_key ctx.cert_store = if @tls_certificate_store @tls_certificate_store else initialize_tls_certificate_store(@tls_ca_certificates) end if !@tls_certificate @logger.warn <<-MSG Using TLS but no client certificate is provided! If RabbitMQ is configured to verify peer certificate, connection upgrade will fail! MSG end if @tls_certificate && !@tls_key @logger.warn "Using TLS but no client private key is provided!" end verify_mode = if @verify_peer OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT else OpenSSL::SSL::VERIFY_NONE end ctx.verify_mode = verify_mode if !@verify_peer @logger.warn <<-MSG Using TLS but peer hostname verification is disabled. This is convenient for local development but prone man-in-the-middle attacks. Please set :verify_peer => true in production! MSG end ssl_version = opts[:tls_protocol] || opts[:ssl_version] ctx.ssl_version = ssl_version if ssl_version ctx end def default_tls_certificates if defined?(JRUBY_VERSION) # see https://github.com/jruby/jruby/issues/1055. MK. [] else default_ca_file = ENV[OpenSSL::X509::DEFAULT_CERT_FILE_ENV] || OpenSSL::X509::DEFAULT_CERT_FILE default_ca_path = ENV[OpenSSL::X509::DEFAULT_CERT_DIR_ENV] || OpenSSL::X509::DEFAULT_CERT_DIR [ default_ca_file, File.join(default_ca_path, 'ca-certificates.crt'), # Ubuntu/Debian File.join(default_ca_path, 'ca-bundle.crt'), # Amazon Linux & Fedora/RHEL File.join(default_ca_path, 'ca-bundle.pem') # OpenSUSE ].uniq end end def initialize_tls_certificate_store(certs) cert_files = [] cert_inlines = [] certs.each do |cert| # if it starts with / then it's a file path that may or may not # exists (e.g. a default OpenSSL path). MK. if File.readable?(cert) || cert =~ /^\// cert_files.push(cert) else cert_inlines.push(cert) end end @logger.debug { "Using CA certificates at #{cert_files.join(', ')}" } @logger.debug { "Using #{cert_inlines.count} inline CA certificates" } if certs.empty? @logger.error "No CA certificates found, add one with :tls_ca_certificates" end OpenSSL::X509::Store.new.tap do |store| cert_files.select { |path| File.readable?(path) }. each { |path| store.add_file(path) } cert_inlines. each { |cert| store.add_cert(OpenSSL::X509::Certificate.new(cert)) } end end def timeout_from(options) options[:connect_timeout] || options[:connection_timeout] || options[:timeout] || DEFAULT_CONNECTION_TIMEOUT end end end bunny-2.6.1/lib/bunny/cruby/0000755000004100000410000000000013015255277015737 5ustar www-datawww-databunny-2.6.1/lib/bunny/cruby/ssl_socket.rb0000644000004100000410000000571713015255277020447 0ustar www-datawww-datarequire "socket" module Bunny begin require "openssl" # TLS-enabled TCP socket that implements convenience # methods found in Bunny::Socket. class SSLSocket < OpenSSL::SSL::SSLSocket READ_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable] WRITE_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable] # Reads given number of bytes with an optional timeout # # @param [Integer] count How many bytes to read # @param [Integer] timeout Timeout # # @return [String] Data read from the socket # @api public def read_fully(count, timeout = nil) return nil if @__bunny_socket_eof_flag__ value = '' begin loop do value << read_nonblock(count - value.bytesize) break if value.bytesize >= count end rescue EOFError => e @__bunny_socket_eof_flag__ = true rescue OpenSSL::SSL::SSLError => e if e.message == "read would block" if IO.select([self], nil, nil, timeout) retry else raise Timeout::Error, "IO timeout when reading #{count} bytes" end else raise e end rescue *READ_RETRY_EXCEPTION_CLASSES => e if IO.select([self], nil, nil, timeout) retry else raise Timeout::Error, "IO timeout when reading #{count} bytes" end end value end # Writes provided data using IO#write_nonblock, taking care of handling # of exceptions it raises when writing would fail (e.g. due to socket buffer # being full). # # IMPORTANT: this method will mutate (slice) the argument. Pass in duplicates # if this is not appropriate in your case. # # @param [String] data Data to write # @param [Integer] timeout Timeout # # @api public def write_nonblock_fully(data, timeout = nil) return nil if @__bunny_socket_eof_flag__ length = data.bytesize total_count = 0 count = 0 loop do begin count = self.write_nonblock(data) rescue OpenSSL::SSL::SSLError => e if e.message == "write would block" if IO.select([], [self], nil, timeout) retry else raise Timeout::Error, "IO timeout when writing to socket" end end raise e rescue *WRITE_RETRY_EXCEPTION_CLASSES if IO.select([], [self], nil, timeout) retry else raise Timeout::Error, "IO timeout when writing to socket" end end total_count += count return total_count if total_count >= length data = data.byteslice(count..-1) end end end rescue LoadError => le puts "Could not load OpenSSL" end end bunny-2.6.1/lib/bunny/cruby/socket.rb0000644000004100000410000000555013015255277017561 0ustar www-datawww-datarequire "socket" module Bunny # TCP socket extension that uses TCP_NODELAY and supports reading # fully. # # Heavily inspired by Dalli by Mike Perham. # @private module Socket attr_accessor :options READ_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable] WRITE_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable] def self.open(host, port, options = {}) socket = ::Socket.tcp(host, port, nil, nil, connect_timeout: options[:connect_timeout]) if ::Socket.constants.include?('TCP_NODELAY') || ::Socket.constants.include?(:TCP_NODELAY) socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true) end socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if options.fetch(:keepalive, true) socket.extend self socket.options = { :host => host, :port => port }.merge(options) socket rescue Errno::ETIMEDOUT raise ClientTimeout end # Reads given number of bytes with an optional timeout # # @param [Integer] count How many bytes to read # @param [Integer] timeout Timeout # # @return [String] Data read from the socket # @api public def read_fully(count, timeout = nil) return nil if @__bunny_socket_eof_flag__ value = '' begin loop do value << read_nonblock(count - value.bytesize) break if value.bytesize >= count end rescue EOFError # @eof will break Rubinius' TCPSocket implementation. MK. @__bunny_socket_eof_flag__ = true rescue *READ_RETRY_EXCEPTION_CLASSES if IO.select([self], nil, nil, timeout) retry else raise Timeout::Error, "IO timeout when reading #{count} bytes" end end value end # read_fully # Writes provided data using IO#write_nonblock, taking care of handling # of exceptions it raises when writing would fail (e.g. due to socket buffer # being full). # # IMPORTANT: this method will mutate (slice) the argument. Pass in duplicates # if this is not appropriate in your case. # # @param [String] data Data to write # @param [Integer] timeout Timeout # # @api public def write_nonblock_fully(data, timeout = nil) return nil if @__bunny_socket_eof_flag__ length = data.bytesize total_count = 0 count = 0 loop do begin count = self.write_nonblock(data) rescue *WRITE_RETRY_EXCEPTION_CLASSES if IO.select([], [self], nil, timeout) retry else raise Timeout::Error, "IO timeout when writing to socket" end end total_count += count return total_count if total_count >= length data = data.byteslice(count..-1) end end end end bunny-2.6.1/lib/bunny/consumer_work_pool.rb0000644000004100000410000000444613015255277021076 0ustar www-datawww-datarequire "thread" module Bunny # Thread pool that dispatches consumer deliveries. Not supposed to be shared between channels # or threads. # # Every channel its own consumer pool. # # @private class ConsumerWorkPool # # API # attr_reader :threads attr_reader :size attr_reader :abort_on_exception def initialize(size = 1, abort_on_exception = false, shutdown_timeout = 60) @size = size @abort_on_exception = abort_on_exception @shutdown_timeout = shutdown_timeout @shutdown_mutex = ::Mutex.new @shutdown_conditional = ::ConditionVariable.new @queue = ::Queue.new @paused = false end def submit(callable = nil, &block) @queue.push(callable || block) end def start @threads = [] @size.times do t = Thread.new(&method(:run_loop)) t.abort_on_exception = true if abort_on_exception @threads << t end @running = true end def running? @running end def backlog @queue.length end def busy? !@queue.empty? end def shutdown(wait_for_workers = false) was_running = running? @running = false @size.times do submit do |*args| throw :terminate end end return if !(wait_for_workers && @shutdown_timeout && was_running) @shutdown_mutex.synchronize do @shutdown_conditional.wait(@shutdown_mutex, @shutdown_timeout) end end def join(timeout = nil) (@threads || []).each { |t| t.join(timeout) } end def pause @running = false @paused = true end def resume @running = true @paused = false @threads.each { |t| t.run } end def kill @running = false (@threads || []).each { |t| t.kill } end protected def run_loop catch(:terminate) do loop do Thread.stop if @paused callable = @queue.pop begin callable.call rescue ::StandardError => e # TODO: use connection logger $stderr.puts e.class.name $stderr.puts e.message end end end @shutdown_mutex.synchronize do @shutdown_conditional.signal unless busy? end end end end bunny-2.6.1/lib/bunny/timeout.rb0000644000004100000410000000014313015255277016624 0ustar www-datawww-datamodule Bunny Timeout = ::Timeout # Backwards compatibility # @private Timer = Timeout end bunny-2.6.1/lib/bunny/heartbeat_sender.rb0000644000004100000410000000311513015255277020437 0ustar www-datawww-datarequire "thread" require "amq/protocol/client" require "amq/protocol/frame" module Bunny # Periodically sends heartbeats, keeping track of the last publishing activity. # # @private class HeartbeatSender # # API # def initialize(transport, logger) @transport = transport @logger = logger @mutex = Monitor.new @last_activity_time = Time.now end def start(period = 30) @mutex.synchronize do # calculate interval as half the given period plus # some compensation for Ruby's implementation inaccuracy # (we cannot get at the nanos level the Java client uses, and # our approach is simplistic). MK. @interval = [(period / 2) - 1, 0.4].max @thread = Thread.new(&method(:run)) end end def stop @mutex.synchronize { @thread.exit } end def signal_activity! @last_activity_time = Time.now end protected def run begin loop do self.beat sleep @interval end rescue IOError => ioe @logger.error "I/O error in the hearbeat sender: #{ioe.message}" stop rescue Exception => e @logger.error "Error in the hearbeat sender: #{e.message}" stop end end def beat now = Time.now if now > (@last_activity_time + @interval) @logger.debug { "Sending a heartbeat, last activity time: #{@last_activity_time}, interval (s): #{@interval}" } @transport.write_without_timeout(AMQ::Protocol::HeartbeatFrame.encode) end end end end bunny-2.6.1/lib/bunny/authentication/0000755000004100000410000000000013015255277017632 5ustar www-datawww-databunny-2.6.1/lib/bunny/authentication/external_mechanism_encoder.rb0000644000004100000410000000172013015255277025524 0ustar www-datawww-datarequire "bunny/authentication/credentials_encoder" module Bunny module Authentication # Encodes credentials using the EXTERNAL mechanism class ExternalMechanismEncoder < CredentialsEncoder auth_mechanism "EXTERNAL", "external" # Encodes a username and password for the EXTERNAL mechanism. Since # authentication is handled by an encapsulating protocol like SSL or # UNIX domain sockets, EXTERNAL doesn't pass along any username or # password information at all and this method always returns the # empty string. # # @param [String] username The username to encode. This parameter is # ignored. # @param [String] password The password to encode. This parameter is # ignored. # @return [String] The username and password, encoded for the # EXTERNAL mechanism. This is always the empty string. def encode_credentials(username, password) "" end end end end bunny-2.6.1/lib/bunny/authentication/plain_mechanism_encoder.rb0000644000004100000410000000075313015255277025012 0ustar www-datawww-datarequire "bunny/authentication/credentials_encoder" module Bunny module Authentication # Encodes credentials using the PLAIN mechanism class PlainMechanismEncoder < CredentialsEncoder auth_mechanism "PLAIN", "plain" # Encodes provided credentials as described in RFC 2595 # @api public # @see http://tools.ietf.org/rfc/rfc2595.txt RFC 2595 def encode_credentials(username, password) "\0#{username}\0#{password}" end end end end bunny-2.6.1/lib/bunny/authentication/credentials_encoder.rb0000644000004100000410000000260613015255277024157 0ustar www-datawww-datamodule Bunny # Contains credentials encoding implementations for various # authentication strategies. module Authentication # Base credentials encoder. Subclasses implement credentials encoding for # a particular authentication mechanism (PLAIN, EXTERNAL, etc). # # @api plugin class CredentialsEncoder # # API # # Session that uses this encoder # @return [Bunny::Session] attr_reader :session # Instantiates a new encoder for the authentication mechanism # used by the provided session. # # @return [Bunny::CredentialsEncoder] def self.for_session(session) registry[session.mechanism].new(session) end # @private def self.registry @@registry ||= Hash.new { raise NotImplementedError } end # Registers an encoder for authentication mechanism # @api plugin def self.auth_mechanism(*mechanisms) mechanisms.each do |m| registry[m] = self end end # Encodes provided credentials according to the specific authentication # mechanism # @return [String] Encoded credentials def encode_credentials(username, challenge) raise NotImplementedError.new("Subclasses must override this method") end protected def initialize(session) @session = session end end end end bunny-2.6.1/lib/bunny/concurrent/0000755000004100000410000000000013015255277016775 5ustar www-datawww-databunny-2.6.1/lib/bunny/concurrent/condition.rb0000644000004100000410000000303313015255277021307 0ustar www-datawww-datarequire "thread" require "monitor" module Bunny # @private module Concurrent # Akin to java.util.concurrent.Condition and intrinsic object monitors (Object#wait, Object#notify, Object#notifyAll) in Java: # threads can wait (block until notified) on a condition other threads notify them about. # Unlike the j.u.c. version, this one has a single waiting set. # # Conditions can optionally be annotated with a description string for ease of debugging. # @private class Condition attr_reader :waiting_threads, :description def initialize(description = nil) @mutex = Monitor.new @waiting_threads = [] @description = description end def wait @mutex.synchronize do t = Thread.current @waiting_threads.push(t) end Thread.stop end def notify @mutex.synchronize do t = @waiting_threads.shift begin t.run if t rescue ThreadError retry end end end def notify_all @mutex.synchronize do @waiting_threads.each do |t| t.run end @waiting_threads.clear end end def waiting_set_size @mutex.synchronize { @waiting_threads.size } end def any_threads_waiting? @mutex.synchronize { !@waiting_threads.empty? } end def none_threads_waiting? @mutex.synchronize { @waiting_threads.empty? } end end end end bunny-2.6.1/lib/bunny/concurrent/continuation_queue.rb0000644000004100000410000000176713015255277023253 0ustar www-datawww-datarequire "thread" module Bunny module Concurrent # Continuation queue implementation for MRI and Rubinius # # @private class ContinuationQueue def initialize @q = [] @lock = ::Mutex.new @cond = ::ConditionVariable.new end def push(item) @lock.synchronize do @q.push(item) @cond.signal end end alias << push def pop poll end def poll(timeout_in_ms = nil) timeout = timeout_in_ms ? timeout_in_ms / 1000.0 : nil @lock.synchronize do if @q.empty? @cond.wait(@lock, timeout) raise ::Timeout::Error if @q.empty? end item = @q.shift @cond.signal item end end def clear @lock.synchronize do @q.clear end end def empty? @q.empty? end def size @q.size end alias length size end end end bunny-2.6.1/lib/bunny/concurrent/linked_continuation_queue.rb0000644000004100000410000000314213015255277024566 0ustar www-datawww-dataif !defined?(JRUBY_VERSION) raise "Bunny::Concurrent::LinkedContinuationQueue can only be used on JRuby!" end require "java" java_import java.util.concurrent.LinkedBlockingQueue java_import java.util.concurrent.TimeUnit module Bunny module Concurrent # Continuation queue implementation for JRuby. # # On JRuby, we'd rather use reliable and heavily battle tested j.u.c. # primitives with well described semantics than informally specified, clumsy # and limited Ruby standard library parts. # # This is an implementation of the continuation queue on top of the linked blocking # queue in j.u.c. # # Compared to the Ruby standard library Queue, there is one limitation: you cannot # push a nil on the queue, it will fail with a null pointer exception. # @private class LinkedContinuationQueue def initialize(*args, &block) @q = LinkedBlockingQueue.new end def push(el, timeout_in_ms = nil) if timeout_in_ms @q.offer(el, timeout_in_ms, TimeUnit::MILLISECONDS) else @q.offer(el) end end alias << push def pop @q.take end def poll(timeout_in_ms = nil) if timeout_in_ms v = @q.poll(timeout_in_ms, TimeUnit::MILLISECONDS) raise ::Timeout::Error.new("operation did not finish in #{timeout_in_ms} ms") if v.nil? v else @q.poll end end def clear @q.clear end def method_missing(selector, *args, &block) @q.__send__(selector, *args, &block) end end end end bunny-2.6.1/lib/bunny/concurrent/synchronized_sorted_set.rb0000644000004100000410000000211313015255277024271 0ustar www-datawww-datarequire "set" require "thread" module Bunny module Concurrent # A SortedSet variation that synchronizes key mutation operations. # # @note This is NOT a complete SortedSet replacement. It only synchronizes operations needed by Bunny. # @api public class SynchronizedSortedSet < SortedSet def initialize(enum = nil) @mutex = Mutex.new super end def add(o) # avoid using Mutex#synchronize because of a Ruby 1.8.7-specific # bug that prevents super from being called from within a block. MK. @mutex.lock begin super ensure @mutex.unlock end end def delete(o) @mutex.lock begin super ensure @mutex.unlock end end def delete_if(&block) @mutex.lock begin super ensure @mutex.unlock end end def include?(o) @mutex.lock begin super ensure @mutex.unlock end end end end end bunny-2.6.1/lib/bunny/concurrent/atomic_fixnum.rb0000644000004100000410000000252013015255277022163 0ustar www-datawww-datarequire "set" require "thread" require "monitor" module Bunny module Concurrent # Minimalistic implementation of a synchronized fixnum value, # designed after (but not implementing the entire API of!) # # @note Designed to be intentionally minimalistic and only cover Bunny's needs. # # @api public class AtomicFixnum def initialize(n = 0) @n = n @mutex = Monitor.new end def get @mutex.synchronize do @n end end alias to_i get def set(n) @mutex.synchronize do @n = n end end def increment @mutex.synchronize do @n = @n + 1 end end alias inc increment alias increment_and_get increment def get_and_add(i) @mutex.synchronize do v = @n @n = @n + i v end end def get_and_increment @mutex.synchronize do v = @n @n = @n + 1 v end end def decrement @mutex.synchronize do @n = @n - 1 end end alias dec decrement alias decrement_and_get decrement def ==(m) @mutex.synchronize { @n == m } end def ===(v) @mutex.synchronize { @n === v } end end end end bunny-2.6.1/lib/bunny/socket.rb0000644000004100000410000000032513015255277016430 0ustar www-datawww-data# See #165. MK. if defined?(JRUBY_VERSION) require "bunny/jruby/socket" module Bunny SocketImpl = JRuby::Socket end else require "bunny/cruby/socket" module Bunny SocketImpl = Socket end end bunny-2.6.1/lib/bunny/exceptions.rb0000644000004100000410000001672613015255277017335 0ustar www-datawww-datamodule Bunny # Base class for all Bunny exceptions # @api public class Exception < ::StandardError end class HostListDepleted < Exception def initialize super("No more hosts to try in the supplied list of hosts") end end # Indicates a network failure. If automatic network # recovery mode is enabled, these will be typically handled # by the client itself. # # @api public class NetworkFailure < Exception attr_reader :cause def initialize(message, cause) super(message) @cause = cause end end # Base class for all channel level exceptions class ChannelLevelException < Exception attr_reader :channel, :channel_close def initialize(message, ch, channel_close) super(message) @channel = ch @channel_close = channel_close end end # Base class for all connection level exceptions class ConnectionLevelException < Exception attr_reader :connection, :connection_close def initialize(message, connection, connection_close) super(message) @connection = connection @connection_close = connection_close end end # Can indicate either a channel or connection-level issue class NotAllowedError < Exception attr_reader :connection, :connection_close def initialize(message, connection, connection_close = nil) super(message) @connection = connection @connection_close = connection_close end end # Raised when TCP connection to RabbitMQ fails because of an unresolved # hostname, connectivity problem, etc class TCPConnectionFailed < Exception attr_reader :hostname, :port def initialize(e, hostname=nil, port=nil) m = case e when String then e when ::Exception then e.message end if hostname && port super("Could not establish TCP connection to #{hostname}:#{port}: #{m}") else super(m) end end end class TCPConnectionFailedForAllHosts < TCPConnectionFailed def initialize super("Could not establish TCP connection to any of the configured hosts", nil, nil) end end # Raised when a frame is sent over an already closed connection class ConnectionClosedError < Exception def initialize(frame) if frame.respond_to?(:method_class) super("Trying to send frame through a closed connection. Frame is #{frame.inspect}, method class is #{frame.method_class}") else super("Trying to send frame through a closed connection. Frame is #{frame.inspect}") end end end class ShutdownSignal < Exception end # Raised when RabbitMQ closes TCP connection before finishing connection # sequence properly. This typically indicates an authentication issue. class PossibleAuthenticationFailureError < Exception # # API # attr_reader :username, :vhost def initialize(username, vhost, password_length) @username = username @vhost = vhost super("Authentication with RabbitMQ failed. Please check your connection settings. Username: #{username}, vhost: #{vhost}, password length: #{password_length}") end # initialize(settings) end # PossibleAuthenticationFailureError # Raised when RabbitMQ closes TCP connection due to an authentication failure. # Relies on RabbitMQ 3.2 Authentication Failure Notifications extension: # http://www.rabbitmq.com/auth-notification.html class AuthenticationFailureError < PossibleAuthenticationFailureError # # API # attr_reader :username, :vhost def initialize(username, vhost, password_length) @username = username @vhost = vhost super(username, vhost, password_length) end # initialize(settings) end # AuthenticationFailureError # backwards compatibility # @private ConnectionError = TCPConnectionFailed # @private ServerDownError = TCPConnectionFailed # Raised when a channel is closed forcefully using rabbitmqctl # or the management UI plugin class ForcedChannelCloseError < ChannelLevelException; end # Raised when a connection is closed forcefully using rabbitmqctl # or the management UI plugin class ForcedConnectionCloseError < ConnectionLevelException; end # @private class MessageError < ConnectionLevelException; end # @private class ProtocolError < ConnectionLevelException; end # Raised when RabbitMQ reports and internal error class InternalError < ConnectionLevelException; end # Raised when read or write I/O operations time out (but only if # a connection is configured to use them) class ClientTimeout < Timeout::Error; end # Raised on initial TCP connection timeout class ConnectionTimeout < Timeout::Error; end # Base exception class for data consistency and framing errors. class InconsistentDataError < Exception end # Raised by adapters when frame does not end with {final octet AMQ::Protocol::Frame::FINAL_OCTET}. # This suggest that there is a bug in adapter or AMQ broker implementation. # # @see https://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf AMQP 0.9.1 specification (Section 2.3) class NoFinalOctetError < InconsistentDataError def initialize super("Frame doesn't end with #{AMQ::Protocol::Frame::FINAL_OCTET} as it must, which means the size is miscalculated.") end end # Raised by adapters when actual frame payload size in bytes is not equal # to the size specified in that frame's header. # This suggest that there is a bug in adapter or AMQ broker implementation. # # @see https://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf AMQP 0.9.1 specification (Section 2.3) class BadLengthError < InconsistentDataError def initialize(expected_length, actual_length) super("Frame payload should be #{expected_length} long, but it's #{actual_length} long.") end end # Raised when a closed channel is used class ChannelAlreadyClosed < Exception attr_reader :channel def initialize(message, ch) super(message) @channel = ch end end # Raised when RabbitMQ responds with 406 PRECONDITION_FAILED class PreconditionFailed < ChannelLevelException end # Raised when RabbitMQ responds with 404 NOT_FOUND class NotFound < ChannelLevelException end # Raised when RabbitMQ responds with 405 RESOUCE_LOCKED class ResourceLocked < ChannelLevelException end # Raised when RabbitMQ responds with 403 ACCESS_REFUSED class AccessRefused < ChannelLevelException end # Raised when RabbitMQ responds with 504 CHANNEL_ERROR class ChannelError < ConnectionLevelException end # Raised when RabbitMQ responds with 503 COMMAND_INVALID class CommandInvalid < ConnectionLevelException end # Raised when RabbitMQ responds with 501 FRAME_ERROR class FrameError < ConnectionLevelException end # Raised when RabbitMQ responds with 505 UNEXPECTED_FRAME class UnexpectedFrame < ConnectionLevelException end # Raised when RabbitMQ responds with 506 RESOURCE_ERROR class ResourceError < ConnectionLevelException end # @private class NetworkErrorWrapper < Exception attr_reader :other def initialize(other) super(other.message) @other = other end end # Raised when RabbitMQ responds with 302 CONNECTION_FORCED # (which means the connection was closed using rabbitmqctl or # RabbitMQ management UI) class ConnectionForced < ConnectionLevelException end # @private class MissingTLSCertificateFile < Exception end # @private class MissingTLSKeyFile < Exception end end bunny-2.6.1/lib/bunny/queue.rb0000644000004100000410000003246513015255277016276 0ustar www-datawww-datarequire "bunny/get_response" module Bunny # Represents AMQP 0.9.1 queue. # # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide class Queue # # API # # @return [Bunny::Channel] Channel this queue uses attr_reader :channel # @return [String] Queue name attr_reader :name # @return [Hash] Options this queue was created with attr_reader :options # @param [Bunny::Channel] channel Channel this queue will use. # @param [String] name Queue name. Pass an empty string to make RabbitMQ generate a unique one. # @param [Hash] opts Queue properties # # @option opts [Boolean] :durable (false) Should this queue be durable? # @option opts [Boolean] :auto_delete (false) Should this queue be automatically deleted when the last consumer disconnects? # @option opts [Boolean] :exclusive (false) Should this queue be exclusive (only can be used by this connection, removed when the connection is closed)? # @option opts [Boolean] :arguments ({}) Additional optional arguments (typically used by RabbitMQ extensions and plugins) # # @see Bunny::Channel#queue # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide # @api public def initialize(channel, name = AMQ::Protocol::EMPTY_STRING, opts = {}) # old Bunny versions pass a connection here. In that case, # we just use default channel from it. MK. @channel = channel @name = name @options = self.class.add_default_options(name, opts) @durable = @options[:durable] @exclusive = @options[:exclusive] @server_named = @name.empty? @auto_delete = @options[:auto_delete] @arguments = @options[:arguments] @bindings = Array.new @default_consumer = nil declare! unless opts[:no_declare] @channel.register_queue(self) end # @return [Boolean] true if this queue was declared as durable (will survive broker restart). # @api public # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide def durable? @durable end # durable? # @return [Boolean] true if this queue was declared as exclusive (limited to just one consumer) # @api public # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide def exclusive? @exclusive end # exclusive? # @return [Boolean] true if this queue was declared as automatically deleted (deleted as soon as last consumer unbinds). # @api public # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide def auto_delete? @auto_delete end # auto_delete? # @return [Boolean] true if this queue was declared as server named. # @api public # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide def server_named? @server_named end # server_named? # @return [Hash] Additional optional arguments (typically used by RabbitMQ extensions and plugins) # @api public def arguments @arguments end def to_s oid = ("0x%x" % (self.object_id << 1)) "<#{self.class.name}:#{oid} @name=\"#{name}\" channel=#{@channel.to_s} @durable=#{@durable} @auto_delete=#{@auto_delete} @exclusive=#{@exclusive} @arguments=#{@arguments}>" end def inspect to_s end # Binds queue to an exchange # # @param [Bunny::Exchange,String] exchange Exchange to bind to # @param [Hash] opts Binding properties # # @option opts [String] :routing_key Routing key # @option opts [Hash] :arguments ({}) Additional optional binding arguments # # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @see http://rubybunny.info/articles/bindings.html Bindings guide # @api public def bind(exchange, opts = {}) @channel.queue_bind(@name, exchange, opts) exchange_name = if exchange.respond_to?(:name) exchange.name else exchange end # store bindings for automatic recovery. We need to be very careful to # not cause an infinite rebinding loop here when we recover. MK. binding = { :exchange => exchange_name, :routing_key => (opts[:routing_key] || opts[:key]), :arguments => opts[:arguments] } @bindings.push(binding) unless @bindings.include?(binding) self end # Unbinds queue from an exchange # # @param [Bunny::Exchange,String] exchange Exchange to unbind from # @param [Hash] opts Binding properties # # @option opts [String] :routing_key Routing key # @option opts [Hash] :arguments ({}) Additional optional binding arguments # # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @see http://rubybunny.info/articles/bindings.html Bindings guide # @api public def unbind(exchange, opts = {}) @channel.queue_unbind(@name, exchange, opts) exchange_name = if exchange.respond_to?(:name) exchange.name else exchange end @bindings.delete_if { |b| b[:exchange] == exchange_name && b[:routing_key] == (opts[:routing_key] || opts[:key]) && b[:arguments] == opts[:arguments] } self end # Adds a consumer to the queue (subscribes for message deliveries). # # @param [Hash] opts Options # # @option opts [Boolean] :ack (false) [DEPRECATED] Use :manual_ack instead # @option opts [Boolean] :manual_ack (false) Will this consumer use manual acknowledgements? # @option opts [Boolean] :exclusive (false) Should this consumer be exclusive for this queue? # @option opts [Boolean] :block (false) Should the call block calling thread? # @option opts [#call] :on_cancellation Block to execute when this consumer is cancelled remotely (e.g. via the RabbitMQ Management plugin) # @option opts [String] :consumer_tag Unique consumer identifier. It is usually recommended to let Bunny generate it for you. # @option opts [Hash] :arguments ({}) Additional (optional) arguments, typically used by RabbitMQ extensions # # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def subscribe(opts = { :consumer_tag => @channel.generate_consumer_tag, :manual_ack => false, :exclusive => false, :block => false, :on_cancellation => nil }, &block) unless opts[:ack].nil? warn "[DEPRECATION] `:ack` is deprecated. Please use `:manual_ack` instead." opts[:manual_ack] = opts[:ack] end ctag = opts.fetch(:consumer_tag, @channel.generate_consumer_tag) consumer = Consumer.new(@channel, self, ctag, !opts[:manual_ack], opts[:exclusive], opts[:arguments]) consumer.on_delivery(&block) consumer.on_cancellation(&opts[:on_cancellation]) if opts[:on_cancellation] @channel.basic_consume_with(consumer) if opts[:block] # joins current thread with the consumers pool, will block # the current thread for as long as the consumer pool is active @channel.work_pool.join end consumer end # Adds a consumer object to the queue (subscribes for message deliveries). # # @param [Bunny::Consumer] consumer a {Bunny::Consumer} subclass that implements consumer interface # @param [Hash] opts Options # # @option opts [Boolean] block (false) Should the call block calling thread? # # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def subscribe_with(consumer, opts = {:block => false}) @channel.basic_consume_with(consumer) @channel.work_pool.join if opts[:block] consumer end # @param [Hash] opts Options # # @option opts [Boolean] :ack (false) [DEPRECATED] Use :manual_ack instead # @option opts [Boolean] :manual_ack (false) Will the message be acknowledged manually? # # @return [Array] Triple of delivery info, message properties and message content. # If the queue is empty, all three will be nils. # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @see Bunny::Queue#subscribe # @api public # # @example # conn = Bunny.new # conn.start # # ch = conn.create_channel # q = ch.queue("test1") # x = ch.default_exchange # x.publish("Hello, everybody!", :routing_key => 'test1') # # delivery_info, properties, payload = q.pop # # puts "This is the message: " + payload + "\n\n" # conn.close def pop(opts = {:manual_ack => false}, &block) unless opts[:ack].nil? warn "[DEPRECATION] `:ack` is deprecated. Please use `:manual_ack` instead." opts[:manual_ack] = opts[:ack] end get_response, properties, content = @channel.basic_get(@name, opts) if block if properties di = GetResponse.new(get_response, @channel) mp = MessageProperties.new(properties) block.call(di, mp, content) else block.call(nil, nil, nil) end else if properties di = GetResponse.new(get_response, @channel) mp = MessageProperties.new(properties) [di, mp, content] else [nil, nil, nil] end end end alias get pop # Publishes a message to the queue via default exchange. Takes the same arguments # as {Bunny::Exchange#publish} # # @see Bunny::Exchange#publish # @see Bunny::Channel#default_exchange # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide def publish(payload, opts = {}) @channel.default_exchange.publish(payload, opts.merge(:routing_key => @name)) self end # Deletes the queue # # @param [Hash] opts Options # # @option opts [Boolean] if_unused (false) Should this queue be deleted only if it has no consumers? # @option opts [Boolean] if_empty (false) Should this queue be deleted only if it has no messages? # # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def delete(opts = {}) @channel.deregister_queue(self) @channel.queue_delete(@name, opts) end # Purges a queue (removes all messages from it) # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide # @api public def purge(opts = {}) @channel.queue_purge(@name, opts) self end # @return [Hash] A hash with information about the number of queue messages and consumers # @see #message_count # @see #consumer_count def status queue_declare_ok = @channel.queue_declare(@name, @options.merge(:passive => true)) {:message_count => queue_declare_ok.message_count, :consumer_count => queue_declare_ok.consumer_count} end # @return [Integer] How many messages the queue has ready (e.g. not delivered but not unacknowledged) def message_count s = self.status s[:message_count] end # @return [Integer] How many active consumers the queue has def consumer_count s = self.status s[:consumer_count] end # # Recovery # # @private def recover_from_network_failure if self.server_named? old_name = @name.dup @name = AMQ::Protocol::EMPTY_STRING @channel.deregister_queue_named(old_name) end # TODO: inject and use logger # puts "Recovering queue #{@name}" begin declare! unless @options[:no_declare] @channel.register_queue(self) rescue Exception => e # TODO: inject and use logger puts "Caught #{e.inspect} while redeclaring and registering #{@name}!" end recover_bindings end # @private def recover_bindings @bindings.each do |b| # TODO: inject and use logger # puts "Recovering binding #{b.inspect}" self.bind(b[:exchange], b) end end # # Implementation # # @private def declare! queue_declare_ok = @channel.queue_declare(@name, @options) @name = queue_declare_ok.queue end protected # @private def self.add_default_options(name, opts, block) { :queue => name, :nowait => (block.nil? && !name.empty?) }.merge(opts) end # @private def self.add_default_options(name, opts) # :nowait is always false for Bunny h = { :queue => name, :nowait => false }.merge(opts) if name.empty? { :passive => false, :durable => false, :exclusive => false, :auto_delete => false, :arguments => nil }.merge(h) else h end end end end bunny-2.6.1/lib/bunny.rb0000644000004100000410000000360013015255277015137 0ustar www-datawww-data# -*- encoding: utf-8; mode: ruby -*- require "timeout" require "bunny/version" require "amq/protocol/client" require "amq/protocol/extensions" require "bunny/framing" require "bunny/exceptions" require "bunny/socket" require "bunny/timeout" begin require "openssl" require "bunny/ssl_socket" rescue LoadError # no-op end require "logger" # Core entities: connection, channel, exchange, queue, consumer require "bunny/session" require "bunny/channel" require "bunny/exchange" require "bunny/queue" require "bunny/consumer" # Bunny is a RabbitMQ client that focuses on ease of use. # @see http://rubybunny.info module Bunny # AMQP protocol version Bunny implements PROTOCOL_VERSION = AMQ::Protocol::PROTOCOL_VERSION # # API # # @return [String] Bunny version def self.version VERSION end # @return [String] AMQP protocol version Bunny implements def self.protocol_version AMQ::Protocol::PROTOCOL_VERSION end # Instantiates a new connection. The actual network # connection is started with {Bunny::Session#start} # # @return [Bunny::Session] # @see Bunny::Session#start # @see http://rubybunny.info/articles/getting_started.html # @see http://rubybunny.info/articles/connecting.html # @api public def self.new(connection_string_or_opts = ENV['RABBITMQ_URL'], opts = {}, &block) if connection_string_or_opts.respond_to?(:keys) && opts.empty? opts = connection_string_or_opts end conn = Session.new(connection_string_or_opts, opts) @default_connection ||= conn conn end def self.run(connection_string_or_opts = ENV['RABBITMQ_URL'], opts = {}, &block) raise ArgumentError, 'Bunny#run requires a block' unless block client = Session.new(connection_string_or_opts, opts) begin client.start block.call(client) ensure client.stop end # backwards compatibility :run_ok end end bunny-2.6.1/repl0000755000004100000410000000005313015255277013600 0ustar www-datawww-data#!/bin/sh bundle exec irb -Ilib -r'bunny' bunny-2.6.1/profiling/0000755000004100000410000000000013015255277014703 5ustar www-datawww-databunny-2.6.1/profiling/basic_publish/0000755000004100000410000000000013015255277017512 5ustar www-datawww-databunny-2.6.1/profiling/basic_publish/with_4K_messages.rb0000644000004100000410000000113213015255277023234 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" require "ruby-prof" conn = Bunny.new conn.start puts puts "-" * 80 puts "Benchmarking on #{RUBY_DESCRIPTION}" n = 50_000 ch = conn.create_channel x = ch.default_exchange s = "z" * 4096 # warm up the JIT, etc puts "Doing a warmup run..." 16000.times { x.publish(s, :routing_key => "anything") } # give OS, the server and so on some time to catch # up sleep 2.0 result = RubyProf.profile do n.times { x.publish(s, :routing_key => "anything") } end printer = RubyProf::FlatPrinter.new(result) printer.print(STDOUT, {}) bunny-2.6.1/.gitignore0000644000004100000410000000032013015255277014675 0ustar www-datawww-data.DS_Store .*.swp *.class *.rbc *.gem /doc/ .yardoc .rvmrc Gemfile.lock .rbx/* .tags .tags_sorted_by_file .Apple* bin/* .bundle/* vendor/* playground/* *.org repl-* debug/* *.dump deploy.docs.sh .ruby-version bunny-2.6.1/bunny.gemspec0000755000004100000410000000170013015255277015413 0ustar www-datawww-data#!/usr/bin/env gem build # encoding: utf-8 require "base64" require File.expand_path("../lib/bunny/version", __FILE__) Gem::Specification.new do |s| s.name = "bunny" s.version = Bunny::VERSION.dup s.homepage = "http://rubybunny.info" s.summary = "Popular easy to use Ruby client for RabbitMQ" s.description = "Easy to use, feature complete Ruby client for RabbitMQ 3.3 and later versions." s.license = "MIT" s.required_ruby_version = Gem::Requirement.new(">= 2.0") # Sorted alphabetically. s.authors = [ "Chris Duncan", "Eric Lindvall", "Jakub Stastny aka botanicus", "Michael S. Klishin", "Stefan Kaes"] s.email = ["michael.s.klishin@gmail.com"] # Dependencies s.add_dependency "amq-protocol", ">= 2.0.1" # Files. s.has_rdoc = true s.extra_rdoc_files = ["README.md"] s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- spec/*`.split("\n") s.require_paths = ["lib"] end bunny-2.6.1/.yardopts0000644000004100000410000000016113015255277014556 0ustar www-datawww-data--no-private --protected --markup="markdown" lib/**/*.rb --main README.md --hide-tag todo - LICENSE ChangeLog.md bunny-2.6.1/CONTRIBUTING.md0000644000004100000410000000601613015255277015146 0ustar www-datawww-data## Pre-requisites The project uses Bundler for dependency management and requires RabbitMQ `3.5+` to be running locally with the `rabbitmq-management` and `rabbitmq_consistent_hash_exchange` plugins enabled. ### Running the Specs The specs require RabbitMQ to be running locally with a specific set of vhosts and users. RabbitMQ can be provisioned and started any way that's convenient to you as long as it has a suitable TLS keys configuration and management plugin enabled. Make sure you have a recent version of RabbitMQ (> `3.5.3`). You can also start a clean RabbitMQ server node on your machine specifically for the bunny specs. This can be done either by using a locally installed RabbitMQ server or by running a RabbitMQ server in a Docker container. #### Using a locally installed RabbitMQ server Run the following command from the base directory of the gem: ``` RABBITMQ_NODENAME=bunny RABBITMQ_CONFIG_FILE=./spec/config/rabbitmq RABBITMQ_ENABLED_PLUGINS_FILE=./spec/config/enabled_plugins rabbitmq-server ``` The specs use the RabbitMQ management plugin and require a TLS port to be available. The config files in the spec/config directory enable these. Next up you'll need to prepare your node for the specs (just once): ``` RABBITMQ_NODENAME=bunny ./bin/ci/before_build ``` And then run the core integration suite: ``` RABBITMQ_NODENAME=bunny CI=true rspec ``` #### Running a RabbitMQ server in a Docker container First off you have to [install Docker](https://docs.docker.com/engine/installation/) (>= 1.9). After Docker has been installed (and the `docker` command is available on your command line path), run ./bin/ci/start_rabbitmq The first time you do this, it will take some time, since it has to download everything it needs to build the Docker image. The RabbitMQ server will run in the foreground in the terminal where you started it. You can stop it by pressing CTRL+C. ### Running Test Suites Prior to running the tests, configure the RabbitMQ permissions by running `./bin/ci/before_build`. The script uses `rabbitmqctl` and `rabbitmq-plugins` to set up RabbitMQ in a way that Bunny test suites expect. Two environment variables, `RABBITMQCTL` and `RABBITMQ_PLUGINS`, are available to control what `rabbitmqctl` and `rabbitmq-plugins` commands will be used. By default they are taken from `PATH` and prefixed with `sudo`. Make sure you have those two installed and then run integration tests: bundle install rake integration It is possible to run all tests: bundle exec rspec -c It is possible to run only integration and regression tests but exclude unit and stress tests: CI=true bundle exec rspec -c spec/higher_level_api/ spec/lower_level_api spec/issues && bundle exec rspec -c spec/higher_level_api/integration/connection_recovery_spec.rb ## Pull Requests Then create a branch and make your changes on it. Once you are done with your changes and all tests pass, write a [good, detailed commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) submit a pull request on GitHub. bunny-2.6.1/LICENSE0000644000004100000410000000221013015255277013712 0ustar www-datawww-dataCopyright (c) 2009 – 2016 Chris Duncan, Jakub Stastny aka botanicus, Michael S. Klishin, Eric Lindvall, Stefan Kaes and contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. bunny-2.6.1/benchmarks/0000755000004100000410000000000013015255277015027 5ustar www-datawww-databunny-2.6.1/benchmarks/queue_declare.rb0000644000004100000410000000102213015255277020152 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" require "benchmark" conn = Bunny.new conn.start ch = conn.create_channel puts puts "-" * 80 puts "Benchmarking on #{RUBY_DESCRIPTION}" n = 4000 # warm up the JIT, etc puts "Doing a warmup run..." n.times { ch.queue("", :exclusive => true) } t = Benchmark.realtime do n.times { ch.queue("", :exclusive => true) } end r = (n.to_f/t.to_f) puts "queue.declare (server-named, exclusive = true) rate: #{(r / 1000).round(2)} KGHz" puts puts "-" * 80 bunny-2.6.1/benchmarks/queue_declare_and_bind.rb0000644000004100000410000000104413015255277021774 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" require "benchmark" conn = Bunny.new conn.start ch = conn.create_channel puts puts "-" * 80 puts "Benchmarking on #{RUBY_DESCRIPTION}" n = 4000 # warm up the JIT, etc puts "Doing a warmup run..." n.times { ch.queue("", :exclusive => true).bind("amq.fanout") } t = Benchmark.realtime do n.times { ch.queue("", :exclusive => true).bind("amq.fanout") } end r = (n.to_f/t.to_f) puts "queue.declare + queue.bind rate: #{(r / 1000).round(2)} KGHz" puts puts "-" * 80 bunny-2.6.1/benchmarks/mutex_and_monitor.rb0000644000004100000410000000130413015255277021105 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "set" require "thread" require "benchmark" require "monitor" puts puts "-" * 80 puts "Benchmarking on #{RUBY_DESCRIPTION}" n = 2_000_000 mx = Mutex.new mt = Monitor.new # warm up the JIT, etc puts "Doing a warmup run..." n.times do |i| mx.synchronize { 1 } mt.synchronize { 1 } end t1 = Benchmark.realtime do n.times do |i| mx.synchronize { 1 } end end r1 = (n.to_f/t1.to_f) t2 = Benchmark.realtime do n.times do |i| mt.synchronize { 1 } end end r2 = (n.to_f/t2.to_f) puts "Mutex#synchronize, rate: #{(r1 / 1000).round(2)} KGHz" puts "Monitor#synchronize, rate: #{(r2 / 1000).round(2)} KGHz" puts puts "-" * 80 bunny-2.6.1/benchmarks/write_vs_write_nonblock.rb0000644000004100000410000000163213015255277022317 0ustar www-datawww-data#!/usr/bin/env ruby require "benchmark" # This tests demonstrates throughput difference of # IO#write and IO#write_nonblock. Note that the two # may not be equivalent depending on your r, w = IO.pipe # buffer size b = 65536 read_loop = Thread.new do loop do begin r.read_nonblock(b) rescue Errno::EWOULDBLOCK, Errno::EAGAIN => e IO.select([r]) retry end end end n = 10_000 # 7 KB s = "a" * (7 * 1024) Benchmark.bm do |meter| meter.report("write:") do n.times { w.write(s.dup) } end meter.report("write + flush:") do n.times { w.write(s.dup); w.flush } end meter.report("write_nonblock:") do n.times do s2 = s.dup begin while !s2.empty? written = w.write_nonblock(s2) s2.slice!(0, written) end rescue Errno::EWOULDBLOCK, Errno::EAGAIN IO.select([], [w]) retry end end end end bunny-2.6.1/benchmarks/queue_declare_bind_and_delete.rb0000644000004100000410000000110113015255277023310 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" require "benchmark" conn = Bunny.new conn.start ch = conn.create_channel puts puts "-" * 80 puts "Benchmarking on #{RUBY_DESCRIPTION}" n = 4000 # warm up the JIT, etc puts "Doing a warmup run..." n.times { ch.queue("", :exclusive => true).bind("amq.fanout").delete } t = Benchmark.realtime do n.times { ch.queue("", :exclusive => true).bind("amq.fanout").delete } end r = (n.to_f/t.to_f) puts "queue.declare + queue.bind + queue.delete rate: #{(r / 1000).round(2)} KGHz" puts puts "-" * 80 bunny-2.6.1/benchmarks/synchronized_sorted_set.rb0000644000004100000410000000152013015255277022324 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "set" require "thread" require "benchmark" require "bunny/concurrent/synchronized_sorted_set" puts puts "-" * 80 puts "Benchmarking on #{RUBY_DESCRIPTION}" n = 2_000_000 s = SortedSet.new # warm up the JIT, etc puts "Doing a warmup run..." n.times do |i| s << 1 s << i s.delete i s << i end t1 = Benchmark.realtime do n.times do |i| s << 1 s << i s.delete i s << i s.length end end r1 = (n.to_f/t1.to_f) s2 = SynchronizedSortedSet.new t2 = Benchmark.realtime do n.times do |i| s2 << 1 s2 << i s2.delete i s2 << i s2.length end end r2 = (n.to_f/t2.to_f) puts "Mixed sorted set ops, rate: #{(r1 / 1000).round(2)} KGHz" puts "Mixed synchronized sorted set ops, rate: #{(r2 / 1000).round(2)} KGHz" puts puts "-" * 80 bunny-2.6.1/benchmarks/basic_publish/0000755000004100000410000000000013015255277017636 5ustar www-datawww-databunny-2.6.1/benchmarks/basic_publish/with_4K_messages.rb0000755000004100000410000000121313015255277023363 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" require "benchmark" conn = Bunny.new conn.start puts puts "-" * 80 puts "Benchmarking on #{RUBY_DESCRIPTION}" n = 50_000 ch = conn.create_channel x = ch.default_exchange s = "z" * 4096 # warm up the JIT, etc puts "Doing a warmup run..." 16000.times { x.publish(s, :routing_key => "anything") } # give OS, the server and so on some time to catch # up sleep 2.0 t = Benchmark.realtime do n.times { x.publish(s, :routing_key => "anything") } end r = (n.to_f/t.to_f) puts "Publishing rate with #{s.bytesize} bytes/msg: #{(r / 1000).round(2)} KGHz" puts puts "-" * 80 bunny-2.6.1/benchmarks/basic_publish/with_128K_messages.rb0000755000004100000410000000122313015255277023533 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" require "benchmark" conn = Bunny.new conn.start puts puts "-" * 80 puts "Benchmarking on #{RUBY_DESCRIPTION}" n = 50_000 ch = conn.create_channel x = ch.default_exchange s = "z" * (1024 * 128) # warm up the JIT, etc puts "Doing a warmup run..." 16000.times { x.publish(s, :routing_key => "anything") } # give OS, the server and so on some time to catch # up sleep 2.0 t = Benchmark.realtime do n.times { x.publish(s, :routing_key => "anything") } end r = (n.to_f/t.to_f) puts "Publishing rate with #{s.bytesize} bytes/msg: #{(r / 1000).round(2)} KGHz" puts puts "-" * 80 bunny-2.6.1/benchmarks/basic_publish/with_1k_messages.rb0000755000004100000410000000121313015255277023420 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" require "benchmark" conn = Bunny.new conn.start puts puts "-" * 80 puts "Benchmarking on #{RUBY_DESCRIPTION}" n = 50_000 ch = conn.create_channel x = ch.default_exchange s = "z" * 1024 # warm up the JIT, etc puts "Doing a warmup run..." 16000.times { x.publish(s, :routing_key => "anything") } # give OS, the server and so on some time to catch # up sleep 2.0 t = Benchmark.realtime do n.times { x.publish(s, :routing_key => "anything") } end r = (n.to_f/t.to_f) puts "Publishing rate with #{s.bytesize} bytes/msg: #{(r / 1000).round(2)} KGHz" puts puts "-" * 80 bunny-2.6.1/benchmarks/basic_publish/with_64K_messages.rb0000755000004100000410000000122213015255277023451 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" require "benchmark" conn = Bunny.new conn.start puts puts "-" * 80 puts "Benchmarking on #{RUBY_DESCRIPTION}" n = 50_000 ch = conn.create_channel x = ch.default_exchange s = "z" * (1024 * 64) # warm up the JIT, etc puts "Doing a warmup run..." 16000.times { x.publish(s, :routing_key => "anything") } # give OS, the server and so on some time to catch # up sleep 2.0 t = Benchmark.realtime do n.times { x.publish(s, :routing_key => "anything") } end r = (n.to_f/t.to_f) puts "Publishing rate with #{s.bytesize} bytes/msg: #{(r / 1000).round(2)} KGHz" puts puts "-" * 80 bunny-2.6.1/benchmarks/channel_open.rb0000644000004100000410000000067513015255277020015 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: utf-8 require "rubygems" require "bunny" require "benchmark" conn = Bunny.new conn.start puts puts "-" * 80 puts "Benchmarking on #{RUBY_DESCRIPTION}" n = 500 # warm up the JIT, etc puts "Doing a warmup run..." 1000.times { conn.create_channel } t = Benchmark.realtime do n.times { conn.create_channel } end r = (n.to_f/t.to_f) puts "channel.open rate: #{(r / 1000).round(2)} KGHz" puts puts "-" * 80 bunny-2.6.1/README.md0000644000004100000410000001402413015255277014172 0ustar www-datawww-data# Bunny, a Ruby RabbitMQ Client Bunny is a RabbitMQ client that focuses on ease of use. It is feature complete, supports all recent RabbitMQ features and does not have any heavyweight dependencies. ## I Know What RabbitMQ and Bunny are, How Do I Get Started? [Right here](http://rubybunny.info/articles/getting_started.html)! ## What is Bunny Good For? One can use Bunny to make Ruby applications interoperate with other applications (both built in Ruby and not). Complexity and size may vary from simple work queues to complex multi-stage data processing workflows that involve many applications built with all kinds of technologies. Specific examples: * Events collectors, metrics & analytics applications can aggregate events produced by various applications (Web and not) in the company network. * A Web application may route messages to a Java app that works with SMS delivery gateways. * MMO games can use flexible routing RabbitMQ provides to propagate event notifications to players and locations. * Price updates from public markets or other sources can be distributed between interested parties, from trading systems to points of sale in a specific geographic region. * Content aggregators may update full-text search and geospatial search indexes by delegating actual indexing work to other applications over RabbitMQ. * Companies may provide streaming/push APIs to their customers, partners or just general public. * Continuous integration systems can distribute builds between multiple machines with various hardware and software configurations using advanced routing features of RabbitMQ. * An application that watches updates from a real-time stream (be it markets data or Twitter stream) can propagate updates to interested parties, including Web applications that display that information in the real time. ## Supported Ruby Versions Modern Bunny versions support * CRuby 2.0 through 2.3 Bunny works sufficiently well on JRuby but there are known JRuby bugs in versions prior to JRuby 9000 that cause high CPU burn. JRuby users should use [March Hare](http://rubymarchhare.info). Bunny `1.7.x` was the last version to support CRuby 1.9.3 and 1.8.7 ## Supported RabbitMQ Versions Bunny `1.5.0` and later versions only support RabbitMQ `3.3+`. Bunny `1.4.x` and earlier supports RabbitMQ 2.x and 3.x. ## Project Maturity Bunny is a mature library (started in early 2009) with a stable public API. ## Installation & Bundler Dependency ### Most Recent Release [![Gem Version](https://badge.fury.io/rb/bunny.png)](http://badge.fury.io/rb/bunny) ### With Rubygems To install Bunny with RubyGems: ``` gem install bunny ``` ### Bundler Dependency To use Bunny in a project managed with Bundler: ``` ruby gem "bunny", ">= 2.5.1" ``` ## Quick Start Below is a small snippet that demonstrates how to publish and synchronously consume ("pull API") messages with Bunny. For a 15 minute tutorial using more practical examples, see [Getting Started with RabbitMQ and Ruby using Bunny](http://rubybunny.info/articles/getting_started.html). ``` ruby require "bunny" # Start a communication session with RabbitMQ conn = Bunny.new conn.start # open a channel ch = conn.create_channel # declare a queue q = ch.queue("test1") # publish a message to the default exchange which then gets routed to this queue q.publish("Hello, everybody!") # fetch a message from the queue delivery_info, metadata, payload = q.pop puts "This is the message: #{payload}" # close the connection conn.stop ``` ## Documentation ### Getting Started For a 15 minute tutorial using more practical examples, see [Getting Started with RabbitMQ and Ruby using Bunny](http://rubybunny.info/articles/getting_started.html). ### Guides Other documentation guides are available at [rubybunny.info](http://rubybunny.info): * [Queues and Consumers](http://rubybunny.info/articles/queues.html) * [Exchanges and Publishers](http://rubybunny.info/articles/exchanges.html) * [AMQP 0.9.1 Model Explained](http://www.rabbitmq.com/tutorials/amqp-concepts.html) * [Connecting to RabbitMQ](http://rubybunny.info/articles/connecting.html) * [Error Handling and Recovery](http://rubybunny.info/articles/error_handling.html) * [TLS/SSL Support](http://rubybunny.info/articles/tls.html) * [Bindings](http://rubybunny.info/articles/bindings.html) * [Using RabbitMQ Extensions with Bunny](http://rubybunny.info/articles/extensions.html) * [Durability and Related Matters](http://rubybunny.info/articles/durability.html) ### API Reference [Bunny API Reference](http://reference.rubybunny.info/). ## Community and Getting Help ### Mailing List [Bunny has a mailing list](http://groups.google.com/group/ruby-amqp). We encourage you to also join the [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users) mailing list. Feel free to ask any questions that you may have. ## Continuous Integration [![Build Status](https://travis-ci.org/ruby-amqp/bunny.png)](https://travis-ci.org/ruby-amqp/bunny/) ### News & Announcements on Twitter To subscribe for announcements of releases, important changes and so on, please follow [@rubyamqp](https://twitter.com/#!/rubyamqp) on Twitter. More detailed announcements can be found in the blogs * [RabbitMQ Ruby clients blog](http://blog.rubyrabbitmq.info) ### Reporting Issues If you find a bug, poor default, missing feature or find any part of the API inconvenient, please [file an issue](http://github.com/ruby-amqp/bunny/issues) on GitHub. When filing an issue, please specify which Bunny and RabbitMQ versions you are using, provide recent RabbitMQ log file contents if possible, and try to explain what behavior you expected and why. Bonus points for contributing failing test cases. ## Other Ruby RabbitMQ Clients The other widely used Ruby RabbitMQ client is [March Hare](http://rubymarchhare.info) (JRuby-only). It's a mature library that require RabbitMQ 3.3.x or later. ## Contributing See [CONTRIBUTING.md](./CONTRIBUTING.md) for more information about running various test suites. ## License Released under the MIT license.